From 469bd744a02e5dcdbcbc338af178d410dd909616 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 8 Dec 2023 15:19:59 +0100 Subject: [PATCH 001/441] ported the full output optimization code from VSCode (#13137) * ported the full output optimization code from VSCode Signed-off-by: Jonah Iden * added commit id to licence header Signed-off-by: Jonah Iden * added file to comment Signed-off-by: Jonah Iden * fixed multiple outputs now updating correctly Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../src/browser/notebook-output-utils.ts | 119 ++++++++++++++++++ .../view-model/notebook-cell-output-model.ts | 11 +- .../renderers/cell-output-webview.tsx | 2 +- 3 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 packages/notebook/src/browser/notebook-output-utils.ts diff --git a/packages/notebook/src/browser/notebook-output-utils.ts b/packages/notebook/src/browser/notebook-output-utils.ts new file mode 100644 index 0000000000000..1ef5082502248 --- /dev/null +++ b/packages/notebook/src/browser/notebook-output-utils.ts @@ -0,0 +1,119 @@ +// ***************************************************************************** +// Copyright (C) 2023 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * Copied from commit 18b2c92451b076943e5b508380e0eba66ba7d934 from file src\vs\workbench\contrib\notebook\common\notebookCommon.ts + *--------------------------------------------------------------------------------------------*/ + +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; + +const textDecoder = new TextDecoder(); + +/** + * Given a stream of individual stdout outputs, this function will return the compressed lines, escaping some of the common terminal escape codes. + * E.g. some terminal escape codes would result in the previous line getting cleared, such if we had 3 lines and + * last line contained such a code, then the result string would be just the first two lines. + * @returns a single VSBuffer with the concatenated and compressed data, and whether any compression was done. + */ +export function compressOutputItemStreams(outputs: Uint8Array[]): { data: BinaryBuffer, didCompression: boolean } { + const buffers: Uint8Array[] = []; + let startAppending = false; + + // Pick the first set of outputs with the same mime type. + for (const output of outputs) { + if ((buffers.length === 0 || startAppending)) { + buffers.push(output); + startAppending = true; + } + } + + let didCompression = compressStreamBuffer(buffers); + const concatenated = BinaryBuffer.concat(buffers.map(buffer => BinaryBuffer.wrap(buffer))); + const data = formatStreamText(concatenated); + didCompression = didCompression || data.byteLength !== concatenated.byteLength; + return { data, didCompression }; +} + +export const MOVE_CURSOR_1_LINE_COMMAND = `${String.fromCharCode(27)}[A`; +const MOVE_CURSOR_1_LINE_COMMAND_BYTES = MOVE_CURSOR_1_LINE_COMMAND.split('').map(c => c.charCodeAt(0)); +const LINE_FEED = 10; +function compressStreamBuffer(streams: Uint8Array[]): boolean { + let didCompress = false; + streams.forEach((stream, index) => { + if (index === 0 || stream.length < MOVE_CURSOR_1_LINE_COMMAND.length) { + return; + } + + const previousStream = streams[index - 1]; + + // Remove the previous line if required. + const command = stream.subarray(0, MOVE_CURSOR_1_LINE_COMMAND.length); + if (command[0] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[0] && command[1] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[1] && command[2] === MOVE_CURSOR_1_LINE_COMMAND_BYTES[2]) { + const lastIndexOfLineFeed = previousStream.lastIndexOf(LINE_FEED); + if (lastIndexOfLineFeed === -1) { + return; + } + + didCompress = true; + streams[index - 1] = previousStream.subarray(0, lastIndexOfLineFeed); + streams[index] = stream.subarray(MOVE_CURSOR_1_LINE_COMMAND.length); + } + }); + return didCompress; +} + +const BACKSPACE_CHARACTER = '\b'.charCodeAt(0); +const CARRIAGE_RETURN_CHARACTER = '\r'.charCodeAt(0); +function formatStreamText(buffer: BinaryBuffer): BinaryBuffer { + // We have special handling for backspace and carriage return characters. + // Don't unnecessary decode the bytes if we don't need to perform any processing. + if (!buffer.buffer.includes(BACKSPACE_CHARACTER) && !buffer.buffer.includes(CARRIAGE_RETURN_CHARACTER)) { + return buffer; + } + // Do the same thing jupyter is doing + return BinaryBuffer.fromString(fixCarriageReturn(fixBackspace(textDecoder.decode(buffer.buffer)))); +} + +/** + * Took this from jupyter/notebook + * https://github.com/jupyter/notebook/blob/b8b66332e2023e83d2ee04f83d8814f567e01a4e/notebook/static/base/js/utils.js + * Remove characters that are overridden by backspace characters + */ +function fixBackspace(txt: string): string { + let tmp = txt; + do { + txt = tmp; + // Cancel out anything-but-newline followed by backspace + tmp = txt.replace(/[^\n]\x08/gm, ''); + } while (tmp.length < txt.length); + return txt; +} + +/** + * Remove chunks that should be overridden by the effect of carriage return characters + * From https://github.com/jupyter/notebook/blob/master/notebook/static/base/js/utils.js + */ +function fixCarriageReturn(txt: string): string { + txt = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline + while (txt.search(/\r[^$]/g) > -1) { + const base = txt.match(/^(.*)\r+/m)![1]; + let insert = txt.match(/\r+(.*)$/m)![1]; + insert = insert + base.slice(insert.length, base.length); + txt = txt.replace(/\r+.*$/m, '\r').replace(/^.*\r/m, insert); + } + return txt; +} diff --git a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts index 0e4dbe936f366..9171dc70323bd 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts @@ -15,8 +15,8 @@ // ***************************************************************************** import { Disposable, Emitter } from '@theia/core'; -import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { CellOutput, CellOutputItem, isTextStreamMime } from '../../common'; +import { compressOutputItemStreams } from '../notebook-output-utils'; export class NotebookCellOutputModel implements Disposable { @@ -73,10 +73,10 @@ export class NotebookCellOutputModel implements Disposable { if (this.outputs.length > 1 && this.outputs.every(item => isTextStreamMime(item.mime))) { // Look for the mimes in the items, and keep track of their order. // Merge the streams into one output item, per mime type. - const mimeOutputs = new Map(); + const mimeOutputs = new Map(); const mimeTypes: string[] = []; this.outputs.forEach(item => { - let items: BinaryBuffer[]; + let items: Uint8Array[]; if (mimeOutputs.has(item.mime)) { items = mimeOutputs.get(item.mime)!; } else { @@ -84,13 +84,14 @@ export class NotebookCellOutputModel implements Disposable { mimeOutputs.set(item.mime, items); mimeTypes.push(item.mime); } - items.push(item.data); + items.push(item.data.buffer); }); this.outputs.length = 0; mimeTypes.forEach(mime => { + const compressionResult = compressOutputItemStreams(mimeOutputs.get(mime)!); this.outputs.push({ mime, - data: BinaryBuffer.concat(mimeOutputs.get(mime)!) + data: compressionResult.data }); }); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 987367cb33abc..cde345671d7f2 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -76,7 +76,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { protected async init(): Promise { this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange)); this.cell.onDidChangeOutputItems(output => { - this.updateOutput({start: this.cell.outputs.findIndex(o => o.getData().outputId === o.outputId), deleteCount: 1, newOutputs: [output]}); + this.updateOutput({start: this.cell.outputs.findIndex(o => o.outputId === output.outputId), deleteCount: 1, newOutputs: [output]}); }); this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); From ad2e10691147fa0184fc96cd17d213f59669ca07 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 13 Dec 2023 15:45:10 +0100 Subject: [PATCH 002/441] Added support for webview/context menu contributions (#13166) * added support for webview/context menu contributions Signed-off-by: Jonah Iden * removed unneccessary log Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../menus/plugin-menu-command-adapter.ts | 1 + .../menus/vscode-theia-menu-mappings.ts | 6 ++-- .../src/main/browser/webview/pre/main.js | 29 ++++++++++++++-- .../src/main/browser/webview/webview.ts | 33 ++++++++++++++++++- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index f0cfaba0a22ff..8dab5d8a80068 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -103,6 +103,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { ['timeline/item/context', (...args) => this.toTimelineArgs(...args)], ['view/item/context', (...args) => this.toTreeArgs(...args)], ['view/title', noArgs], + ['webview/context', noArgs] ]).forEach(([contributionPoint, adapter]) => { if (adapter) { const paths = codeToTheiaMappings.get(contributionPoint); diff --git a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts index 3b15628359f8a..c7149296b9b43 100644 --- a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts +++ b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts @@ -29,7 +29,7 @@ import { ScmTreeWidget } from '@theia/scm/lib/browser/scm-tree-widget'; import { TIMELINE_ITEM_CONTEXT_MENU } from '@theia/timeline/lib/browser/timeline-tree-widget'; import { COMMENT_CONTEXT, COMMENT_THREAD_CONTEXT, COMMENT_TITLE } from '../comments/comment-thread-widget'; import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget'; -import { WebviewWidget } from '../webview/webview'; +import { WEBVIEW_CONTEXT_MENU, WebviewWidget } from '../webview/webview'; import { EDITOR_LINENUMBER_CONTEXT_MENU } from '@theia/editor/lib/browser/editor-linenumber-contribution'; import { TEST_VIEW_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-view-contribution'; @@ -58,7 +58,8 @@ export const implementedVSCodeContributionPoints = [ 'timeline/item/context', 'testing/item/context', 'view/item/context', - 'view/title' + 'view/title', + 'webview/context' ] as const; export type ContributionPoint = (typeof implementedVSCodeContributionPoints)[number]; @@ -85,6 +86,7 @@ export const codeToTheiaMappings = new Map([ ['timeline/item/context', [TIMELINE_ITEM_CONTEXT_MENU]], ['view/item/context', [VIEW_ITEM_CONTEXT_MENU]], ['view/title', [PLUGIN_VIEW_TITLE_MENU]], + ['webview/context', [WEBVIEW_CONTEXT_MENU]] ]); type CodeEditorWidget = EditorWidget | WebviewWidget; diff --git a/packages/plugin-ext/src/main/browser/webview/pre/main.js b/packages/plugin-ext/src/main/browser/webview/pre/main.js index 59105add0f051..004f20920b4fe 100644 --- a/packages/plugin-ext/src/main/browser/webview/pre/main.js +++ b/packages/plugin-ext/src/main/browser/webview/pre/main.js @@ -339,10 +339,35 @@ delete window.frameElement; clientY: e.clientY, ctrlKey: e.ctrlKey, metaKey: e.metaKey, - shiftKey: e.shiftKey + shiftKey: e.shiftKey, + // @ts-ignore the dataset should exist if the target is an element }); }; + const handleContextMenu = (e) => { + if (e.defaultPrevented) { + return; + } + + e.preventDefault(); + + host.postMessage('did-context-menu', { + clientX: e.clientX, + clientY: e.clientY, + context: findVscodeContext(e.target) + }); + }; + + function findVscodeContext(node) { + if (node) { + if (node.dataset?.vscodeContext) { + return JSON.parse(node.dataset.vscodeContext); + } + return findVscodeContext(node.parentElement); + } + return {}; + } + function preventDefaultBrowserHotkeys(e) { var isOSX = navigator.platform.toUpperCase().indexOf('MAC') >= 0; @@ -602,7 +627,7 @@ delete window.frameElement; newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); newFrame.contentWindow.addEventListener('mousedown', handleInnerMousedown); newFrame.contentWindow.addEventListener('mouseup', handleInnerMouseup); - newFrame.contentWindow.addEventListener('contextmenu', e => e.preventDefault()); + newFrame.contentWindow.addEventListener('contextmenu', handleContextMenu); if (host.onIframeLoaded) { host.onIframeLoaded(newFrame); diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts index f7956a6ee1e71..2d35997b5cd97 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview.ts @@ -51,10 +51,16 @@ import { BinaryBufferReadableStream } from '@theia/core/lib/common/buffer'; import { ViewColumn } from '../../../plugin/types-impl'; import { ExtractableWidget } from '@theia/core/lib/browser/widgets/extractable-widget'; import { BadgeWidget } from '@theia/core/lib/browser/view-container'; +import { MenuPath } from '@theia/core'; +import { ContextMenuRenderer } from '@theia/core/lib/browser'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { PluginViewWidget } from '../view/plugin-view-widget'; // Style from core const TRANSPARENT_OVERLAY_STYLE = 'theia-transparent-overlay'; +export const WEBVIEW_CONTEXT_MENU: MenuPath = ['webview-context-menu']; + /* eslint-disable @typescript-eslint/no-explicit-any */ export const enum WebviewMessageChannels { @@ -70,7 +76,8 @@ export const enum WebviewMessageChannels { didKeydown = 'did-keydown', didMouseDown = 'did-mousedown', didMouseUp = 'did-mouseup', - onconsole = 'onconsole' + onconsole = 'onconsole', + didcontextmenu = 'did-context-menu' } export interface WebviewContentOptions { @@ -152,6 +159,12 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract @inject(WebviewResourceCache) protected readonly resourceCache: WebviewResourceCache; + @inject(ContextMenuRenderer) + protected readonly contextMenuRenderer: ContextMenuRenderer; + + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + viewState: WebviewPanelViewState = { visible: false, active: false, @@ -357,6 +370,10 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract this.dispatchMouseEvent('mouseup', data); })); + this.toHide.push(this.on(WebviewMessageChannels.didcontextmenu, (event: { clientX: number, clientY: number, context: any }) => { + this.handleContextMenu(event); + })); + this.style(); this.toHide.push(this.themeDataProvider.onDidChangeThemeData(() => this.style())); @@ -380,6 +397,20 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract })); } + handleContextMenu(event: { clientX: number, clientY: number, context: any }): void { + const domRect = this.node.getBoundingClientRect(); + this.contextKeyService.with(this.parent instanceof PluginViewWidget ? + { webviewId: this.parent.options.viewId, ...event.context } : {}, + () => { + this.contextMenuRenderer.render({ + menuPath: WEBVIEW_CONTEXT_MENU, + anchor: { + x: domRect.x + event.clientX, y: domRect.y + event.clientY + } + }); + }); + } + protected async getRedirect(url: string): Promise { const uri = new URI(url); const localhost = this.externalUriService.parseLocalhost(uri); From 0910b027f9606e676a0a73055c88fdadd20176fc Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 14 Dec 2023 11:14:12 +0100 Subject: [PATCH 003/441] General notebook API improvements (#13012) Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 4 +- packages/notebook/src/browser/index.ts | 1 + .../src/browser/notebook-frontend-module.ts | 4 +- .../notebook/src/browser/notebook-types.ts | 172 ++++++++++++++++ .../service/notebook-cell-context-manager.ts | 29 +-- .../service/notebook-editor-widget-service.ts | 4 +- .../service/notebook-execution-service.ts | 20 +- .../notebook-execution-state-service.ts | 7 +- .../notebook-kernel-history-service.ts | 19 +- .../notebook-kernel-quick-pick-service.ts | 65 +++--- .../browser/view-model/notebook-cell-model.ts | 33 +++- .../src/browser/view-model/notebook-model.ts | 97 ++++----- .../src/browser/view/notebook-cell-editor.tsx | 12 +- .../browser/view/notebook-cell-list-view.tsx | 11 +- .../view/notebook-cell-toolbar-factory.tsx | 4 +- .../browser/view/notebook-cell-toolbar.tsx | 13 +- .../browser/view/notebook-main-toolbar.tsx | 9 + .../notebook/src/common/notebook-common.ts | 186 +----------------- .../plugin-ext/src/common/plugin-api-rpc.ts | 2 +- .../notebook-documents-and-editors-main.ts | 22 +-- .../notebooks/notebook-documents-main.ts | 23 ++- .../main/browser/notebooks/notebook-dto.ts | 58 +++--- .../notebooks/notebook-kernels-main.ts | 2 +- .../main/browser/notebooks/notebooks-main.ts | 15 +- .../renderers/cell-output-webview.tsx | 4 +- .../src/plugin/notebook/notebook-document.ts | 16 +- .../plugin-ext/src/plugin/type-converters.ts | 123 +----------- 27 files changed, 435 insertions(+), 520 deletions(-) create mode 100644 packages/notebook/src/browser/notebook-types.ts diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index abd9516f3b346..9d73b6a766323 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -20,7 +20,7 @@ import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/brows import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from '../service/notebook-service'; import { CellEditType, CellKind } from '../../common'; -import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; +import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @@ -66,7 +66,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon protected notebookService: NotebookService; @inject(NotebookKernelQuickPickService) - protected notebookKernelQuickPickService: KernelPickerMRUStrategy; + protected notebookKernelQuickPickService: NotebookKernelQuickPickService; @inject(NotebookExecutionService) protected notebookExecutionService: NotebookExecutionService; diff --git a/packages/notebook/src/browser/index.ts b/packages/notebook/src/browser/index.ts index eccec73435328..1c213a4415eeb 100644 --- a/packages/notebook/src/browser/index.ts +++ b/packages/notebook/src/browser/index.ts @@ -24,3 +24,4 @@ export * from './service/notebook-execution-state-service'; export * from './service/notebook-model-resolver-service'; export * from './service/notebook-renderer-messaging-service'; export * from './renderers/cell-output-webview'; +export * from './notebook-types'; diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index c3602f522216d..7839f0aa1f631 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -37,7 +37,7 @@ import { NotebookActionsContribution } from './contributions/notebook-actions-co import { NotebookExecutionService } from './service/notebook-execution-service'; import { NotebookExecutionStateService } from './service/notebook-execution-state-service'; import { NotebookKernelService } from './service/notebook-kernel-service'; -import { KernelPickerMRUStrategy, NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service'; +import { NotebookKernelQuickPickService } from './service/notebook-kernel-quick-pick-service'; import { NotebookKernelHistoryService } from './service/notebook-kernel-history-service'; import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service'; import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service'; @@ -65,7 +65,7 @@ export default new ContainerModule(bind => { bind(NotebookKernelService).toSelf().inSingletonScope(); bind(NotebookRendererMessagingService).toSelf().inSingletonScope(); bind(NotebookKernelHistoryService).toSelf().inSingletonScope(); - bind(NotebookKernelQuickPickService).to(KernelPickerMRUStrategy).inSingletonScope(); + bind(NotebookKernelQuickPickService).toSelf().inSingletonScope(); bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); diff --git a/packages/notebook/src/browser/notebook-types.ts b/packages/notebook/src/browser/notebook-types.ts new file mode 100644 index 0000000000000..a1ac52ae52370 --- /dev/null +++ b/packages/notebook/src/browser/notebook-types.ts @@ -0,0 +1,172 @@ +// ***************************************************************************** +// Copyright (C) 2023 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + CellData, CellEditType, CellMetadataEdit, CellOutput, CellOutputItem, CellRange, NotebookCellContentChangeEvent, + NotebookCellInternalMetadata, + NotebookCellsChangeInternalMetadataEvent, + NotebookCellsChangeLanguageEvent, + NotebookCellsChangeMetadataEvent, + NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata +} from '../common'; +import { NotebookCell } from './view-model/notebook-cell-model'; + +export interface NotebookTextModelChangedEvent { + readonly rawEvents: NotebookContentChangedEvent[]; + // readonly versionId: number; + readonly synchronous?: boolean; + readonly endSelectionState?: SelectionState; +}; + +export type NotebookContentChangedEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | + NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | + NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | + NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent); // & { transient: boolean }; + +export interface NotebookCellsInitializeEvent { + readonly kind: NotebookCellsChangeType.Initialize; + readonly changes: NotebookCellTextModelSplice[]; +} + +export interface NotebookDocumentChangeMetadataEvent { + readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata; + readonly metadata: NotebookDocumentMetadata; +} + +export interface NotebookCellsModelChangedEvent { + readonly kind: NotebookCellsChangeType.ModelChange; + readonly changes: NotebookCellTextModelSplice[]; +} + +export interface NotebookModelWillAddRemoveEvent { + readonly rawEvent: NotebookCellsModelChangedEvent; +}; + +export interface NotebookCellsModelMoveEvent { + readonly kind: NotebookCellsChangeType.Move; + readonly index: number; + readonly length: number; + readonly newIdx: number; + readonly cells: T[]; +} + +export interface NotebookOutputChangedEvent { + readonly kind: NotebookCellsChangeType.Output; + readonly index: number; + readonly outputs: CellOutput[]; + readonly append: boolean; +} + +export interface NotebookOutputItemChangedEvent { + readonly kind: NotebookCellsChangeType.OutputItem; + readonly index: number; + readonly outputId: string; + readonly outputItems: CellOutputItem[]; + readonly append: boolean; +} + +export interface NotebookDocumentUnknownChangeEvent { + readonly kind: NotebookCellsChangeType.Unknown; +} + +export enum SelectionStateType { + Handle = 0, + Index = 1 +} + +export interface SelectionHandleState { + kind: SelectionStateType.Handle; + primary: number | null; + selections: number[]; +} + +export interface SelectionIndexState { + kind: SelectionStateType.Index; + focus: CellRange; + selections: CellRange[]; +} + +export type SelectionState = SelectionHandleState | SelectionIndexState; + +export interface NotebookModelWillAddRemoveEvent { + readonly newCellIds?: number[]; + readonly rawEvent: NotebookCellsModelChangedEvent; +}; + +export interface CellOutputEdit { + editType: CellEditType.Output; + index: number; + outputs: CellOutput[]; + append?: boolean; +} + +export interface CellOutputEditByHandle { + editType: CellEditType.Output; + handle: number; + outputs: CellOutput[]; + append?: boolean; +} + +export interface CellOutputItemEdit { + editType: CellEditType.OutputItems; + items: CellOutputItem[]; + outputId: string; + append?: boolean; +} + +export interface CellLanguageEdit { + editType: CellEditType.CellLanguage; + index: number; + language: string; +} + +export interface DocumentMetadataEdit { + editType: CellEditType.DocumentMetadata; + metadata: NotebookDocumentMetadata; +} + +export interface CellMoveEdit { + editType: CellEditType.Move; + index: number; + length: number; + newIdx: number; +} + +export interface CellReplaceEdit { + editType: CellEditType.Replace; + index: number; + count: number; + cells: CellData[]; +} + +export type ImmediateCellEditOperation = CellOutputEditByHandle | CellOutputItemEdit | CellPartialInternalMetadataEditByHandle; // add more later on +export type CellEditOperation = ImmediateCellEditOperation | CellReplaceEdit | CellOutputEdit | + CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit; // add more later on + +export type NullablePartialNotebookCellInternalMetadata = { + [Key in keyof Partial]: NotebookCellInternalMetadata[Key] | null +}; +export interface CellPartialInternalMetadataEditByHandle { + editType: CellEditType.PartialInternalMetadata; + handle: number; + internalMetadata: NullablePartialNotebookCellInternalMetadata; +} + +export interface NotebookCellOutputsSplice { + start: number; + deleteCount: number; + newOutputs: CellOutput[]; +}; diff --git a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts b/packages/notebook/src/browser/service/notebook-cell-context-manager.ts index aefe1655a4ad6..200e3e2f4dff9 100644 --- a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-cell-context-manager.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { inject, injectable } from '@theia/core/shared/inversify'; -import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE } from '../contributions/notebook-context-keys'; import { Disposable, DisposableCollection, Emitter } from '@theia/core'; @@ -23,7 +23,7 @@ import { CellKind } from '../../common'; import { NotebookExecutionStateService } from '../service/notebook-execution-state-service'; @injectable() -export class NotebookCellContextManager implements Disposable { +export class NotebookCellContextManager implements NotebookCellContextManager, Disposable { @inject(ContextKeyService) protected contextKeyService: ContextKeyService; @inject(NotebookExecutionStateService) @@ -31,9 +31,10 @@ export class NotebookCellContextManager implements Disposable { protected readonly toDispose = new DisposableCollection(); + protected currentStore: ScopedValueStore; protected currentContext: HTMLLIElement; - protected readonly onDidChangeContextEmitter = new Emitter(); + protected readonly onDidChangeContextEmitter = new Emitter(); readonly onDidChangeContext = this.onDidChangeContextEmitter.event; updateCellContext(cell: NotebookCellModel, newHtmlContext: HTMLLIElement): void { @@ -41,28 +42,32 @@ export class NotebookCellContextManager implements Disposable { this.toDispose.dispose(); this.currentContext = newHtmlContext; - const currentStore = this.contextKeyService.createScoped(newHtmlContext); - this.toDispose.push(currentStore); + this.currentStore = this.contextKeyService.createScoped(newHtmlContext); - currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown'); + this.currentStore.setContext(NOTEBOOK_CELL_TYPE, cell.cellKind === CellKind.Code ? 'code' : 'markdown'); + + this.toDispose.push(this.contextKeyService.onDidChange(e => { + this.onDidChangeContextEmitter.fire(e); + })); this.toDispose.push(cell.onDidRequestCellEditChange(cellEdit => { - currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); - this.onDidChangeContextEmitter.fire(); + this.currentStore?.setContext(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, cellEdit); + this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_MARKDOWN_EDIT_MODE) }); })); this.toDispose.push(this.executionStateService.onDidChangeExecution(e => { if (e.affectsCell(cell.uri)) { - currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); - currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); - this.onDidChangeContextEmitter.fire(); + this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); + this.currentStore?.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); + this.onDidChangeContextEmitter.fire({ affects: keys => keys.has(NOTEBOOK_CELL_EXECUTING) || keys.has(NOTEBOOK_CELL_EXECUTION_STATE) }); } })); - this.onDidChangeContextEmitter.fire(); + this.onDidChangeContextEmitter.fire({ affects: keys => true }); } } dispose(): void { this.toDispose.dispose(); + this.currentStore?.dispose(); this.onDidChangeContextEmitter.dispose(); } } diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index f8307cdb9bd6f..3416285f4adc7 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -86,8 +86,8 @@ export class NotebookEditorWidgetService implements Disposable { return this.notebookEditors.get(editorId); } - listNotebookEditors(): readonly NotebookEditorWidget[] { - return [...this.notebookEditors].map(e => e[1]); + getNotebookEditors(): readonly NotebookEditorWidget[] { + return Array.from(this.notebookEditors.values()); } } diff --git a/packages/notebook/src/browser/service/notebook-execution-service.ts b/packages/notebook/src/browser/service/notebook-execution-service.ts index abfbbaaf4795e..022607a5720ff 100644 --- a/packages/notebook/src/browser/service/notebook-execution-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-service.ts @@ -23,11 +23,10 @@ import { CellExecution, NotebookExecutionStateService } from '../service/noteboo import { CellKind, NotebookCellExecutionState } from '../../common'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookModel } from '../view-model/notebook-model'; -import { NotebookKernelService, NotebookKernel } from './notebook-kernel-service'; +import { NotebookKernelService } from './notebook-kernel-service'; import { CommandService, Disposable } from '@theia/core'; -import { NotebookKernelQuickPickService, NotebookKernelQuickPickServiceImpl } from './notebook-kernel-quick-pick-service'; +import { NotebookKernelQuickPickService } from './notebook-kernel-quick-pick-service'; import { NotebookKernelHistoryService } from './notebook-kernel-history-service'; -import { NotebookCommands } from '../contributions/notebook-actions-contribution'; export interface CellExecutionParticipant { onWillExecuteCell(executions: CellExecution[]): Promise; @@ -49,7 +48,7 @@ export class NotebookExecutionService { protected commandService: CommandService; @inject(NotebookKernelQuickPickService) - protected notebookKernelQuickPickService: NotebookKernelQuickPickServiceImpl; + protected notebookKernelQuickPickService: NotebookKernelQuickPickService; private readonly cellExecutionParticipants = new Set(); @@ -69,7 +68,7 @@ export class NotebookExecutionService { } } - const kernel = await this.resolveKernel(notebook); + const kernel = await this.notebookKernelHistoryService.resolveSelectedKernel(notebook); if (!kernel) { // clear all pending cell executions @@ -125,15 +124,4 @@ export class NotebookExecutionService { this.cancelNotebookCellHandles(notebook, Array.from(cells, cell => cell.handle)); } - async resolveKernel(notebook: NotebookModel): Promise { - const alreadySelected = this.notebookKernelHistoryService.getKernels(notebook); - - if (alreadySelected.selected) { - return alreadySelected.selected; - } - - await this.commandService.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, notebook); - const { selected } = this.notebookKernelHistoryService.getKernels(notebook); - return selected; - } } diff --git a/packages/notebook/src/browser/service/notebook-execution-state-service.ts b/packages/notebook/src/browser/service/notebook-execution-state-service.ts index 51ed8dc2059c5..3512293d78b4b 100644 --- a/packages/notebook/src/browser/service/notebook-execution-state-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-state-service.ts @@ -23,8 +23,9 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { NotebookService } from './notebook-service'; import { CellEditType, CellExecuteOutputEdit, CellExecuteOutputItemEdit, CellExecutionUpdateType, - CellUri, CellPartialInternalMetadataEditByHandle, NotebookCellExecutionState, CellEditOperation, NotebookCellInternalMetadata + CellUri, NotebookCellExecutionState, NotebookCellInternalMetadata } from '../../common'; +import { CellPartialInternalMetadataEditByHandle, CellEditOperation } from '../notebook-types'; import { NotebookModel } from '../view-model/notebook-model'; import { v4 } from 'uuid'; @@ -43,10 +44,6 @@ export interface CellExecutionStateUpdate { isPaused?: boolean; } -export interface ICellExecutionComplete { - runEndTime?: number; - lastRunSuccess?: boolean; -} export enum NotebookExecutionType { cell, notebook diff --git a/packages/notebook/src/browser/service/notebook-kernel-history-service.ts b/packages/notebook/src/browser/service/notebook-kernel-history-service.ts index a28cba3bbccc4..5115820134209 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-history-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-history-service.ts @@ -21,7 +21,9 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { StorageService } from '@theia/core/lib/browser'; import { NotebookKernel, NotebookTextModelLike, NotebookKernelService } from './notebook-kernel-service'; -import { Disposable } from '@theia/core'; +import { CommandService, Disposable } from '@theia/core'; +import { NotebookModel } from '../view-model/notebook-model'; +import { NotebookCommands } from '../contributions/notebook-actions-contribution'; interface KernelsList { [viewType: string]: string[]; @@ -43,6 +45,9 @@ export class NotebookKernelHistoryService implements Disposable { @inject(NotebookKernelService) protected notebookKernelService: NotebookKernelService; + @inject(CommandService) + protected commandService: CommandService; + declare serviceBrand: undefined; private static STORAGE_KEY = 'notebook.kernelHistory'; @@ -68,6 +73,18 @@ export class NotebookKernelHistoryService implements Disposable { }; } + async resolveSelectedKernel(notebook: NotebookModel): Promise { + const alreadySelected = this.getKernels(notebook); + + if (alreadySelected.selected) { + return alreadySelected.selected; + } + + await this.commandService.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, notebook); + const { selected } = this.getKernels(notebook); + return selected; + } + addMostRecentKernel(kernel: NotebookKernel): void { const viewType = kernel.viewType; const recentKernels = this.mostRecentKernelsMap[viewType] ?? [kernel.id]; diff --git a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts index 83a123082f884..874672cee0ab7 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts @@ -29,8 +29,6 @@ import debounce = require('@theia/core/shared/lodash.debounce'); export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter'; -export const NotebookKernelQuickPickService = Symbol('NotebookKernelQuickPickService'); - type KernelPick = QuickPickItem & { kernel: NotebookKernel }; function isKernelPick(item: QuickPickInput): item is KernelPick { return 'kernel' in item; @@ -82,7 +80,7 @@ function toKernelQuickPick(kernel: NotebookKernel, selected: NotebookKernel | un } @injectable() -export abstract class NotebookKernelQuickPickServiceImpl { +export class NotebookKernelQuickPickService { @inject(NotebookKernelService) protected readonly notebookKernelService: NotebookKernelService; @@ -91,6 +89,12 @@ export abstract class NotebookKernelQuickPickServiceImpl { @inject(CommandService) protected readonly commandService: CommandService; + @inject(OpenerService) + protected openerService: OpenerService; + + @inject(NotebookKernelHistoryService) + protected notebookKernelHistoryService: NotebookKernelHistoryService; + async showQuickPick(editor: NotebookModel, wantedId?: string, skipAutoRun?: boolean): Promise { const notebook = editor; const matchResult = this.getMatchingResult(notebook); @@ -200,40 +204,6 @@ export abstract class NotebookKernelQuickPickServiceImpl { return false; } - protected getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult { - return this.notebookKernelService.getMatchingKernel(notebook); - } - - protected abstract getKernelPickerQuickPickItems(matchResult: NotebookKernelMatchResult): QuickPickInput[]; - - protected async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, quickPickItems: KernelQuickPickItem[]): Promise { - if (isKernelPick(pick)) { - const newKernel = pick.kernel; - this.selectKernel(editor, newKernel); - return true; - } - - if (isSourcePick(pick)) { - // selected explicitly, it should trigger the execution? - pick.action.run(this.commandService); - } - - return true; - } - - protected selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void { - this.notebookKernelService.selectKernelForNotebook(kernel, notebook); - } -} -@injectable() -export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl { - - @inject(OpenerService) - protected openerService: OpenerService; - - @inject(NotebookKernelHistoryService) - protected notebookKernelHistoryService: NotebookKernelHistoryService; - protected getKernelPickerQuickPickItems(matchResult: NotebookKernelMatchResult): QuickPickInput[] { const quickPickItems: QuickPickInput[] = []; @@ -266,17 +236,17 @@ export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl return quickPickItems; } - protected override selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void { + protected selectKernel(notebook: NotebookModel, kernel: NotebookKernel): void { const currentInfo = this.notebookKernelService.getMatchingKernel(notebook); if (currentInfo.selected) { // there is already a selected kernel this.notebookKernelHistoryService.addMostRecentKernel(currentInfo.selected); } - super.selectKernel(notebook, kernel); + this.notebookKernelService.selectKernelForNotebook(kernel, notebook); this.notebookKernelHistoryService.addMostRecentKernel(kernel); } - protected override getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult { + protected getMatchingResult(notebook: NotebookModel): NotebookKernelMatchResult { const { selected, all } = this.notebookKernelHistoryService.getKernels(notebook); const matchingResult = this.notebookKernelService.getMatchingKernel(notebook); return { @@ -287,12 +257,23 @@ export class KernelPickerMRUStrategy extends NotebookKernelQuickPickServiceImpl }; } - protected override async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise { + protected async handleQuickPick(editor: NotebookModel, pick: KernelQuickPickItem, items: KernelQuickPickItem[]): Promise { if (pick.id === 'selectAnother') { return this.displaySelectAnotherQuickPick(editor, items.length === 1 && items[0] === pick); } - return super.handleQuickPick(editor, pick, items); + if (isKernelPick(pick)) { + const newKernel = pick.kernel; + this.selectKernel(editor, newKernel); + return true; + } + + if (isSourcePick(pick)) { + // selected explicitly, it should trigger the execution? + pick.action.run(this.commandService); + } + + return true; } private async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise { diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index bd804be68afbd..1389d59c0dd7b 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -20,12 +20,14 @@ import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; +import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { - CellInternalMetadataChangedEvent, CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata, - NotebookCellMetadata, NotebookCellOutputsSplice, CellOutput, CellData, NotebookCell, CellOutputItem + CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata, + NotebookCellMetadata, CellOutput, CellData, CellOutputItem } from '../../common'; +import { NotebookCellOutputsSplice } from '../notebook-types'; import { NotebookCellOutputModel } from './notebook-cell-output-model'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); @@ -47,7 +49,28 @@ const NotebookCellContextManager = Symbol('NotebookCellContextManager'); interface NotebookCellContextManager { updateCellContext(cell: NotebookCellModel, context: HTMLElement): void; dispose(): void; - onDidChangeContext: Event; + onDidChangeContext: Event; +} + +export interface CellInternalMetadataChangedEvent { + readonly lastRunSuccessChanged?: boolean; +} + +export interface NotebookCell { + readonly uri: URI; + handle: number; + language: string; + cellKind: CellKind; + outputs: CellOutput[]; + metadata: NotebookCellMetadata; + internalMetadata: NotebookCellInternalMetadata; + text: string; + onDidChangeOutputs?: Event; + onDidChangeOutputItems?: Event; + onDidChangeLanguage: Event; + onDidChangeMetadata: Event; + onDidChangeInternalMetadata: Event; + } const NotebookCellModelProps = Symbol('NotebookModelProps'); @@ -144,6 +167,7 @@ export class NotebookCellModel implements NotebookCell, Disposable { } set source(source: string) { this.props.source = source; + this.textModel?.textEditorModel.setValue(source); } get language(): string { return this.props.language; @@ -274,6 +298,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { const ref = await this.textModelService.createModelReference(this.uri); this.textModel = ref.object; + this.textModel.onDidChangeContent(e => { + this.props.source = e.model.getText(); + }); return ref.object; } } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index e8f1b897e3add..9ec960c104787 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -17,18 +17,16 @@ import { Disposable, Emitter, URI } from '@theia/core'; import { Saveable, SaveOptions } from '@theia/core/lib/browser'; import { - CellData, - CellEditOperation, CellEditType, CellUri, NotebookCellInternalMetadata, + CellData, CellEditType, CellUri, NotebookCellInternalMetadata, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookData, - NotebookDocumentMetadata, NotebookModelWillAddRemoveEvent, - NotebookTextModelChangedEvent, NullablePartialNotebookCellInternalMetadata + NotebookDocumentMetadata, } from '../../common'; +import { NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, CellEditOperation, NullablePartialNotebookCellInternalMetadata } from '../notebook-types'; import { NotebookSerializer } from '../service/notebook-service'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model'; import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; -import { NotebookKernel } from '../service/notebook-kernel-service'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; export const NotebookModelFactory = Symbol('NotebookModelFactory'); @@ -62,7 +60,7 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidAddOrRemoveCellEmitter = new Emitter(); readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event; - protected readonly onDidChangeContentEmitter = new Emitter(); + protected readonly onDidChangeContentEmitter = new Emitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; @inject(FileService) @@ -81,11 +79,19 @@ export class NotebookModel implements Saveable, Disposable { protected cellModelFactory: NotebookCellModelFactory; readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; - nextHandle: number = 0; + protected nextHandle: number = 0; - kernel?: NotebookKernel; + protected _dirty: boolean = false; + + set dirty(dirty: boolean) { + this._dirty = dirty; + this.onDirtyChangedEmitter.fire(); + } + + get dirty(): boolean { + return this._dirty; + } - dirty: boolean; selectedCell?: NotebookCellModel; protected dirtyCells: NotebookCellModel[] = []; @@ -121,18 +127,6 @@ export class NotebookModel implements Saveable, Disposable { this.metadata = this.metadata; - this.modelService.onDidCreate(editorModel => { - const modelUri = new URI(editorModel.uri); - if (modelUri.scheme === CellUri.scheme) { - const cellUri = CellUri.parse(modelUri); - if (cellUri && cellUri.notebook.isEqual(this.uri)) { - const cell = this.cells.find(c => c.handle === cellUri.handle); - if (cell) { - cell.textModel = editorModel; - } - } - } - }); this.nextHandle = this.cells.length; } @@ -147,7 +141,6 @@ export class NotebookModel implements Saveable, Disposable { async save(options: SaveOptions): Promise { this.dirtyCells = []; this.dirty = false; - this.onDirtyChangedEmitter.fire(); const serializedNotebook = await this.props.serializer.fromNotebook({ cells: this.cells.map(cell => cell.getData()), @@ -170,9 +163,34 @@ export class NotebookModel implements Saveable, Disposable { }; } + async applySnapshot(snapshot: Saveable.Snapshot): Promise { + const rawData = 'read' in snapshot ? snapshot.read() : snapshot.value; + if (!rawData) { + throw new Error('could not read notebook snapshot'); + } + const data = JSON.parse(rawData); + const cells = data.cells.map((cell: CellData, index: number) => { + const handle = this.nextHandle++; + return this.cellModelFactory({ + uri: CellUri.generate(this.uri, handle), + handle: handle, + source: cell.source, + language: cell.language, + cellKind: cell.cellKind, + outputs: cell.outputs, + metadata: cell.metadata, + internalMetadata: cell.internalMetadata, + collapseState: cell.collapseState + }); + }); + this.addCellOutputListeners(cells); + + this.metadata = data.metadata; + + } + async revert(options?: Saveable.RevertOptions): Promise { this.dirty = false; - this.onDirtyChangedEmitter.fire(); } isDirty(): boolean { @@ -186,8 +204,8 @@ export class NotebookModel implements Saveable, Disposable { this.dirtyCells.splice(this.dirtyCells.indexOf(cell), 1); } - const oldDirtyState = this.dirty; - this.dirty = this.dirtyCells.length > 0; + const oldDirtyState = this._dirty; + this._dirty = this.dirtyCells.length > 0; if (this.dirty !== oldDirtyState) { this.onDirtyChangedEmitter.fire(); } @@ -211,7 +229,6 @@ export class NotebookModel implements Saveable, Disposable { for (const cell of cells) { cell.onDidChangeOutputs(() => { this.dirty = true; - this.onDirtyChangedEmitter.fire(); }); } } @@ -294,7 +311,7 @@ export class NotebookModel implements Saveable, Disposable { }); this.addCellOutputListeners(cells); - const changes: NotebookCellTextModelSplice[] = [[start, deleteCount, cells]]; + const changes: NotebookCellTextModelSplice[] = [{ start, deleteCount, newItems: cells }]; const deletedCells = this.cells.splice(start, deleteCount, ...cells); @@ -308,8 +325,8 @@ export class NotebookModel implements Saveable, Disposable { async () => this.replaceCells(start, deleteCount, newCells, false)); } - this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes } }); - this.onDidChangeContentEmitter.fire({ rawEvents: [{ kind: NotebookCellsChangeType.ModelChange, changes }] }); + this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); + this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]); } private changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void { @@ -323,11 +340,9 @@ export class NotebookModel implements Saveable, Disposable { } cell.internalMetadata = newInternalMetadata; - this.onDidChangeContentEmitter.fire({ - rawEvents: [ - { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata } - ] - }); + this.onDidChangeContentEmitter.fire([ + { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata } + ]); } private updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void { @@ -340,10 +355,7 @@ export class NotebookModel implements Saveable, Disposable { } this.metadata = metadata; - this.onDidChangeContentEmitter.fire({ - rawEvents: [{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }], - synchronous: true, - }); + this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]); } private changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void { @@ -353,10 +365,7 @@ export class NotebookModel implements Saveable, Disposable { cell.language = languageId; - this.onDidChangeContentEmitter.fire({ - rawEvents: [{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }], - synchronous: true, - }); + this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }]); } private moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean { @@ -369,9 +378,7 @@ export class NotebookModel implements Saveable, Disposable { const cells = this.cells.splice(fromIndex, length); this.cells.splice(toIndex, 0, ...cells); - this.onDidChangeContentEmitter.fire({ - rawEvents: [{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }], - }); + this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }]); return true; } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index a029e9b4ced1e..723e2ef3b5b10 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -77,13 +77,12 @@ export class CellEditor extends React.Component { })); this.toDispose.push(this.editor.onDocumentContentChanged(e => { notebookModel.cellDirtyChanged(cell, true); - cell.source = e.document.getText(); })); } } - protected assignRef = (component: HTMLDivElement) => { - this.container = component; + protected setContainer(component: HTMLDivElement | null): void { + this.container = component ?? undefined; }; protected handleResize = () => { @@ -91,7 +90,10 @@ export class CellEditor extends React.Component { }; override render(): React.ReactNode { - return
; - } + return
this.setContainer(container)}> + +
; + } } diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index ada36f51fc247..f1ea9aab4945a 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -47,7 +47,11 @@ export class NotebookCellListView extends React.Component { - this.setState({ selectedCell: undefined }); + if (e.newCellIds && e.newCellIds.length > 0) { + this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) }); + } else { + this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell)}); + } })); } @@ -101,7 +105,8 @@ export class NotebookCellListView extends React.Component, index: number): void { event.stopPropagation(); - event.dataTransfer.setData('text/notebook-cell-index', index.toString()); + event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString()); + event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source); } protected onDragOver(event: React.DragEvent, cell: NotebookCellModel, position?: 'top' | 'bottom'): void { @@ -112,7 +117,7 @@ export class NotebookCellListView extends React.Component, dropElementIndex: number): void { - const index = parseInt(event.dataTransfer.getData('text/notebook-cell-index')); + const index = parseInt(event.dataTransfer.getData('text/theia-notebook-cell-index')); const isTargetBelow = index < dropElementIndex; let newIdx = this.state.dragOverIndicator?.position === 'top' ? dropElementIndex : dropElementIndex + 1; newIdx = isTargetBelow ? newIdx - 1 : newIdx; diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx index 31fb3a15d2e5c..ac7038a893a29 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx @@ -29,6 +29,7 @@ export interface NotebookCellToolbarItem { icon?: string; label?: string; onClick: (e: React.MouseEvent) => void; + contextKeys?: Set } @injectable() @@ -85,7 +86,8 @@ export class NotebookCellToolbarFactory { includeAnchorArg: false, args: [notebookModel, cell, output] }) : - () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output) + () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output), + contextKeys: menuNode.when ? this.contextKeyService.parseKeys(menuNode.when) : undefined }; } } diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx index d1e366eb43b1b..adcdc2d3a83fa 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx @@ -17,24 +17,27 @@ import * as React from '@theia/core/shared/react'; import { ACTION_ITEM } from '@theia/core/lib/browser'; import { NotebookCellToolbarItem } from './notebook-cell-toolbar-factory'; import { DisposableCollection, Event } from '@theia/core'; +import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service'; export interface NotebookCellToolbarProps { getMenuItems: () => NotebookCellToolbarItem[]; - onContextKeysChanged: Event; + onContextKeysChanged: Event; } interface NotebookCellToolbarState { inlineItems: NotebookCellToolbarItem[]; } -abstract class NotebookCellActionItems extends React.Component { +abstract class NotebookCellActionBar extends React.Component { protected toDispose = new DisposableCollection(); constructor(props: NotebookCellToolbarProps) { super(props); this.toDispose.push(props.onContextKeysChanged(e => { - this.setState({ inlineItems: this.props.getMenuItems() }); + if (this.props.getMenuItems().some(item => item.contextKeys ? e.affects(item.contextKeys) : false)) { + this.setState({ inlineItems: this.props.getMenuItems() }); + } })); this.state = { inlineItems: this.props.getMenuItems() }; } @@ -49,7 +52,7 @@ abstract class NotebookCellActionItems extends React.Component @@ -59,7 +62,7 @@ export class NotebookCellToolbar extends NotebookCellActionItems { } -export class NotebookCellSidebar extends NotebookCellActionItems { +export class NotebookCellSidebar extends NotebookCellActionBar { override render(): React.ReactNode { return
diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index bc12319b10ca5..80e21e1974d34 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -66,6 +66,15 @@ export class NotebookMainToolbar extends React.Component(); + this.getMenuItems().filter(item => item.when).forEach(item => props.contextKeyService.parseKeys(item.when!)?.forEach(key => contextKeys.add(key))); + props.contextKeyService.onDidChange(e => { + if (e.affects(contextKeys)) { + this.forceUpdate(); + } + }); } override componentWillUnmount(): void { diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index 7fb5a4dbc26cc..e5e9591261398 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -14,11 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { CancellationToken, Command, Event, URI } from '@theia/core'; +import { Command, URI } from '@theia/core'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { UriComponents } from '@theia/core/lib/common/uri'; -import { CellRange } from './notebook-range'; export enum CellKind { Markup = 1, @@ -88,23 +87,6 @@ export interface NotebookCellCollapseState { outputCollapsed?: boolean; } -export interface NotebookCell { - readonly uri: URI; - handle: number; - language: string; - cellKind: CellKind; - outputs: CellOutput[]; - metadata: NotebookCellMetadata; - internalMetadata: NotebookCellInternalMetadata; - text: string; - onDidChangeOutputs?: Event; - onDidChangeOutputItems?: Event; - onDidChangeLanguage: Event; - onDidChangeMetadata: Event; - onDidChangeInternalMetadata: Event; - -} - export interface CellData { source: string; language: string; @@ -115,13 +97,6 @@ export interface CellData { collapseState?: NotebookCellCollapseState; } -export interface CellReplaceEdit { - editType: CellEditType.Replace; - index: number; - count: number; - cells: CellData[]; -} - export interface NotebookDocumentMetadataEdit { editType: CellEditType.DocumentMetadata; metadata: NotebookDocumentMetadata; @@ -140,32 +115,11 @@ export interface NotebookContributionData { exclusive: boolean; } -export interface NotebookCellStatusBarItemList { - items: NotebookCellStatusBarItem[]; - dispose?(): void; -} - -export interface NotebookCellStatusBarItemProvider { - viewType: string; - onDidChangeStatusBarItems?: Event; - provideCellStatusBarItems(uri: UriComponents, index: number, token: CancellationToken): Promise; -} - -export interface NotebookCellOutputsSplice { - start: number /* start */; - deleteCount: number /* delete count */; - newOutputs: CellOutput[]; -}; - -export interface CellInternalMetadataChangedEvent { - readonly lastRunSuccessChanged?: boolean; -} - -export type NotebookCellTextModelSplice = [ +export interface NotebookCellTextModelSplice { start: number, deleteCount: number, newItems: T[] -]; +}; export enum NotebookCellsChangeType { ModelChange = 1, @@ -182,69 +136,12 @@ export enum NotebookCellsChangeType { Unknown = 100 } -export enum SelectionStateType { - Handle = 0, - Index = 1 -} -export interface SelectionHandleState { - kind: SelectionStateType.Handle; - primary: number | null; - selections: number[]; -} - -export interface SelectionIndexState { - kind: SelectionStateType.Index; - focus: CellRange; - selections: CellRange[]; -} - -export type SelectionState = SelectionHandleState | SelectionIndexState; - -export interface NotebookTextModelChangedEvent { - readonly rawEvents: NotebookRawContentEvent[]; - // readonly versionId: number; - readonly synchronous?: boolean; - readonly endSelectionState?: SelectionState; -}; - -export interface NotebookCellsInitializeEvent { - readonly kind: NotebookCellsChangeType.Initialize; - readonly changes: NotebookCellTextModelSplice[]; -} - export interface NotebookCellsChangeLanguageEvent { readonly kind: NotebookCellsChangeType.ChangeCellLanguage; readonly index: number; readonly language: string; } -export interface NotebookCellsModelChangedEvent { - readonly kind: NotebookCellsChangeType.ModelChange; - readonly changes: NotebookCellTextModelSplice[]; -} - -export interface NotebookCellsModelMoveEvent { - readonly kind: NotebookCellsChangeType.Move; - readonly index: number; - readonly length: number; - readonly newIdx: number; - readonly cells: T[]; -} - -export interface NotebookOutputChangedEvent { - readonly kind: NotebookCellsChangeType.Output; - readonly index: number; - readonly outputs: CellOutput[]; - readonly append: boolean; -} - -export interface NotebookOutputItemChangedEvent { - readonly kind: NotebookCellsChangeType.OutputItem; - readonly index: number; - readonly outputId: string; - readonly outputItems: CellOutputItem[]; - readonly append: boolean; -} export interface NotebookCellsChangeMetadataEvent { readonly kind: NotebookCellsChangeType.ChangeCellMetadata; readonly index: number; @@ -257,36 +154,11 @@ export interface NotebookCellsChangeInternalMetadataEvent { readonly internalMetadata: NotebookCellInternalMetadata; } -export interface NotebookDocumentChangeMetadataEvent { - readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata; - readonly metadata: NotebookDocumentMetadata; -} - -export interface NotebookDocumentUnknownChangeEvent { - readonly kind: NotebookCellsChangeType.Unknown; -} - export interface NotebookCellContentChangeEvent { readonly kind: NotebookCellsChangeType.ChangeCellContent; readonly index: number; } -export type NotebookRawContentEvent = (NotebookCellsInitializeEvent | NotebookDocumentChangeMetadataEvent | NotebookCellContentChangeEvent | - NotebookCellsModelChangedEvent | NotebookCellsModelMoveEvent | NotebookOutputChangedEvent | NotebookOutputItemChangedEvent | - NotebookCellsChangeLanguageEvent | NotebookCellsChangeMetadataEvent | - NotebookCellsChangeInternalMetadataEvent | NotebookDocumentUnknownChangeEvent); // & { transient: boolean }; - -export interface NotebookModelChangedEvent { - readonly rawEvents: NotebookRawContentEvent[]; - readonly versionId: number; - // readonly synchronous: boolean | undefined; - // readonly endSelectionState: ISelectionState | undefined; -}; - -export interface NotebookModelWillAddRemoveEvent { - readonly rawEvent: NotebookCellsModelChangedEvent; -}; - export enum NotebookCellExecutionState { Unconfirmed = 1, Pending = 2, @@ -321,51 +193,12 @@ export interface CellExecutionStateUpdateDto { isPaused?: boolean; } -export interface CellOutputEdit { - editType: CellEditType.Output; - index: number; - outputs: CellOutput[]; - append?: boolean; -} - -export interface CellOutputEditByHandle { - editType: CellEditType.Output; - handle: number; - outputs: CellOutput[]; - append?: boolean; -} - -export interface CellOutputItemEdit { - editType: CellEditType.OutputItems; - items: CellOutputItem[]; - outputId: string; - append?: boolean; -} - export interface CellMetadataEdit { editType: CellEditType.Metadata; index: number; metadata: NotebookCellMetadata; } -export interface CellLanguageEdit { - editType: CellEditType.CellLanguage; - index: number; - language: string; -} - -export interface DocumentMetadataEdit { - editType: CellEditType.DocumentMetadata; - metadata: NotebookDocumentMetadata; -} - -export interface CellMoveEdit { - editType: CellEditType.Move; - index: number; - length: number; - newIdx: number; -} - export const enum CellEditType { Replace = 1, Output = 2, @@ -378,19 +211,6 @@ export const enum CellEditType { PartialInternalMetadata = 9, } -export type ImmediateCellEditOperation = CellOutputEditByHandle | CellOutputItemEdit | CellPartialInternalMetadataEditByHandle; // add more later on -export type CellEditOperation = ImmediateCellEditOperation | CellReplaceEdit | CellOutputEdit | - CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit; // add more later on - -export type NullablePartialNotebookCellInternalMetadata = { - [Key in keyof Partial]: NotebookCellInternalMetadata[Key] | null -}; -export interface CellPartialInternalMetadataEditByHandle { - editType: CellEditType.PartialInternalMetadata; - handle: number; - internalMetadata: NullablePartialNotebookCellInternalMetadata; -} - export interface NotebookKernelSourceAction { readonly label: string; readonly description?: string; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 71b67d64b82a7..c44cdbf396c54 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -2342,7 +2342,7 @@ export interface NotebookOutputItemDto { export interface NotebookOutputDto { outputId: string; items: NotebookOutputItemDto[]; - metadata?: Record; + metadata?: Record; } export interface NotebookCellDataDto { diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index 166869ef0d79c..a421ac4ae8f63 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -42,7 +42,7 @@ interface NotebookAndEditorDelta { } class NotebookAndEditorState { - static delta(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): NotebookAndEditorDelta { + static computeDelta(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): NotebookAndEditorDelta { if (!before) { return { addedDocuments: [...after.documents], @@ -148,7 +148,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain const editors = new Map(); const visibleEditorsMap = new Map(); - for (const editor of this.notebookEditorService.listNotebookEditors()) { + for (const editor of this.notebookEditorService.getNotebookEditors()) { if (editor.model) { editors.set(editor.id, editor); } @@ -176,12 +176,12 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain new Set(this.notebookService.listNotebookDocuments()), editors, activeEditor, visibleEditorsMap); - await this.onDelta(NotebookAndEditorState.delta(this.currentState, newState)); + await this.onDelta(NotebookAndEditorState.computeDelta(this.currentState, newState)); this.currentState = newState; } private async onDelta(delta: NotebookAndEditorDelta): Promise { - if (NotebooksAndEditorsMain._isDeltaEmpty(delta)) { + if (NotebooksAndEditorsMain.isDeltaEmpty(delta)) { return; } @@ -204,23 +204,23 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain this.notebookEditorsMain.handleEditorsAdded(delta.addedEditors); } - private static _isDeltaEmpty(delta: NotebookAndEditorDelta): boolean { - if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) { + private static isDeltaEmpty(delta: NotebookAndEditorDelta): boolean { + if (delta.addedDocuments?.length) { return false; } - if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) { + if (delta.removedDocuments?.length) { return false; } - if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) { + if (delta.addedEditors?.length) { return false; } - if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) { + if (delta.removedEditors?.length) { return false; } - if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) { + if (delta.visibleEditors?.length) { return false; } - if (delta.newActiveEditor !== undefined) { + if (delta.newActiveEditor) { return false; } return true; diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index d989535659219..04fe85dbcaacc 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -20,7 +20,7 @@ import { interfaces } from '@theia/core/shared/inversify'; import { NotebookModelResolverService } from '@theia/notebook/lib/browser'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { NotebookCellsChangeType } from '@theia/notebook/lib/common'; -import { MAIN_RPC_CONTEXT, NotebookCellDto, NotebookCellsChangedEventDto, NotebookDataDto, NotebookDocumentsExt, NotebookDocumentsMain } from '../../../common'; +import { MAIN_RPC_CONTEXT, NotebookCellsChangedEventDto, NotebookDataDto, NotebookDocumentsExt, NotebookDocumentsMain } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; @@ -54,23 +54,22 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { handleNotebooksAdded(notebooks: readonly NotebookModel[]): void { - for (const textModel of notebooks) { - const disposableStore = new DisposableCollection(); - disposableStore.push(textModel.onDidChangeContent(event => { + for (const notebook of notebooks) { + const listener = notebook.onDidChangeContent(events => { const eventDto: NotebookCellsChangedEventDto = { versionId: 1, // TODO implement version ID support rawEvents: [] }; - for (const e of event.rawEvents) { + for (const e of events) { switch (e.kind) { case NotebookCellsChangeType.ModelChange: eventDto.rawEvents.push({ kind: e.kind, changes: e.changes.map(diff => - [diff[0], diff[1], diff[2].map(NotebookDto.toNotebookCellDto)] as [number, number, NotebookCellDto[]]) + ({ ...diff, newItems: diff.newItems.map(NotebookDto.toNotebookCellDto) })) }); break; case NotebookCellsChangeType.Move: @@ -106,20 +105,20 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { } } - const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata); + const hasDocumentMetadataChangeEvent = events.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata); // using the model resolver service to know if the model is dirty or not. // assuming this is the first listener it can mean that at first the model // is marked as dirty and that another event is fired this.proxy.$acceptModelChanged( - textModel.uri.toComponents(), + notebook.uri.toComponents(), eventDto, - textModel.isDirty(), - hasDocumentMetadataChangeEvent ? textModel.metadata : undefined + notebook.isDirty(), + hasDocumentMetadataChangeEvent ? notebook.metadata : undefined ); - })); + }); - this.documentEventListenersMapping.set(textModel.uri.toString(), disposableStore); + this.documentEventListenersMapping.set(notebook.uri.toString(), new DisposableCollection(listener)); } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts index e6afbb8d79ed6..491613ff55267 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts @@ -18,6 +18,8 @@ import { OS } from '@theia/core'; import * as notebookCommon from '@theia/notebook/lib/common'; import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import * as rpc from '../../../common'; +import { CellExecutionUpdateType } from '@theia/notebook/lib/common'; +import { CellExecuteUpdate, CellExecutionComplete } from '@theia/notebook/lib/browser'; export namespace NotebookDto { @@ -102,40 +104,28 @@ export namespace NotebookDto { }; } - // export function fromCellExecuteUpdateDto(data: extHostProtocol.ICellExecuteUpdateDto): ICellExecuteUpdate { - // if (data.editType === CellExecutionUpdateType.Output) { - // return { - // editType: data.editType, - // cellHandle: data.cellHandle, - // append: data.append, - // outputs: data.outputs.map(fromNotebookOutputDto) - // }; - // } else if (data.editType === CellExecutionUpdateType.OutputItems) { - // return { - // editType: data.editType, - // append: data.append, - // outputId: data.outputId, - // items: data.items.map(fromNotebookOutputItemDto) - // }; - // } else { - // return data; - // } - // } + export function fromCellExecuteUpdateDto(data: rpc.CellExecuteUpdateDto): CellExecuteUpdate { + if (data.editType === CellExecutionUpdateType.Output) { + return { + editType: data.editType, + cellHandle: data.cellHandle, + append: data.append, + outputs: data.outputs.map(fromNotebookOutputDto) + }; + } else if (data.editType === CellExecutionUpdateType.OutputItems) { + return { + editType: data.editType, + outputId: data.outputId, + append: data.append, + items: data.items.map(fromNotebookOutputItemDto) + }; + } else { + return data; + } + } - // export function fromCellExecuteCompleteDto(data: extHostProtocol.ICellExecutionCompleteDto): ICellExecutionComplete { - // return data; - // } + export function fromCellExecuteCompleteDto(data: rpc.CellExecutionCompleteDto): CellExecutionComplete { + return data; + } - // export function fromCellEditOperationDto(edit: extHostProtocol.ICellEditOperationDto): notebookCommon.ICellEditOperation { - // if (edit.editType === notebookCommon.CellEditType.Replace) { - // return { - // editType: edit.editType, - // index: edit.index, - // count: edit.count, - // cells: edit.cells.map(fromNotebookCellDataDto) - // }; - // } else { - // return edit; - // } - // } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts index 6f9e735f1f112..d3ea21f53c753 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts @@ -27,7 +27,7 @@ import { CellExecution, NotebookExecutionStateService, NotebookKernelChangeEvent import { combinedDisposable } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; import { interfaces } from '@theia/core/shared/inversify'; import { NotebookKernelSourceAction } from '@theia/notebook/lib/common'; -import { NotebookDto } from '../../../plugin/type-converters'; +import { NotebookDto } from './notebook-dto'; abstract class NotebookKernel { private readonly onDidChangeEmitter = new Emitter(); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts index 19e3b39d62164..9764b6242ccde 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts @@ -14,9 +14,9 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { CancellationToken, DisposableCollection, Emitter } from '@theia/core'; +import { CancellationToken, DisposableCollection, Emitter, Event } from '@theia/core'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { NotebookCellStatusBarItemList, NotebookCellStatusBarItemProvider, NotebookData, TransientOptions } from '@theia/notebook/lib/common'; +import { NotebookCellStatusBarItem, NotebookData, TransientOptions } from '@theia/notebook/lib/common'; import { NotebookService } from '@theia/notebook/lib/browser'; import { Disposable } from '@theia/plugin'; import { MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common'; @@ -25,6 +25,17 @@ import { NotebookDto } from './notebook-dto'; import { UriComponents } from '@theia/core/lib/common/uri'; import { HostedPluginSupport } from '../../../hosted/browser/hosted-plugin'; +export interface NotebookCellStatusBarItemList { + items: NotebookCellStatusBarItem[]; + dispose?(): void; +} + +export interface NotebookCellStatusBarItemProvider { + viewType: string; + onDidChangeStatusBarItems?: Event; + provideCellStatusBarItems(uri: UriComponents, index: number, token: CancellationToken): Promise; +} + export class NotebooksMainImpl implements NotebooksMain { private readonly disposables = new DisposableCollection(); diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index cde345671d7f2..fbd832304adcb 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -20,7 +20,7 @@ import * as React from '@theia/core/shared/react'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; -import { NotebookRendererMessagingService, CellOutputWebview, NotebookRendererRegistry, NotebookEditorWidgetService } from '@theia/notebook/lib/browser'; +import { NotebookRendererMessagingService, CellOutputWebview, NotebookRendererRegistry, NotebookEditorWidgetService, NotebookCellOutputsSplice } from '@theia/notebook/lib/browser'; import { v4 } from 'uuid'; import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import { WebviewWidget } from '../../webview/webview'; @@ -28,7 +28,7 @@ import { Message, WidgetManager } from '@theia/core/lib/browser'; import { outputWebviewPreload, PreloadContext } from './output-webview-internal'; import { WorkspaceTrustService } from '@theia/workspace/lib/browser'; import { ChangePreferredMimetypeMessage, FromWebviewMessage, OutputChangedMessage } from './webview-communication'; -import { CellUri, NotebookCellOutputsSplice } from '@theia/notebook/lib/common'; +import { CellUri } from '@theia/notebook/lib/common'; import { Disposable, DisposableCollection, nls, QuickPickService } from '@theia/core'; import { NotebookCellOutputModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-output-model'; diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts index a49954a6fe125..66b63479be6fb 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts @@ -288,17 +288,17 @@ export class NotebookDocument implements Disposable { this.setCellOutputs(rawEvent.index, rawEvent.outputs); relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, outputs: this.cells[rawEvent.index].apiCell.outputs }); - // } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.OutputItem) { - // this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems); - // relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, outputs: this.cells[rawEvent.index].apiCell.outputs }); + // } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.OutputItem) { + // this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems); + // relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, outputs: this.cells[rawEvent.index].apiCell.outputs }); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellLanguage) { this.changeCellLanguage(rawEvent.index, rawEvent.language); relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, document: this.cells[rawEvent.index].apiCell.document }); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellContent) { relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, document: this.cells[rawEvent.index].apiCell.document }); - // } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMime) { - // this._changeCellMime(rawEvent.index, rawEvent.mime); + // } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMime) { + // this._changeCellMime(rawEvent.index, rawEvent.mime); } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeCellMetadata) { this.changeCellMetadata(rawEvent.index, rawEvent.metadata); relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, metadata: this.cells[rawEvent.index].apiCell.metadata }); @@ -351,7 +351,7 @@ export class NotebookDocument implements Disposable { const removedCellDocuments: UriComponents[] = []; splices.reverse().forEach(splice => { - const cellDtos = splice[2]; + const cellDtos = splice.newItems; const newCells = cellDtos.map((cell: NotebookCellDto) => { const extCell = new Cell(this, this.editorsAndDocuments, cell); @@ -361,8 +361,8 @@ export class NotebookDocument implements Disposable { return extCell; }); - const changeEvent = new RawContentChangeEvent(splice[0], splice[1], [], newCells); - const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells); + const changeEvent = new RawContentChangeEvent(splice.start, splice.deleteCount, [], newCells); + const deletedItems = this.cells.splice(splice.start, splice.deleteCount, ...newCells); for (const cell of deletedItems) { removedCellDocuments.push(cell.uri.toComponents()); changeEvent.deletedItems.push(cell.apiCell); diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 512c81b359e75..405d6873b7050 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -31,8 +31,7 @@ import { DisposableCollection, Mutable, isEmptyObject, isObject } from '@theia/c import * as notebooks from '@theia/notebook/lib/common'; import { CommandsConverter } from './command-registry'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { CellData, CellExecutionUpdateType, CellOutput, CellOutputItem, CellRange, isTextStreamMime } from '@theia/notebook/lib/common'; -import { CellExecuteUpdate, CellExecutionComplete } from '@theia/notebook/lib/browser'; +import { CellRange, isTextStreamMime } from '@theia/notebook/lib/common'; import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering'; import { TestItemDTO, TestMessageDTO } from '../common/test-types'; @@ -1636,126 +1635,6 @@ export namespace NotebookKernelSourceAction { } } -export namespace NotebookDto { - - export function toNotebookOutputItemDto(item: CellOutputItem): rpc.NotebookOutputItemDto { - return { - mime: item.mime, - valueBytes: item.data - }; - } - - export function toNotebookOutputDto(output: CellOutput): rpc.NotebookOutputDto { - return { - outputId: output.outputId, - metadata: output.metadata, - items: output.outputs.map(toNotebookOutputItemDto) - }; - } - - export function toNotebookCellDataDto(cell: CellData): rpc.NotebookCellDataDto { - return { - cellKind: cell.cellKind, - language: cell.language, - source: cell.source, - internalMetadata: cell.internalMetadata, - metadata: cell.metadata, - outputs: cell.outputs.map(toNotebookOutputDto) - }; - } - - // export function toNotebookDataDto(data: NotebookData): rpc.NotebookDataDto { - // return { - // metadata: data.metadata, - // cells: data.cells.map(toNotebookCellDataDto) - // }; - // } - - export function fromNotebookOutputItemDto(item: rpc.NotebookOutputItemDto): CellOutputItem { - return { - mime: item.mime, - data: item.valueBytes - }; - } - - export function fromNotebookOutputDto(output: rpc.NotebookOutputDto): CellOutput { - return { - outputId: output.outputId, - metadata: output.metadata, - outputs: output.items.map(fromNotebookOutputItemDto) - }; - } - - export function fromNotebookCellDataDto(cell: rpc.NotebookCellDataDto): CellData { - return { - cellKind: cell.cellKind, - language: cell.language, - source: cell.source, - outputs: cell.outputs.map(fromNotebookOutputDto), - metadata: cell.metadata, - internalMetadata: cell.internalMetadata - }; - } - - // export function fromNotebookDataDto(data: rpc.NotebookDataDto): NotebookData { - // return { - // metadata: data.metadata, - // cells: data.cells.map(fromNotebookCellDataDto) - // }; - // } - - // export function toNotebookCellDto(cell: Cell): rpc.NotebookCellDto { - // return { - // handle: cell.handle, - // uri: cell.uri, - // source: cell.textBuffer.getLinesContent(), - // eol: cell.textBuffer.getEOL(), - // language: cell.language, - // cellKind: cell.cellKind, - // outputs: cell.outputs.map(toNotebookOutputDto), - // metadata: cell.metadata, - // internalMetadata: cell.internalMetadata, - // }; - // } - - export function fromCellExecuteUpdateDto(data: rpc.CellExecuteUpdateDto): CellExecuteUpdate { - if (data.editType === CellExecutionUpdateType.Output) { - return { - editType: data.editType, - cellHandle: data.cellHandle, - append: data.append, - outputs: data.outputs.map(fromNotebookOutputDto) - }; - } else if (data.editType === CellExecutionUpdateType.OutputItems) { - return { - editType: data.editType, - append: data.append, - outputId: data.outputId, - items: data.items.map(fromNotebookOutputItemDto) - }; - } else { - return data; - } - } - - export function fromCellExecuteCompleteDto(data: rpc.CellExecutionCompleteDto): CellExecutionComplete { - return data; - } - - // export function fromCellEditOperationDto(edit: rpc.CellEditOperationDto): CellEditOperation { - // if (edit.editType === CellEditType.Replace) { - // return { - // editType: edit.editType, - // index: edit.index, - // count: edit.count, - // cells: edit.cells.map(fromNotebookCellDataDto) - // }; - // } else { - // return edit; - // } - // } -} - export namespace TestMessage { export function from(message: theia.TestMessage | readonly theia.TestMessage[]): TestMessageDTO[] { if (isReadonlyArray(message)) { From de4c42433361b50652ee9ba6d6d3b508ba55bc99 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 14 Dec 2023 13:30:17 +0100 Subject: [PATCH 004/441] Fix notebook kernel selection (#13171) --- .../src/browser/notebook-editor-widget.tsx | 16 ++++++--- .../service/notebook-editor-widget-service.ts | 29 +++++++-------- .../notebook-kernel-quick-pick-service.ts | 13 ++++--- .../notebook/src/common/notebook-common.ts | 23 +++++++++++- .../notebook-documents-and-editors-main.ts | 36 +++++++++++++------ .../plugin-ext/src/plugin/command-registry.ts | 11 ++++-- .../src/plugin/notebook/notebooks.ts | 10 +++--- 7 files changed, 90 insertions(+), 48 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 6ca2be986332c..ca70be1b17a5a 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -28,6 +28,7 @@ import { inject, injectable, interfaces, postConstruct } from '@theia/core/share import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol'; import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service'; import { NotebookMainToolbarRenderer } from './view/notebook-main-toolbar'; +import { Deferred } from '@theia/core/lib/common/promise-util'; export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory'); @@ -82,11 +83,16 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected readonly renderers = new Map(); protected _model?: NotebookModel; + protected _ready: Deferred = new Deferred(); get notebookType(): string { return this.props.notebookType; } + get ready(): Promise { + return this._ready.promise; + } + get model(): NotebookModel | undefined { return this._model; } @@ -103,17 +109,17 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.renderers.set(CellKind.Markup, this.markdownCellRenderer); this.renderers.set(CellKind.Code, this.codeCellRenderer); - this.waitForData(); - + this._ready.resolve(this.waitForData()); } - protected async waitForData(): Promise { + protected async waitForData(): Promise { this._model = await this.props.notebookData; this.saveable.delegate = this._model; this.toDispose.push(this._model); // Ensure that the model is loaded before adding the editor this.notebookEditorService.addNotebookEditor(this); this.update(); + return this._model; } protected override onActivateRequest(msg: Message): void { @@ -130,11 +136,11 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa } undo(): void { - this.model?.undo(); + this._model?.undo(); } redo(): void { - this.model?.redo(); + this._model?.redo(); } protected render(): ReactNode { diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index 3416285f4adc7..e0dd0419977bf 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -19,13 +19,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableCollection, Emitter } from '@theia/core'; +import { Emitter } from '@theia/core'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { ApplicationShell } from '@theia/core/lib/browser'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @injectable() -export class NotebookEditorWidgetService implements Disposable { +export class NotebookEditorWidgetService { @inject(ApplicationShell) protected applicationShell: ApplicationShell; @@ -40,27 +40,22 @@ export class NotebookEditorWidgetService implements Disposable { private readonly onDidChangeFocusedEditorEmitter = new Emitter(); readonly onDidChangeFocusedEditor = this.onDidChangeFocusedEditorEmitter.event; - private readonly toDispose = new DisposableCollection(); - focusedEditor?: NotebookEditorWidget = undefined; @postConstruct() protected init(): void { - this.toDispose.push(this.applicationShell.onDidChangeActiveWidget(event => { - if (event.newValue instanceof NotebookEditorWidget && event.newValue !== this.focusedEditor) { - this.focusedEditor = event.newValue; - this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor); - } else { + this.applicationShell.onDidChangeActiveWidget(event => { + if (event.newValue instanceof NotebookEditorWidget) { + if (event.newValue !== this.focusedEditor) { + this.focusedEditor = event.newValue; + this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor); + } + } else if (event.newValue) { + // Only unfocus editor if a new widget has been focused + this.focusedEditor = undefined; this.onDidChangeFocusedEditorEmitter.fire(undefined); } - })); - } - - dispose(): void { - this.onNotebookEditorAddEmitter.dispose(); - this.onNotebookEditorRemoveEmitter.dispose(); - this.onDidChangeFocusedEditorEmitter.dispose(); - this.toDispose.dispose(); + }); } // --- editor management diff --git a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts index 874672cee0ab7..b1387fa7241d5 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts @@ -18,13 +18,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ArrayUtils, Command, CommandService, DisposableCollection, Event, nls, QuickInputButton, QuickInputService, QuickPickInput, QuickPickItem, URI, } from '@theia/core'; +import { ArrayUtils, CommandService, DisposableCollection, Event, nls, QuickInputButton, QuickInputService, QuickPickInput, QuickPickItem, URI, } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { NotebookKernelService, NotebookKernel, NotebookKernelMatchResult, SourceCommand } from './notebook-kernel-service'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookEditorWidget } from '../notebook-editor-widget'; import { codicon, OpenerService } from '@theia/core/lib/browser'; import { NotebookKernelHistoryService } from './notebook-kernel-history-service'; +import { NotebookCommand, NotebookModelResource } from '../../common'; import debounce = require('@theia/core/shared/lodash.debounce'); export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter'; @@ -43,7 +44,7 @@ function isSourcePick(item: QuickPickInput): item is SourcePick { } type InstallExtensionPick = QuickPickItem & { extensionIds: string[] }; -type KernelSourceQuickPickItem = QuickPickItem & { command: Command; documentation?: string }; +type KernelSourceQuickPickItem = QuickPickItem & { command: NotebookCommand; documentation?: string }; function isKernelSourceQuickPickItem(item: QuickPickItem): item is KernelSourceQuickPickItem { return 'command' in item; } @@ -469,10 +470,8 @@ export class NotebookKernelQuickPickService { quickPick.show(); } - private async executeCommand(notebook: NotebookModel, command: string | Command): Promise { - const id = typeof command === 'string' ? command : command.id; - - return this.commandService.executeCommand(id, { uri: notebook.uri }); - + private async executeCommand(notebook: NotebookModel, command: NotebookCommand): Promise { + const args = (command.arguments || []).concat([NotebookModelResource.create(notebook.uri)]); + return this.commandService.executeCommand(command.id, ...args); } } diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index e5e9591261398..0812027f0ca5a 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -14,11 +14,19 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, URI } from '@theia/core'; +import { Command, URI, isObject } from '@theia/core'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { UriComponents } from '@theia/core/lib/common/uri'; +export interface NotebookCommand { + id: string; + title?: string; + tooltip?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + arguments?: any[]; +} + export enum CellKind { Markup = 1, Code = 2 @@ -159,6 +167,19 @@ export interface NotebookCellContentChangeEvent { readonly index: number; } +export interface NotebookModelResource { + notebookModelUri: URI; +} + +export namespace NotebookModelResource { + export function is(item: unknown): item is NotebookModelResource { + return isObject(item) && item.notebookModelUri instanceof URI; + } + export function create(uri: URI): NotebookModelResource { + return { notebookModelUri: uri }; + } +} + export enum NotebookCellExecutionState { Unconfirmed = 1, Pending = 2, diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index a421ac4ae8f63..baad9444acdfa 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -23,7 +23,7 @@ import { interfaces } from '@theia/core/shared/inversify'; import { UriComponents } from '@theia/core/lib/common/uri'; import { NotebookEditorWidget, NotebookService, NotebookEditorWidgetService } from '@theia/notebook/lib/browser'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; -import { MAIN_RPC_CONTEXT, NotebookDocumentsAndEditorsDelta, NotebookDocumentsAndEditorsMain, NotebookModelAddedData, NotebooksExt } from '../../../common'; +import { MAIN_RPC_CONTEXT, NotebookDocumentsAndEditorsDelta, NotebookDocumentsAndEditorsMain, NotebookEditorAddData, NotebookModelAddedData, NotebooksExt } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; import { WidgetManager } from '@theia/core/lib/browser'; @@ -31,6 +31,7 @@ import { NotebookEditorsMainImpl } from './notebook-editors-main'; import { NotebookDocumentsMainImpl } from './notebook-documents-main'; import { diffMaps, diffSets } from '../../../common/collections'; import { Mutex } from 'async-mutex'; +import throttle = require('@theia/core/shared/lodash.throttle'); interface NotebookAndEditorDelta { removedDocuments: UriComponents[]; @@ -106,12 +107,12 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain this.notebookEditorService = container.get(NotebookEditorWidgetService); this.WidgetManager = container.get(WidgetManager); - this.notebookService.onDidAddNotebookDocument(async () => this.updateState(), this, this.disposables); - this.notebookService.onDidRemoveNotebookDocument(async () => this.updateState(), this, this.disposables); + this.notebookService.onDidAddNotebookDocument(async () => this.throttleStateUpdate(), this, this.disposables); + this.notebookService.onDidRemoveNotebookDocument(async () => this.throttleStateUpdate(), this, this.disposables); // this.WidgetManager.onActiveEditorChanged(() => this.updateState(), this, this.disposables); this.notebookEditorService.onDidAddNotebookEditor(async editor => this.handleEditorAdd(editor), this, this.disposables); this.notebookEditorService.onDidRemoveNotebookEditor(async editor => this.handleEditorRemove(editor), this, this.disposables); - this.notebookEditorService.onDidChangeFocusedEditor(async editor => this.updateState(editor), this, this.disposables); + this.notebookEditorService.onDidChangeFocusedEditor(async editor => this.throttleStateUpdate(editor), this, this.disposables); } dispose(): void { @@ -129,16 +130,18 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain } else { this.editorListeners.set(editor.id, [disposable]); } - await this.updateState(); + await this.throttleStateUpdate(); } private handleEditorRemove(editor: NotebookEditorWidget): void { const listeners = this.editorListeners.get(editor.id); listeners?.forEach(listener => listener.dispose()); this.editorListeners.delete(editor.id); - this.updateState(); + this.throttleStateUpdate(); } + private throttleStateUpdate = throttle((focusedEditor?: NotebookEditorWidget) => this.updateState(focusedEditor), 100); + private async updateState(focusedEditor?: NotebookEditorWidget): Promise { await this.updateMutex.runExclusive(async () => this.doUpdateState(focusedEditor)); } @@ -149,9 +152,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain const visibleEditorsMap = new Map(); for (const editor of this.notebookEditorService.getNotebookEditors()) { - if (editor.model) { - editors.set(editor.id, editor); - } + editors.set(editor.id, editor); } const activeNotebookEditor = this.notebookEditorService.focusedEditor; @@ -167,7 +168,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain const notebookEditors = this.WidgetManager.getWidgets(NotebookEditorWidget.ID) as NotebookEditorWidget[]; for (const notebookEditor of notebookEditors) { - if (notebookEditor?.model && editors.has(notebookEditor.id) && notebookEditor.isVisible) { + if (editors.has(notebookEditor.id) && notebookEditor.isVisible) { visibleEditorsMap.set(notebookEditor.id, notebookEditor); } } @@ -191,7 +192,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain newActiveEditor: delta.newActiveEditor, visibleEditors: delta.visibleEditors, addedDocuments: delta.addedDocuments.map(NotebooksAndEditorsMain.asModelAddData), - // addedEditors: delta.addedEditors.map(this.asEditorAddData, this), + addedEditors: delta.addedEditors.map(NotebooksAndEditorsMain.asEditorAddData), }; // send to extension FIRST @@ -235,4 +236,17 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain cells: e.cells.map(NotebookDto.toNotebookCellDto) }; } + + private static asEditorAddData(notebookEditor: NotebookEditorWidget): NotebookEditorAddData { + const uri = notebookEditor.getResourceUri(); + if (!uri) { + throw new Error('Notebook editor without resource URI'); + } + return { + id: notebookEditor.id, + documentUri: uri.toComponents(), + selections: [], + visibleRanges: [] + }; + } } diff --git a/packages/plugin-ext/src/plugin/command-registry.ts b/packages/plugin-ext/src/plugin/command-registry.ts index e30576309736d..0db2f4c16c354 100644 --- a/packages/plugin-ext/src/plugin/command-registry.ts +++ b/packages/plugin-ext/src/plugin/command-registry.ts @@ -204,11 +204,16 @@ export class CommandsConverter { // eslint-disable-next-line @typescript-eslint/no-explicit-any private executeSafeCommand(...args: any[]): PromiseLike { - const command = this.commandsMap.get(args[0]); + const handle = args[0]; + if (typeof handle !== 'number') { + return Promise.reject(`Invalid handle ${handle}`); + } + const command = this.commandsMap.get(handle); if (!command || !command.command) { - return Promise.reject(`command ${args[0]} not found`); + return Promise.reject(`Safe command with handle ${handle} not found`); } - return this.commands.executeCommand(command.command, ...(command.arguments || [])); + const allArgs = (command.arguments ?? []).concat(args.slice(1)); + return this.commands.executeCommand(command.command, ...allArgs); } } diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 773c9c3ba081b..58aa248cb4396 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -36,6 +36,7 @@ import { NotebookDocument } from './notebook-document'; import { NotebookEditor } from './notebook-editor'; import { EditorsAndDocumentsExtImpl } from '../editors-and-documents'; import { DocumentsExtImpl } from '../documents'; +import { NotebookModelResource } from '@theia/notebook/lib/common'; export class NotebooksExtImpl implements NotebooksExt { @@ -82,11 +83,12 @@ export class NotebooksExtImpl implements NotebooksExt { this.notebookEditors = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN); commands.registerArgumentProcessor({ - processArgument: (arg: { uri: URI }) => { - if (arg && arg.uri && this.documents.has(arg.uri.toString())) { - return this.documents.get(arg.uri.toString())?.apiNotebook; + processArgument: arg => { + if (NotebookModelResource.is(arg)) { + return this.documents.get(arg.notebookModelUri.toString())?.apiNotebook; + } else { + return arg; } - return arg; } }); } From ee085d548c3921b719151ac320c799358b9fd48b Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Thu, 14 Dec 2023 15:57:56 +0100 Subject: [PATCH 005/441] fix: do not reject unknown schemes in WindowStateExt.asExternalUri (#13057) Fixes #128210 Contributed on behalf of STMicroelectronics Signed-off-by: Alexandra Buzila --- packages/plugin-ext/src/plugin/window-state.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/plugin/window-state.ts b/packages/plugin-ext/src/plugin/window-state.ts index 160356282c8e9..4b4e58aac3bbd 100644 --- a/packages/plugin-ext/src/plugin/window-state.ts +++ b/packages/plugin-ext/src/plugin/window-state.ts @@ -19,7 +19,6 @@ import { WindowState } from '@theia/plugin'; import { WindowStateExt, WindowMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { RPCProtocol } from '../common/rpc-protocol'; -import { Schemes } from '../common/uri-components'; export class WindowStateExtImpl implements WindowStateExt { @@ -50,6 +49,10 @@ export class WindowStateExtImpl implements WindowStateExt { } openUri(uri: URI): Promise { + if (!uri.scheme.trim().length) { + throw new Error('Invalid scheme - cannot be empty'); + } + return this.proxy.$openUri(uri); } @@ -57,9 +60,6 @@ export class WindowStateExtImpl implements WindowStateExt { if (!target.scheme.trim().length) { throw new Error('Invalid scheme - cannot be empty'); } - if (Schemes.http !== target.scheme && Schemes.https !== target.scheme) { - throw new Error(`Invalid scheme '${target.scheme}'`); - } const uri = await this.proxy.$asExternalUri(target); return URI.revive(uri); From 2b1c71eb845838842c8bb617440adc294e37289f Mon Sep 17 00:00:00 2001 From: Beniamino Ventura <105274814+bvenreply@users.noreply.github.com> Date: Fri, 15 Dec 2023 00:42:58 +0100 Subject: [PATCH 006/441] fix: set `display: block` on plugin icon pseudo-element (#13096) (#13101) Fix the icon pseudo-element being displayed as `inline` by default. This meant that the `width` and `height` properties were being ignored and the icons displayed with 0 width. Also fix css syntax for `width` and `height` properties. Signed-off-by: Beniamino Ventura Co-authored-by: Beniamino Ventura --- packages/plugin-ext/src/main/browser/plugin-shared-style.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/plugin-shared-style.ts b/packages/plugin-ext/src/main/browser/plugin-shared-style.ts index 8fa32a52d9fba..760de38475cad 100644 --- a/packages/plugin-ext/src/main/browser/plugin-shared-style.ts +++ b/packages/plugin-ext/src/main/browser/plugin-shared-style.ts @@ -125,8 +125,9 @@ export class PluginSharedStyle { toDispose.push(this.insertRule('.' + iconClass + '::before', theme => ` content: ""; background-position: 2px; - width: ${size}'px'; - height: ${size}'px'; + display: block; + width: ${size}px; + height: ${size}px; background: center no-repeat url("${theme.type === 'light' ? lightIconUrl : darkIconUrl}"); background-size: ${size}px; `)); From c30a79f90d9393e392cabfebbce612695cbe0d40 Mon Sep 17 00:00:00 2001 From: bryanchenmchp <146364035+bryanchenmchp@users.noreply.github.com> Date: Thu, 14 Dec 2023 18:43:51 -0500 Subject: [PATCH 007/441] Use app shell methods for expand / collapse for toggle terminal command (#13131) * Use app shell methods for expand / collapse Signed-off-by: Bryan Chen --- CHANGELOG.md | 3 +++ .../src/browser/terminal-frontend-contribution.ts | 12 ++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0599a3bed35b0..77a91d23b8776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +## Unreleased +- [terminal] Use application shell methods for expanding/collapsing bottom panel for "Terminal: Toggle Terminal" command [#13131](https://github.com/eclipse-theia/theia/pull/13131) + ## v1.44.0 - 11/30/2023 - [application-manager] added option to copy `trash` dependency to the bundle [#13112](https://github.com/eclipse-theia/theia/pull/13112) diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index 2263f1402e692..0602a6ca5f078 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -637,7 +637,6 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu } protected toggleTerminal(): void { - const terminals = this.shell.getWidgets('bottom').filter(w => w instanceof TerminalWidget); if (terminals.length === 0) { @@ -645,20 +644,17 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu return; } - if (this.shell.bottomPanel.isHidden) { - this.shell.bottomPanel.setHidden(false); + if (!this.shell.isExpanded('bottom')) { + this.shell.expandPanel('bottom'); terminals[0].activate(); - return; - } - - if (this.shell.bottomPanel.isVisible) { + } else { const visibleTerminal = terminals.find(t => t.isVisible); if (!visibleTerminal) { this.shell.bottomPanel.activateWidget(terminals[0]); } else if (this.shell.activeWidget !== visibleTerminal) { this.shell.bottomPanel.activateWidget(visibleTerminal); } else { - this.shell.bottomPanel.setHidden(true); + this.shell.collapsePanel('bottom'); } } From c38af6644ddc0a677f52191affeef04c24038dcc Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 18 Dec 2023 08:41:49 +0100 Subject: [PATCH 008/441] Incorrect "Unsupported activation event" error in stdout #12953 * move supported activation events to PluginManagerInitializeParams * add env variable which allows adding additional activation events --- .../plugin-ext/src/common/plugin-api-rpc.ts | 1 + .../src/hosted/browser/hosted-plugin.ts | 34 ++++++++++++++++++- .../plugin-ext/src/plugin/plugin-manager.ts | 28 +++------------ 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index c44cdbf396c54..5516220d17cd3 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -234,6 +234,7 @@ export interface PluginManagerInitializeParams { extApi?: ExtPluginApi[] webview: WebviewInitData jsonValidation: PluginJsonValidationContribution[] + supportedActivationEvents?: string[] } export interface PluginManagerStartParams { diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 13679f53bd200..297f9295eac1b 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -80,6 +80,30 @@ export const ALL_ACTIVATION_EVENT = '*'; @injectable() export class HostedPluginSupport { + protected static ADDITIONAL_ACTIVATION_EVENTS_ENV = 'ADDITIONAL_ACTIVATION_EVENTS'; + protected static BUILTIN_ACTIVATION_EVENTS = [ + '*', + 'onLanguage', + 'onCommand', + 'onDebug', + 'onDebugInitialConfigurations', + 'onDebugResolve', + 'onDebugAdapterProtocolTracker', + 'onDebugDynamicConfigurations', + 'onTaskType', + 'workspaceContains', + 'onView', + 'onUri', + 'onTerminalProfile', + 'onWebviewPanel', + 'onFileSystem', + 'onCustomEditor', + 'onStartupFinished', + 'onAuthenticationRequest', + 'onNotebook', + 'onNotebookSerializer' + ]; + protected readonly clientId = UUID.uuid4(); protected container: interfaces.Container; @@ -519,6 +543,13 @@ export class HostedPluginSupport { } const isElectron = environment.electron.is(); + + const supportedActivationEvents = [...HostedPluginSupport.BUILTIN_ACTIVATION_EVENTS]; + const additionalActivationEvents = await this.envServer.getValue(HostedPluginSupport.ADDITIONAL_ACTIVATION_EVENTS_ENV); + if (additionalActivationEvents && additionalActivationEvents.value) { + additionalActivationEvents.value.split(',').forEach(event => supportedActivationEvents.push(event)); + } + await manager.$init({ preferences: getPreferences(this.preferenceProviderProvider, this.workspaceService.tryGetRoots()), globalState, @@ -536,7 +567,8 @@ export class HostedPluginSupport { webviewResourceRoot, webviewCspSource }, - jsonValidation + jsonValidation, + supportedActivationEvents }); if (toDisconnect.disposed) { return undefined; diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index e3a95e87970de..6486441dfda89 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -79,29 +79,6 @@ class ActivatedPlugin { export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { - static SUPPORTED_ACTIVATION_EVENTS = new Set([ - '*', - 'onLanguage', - 'onCommand', - 'onDebug', - 'onDebugInitialConfigurations', - 'onDebugResolve', - 'onDebugAdapterProtocolTracker', - 'onDebugDynamicConfigurations', - 'onTaskType', - 'workspaceContains', - 'onView', - 'onUri', - 'onTerminalProfile', - 'onWebviewPanel', - 'onFileSystem', - 'onCustomEditor', - 'onStartupFinished', - 'onAuthenticationRequest', - 'onNotebook', - 'onNotebookSerializer' - ]); - private configStorage: ConfigStorage | undefined; private readonly registry = new Map(); private readonly activations = new Map Promise)[] | undefined>(); @@ -113,6 +90,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { private onDidChangeEmitter = new Emitter(); private messageRegistryProxy: MessageRegistryMain; private notificationMain: NotificationMain; + private supportedActivationEvents: Set; protected fireOnDidChange(): void { this.onDidChangeEmitter.fire(undefined); } @@ -219,6 +197,8 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { this.webview.init(params.webview); this.jsonValidation = params.jsonValidation; + + this.supportedActivationEvents = new Set(params.supportedActivationEvents ?? []); } async $start(params: PluginManagerStartParams): Promise { @@ -263,7 +243,7 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { const activation = () => this.$activatePlugin(plugin.model.id); // an internal activation event is a subject to change this.setActivation(`onPlugin:${plugin.model.id}`, activation); - const unsupportedActivationEvents = plugin.rawModel.activationEvents.filter(e => !PluginManagerExtImpl.SUPPORTED_ACTIVATION_EVENTS.has(e.split(':')[0])); + const unsupportedActivationEvents = plugin.rawModel.activationEvents.filter(e => !this.supportedActivationEvents.has(e.split(':')[0])); if (unsupportedActivationEvents.length) { console.warn(`Unsupported activation events: ${unsupportedActivationEvents.join(', ')}, please open an issue: https://github.com/eclipse-theia/theia/issues/new`); } From 2d30b295c1158f401d63591eb67b46e93cd6f318 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Mon, 18 Dec 2023 09:56:37 +0100 Subject: [PATCH 009/441] Improve documentation on the addition of Plugin API in the plugin host (#13153) * Improve documentation on the addition of Plugin API in the plugin host Fixes https://github.com/eclipse-theia/theia/issues/13067 --- doc/Plugin-API.md | 6 +- .../doc/how-to-add-new-custom-plugin-api.md | 285 ++++++++++++++++++ .../doc/how-to-add-new-plugin-namespace.md | 112 ------- 3 files changed, 288 insertions(+), 115 deletions(-) create mode 100644 packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md delete mode 100644 packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md diff --git a/doc/Plugin-API.md b/doc/Plugin-API.md index 729619fc5b04d..fc63f73818803 100644 --- a/doc/Plugin-API.md +++ b/doc/Plugin-API.md @@ -75,7 +75,7 @@ The `Ext` side then gets the cached object, executes appropriate functions and r ## Adding new API -This section gives an introduction to extending Theia’s plugin API. If you want to add a whole new namespace in your own extension, see this [readme](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md). +This section gives an introduction to extending Theia’s plugin API. If you want to add a complete custom plugin API in your own extension, see this [readme](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md). For adding new API, the first step is to declare it in the [theia.d.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin/src/theia.d.ts) file in the plugin package. In a second step, the implementation for the new API must be made available in the returned object of the API factory in [plugin-context.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/plugin-context.ts). @@ -173,7 +173,7 @@ They can be added here and the added to the API object created in the API factor Talk by Thomas Maeder on writing plugin API: -Adding a new plugin API namespace outside of theia plugin API: [how-to-add-new-plugin-namespace.md](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md) +Adding a new custom plugin API outside of Theia plugin API: [how-to-add-new-custom-plugin-api.md](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md) Theia Plugin Implementation wiki page: @@ -183,4 +183,4 @@ Theia versus VS Code API Comparator: -Example of creating a custom namespace API and using in VS Code extensions: https://github.com/thegecko/vscode-theia-extension +Example of creating a custom namespace API and using in VS Code extensions: diff --git a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md new file mode 100644 index 0000000000000..8ef05c2e32592 --- /dev/null +++ b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md @@ -0,0 +1,285 @@ +# How to add new custom plugin API + +As a Theia developer, you might want to make your app extensible by plugins in ways that are unique to your application. +That will require API that goes beyond what's in the VS Code Extension API and the Theia plugin API. +You can do that by implementing a Theia extension that creates and exposes an API object within the plugin host. +The API object can be imported by your plugins and exposes one or more API namespaces. + +Depending on the plugin host we can either provide a frontend or backend plugin API: + +- In the backend plugin host that runs in the Node environment in a separate process, we adapt the module loading to return a custom API object instead of loading a module with a particular name. +- In the frontend plugin host that runs in the browser environment via a web worker, we import the API scripts and put it in the global context. + +In this document we focus on the implementation of a custom backend plugin API. +However, both APIs can be provided by implementing and binding an `ExtPluginApiProvider` which should be packaged as a Theia extension. + +## Declare your plugin API provider + +The plugin API provider is executed on the respective plugin host to add your custom API object and namespaces. +Add `@theia/plugin-ext` as a dependency in your `package.json` + +Example Foo Plugin API provider: + +```typescript +@injectable() +export class FooExtPluginApiProvider implements ExtPluginApiProvider { + provideApi(): ExtPluginApi { + return { + frontendExtApi: { + initPath: '/path/to/foo/api/implementation.js', + initFunction: 'fooInitializationFunction', + initVariable: 'foo_global_variable' + }, + backendInitPath: path.join(__dirname, 'foo-init') + }; + } +} +``` + +Register your Plugin API provider in a backend module: + +```typescript + bind(FooExtPluginApiProvider).toSelf().inSingletonScope(); + bind(Symbol.for(ExtPluginApiProvider)).toService(FooExtPluginApiProvider); +``` + +## Define your API + +To ease the usage of your API, it should be developed as separate npm package that can be easily imported without any additional dependencies, cf, the VS Code API or the Theia Plugin API. + +Example `foo.d.ts`: + +```typescript +declare module '@bar/foo' { + export class Foo { } + + export namespace fooBar { + export function getFoo(): Promise; + } +} +``` + +## Implement your plugin API provider + +In our example, we aim to provide a new API object for the backend. +Theia expects that the `backendInitPath` that we specified in our API provider is a function called `provideApi` that follows the `ExtPluginApiBackendInitializationFn` signature. + +Example `node/foo-init.ts`: + +```typescript +import * as fooBarAPI from '@bar/foo'; + +// Factory to create an API object for each plugin. +let apiFactory: (plugin: Plugin) => typeof fooBarAPI; + +// Map key is the plugin ID. Map value is the FooBar API object. +const pluginsApiImpl = new Map(); + +// Singleton API object to use as a last resort. +let defaultApi: typeof fooBarAPI; + +// Have we hooked into the module loader yet? +let hookedModuleLoader = false; + +let plugins: PluginManager; + +// Theia expects an exported 'provideApi' function +export const provideApi: ExtPluginApiBackendInitializationFn = (rpc: RPCProtocol, manager: PluginManager) => { + apiFactory = createAPIFactory(rpc); + plugins = manager; + + if (!hookedModuleLoader) { + overrideInternalLoad(); + hookedModuleLoader = true; + } +}; + +function overrideInternalLoad(): void { + const module = require('module'); + const internalLoad = module._load; + + module._load = function (request: string, parent: any, isMain: {}) { + if (request !== '@bar/foo') { + // Pass the request to the next implementation down the chain + return internalLoad.apply(this, arguments); + } + + // create custom API object and return that as a result of loading '@bar/foo' + const plugin = findPlugin(parent.filename); + if (plugin) { + let apiImpl = pluginsApiImpl.get(plugin.model.id); + if (!apiImpl) { + apiImpl = apiFactory(plugin); + pluginsApiImpl.set(plugin.model.id, apiImpl); + } + return apiImpl; + } + + if (!defaultApi) { + console.warn(`Could not identify plugin for '@bar/foo' require call from ${parent.filename}`); + defaultApi = apiFactory(emptyPlugin); + } + + return defaultApi; + }; +} + +function findPlugin(filePath: string): Plugin | undefined { + return plugins.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder)); +} +``` + +## Implement your API object + +We create a dedicated API object for each individual plugin as part of the module loading process. +Each API object is returned as part of the module loading process if a script imports `@bar/foo` and should therefore match the API definition that we provided in the `*.d.ts` file. +Multiple imports will not lead to the creation of multiple API objects as we cache it in our custom `overrideInternalLoad` function. + +Example `node/foo-init.ts` (continued): + +```typescript +export function createAPIFactory(rpc: RPCProtocol): ApiFactory { + const fooExtImpl = new FooExtImpl(rpc); + return function (plugin: Plugin): typeof fooBarAPI { + const FooBar: typeof fooBarAPI.fooBar = { + getFoo(): Promise { + return fooExtImpl.getFooImpl(); + } + } + return { + fooBar : FooBar + }; + } +} +``` + +In the example above the API object creates a local object that will fulfill the API contract. +The implementation details are hidden by the object and it could be a local implementation that only lives inside the plugin host but it could also be an implementation that uses the `RPCProtocol` to communicate with the main application to trigger changes, register functionality or retrieve information. + +### Implement Main-Ext communication + +In this document, we will only highlight the individual parts needed to establish the communication between the main application and the external plugin host. +For a more elaborate example of an API that communicates with the main application, please have a look at the definition of the [Theia Plugin API](https://github.com/eclipse-theia/theia/blob/master/doc/Plugin-API.md). + +First, we need to establish the communication on the RPC protocol by providing an implementation for our own side and generating a proxy for the opposite side. +Proxies are identified using dedicated identifiers so we set them up first, together with the expected interfaces. +`Ext` and `Main` interfaces contain the functions called over RCP and must start with `$`. +Due to the asynchronous nature of the communication over RPC, the result should always be a `Promise` or `PromiseLike`. + +Example `common/foo-api-rpc.ts`: + +```typescript +export interface FooMain { + $getFooImpl(): Promise; +} + +export interface FooExt { + // placeholder for callbacks for the main application to the extension +} + +// Plugin host will obtain a proxy using these IDs, main application will register an implementation for it. +export const FOO_PLUGIN_RPC_CONTEXT = { + FOO_MAIN: createProxyIdentifier('FooMain') +}; + +// Main application will obtain a proxy using these IDs, plugin host will register an implementation for it. +export const FOO_MAIN_RPC_CONTEXT = { + FOO_EXT: createProxyIdentifier('FooExt') +}; +``` + +On the plugin host side we can register our implementation and retrieve the proxy as part of our `createAPIFactory` implementation: + +Example `plugin/foo-ext.ts`: + +```typescript +export class FooExtImpl implements FooExt { + // Main application RCP counterpart + private proxy: FooMain; + + constructor(rpc: RPCProtocol) { + rpc.set(FOO_MAIN_RPC_CONTEXT.FOO_EXT, this); // register ourselves + this.proxy = rpc.getProxy(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN); // retrieve proxy + } + + getFooImpl(): Promise { + return this.proxy.$getFooImpl(); + } +} +``` + +On the main side we need to implement the counterpart of the ExtPluginApiProvider, the `MainPluginApiProvider`, and expose it in a browser frontend module: + +Example `main/browser/foo-main.ts`: + +```typescript +@injectable() +export class FooMainImpl implements FooMain { + @inject(MessageService) protected messageService: MessageService; + protected proxy: FooExt; + + init(rpc: RPCProtocol) { + // We would use this if we had a need to call back into the plugin-host/plugin + this.proxy = rpc.getProxy(FOO_MAIN_RPC_CONTEXT.FOO_EXT); + } + + async $getFooImpl(): Promise { + this.messageService.info('We were called from the plugin-host at the behest of the plugin.'); + return new Foo(); + } +} + +@injectable() +export class FooMainPluginApiProvider implements MainPluginApiProvider { + @inject(MessageService) protected messageService: MessageService; + + initialize(rpc: RPCProtocol, container: interfaces.Container): void { + this.messageService.info('Initialize RPC communication for FooMain!'); + // create a new FooMainImpl as it is not bound as singleton + const fooMainImpl = container.get(FooMainImpl); + fooMainImpl.init(rpc); + rpc.set(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN, fooMainImpl); + } +} + +export default new ContainerModule(bind => { + bind(FooMainImpl).toSelf(); + bind(MainPluginApiProvider).to(FooMainPluginApiProvider).inSingletonScope(); +}); +``` + +In this example, we can already see the big advantage of going to the main application side as we have full access to our Theia services. + +## Usage in a plugin + +When using the API in a plugin the user can simply use the API as follows: + +```typescript +import * as foo from '@bar/foo'; + +foo.fooBar.getFoo(); +``` + +## Packaging + +When bundling our application with the generated `gen-webpack.node.config.js` we need to make sure that our initialization function is bundled as a `commonjs2` library so it can be dynamically loaded. +Adjust the `webpack.config.js` accordingly: + +```typescript +const configs = require('./gen-webpack.config.js'); +const nodeConfig = require('./gen-webpack.node.config.js'); + +if (nodeConfig.config.entry) { + /** + * Add our initialization function. If unsure, look at the already generated entries for + * the nodeConfig where an entry is added for the default 'backend-init-theia' initialization. + */ + nodeConfig.config.entry['foo-init'] = { + import: require.resolve('@namespace/package/lib/node/foo-init'), + library: { type: 'commonjs2' } + }; +} + +module.exports = [...configs, nodeConfig.config]; + +``` diff --git a/packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md b/packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md deleted file mode 100644 index e8f8611374770..0000000000000 --- a/packages/plugin-ext/doc/how-to-add-new-plugin-namespace.md +++ /dev/null @@ -1,112 +0,0 @@ -# This document describes how to add new plugin api namespace - -New Plugin API namespace should be packaged as Theia extension - -## Provide your API or namespace - -This API developed in the way that you provide your API as separate npm package. -In that package you can declare your api. -Example `foo.d.ts`: - -```typescript - declare module '@bar/foo' { - export namespace fooBar { - export function getFoo(): Foo; - } - } -``` - -## Declare `ExtPluginApiProvider` implementation - -```typescript -@injectable() -export class FooPluginApiProvider implements ExtPluginApiProvider { - provideApi(): ExtPluginApi { - return { - frontendExtApi: { - initPath: '/path/to/foo/api/implementation.js', - initFunction: 'fooInitializationFunction', - initVariable: 'foo_global_variable' - }, - backendInitPath: path.join(__dirname, 'path/to/backend/foo/implementation.js') - }; - } -} -``` - -## Then you need to register `FooPluginApiProvider`, add next sample in your backend module - -Example: - -```typescript - bind(FooPluginApiProvider).toSelf().inSingletonScope(); - bind(Symbol.for(ExtPluginApiProvider)).toService(FooPluginApiProvider); -``` - -## Next you need to implement `ExtPluginApiBackendInitializationFn`, which should handle `@bar/foo` module loading and instantiate `@foo/bar` API object, `path/to/backend/foo/implementation.js` example : - -```typescript -export const provideApi: ExtPluginApiBackendInitializationFn = (rpc: RPCProtocol, pluginManager: PluginManager) => { - cheApiFactory = createAPIFactory(rpc); - plugins = pluginManager; - - if (!isLoadOverride) { - overrideInternalLoad(); - isLoadOverride = true; - } - -}; - -function overrideInternalLoad(): void { - const module = require('module'); - const internalLoad = module._load; - - module._load = function (request: string, parent: any, isMain: {}) { - if (request !== '@bar/foo') { - return internalLoad.apply(this, arguments); - } - - const plugin = findPlugin(parent.filename); - if (plugin) { - let apiImpl = pluginsApiImpl.get(plugin.model.id); - if (!apiImpl) { - apiImpl = cheApiFactory(plugin); - pluginsApiImpl.set(plugin.model.id, apiImpl); - } - return apiImpl; - } - - if (!defaultApi) { - console.warn(`Could not identify plugin for '@bar/foo' require call from ${parent.filename}`); - defaultApi = cheApiFactory(emptyPlugin); - } - - return defaultApi; - }; -} - -function findPlugin(filePath: string): Plugin | undefined { - return plugins.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder)); -} -``` - -## Next you need to implement `createAPIFactory` factory function - -Example: - -```typescript -import * as fooApi from '@bar/foo'; -export function createAPIFactory(rpc: RPCProtocol): ApiFactory { - const fooBarImpl = new FooBarImpl(rpc); - return function (plugin: Plugin): typeof fooApi { - const FooBar: typeof fooApi.fooBar = { - getFoo(): fooApi.Foo{ - return fooBarImpl.getFooImpl(); - } - } - return { - fooBar : FooBar - }; - } - -``` From d2bc79bc1aac9cc424da7ac7945fbbb5d71f28c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 18 Dec 2023 16:36:58 +0100 Subject: [PATCH 010/441] Introduce timeout for keeping connection contexts alive (#13082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #12823 - refactor front end to allow for multiple reconnections - remove IWebsockt abstractions - separate front end connections from service channel management - introduce mechanism to reconnect front end to existing connection context based on timeouts Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../src/application-props.ts | 15 +- .../updater/sample-updater-frontend-module.ts | 2 +- .../update/sample-updater-main-module.ts | 4 +- examples/browser/package.json | 9 +- examples/electron/package.json | 8 +- .../browser/connection-status-service.spec.ts | 12 +- .../src/browser/connection-status-service.ts | 4 +- .../browser/messaging/connection-source.ts | 26 +++ .../browser/messaging/frontend-id-provider.ts | 37 +++ .../messaging/messaging-frontend-module.ts | 22 +- .../messaging/service-connection-provider.ts | 126 +++++++++++ .../messaging/ws-connection-provider.ts | 157 ++----------- .../browser/messaging/ws-connection-source.ts | 210 ++++++++++++++++++ .../core/src/common/message-rpc/channel.ts | 6 +- .../src/common/message-rpc/message-buffer.ts | 6 + .../message-rpc/uint8-array-message-buffer.ts | 7 + .../messaging/abstract-connection-provider.ts | 115 ---------- .../common/messaging/connection-management.ts | 43 ++++ packages/core/src/common/messaging/handler.ts | 2 + .../common/messaging/socket-write-buffer.ts | 52 +++++ .../common/messaging/web-socket-channel.ts | 73 +++--- .../electron-frontend-id-provider.ts | 25 +++ ...r.ts => electron-ipc-connection-source.ts} | 30 +-- ...=> electron-local-ws-connection-source.ts} | 4 +- .../electron-messaging-frontend-module.ts | 61 ++++- ...er.ts => electron-ws-connection-source.ts} | 23 +- packages/core/src/electron-browser/preload.ts | 16 +- .../window/electron-window-module.ts | 2 +- .../window/electron-window-service.ts | 11 + .../core/src/electron-common/electron-api.ts | 7 + .../src/electron-main/electron-api-main.ts | 21 +- .../electron-main-application-module.ts | 10 +- .../messaging/electron-connection-handler.ts | 0 .../electron-messaging-contribution.ts | 88 ++++---- .../messaging/electron-messaging-service.ts | 1 + .../electron-main/theia-electron-window.ts | 7 +- .../electron-token-messaging-contribution.ts | 41 ---- .../messaging/default-messaging-service.ts | 129 +++++++++++ .../messaging/frontend-connection-service.ts | 24 ++ .../messaging/messaging-backend-module.ts | 35 ++- .../node/messaging/messaging-contribution.ts | 197 ---------------- .../src/node/messaging/messaging-service.ts | 6 +- .../messaging/test/test-web-socket-channel.ts | 59 ++--- .../src/node/messaging/websocket-endpoint.ts | 79 +++++++ .../websocket-frontend-connection-service.ts | 171 ++++++++++++++ .../src/browser/debug-session-contribution.ts | 10 +- .../src/node/debug-adapter-session-manager.ts | 2 +- .../plugin-ext/src/common/proxy-handler.ts | 4 +- .../src/hosted/browser/hosted-plugin.ts | 1 + .../task/src/node/task-server.slow-spec.ts | 12 +- .../src/browser/terminal-widget-impl.ts | 14 +- ...terminal-backend-contribution.slow-spec.ts | 14 +- .../src/node/terminal-backend-contribution.ts | 2 +- 53 files changed, 1320 insertions(+), 722 deletions(-) create mode 100644 packages/core/src/browser/messaging/connection-source.ts create mode 100644 packages/core/src/browser/messaging/frontend-id-provider.ts create mode 100644 packages/core/src/browser/messaging/service-connection-provider.ts create mode 100644 packages/core/src/browser/messaging/ws-connection-source.ts delete mode 100644 packages/core/src/common/messaging/abstract-connection-provider.ts create mode 100644 packages/core/src/common/messaging/connection-management.ts create mode 100644 packages/core/src/common/messaging/socket-write-buffer.ts create mode 100644 packages/core/src/electron-browser/messaging/electron-frontend-id-provider.ts rename packages/core/src/electron-browser/messaging/{electron-ipc-connection-provider.ts => electron-ipc-connection-source.ts} (61%) rename packages/core/src/electron-browser/messaging/{electron-local-ws-connection-provider.ts => electron-local-ws-connection-source.ts} (89%) rename packages/core/src/electron-browser/messaging/{electron-ws-connection-provider.ts => electron-ws-connection-source.ts} (67%) rename packages/core/src/{electron-common => electron-main}/messaging/electron-connection-handler.ts (100%) delete mode 100644 packages/core/src/electron-node/token/electron-token-messaging-contribution.ts create mode 100644 packages/core/src/node/messaging/default-messaging-service.ts create mode 100644 packages/core/src/node/messaging/frontend-connection-service.ts delete mode 100644 packages/core/src/node/messaging/messaging-contribution.ts create mode 100644 packages/core/src/node/messaging/websocket-endpoint.ts create mode 100644 packages/core/src/node/messaging/websocket-frontend-connection-service.ts diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index e0c9ffca031fa..78bcece1c011e 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -86,7 +86,8 @@ export namespace FrontendApplicationConfig { defaultIconTheme: 'theia-file-icons', electron: ElectronFrontendApplicationConfig.DEFAULT, defaultLocale: '', - validatePreferencesSchema: true + validatePreferencesSchema: true, + reloadOnReconnect: false }; export interface Partial extends ApplicationConfig { @@ -132,6 +133,12 @@ export namespace FrontendApplicationConfig { * Defaults to `true`. */ readonly validatePreferencesSchema?: boolean; + + /** + * When 'true', the window will reload in case the front end reconnects to a back-end, + * but the back end does not have a connection context for this front end anymore. + */ + readonly reloadOnReconnect?: boolean; } } @@ -142,6 +149,7 @@ export type BackendApplicationConfig = RequiredRecursive { bind(SampleUpdaterImpl).toSelf().inSingletonScope(); bind(SampleUpdater).toService(SampleUpdaterImpl); bind(ElectronMainApplicationContribution).toService(SampleUpdater); - bind(ElectronConnectionHandler).toDynamicValue(context => + bind(ConnectionHandler).toDynamicValue(context => new RpcConnectionHandler(SampleUpdaterPath, client => { const server = context.container.get(SampleUpdater); server.setClient(client); diff --git a/examples/browser/package.json b/examples/browser/package.json index ad2289ee0f4c5..b68dbb7208c41 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -9,8 +9,15 @@ "applicationName": "Theia Browser Example", "preferences": { "files.enableTrash": false - } + }, + "reloadOnReconnect": true } + }, + "backend": { + "config": { + "frontendConnectionTimeout": 3000 + } + } }, "dependencies": { diff --git a/examples/electron/package.json b/examples/electron/package.json index 6fd2bffc3ea17..11646f24e893a 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -9,7 +9,13 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Theia Electron Example" + "applicationName": "Theia Electron Example", + "reloadOnReconnect": true + } + }, + "backend": { + "config": { + "frontendConnectionTimeout": -1 } } }, diff --git a/packages/core/src/browser/connection-status-service.spec.ts b/packages/core/src/browser/connection-status-service.spec.ts index 2d570f0ec62f6..3e3d8fb45efc6 100644 --- a/packages/core/src/browser/connection-status-service.spec.ts +++ b/packages/core/src/browser/connection-status-service.spec.ts @@ -33,8 +33,8 @@ import { MockConnectionStatusService } from './test/mock-connection-status-servi import * as sinon from 'sinon'; import { Container } from 'inversify'; -import { WebSocketConnectionProvider } from './messaging/ws-connection-provider'; import { ILogger, Emitter, Loggable } from '../common'; +import { WebSocketConnectionSource } from './messaging/ws-connection-source'; disableJSDOM(); @@ -101,7 +101,7 @@ describe('frontend-connection-status', function (): void { let timer: sinon.SinonFakeTimers; let pingSpy: sinon.SinonSpy; beforeEach(() => { - const mockWebSocketConnectionProvider = sinon.createStubInstance(WebSocketConnectionProvider); + const mockWebSocketConnectionSource = sinon.createStubInstance(WebSocketConnectionSource); const mockPingService: PingService = { ping(): Promise { return Promise.resolve(undefined); @@ -118,11 +118,11 @@ describe('frontend-connection-status', function (): void { testContainer.bind(PingService).toConstantValue(mockPingService); testContainer.bind(ILogger).toConstantValue(mockILogger); testContainer.bind(ConnectionStatusOptions).toConstantValue({ offlineTimeout: OFFLINE_TIMEOUT }); - testContainer.bind(WebSocketConnectionProvider).toConstantValue(mockWebSocketConnectionProvider); + testContainer.bind(WebSocketConnectionSource).toConstantValue(mockWebSocketConnectionSource); - sinon.stub(mockWebSocketConnectionProvider, 'onSocketDidOpen').value(mockSocketOpenedEmitter.event); - sinon.stub(mockWebSocketConnectionProvider, 'onSocketDidClose').value(mockSocketClosedEmitter.event); - sinon.stub(mockWebSocketConnectionProvider, 'onIncomingMessageActivity').value(mockIncomingMessageActivityEmitter.event); + sinon.stub(mockWebSocketConnectionSource, 'onSocketDidOpen').value(mockSocketOpenedEmitter.event); + sinon.stub(mockWebSocketConnectionSource, 'onSocketDidClose').value(mockSocketClosedEmitter.event); + sinon.stub(mockWebSocketConnectionSource, 'onIncomingMessageActivity').value(mockIncomingMessageActivityEmitter.event); timer = sinon.useFakeTimers(); diff --git a/packages/core/src/browser/connection-status-service.ts b/packages/core/src/browser/connection-status-service.ts index 790a702480f00..0a4b58006dfe3 100644 --- a/packages/core/src/browser/connection-status-service.ts +++ b/packages/core/src/browser/connection-status-service.ts @@ -19,8 +19,8 @@ import { ILogger } from '../common/logger'; import { Event, Emitter } from '../common/event'; import { DefaultFrontendApplicationContribution } from './frontend-application-contribution'; import { StatusBar, StatusBarAlignment } from './status-bar/status-bar'; -import { WebSocketConnectionProvider } from './messaging/ws-connection-provider'; import { Disposable, DisposableCollection, nls } from '../common'; +import { WebSocketConnectionSource } from './messaging/ws-connection-source'; /** * Service for listening on backend connection changes. @@ -119,7 +119,7 @@ export class FrontendConnectionStatusService extends AbstractConnectionStatusSer private scheduledPing: number | undefined; - @inject(WebSocketConnectionProvider) protected readonly wsConnectionProvider: WebSocketConnectionProvider; + @inject(WebSocketConnectionSource) protected readonly wsConnectionProvider: WebSocketConnectionSource; @inject(PingService) protected readonly pingService: PingService; @postConstruct() diff --git a/packages/core/src/browser/messaging/connection-source.ts b/packages/core/src/browser/messaging/connection-source.ts new file mode 100644 index 0000000000000..8e48110edb360 --- /dev/null +++ b/packages/core/src/browser/messaging/connection-source.ts @@ -0,0 +1,26 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Channel, Event } from '../../common'; + +export const ConnectionSource = Symbol('ConnectionSource'); + +/** + * A ConnectionSource creates a Channel. The channel is valid until it sends a close event. + */ +export interface ConnectionSource { + onConnectionDidOpen: Event; +} diff --git a/packages/core/src/browser/messaging/frontend-id-provider.ts b/packages/core/src/browser/messaging/frontend-id-provider.ts new file mode 100644 index 0000000000000..24fd5aa67e398 --- /dev/null +++ b/packages/core/src/browser/messaging/frontend-id-provider.ts @@ -0,0 +1,37 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from 'inversify'; +import { generateUuid } from '../../common/uuid'; + +export const FrontendIdProvider = Symbol('FrontendIdProvider'); + +/** + * A FronendIdProvider computes an id for an instance of the front end that may be reconnected to a back end + * connection context. + */ +export interface FrontendIdProvider { + getId(): string; +} + +@injectable() +export class BrowserFrontendIdProvider implements FrontendIdProvider { + protected readonly id = generateUuid(); // generate a new id each time we load the application + + getId(): string { + return this.id; + } +} diff --git a/packages/core/src/browser/messaging/messaging-frontend-module.ts b/packages/core/src/browser/messaging/messaging-frontend-module.ts index f852c8f651231..a7c742c0cb9d8 100644 --- a/packages/core/src/browser/messaging/messaging-frontend-module.ts +++ b/packages/core/src/browser/messaging/messaging-frontend-module.ts @@ -15,9 +15,27 @@ // ***************************************************************************** import { ContainerModule } from 'inversify'; -import { LocalWebSocketConnectionProvider, WebSocketConnectionProvider } from './ws-connection-provider'; +import { BrowserFrontendIdProvider, FrontendIdProvider } from './frontend-id-provider'; +import { WebSocketConnectionSource } from './ws-connection-source'; +import { LocalConnectionProvider, RemoteConnectionProvider, ServiceConnectionProvider } from './service-connection-provider'; +import { ConnectionSource } from './connection-source'; +import { ConnectionCloseService, connectionCloseServicePath } from '../../common/messaging/connection-management'; +import { WebSocketConnectionProvider } from './ws-connection-provider'; + +const backendServiceProvider = Symbol('backendServiceProvider'); export const messagingFrontendModule = new ContainerModule(bind => { + bind(ConnectionCloseService).toDynamicValue(ctx => WebSocketConnectionProvider.createProxy(ctx.container, connectionCloseServicePath)).inSingletonScope(); + bind(BrowserFrontendIdProvider).toSelf().inSingletonScope(); + bind(FrontendIdProvider).toService(BrowserFrontendIdProvider); + bind(WebSocketConnectionSource).toSelf().inSingletonScope(); + bind(backendServiceProvider).toDynamicValue(ctx => { + bind(ServiceConnectionProvider).toSelf().inSingletonScope(); + const container = ctx.container.createChild(); + container.bind(ConnectionSource).toService(WebSocketConnectionSource); + return container.get(ServiceConnectionProvider); + }).inSingletonScope(); + bind(LocalConnectionProvider).toService(backendServiceProvider); + bind(RemoteConnectionProvider).toService(backendServiceProvider); bind(WebSocketConnectionProvider).toSelf().inSingletonScope(); - bind(LocalWebSocketConnectionProvider).toService(WebSocketConnectionProvider); }); diff --git a/packages/core/src/browser/messaging/service-connection-provider.ts b/packages/core/src/browser/messaging/service-connection-provider.ts new file mode 100644 index 0000000000000..d946b0b289c47 --- /dev/null +++ b/packages/core/src/browser/messaging/service-connection-provider.ts @@ -0,0 +1,126 @@ +// ***************************************************************************** +// Copyright (C) 2020 Ericsson and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, interfaces, postConstruct } from 'inversify'; +import { Channel, RpcProxy, RpcProxyFactory } from '../../common'; +import { ChannelMultiplexer } from '../../common/message-rpc/channel'; +import { Deferred } from '../../common/promise-util'; +import { ConnectionSource } from './connection-source'; + +export const LocalConnectionProvider = Symbol('LocalConnectionProvider'); +export const RemoteConnectionProvider = Symbol('RemoteConnectionProvider'); + +export namespace ServiceConnectionProvider { + export type ConnectionHandler = (path: String, channel: Channel) => void; +} + +/** + * This class manages the channels for remote services in the back end + */ +@injectable() +export class ServiceConnectionProvider { + + static createProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { + return container.get(RemoteConnectionProvider).createProxy(path, arg); + } + + static createLocalProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { + return container.get(LocalConnectionProvider).createProxy(path, arg); + } + + static createHandler(container: interfaces.Container, path: string, arg?: object): void { + const remote = container.get(RemoteConnectionProvider); + const local = container.get(LocalConnectionProvider); + remote.createProxy(path, arg); + if (remote !== local) { + local.createProxy(path, arg); + } + } + + protected readonly channelHandlers = new Map(); + + /** + * Create a proxy object to remote interface of T type + * over a web socket connection for the given path and proxy factory. + */ + createProxy(path: string, factory: RpcProxyFactory): RpcProxy; + /** + * Create a proxy object to remote interface of T type + * over a web socket connection for the given path. + * + * An optional target can be provided to handle + * notifications and requests from a remote side. + */ + createProxy(path: string, target?: object): RpcProxy; + createProxy(path: string, arg?: object): RpcProxy { + const factory = arg instanceof RpcProxyFactory ? arg : new RpcProxyFactory(arg); + this.listen(path, (_, c) => factory.listen(c), true); + return factory.createProxy(); + } + + protected channelMultiplexer: ChannelMultiplexer; + + private channelReadyDeferred = new Deferred(); + protected get channelReady(): Promise { + return this.channelReadyDeferred.promise; + } + + @postConstruct() + init(): void { + this.connectionSource.onConnectionDidOpen(channel => this.handleChannelCreated(channel)); + } + + @inject(ConnectionSource) + protected connectionSource: ConnectionSource; + + /** + * This method must be invoked by subclasses when they have created the main channel. + * @param mainChannel + */ + protected handleChannelCreated(channel: Channel): void { + channel.onClose(() => { + this.handleChannelClosed(channel); + }); + + this.channelMultiplexer = new ChannelMultiplexer(channel); + this.channelReadyDeferred.resolve(); + for (const entry of this.channelHandlers.entries()) { + this.openChannel(entry[0], entry[1]); + } + } + + handleChannelClosed(channel: Channel): void { + this.channelReadyDeferred = new Deferred(); + } + + /** + * Install a connection handler for the given path. + */ + listen(path: string, handler: ServiceConnectionProvider.ConnectionHandler, reconnect: boolean): void { + this.openChannel(path, handler).then(() => { + if (reconnect) { + this.channelHandlers.set(path, handler); + } + }); + + } + + private async openChannel(path: string, handler: ServiceConnectionProvider.ConnectionHandler): Promise { + await this.channelReady; + const newChannel = await this.channelMultiplexer.open(path); + handler(path, newChannel); + } +} diff --git a/packages/core/src/browser/messaging/ws-connection-provider.ts b/packages/core/src/browser/messaging/ws-connection-provider.ts index 822110c58fd8b..676990461b061 100644 --- a/packages/core/src/browser/messaging/ws-connection-provider.ts +++ b/packages/core/src/browser/messaging/ws-connection-provider.ts @@ -14,160 +14,35 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, interfaces, decorate, unmanaged } from 'inversify'; -import { RpcProxyFactory, RpcProxy, Emitter, Event, Channel } from '../../common'; -import { Endpoint } from '../endpoint'; -import { AbstractConnectionProvider } from '../../common/messaging/abstract-connection-provider'; -import { io, Socket } from 'socket.io-client'; -import { IWebSocket, WebSocketChannel } from '../../common/messaging/web-socket-channel'; +import { injectable, interfaces, decorate, unmanaged, inject } from 'inversify'; +import { RpcProxyFactory, RpcProxy } from '../../common'; +import { RemoteConnectionProvider, ServiceConnectionProvider } from './service-connection-provider'; decorate(injectable(), RpcProxyFactory); decorate(unmanaged(), RpcProxyFactory, 0); -export const LocalWebSocketConnectionProvider = Symbol('LocalWebSocketConnectionProvider'); - -export interface WebSocketOptions { - /** - * True by default. - */ - reconnecting?: boolean; -} - +/** + * @deprecated This class serves to keep API compatiliblity for a while. Use {@linkcode ServiceConnectionProvider} instead. + */ @injectable() -export class WebSocketConnectionProvider extends AbstractConnectionProvider { - - protected readonly onSocketDidOpenEmitter: Emitter = new Emitter(); - get onSocketDidOpen(): Event { - return this.onSocketDidOpenEmitter.event; - } - - protected readonly onSocketDidCloseEmitter: Emitter = new Emitter(); - get onSocketDidClose(): Event { - return this.onSocketDidCloseEmitter.event; - } +export class WebSocketConnectionProvider { + @inject(RemoteConnectionProvider) + private readonly remoteConnectionProvider: ServiceConnectionProvider; - static override createProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { - return container.get(WebSocketConnectionProvider).createProxy(path, arg); + static createProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { + return ServiceConnectionProvider.createProxy(container, path, arg); } static createLocalProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { - return container.get(LocalWebSocketConnectionProvider).createProxy(path, arg); + return ServiceConnectionProvider.createLocalProxy(container, path, arg); } static createHandler(container: interfaces.Container, path: string, arg?: object): void { - const remote = container.get(WebSocketConnectionProvider); - const local = container.get(LocalWebSocketConnectionProvider); - remote.createProxy(path, arg); - if (remote !== local) { - local.createProxy(path, arg); - } + return ServiceConnectionProvider.createHandler(container, path, arg); } - protected readonly socket: Socket; - - constructor() { - super(); - const url = this.createWebSocketUrl(WebSocketChannel.wsPath); - this.socket = this.createWebSocket(url); - this.socket.on('connect', () => { - this.initializeMultiplexer(); - if (this.reconnectChannelOpeners.length > 0) { - this.reconnectChannelOpeners.forEach(opener => opener()); - this.reconnectChannelOpeners = []; - } - this.socket.on('disconnect', () => this.fireSocketDidClose()); - this.socket.on('message', () => this.onIncomingMessageActivityEmitter.fire(undefined)); - this.fireSocketDidOpen(); - }); - this.socket.connect(); - } - - protected createMainChannel(): Channel { - return new WebSocketChannel(this.toIWebSocket(this.socket)); - } - - protected toIWebSocket(socket: Socket): IWebSocket { - return { - close: () => { - socket.removeAllListeners('disconnect'); - socket.removeAllListeners('error'); - socket.removeAllListeners('message'); - }, - isConnected: () => socket.connected, - onClose: cb => socket.on('disconnect', reason => cb(reason)), - onError: cb => socket.on('error', reason => cb(reason)), - onMessage: cb => socket.on('message', data => cb(data)), - send: message => socket.emit('message', message) - }; - } - - override async openChannel(path: string, handler: (channel: Channel) => void, options?: WebSocketOptions): Promise { - if (this.socket.connected) { - return super.openChannel(path, handler, options); - } else { - const openChannel = () => { - this.socket.off('connect', openChannel); - this.openChannel(path, handler, options); - }; - this.socket.on('connect', openChannel); - } - } - - /** - * @param path The handler to reach in the backend. - */ - protected createWebSocketUrl(path: string): string { - // Since we are using Socket.io, the path should look like the following: - // proto://domain.com/{path} - return this.createEndpoint(path).getWebSocketUrl().withPath(path).toString(); - } - - protected createHttpWebSocketUrl(path: string): string { - return this.createEndpoint(path).getRestUrl().toString(); - } - - protected createEndpoint(path: string): Endpoint { - return new Endpoint({ path }); - } - - /** - * Creates a web socket for the given url - */ - protected createWebSocket(url: string): Socket { - return io(url, { - path: this.createSocketIoPath(url), - reconnection: true, - reconnectionDelay: 1000, - reconnectionDelayMax: 10000, - reconnectionAttempts: Infinity, - extraHeaders: { - // Socket.io strips the `origin` header - // We need to provide our own for validation - 'fix-origin': window.location.origin - } - }); - } - - /** - * Path for Socket.io to make its requests to. - */ - protected createSocketIoPath(url: string): string | undefined { - if (location.protocol === Endpoint.PROTO_FILE) { - return '/socket.io'; - } - let { pathname } = location; - if (!pathname.endsWith('/')) { - pathname += '/'; - } - return pathname + 'socket.io'; - } - - protected fireSocketDidOpen(): void { - this.onSocketDidOpenEmitter.fire(undefined); - } - - protected fireSocketDidClose(): void { - this.onSocketDidCloseEmitter.fire(undefined); + createProxy(path: string, target?: object): RpcProxy; + createProxy(path: string, factory: RpcProxyFactory): RpcProxy { + return this.remoteConnectionProvider.createProxy(path, factory); } } - diff --git a/packages/core/src/browser/messaging/ws-connection-source.ts b/packages/core/src/browser/messaging/ws-connection-source.ts new file mode 100644 index 0000000000000..a1e9b83814035 --- /dev/null +++ b/packages/core/src/browser/messaging/ws-connection-source.ts @@ -0,0 +1,210 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { AbstractChannel, Channel, Disposable, DisposableCollection, Emitter, Event, servicesPath } from '../../common'; +import { ConnectionSource } from './connection-source'; +import { Socket, io } from 'socket.io-client'; +import { Endpoint } from '../endpoint'; +import { ForwardingChannel } from '../../common/message-rpc/channel'; +import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer'; +import { inject, injectable, postConstruct } from 'inversify'; +import { FrontendIdProvider } from './frontend-id-provider'; +import { FrontendApplicationConfigProvider } from '../frontend-application-config-provider'; +import { SocketWriteBuffer } from '../../common/messaging/socket-write-buffer'; +import { ConnectionManagementMessages } from '../../common/messaging/connection-management'; + +@injectable() +export class WebSocketConnectionSource implements ConnectionSource { + static readonly NO_CONNECTION = ''; + + @inject(FrontendIdProvider) + protected readonly frontendIdProvider: FrontendIdProvider; + + private readonly writeBuffer = new SocketWriteBuffer(); + + private _socket: Socket; + get socket(): Socket { + return this._socket; + } + + protected currentChannel: AbstractChannel; + + protected readonly onConnectionDidOpenEmitter: Emitter = new Emitter(); + get onConnectionDidOpen(): Event { + return this.onConnectionDidOpenEmitter.event; + } + + protected readonly onSocketDidOpenEmitter: Emitter = new Emitter(); + get onSocketDidOpen(): Event { + return this.onSocketDidOpenEmitter.event; + } + + protected readonly onSocketDidCloseEmitter: Emitter = new Emitter(); + get onSocketDidClose(): Event { + return this.onSocketDidCloseEmitter.event; + } + + protected readonly onIncomingMessageActivityEmitter: Emitter = new Emitter(); + get onIncomingMessageActivity(): Event { + return this.onIncomingMessageActivityEmitter.event; + } + + constructor() { + } + + @postConstruct() + openSocket(): void { + const url = this.createWebSocketUrl(servicesPath); + this._socket = this.createWebSocket(url); + + this._socket.on('connect', () => { + this.onSocketDidOpenEmitter.fire(); + this.handleSocketConnected(); + }); + + this._socket.on('disconnect', () => { + this.onSocketDidCloseEmitter.fire(); + }); + + this._socket.on('error', reason => { + if (this.currentChannel) { + this.currentChannel.onErrorEmitter.fire(reason); + }; + }); + this._socket.connect(); + } + + protected handleSocketConnected(): void { + if (this.currentChannel) { + const reconnectListener = (hasConnection: boolean) => { + this._socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); + if (hasConnection) { + this.writeBuffer.flush(this.socket); + } else { + if (FrontendApplicationConfigProvider.get().reloadOnReconnect) { + window.location.reload(); // this might happen in the preload module, when we have no window service yet + } else { + this.connectNewChannel(); + } + } + }; + this._socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); + this._socket.emit(ConnectionManagementMessages.RECONNECT, this.frontendIdProvider.getId()); + } else { + const initialConnectListener = () => { + this._socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + this.connectNewChannel(); + }; + this._socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + this._socket.emit(ConnectionManagementMessages.INITIAL_CONNECT, this.frontendIdProvider.getId()); + } + } + + connectNewChannel(): void { + if (this.currentChannel) { + this.writeBuffer.drain(); + this.currentChannel.close(); + this.currentChannel.onCloseEmitter.fire({ reason: 'reconnecting channel' }); + } + this.currentChannel = this.createChannel(); + this.onConnectionDidOpenEmitter.fire(this.currentChannel); + } + + protected createChannel(): AbstractChannel { + const toDispose = new DisposableCollection(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const messageHandler = (data: any) => { + this.onIncomingMessageActivityEmitter.fire(); + if (this.currentChannel) { + // In the browser context socketIO receives binary messages as ArrayBuffers. + // So we have to convert them to a Uint8Array before delegating the message to the read buffer. + const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data; + this.currentChannel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(buffer)); + }; + }; + this._socket.on('message', messageHandler); + toDispose.push(Disposable.create(() => { + this.socket.off('message', messageHandler); + })); + + const channel = new ForwardingChannel('any', () => { + toDispose.dispose(); + }, () => { + const result = new Uint8ArrayWriteBuffer(); + + result.onCommit(buffer => { + if (this.socket.connected) { + this.socket.send(buffer); + } else { + this.writeBuffer.buffer(buffer); + } + }); + + return result; + }); + return channel; + } + + /** + * @param path The handler to reach in the backend. + */ + protected createWebSocketUrl(path: string): string { + // Since we are using Socket.io, the path should look like the following: + // proto://domain.com/{path} + return this.createEndpoint(path).getWebSocketUrl().withPath(path).toString(); + } + + protected createHttpWebSocketUrl(path: string): string { + return this.createEndpoint(path).getRestUrl().toString(); + } + + protected createEndpoint(path: string): Endpoint { + return new Endpoint({ path }); + } + + /** + * Creates a web socket for the given url + */ + protected createWebSocket(url: string): Socket { + return io(url, { + path: this.createSocketIoPath(url), + reconnection: true, + reconnectionDelay: 1000, + reconnectionDelayMax: 10000, + reconnectionAttempts: Infinity, + extraHeaders: { + // Socket.io strips the `origin` header + // We need to provide our own for validation + 'fix-origin': window.location.origin + } + }); + } + + /** + * Path for Socket.io to make its requests to. + */ + protected createSocketIoPath(url: string): string | undefined { + if (location.protocol === Endpoint.PROTO_FILE) { + return '/socket.io'; + } + let { pathname } = location; + if (!pathname.endsWith('/')) { + pathname += '/'; + } + return pathname + 'socket.io'; + } +} diff --git a/packages/core/src/common/message-rpc/channel.ts b/packages/core/src/common/message-rpc/channel.ts index 05a9ba2426dfc..1f2b5047801d8 100644 --- a/packages/core/src/common/message-rpc/channel.ts +++ b/packages/core/src/common/message-rpc/channel.ts @@ -220,6 +220,8 @@ export class ChannelMultiplexer implements Disposable { this.openChannels.set(id, channel); resolve(channel); this.onOpenChannelEmitter.fire({ id, channel }); + } else { + console.error(`not expecting ack-open on for ${id}`); } } @@ -234,6 +236,8 @@ export class ChannelMultiplexer implements Disposable { } this.underlyingChannel.getWriteBuffer().writeUint8(MessageTypes.AckOpen).writeString(id).commit(); this.onOpenChannelEmitter.fire({ id, channel }); + } else { + console.error(`channel already open: ${id}`); } } @@ -275,7 +279,7 @@ export class ChannelMultiplexer implements Disposable { } open(id: string): Promise { - if (this.openChannels.has(id)) { + if (this.openChannels.has(id) || this.pendingOpen.has(id)) { throw new Error(`Another channel with the id '${id}' is already open.`); } const result = new Promise((resolve, reject) => { diff --git a/packages/core/src/common/message-rpc/message-buffer.ts b/packages/core/src/common/message-rpc/message-buffer.ts index d0b2fad0e351a..a27c41f3c77ca 100644 --- a/packages/core/src/common/message-rpc/message-buffer.ts +++ b/packages/core/src/common/message-rpc/message-buffer.ts @@ -25,6 +25,7 @@ export interface WriteBuffer { writeBytes(value: Uint8Array): this writeNumber(value: number): this writeLength(value: number): this + writeRaw(bytes: Uint8Array): this; /** * Makes any writes to the buffer permanent, for example by sending the writes over a channel. * You must obtain a new write buffer after committing @@ -71,6 +72,11 @@ export class ForwardingWriteBuffer implements WriteBuffer { return this; } + writeRaw(bytes: Uint8Array): this { + this.underlying.writeRaw(bytes); + return this; + } + commit(): void { this.underlying.commit(); } diff --git a/packages/core/src/common/message-rpc/uint8-array-message-buffer.ts b/packages/core/src/common/message-rpc/uint8-array-message-buffer.ts index 5b4294b3d57aa..af07a35a48461 100644 --- a/packages/core/src/common/message-rpc/uint8-array-message-buffer.ts +++ b/packages/core/src/common/message-rpc/uint8-array-message-buffer.ts @@ -76,6 +76,13 @@ export class Uint8ArrayWriteBuffer implements WriteBuffer, Disposable { return this; } + writeRaw(bytes: Uint8Array): this { + this.ensureCapacity(bytes.byteLength); + this.buffer.set(bytes, this.offset); + this.offset += bytes.byteLength; + return this; + } + writeUint16(value: number): this { this.ensureCapacity(2); this.msg.setUint16(this.offset, value); diff --git a/packages/core/src/common/messaging/abstract-connection-provider.ts b/packages/core/src/common/messaging/abstract-connection-provider.ts deleted file mode 100644 index 2e2c8c8956232..0000000000000 --- a/packages/core/src/common/messaging/abstract-connection-provider.ts +++ /dev/null @@ -1,115 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2020 Ericsson and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -import { injectable, interfaces } from 'inversify'; -import { Emitter, Event } from '../event'; -import { ConnectionHandler } from './handler'; -import { RpcProxy, RpcProxyFactory } from './proxy-factory'; -import { Channel, ChannelMultiplexer } from '../message-rpc/channel'; - -/** - * Factor common logic according to `ElectronIpcConnectionProvider` and - * `WebSocketConnectionProvider`. This class handles channels in a somewhat - * generic way. - */ -@injectable() -export abstract class AbstractConnectionProvider { - - /** - * Create a proxy object to remote interface of T type - * over an electron ipc connection for the given path and proxy factory. - */ - static createProxy(container: interfaces.Container, path: string, factory: RpcProxyFactory): RpcProxy; - /** - * Create a proxy object to remote interface of T type - * over an electron ipc connection for the given path. - * - * An optional target can be provided to handle - * notifications and requests from a remote side. - */ - static createProxy(container: interfaces.Container, path: string, target?: object): RpcProxy { - throw new Error('abstract'); - } - - protected readonly onIncomingMessageActivityEmitter: Emitter = new Emitter(); - get onIncomingMessageActivity(): Event { - return this.onIncomingMessageActivityEmitter.event; - } - - /** - * Create a proxy object to remote interface of T type - * over a web socket connection for the given path and proxy factory. - */ - createProxy(path: string, factory: RpcProxyFactory): RpcProxy; - /** - * Create a proxy object to remote interface of T type - * over a web socket connection for the given path. - * - * An optional target can be provided to handle - * notifications and requests from a remote side. - */ - createProxy(path: string, target?: object): RpcProxy; - createProxy(path: string, arg?: object): RpcProxy { - const factory = arg instanceof RpcProxyFactory ? arg : new RpcProxyFactory(arg); - this.listen({ - path, - onConnection: c => factory.listen(c) - }); - return factory.createProxy(); - } - - protected channelMultiplexer?: ChannelMultiplexer; - - // A set of channel opening functions that are executed if the backend reconnects to restore the - // the channels that were open before the disconnect occurred. - protected reconnectChannelOpeners: Array<() => Promise> = []; - - protected initializeMultiplexer(): void { - const mainChannel = this.createMainChannel(); - mainChannel.onMessage(() => this.onIncomingMessageActivityEmitter.fire()); - this.channelMultiplexer = new ChannelMultiplexer(mainChannel); - } - - /** - * Install a connection handler for the given path. - */ - listen(handler: ConnectionHandler, options?: AbstractOptions): void { - this.openChannel(handler.path, channel => { - handler.onConnection(channel); - }, options); - } - - async openChannel(path: string, handler: (channel: Channel) => void, options?: AbstractOptions): Promise { - if (!this.channelMultiplexer) { - throw new Error('The channel multiplexer has not been initialized yet!'); - } - const newChannel = await this.channelMultiplexer.open(path); - newChannel.onClose(() => { - const { reconnecting } = { reconnecting: true, ...options }; - if (reconnecting) { - this.reconnectChannelOpeners.push(() => this.openChannel(path, handler, options)); - } - }); - - handler(newChannel); - } - - /** - * Create the main connection that is used for multiplexing all service channels. - */ - protected abstract createMainChannel(): Channel; - -} diff --git a/packages/core/src/common/messaging/connection-management.ts b/packages/core/src/common/messaging/connection-management.ts new file mode 100644 index 0000000000000..6c019401d00ff --- /dev/null +++ b/packages/core/src/common/messaging/connection-management.ts @@ -0,0 +1,43 @@ + +// ***************************************************************************** +// Copyright (C) 2017 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export const ConnectionCloseService = Symbol('ConnectionCloseService'); +export const connectionCloseServicePath = '/services/ChannelCloseService'; + +/** + * These messages are used to negotiate service reconnection between a front ends and back end. + * Whenever a front end first connects to a back end, it sends the ${@link ConnectionManagementMessages#INITIAL_CONNECT} message + * together with its front end id. + * The back end then starts a new front end connection context for that front end. If the back end already had another connection context + * for the given front end id, it gets discarded. + * If the front end reconnects after a websocket disconnect, it sends the ${@link ConnectionManagementMessages#RECONNECT} message + * together with its front end id.. + * If the back end still has a connection context for the front end id, the context is reconnected and the back end replies with the value true. + * If there is no context anymore, the back end replies with value false. The front end can then either do an initial connect or reload + * the whole UI. + */ +export namespace ConnectionManagementMessages { + export const INITIAL_CONNECT = 'initialConnection'; + export const RECONNECT = 'reconnect'; +} + +/** + * A service to mark a front end as unused. As soon as it disconnects from the back end, the connection context will be discarded. + */ +export interface ConnectionCloseService { + markForClose(frontEndId: string): Promise; +} diff --git a/packages/core/src/common/messaging/handler.ts b/packages/core/src/common/messaging/handler.ts index 204125be8a203..0bdfac3e34e47 100644 --- a/packages/core/src/common/messaging/handler.ts +++ b/packages/core/src/common/messaging/handler.ts @@ -16,6 +16,8 @@ import { Channel } from '../message-rpc/channel'; +export const servicesPath = '/services'; + export const ConnectionHandler = Symbol('ConnectionHandler'); export interface ConnectionHandler { diff --git a/packages/core/src/common/messaging/socket-write-buffer.ts b/packages/core/src/common/messaging/socket-write-buffer.ts new file mode 100644 index 0000000000000..36d8dc17f750b --- /dev/null +++ b/packages/core/src/common/messaging/socket-write-buffer.ts @@ -0,0 +1,52 @@ +// ***************************************************************************** +// Copyright (C) 2018 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { WebSocket } from './web-socket-channel'; + +export class SocketWriteBuffer { + private static DISCONNECTED_BUFFER_SIZE = 100 * 1024; + + private disconnectedBuffer: Uint8Array | undefined; + private bufferWritePosition = 0; + + buffer(data: Uint8Array): void { + this.ensureWriteBuffer(data.byteLength); + this.disconnectedBuffer?.set(data, this.bufferWritePosition); + this.bufferWritePosition += data.byteLength; + } + + protected ensureWriteBuffer(byteLength: number): void { + if (!this.disconnectedBuffer) { + this.disconnectedBuffer = new Uint8Array(SocketWriteBuffer.DISCONNECTED_BUFFER_SIZE); + this.bufferWritePosition = 0; + } + + if (this.bufferWritePosition + byteLength > this.disconnectedBuffer.byteLength) { + throw new Error(`Max disconnected buffer size exceeded by adding ${byteLength} bytes`); + } + } + + flush(socket: WebSocket): void { + if (this.disconnectedBuffer) { + socket.send(this.disconnectedBuffer.slice(0, this.bufferWritePosition)); + this.disconnectedBuffer = undefined; + } + } + + drain(): void { + this.disconnectedBuffer = undefined; + } +} diff --git a/packages/core/src/common/messaging/web-socket-channel.ts b/packages/core/src/common/messaging/web-socket-channel.ts index 4f98d5269fb32..9b4c61fee9d7e 100644 --- a/packages/core/src/common/messaging/web-socket-channel.ts +++ b/packages/core/src/common/messaging/web-socket-channel.ts @@ -19,7 +19,11 @@ import { WriteBuffer } from '../message-rpc'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../message-rpc/uint8-array-message-buffer'; import { AbstractChannel } from '../message-rpc/channel'; -import { Disposable } from '../disposable'; +import { Socket as ClientSocket } from 'socket.io-client'; +import { Socket as ServerSocket } from 'socket.io'; +import { Emitter } from 'vscode-languageserver-protocol'; + +export type WebSocket = ClientSocket | ServerSocket; /** * A channel that manages the main websocket connection between frontend and backend. All service channels @@ -29,65 +33,44 @@ import { Disposable } from '../disposable'; export class WebSocketChannel extends AbstractChannel { static wsPath = '/services'; - constructor(protected readonly socket: IWebSocket) { + private onDidConnectEmitter = new Emitter(); + onDidConnect = this.onDidConnectEmitter.event; + + constructor(protected readonly socket: WebSocket) { super(); - this.toDispose.push(Disposable.create(() => socket.close())); - socket.onClose((reason, code) => this.onCloseEmitter.fire({ reason, code })); - socket.onClose(() => this.close()); - socket.onError(error => this.onErrorEmitter.fire(error)); - socket.onMessage(data => this.onMessageEmitter.fire(() => { + socket.on('connect', () => { + this.onDidConnectEmitter.fire(); + }); + + socket.on('disconnect', reason => { + this.onCloseEmitter.fire({ + reason: reason + }); + }); + + socket.on('error', reason => this.onErrorEmitter.fire(reason)); + socket.on('message', data => { // In the browser context socketIO receives binary messages as ArrayBuffers. // So we have to convert them to a Uint8Array before delegating the message to the read buffer. const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data; - return new Uint8ArrayReadBuffer(buffer); - })); + this.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(buffer)); + }); } getWriteBuffer(): WriteBuffer { const result = new Uint8ArrayWriteBuffer(); result.onCommit(buffer => { - if (this.socket.isConnected()) { + if (this.socket.connected) { this.socket.send(buffer); } }); return result; } -} -/** - * An abstraction that enables reuse of the `{@link WebSocketChannel} class in the frontend and backend - * independent of the actual underlying socket implementation. - */ -export interface IWebSocket { - /** - * Sends the given message over the web socket in binary format. - * @param message The binary message. - */ - send(message: Uint8Array): void; - /** - * Closes the websocket from the local side. - */ - close(): void; - /** - * The connection state of the web socket. - */ - isConnected(): boolean; - /** - * Listener callback to handle incoming messages. - * @param cb The callback. - */ - onMessage(cb: (message: Uint8Array) => void): void; - /** - * Listener callback to handle socket errors. - * @param cb The callback. - */ - onError(cb: (reason: any) => void): void; - /** - * Listener callback to handle close events (Remote side). - * @param cb The callback. - */ - onClose(cb: (reason: string, code?: number) => void): void; + override close(): void { + this.socket.disconnect(); + super.close(); + } } - diff --git a/packages/core/src/electron-browser/messaging/electron-frontend-id-provider.ts b/packages/core/src/electron-browser/messaging/electron-frontend-id-provider.ts new file mode 100644 index 0000000000000..1e8c86a5667bd --- /dev/null +++ b/packages/core/src/electron-browser/messaging/electron-frontend-id-provider.ts @@ -0,0 +1,25 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from 'inversify'; +import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider'; + +@injectable() +export class ElectronFrontendIdProvider implements FrontendIdProvider { + getId(): string { + return window.electronTheiaCore.WindowMetadata.webcontentId; + } +} diff --git a/packages/core/src/electron-browser/messaging/electron-ipc-connection-provider.ts b/packages/core/src/electron-browser/messaging/electron-ipc-connection-source.ts similarity index 61% rename from packages/core/src/electron-browser/messaging/electron-ipc-connection-provider.ts rename to packages/core/src/electron-browser/messaging/electron-ipc-connection-source.ts index 1551e3ab9c8ed..5d02af95b7446 100644 --- a/packages/core/src/electron-browser/messaging/electron-ipc-connection-provider.ts +++ b/packages/core/src/electron-browser/messaging/electron-ipc-connection-source.ts @@ -16,32 +16,36 @@ import { injectable, interfaces } from 'inversify'; import { RpcProxy } from '../../common/messaging'; -import { AbstractConnectionProvider } from '../../common/messaging/abstract-connection-provider'; -import { AbstractChannel, Channel, WriteBuffer } from '../../common'; +import { AbstractChannel, Channel, Emitter, Event, MaybePromise, WriteBuffer } from '../../common'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer'; +import { ServiceConnectionProvider } from '../../browser/messaging/service-connection-provider'; +import { ConnectionSource } from '../../browser/messaging/connection-source'; +import { FrontendApplicationContribution } from '../../browser'; export interface ElectronIpcOptions { } +export const ElectronMainConnectionProvider = Symbol('ElectronMainConnectionProvider'); + /** * Connection provider between the Theia frontend and the electron-main process via IPC. */ -@injectable() -export class ElectronIpcConnectionProvider extends AbstractConnectionProvider { +export namespace ElectronIpcConnectionProvider { - static override createProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { - return container.get(ElectronIpcConnectionProvider).createProxy(path, arg); + export function createProxy(container: interfaces.Container, path: string, arg?: object): RpcProxy { + return container.get(ElectronMainConnectionProvider).createProxy(path, arg); } +} - constructor() { - super(); - this.initializeMultiplexer(); - } +@injectable() +export class ElectronIpcConnectionSource implements ConnectionSource, FrontendApplicationContribution { + protected readonly onConnectionDidOpenEmitter: Emitter = new Emitter(); + onConnectionDidOpen: Event = this.onConnectionDidOpenEmitter.event; - protected createMainChannel(): Channel { - return new ElectronIpcRendererChannel(); + onStart(): MaybePromise { + const channel = new ElectronIpcRendererChannel(); + this.onConnectionDidOpenEmitter.fire(channel); } - } export class ElectronIpcRendererChannel extends AbstractChannel { diff --git a/packages/core/src/electron-browser/messaging/electron-local-ws-connection-provider.ts b/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts similarity index 89% rename from packages/core/src/electron-browser/messaging/electron-local-ws-connection-provider.ts rename to packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts index 01c7912cfe089..f62e740a02d48 100644 --- a/packages/core/src/electron-browser/messaging/electron-local-ws-connection-provider.ts +++ b/packages/core/src/electron-browser/messaging/electron-local-ws-connection-source.ts @@ -15,8 +15,8 @@ // ***************************************************************************** import { injectable } from 'inversify'; -import { WebSocketConnectionProvider } from '../../browser/messaging/ws-connection-provider'; import { Endpoint } from '../../browser/endpoint'; +import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source'; export function getLocalPort(): string | undefined { const params = new URLSearchParams(location.search); @@ -24,7 +24,7 @@ export function getLocalPort(): string | undefined { } @injectable() -export class ElectronLocalWebSocketConnectionProvider extends WebSocketConnectionProvider { +export class ElectronLocalWebSocketConnectionSource extends WebSocketConnectionSource { protected override createEndpoint(path: string): Endpoint { const localPort = getLocalPort(); diff --git a/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts b/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts index d62fc02a00607..046fb542b7c73 100644 --- a/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts +++ b/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts @@ -16,26 +16,63 @@ import { ContainerModule } from 'inversify'; import { FrontendApplicationContribution } from '../../browser/frontend-application-contribution'; -import { LocalWebSocketConnectionProvider, WebSocketConnectionProvider } from '../../browser/messaging/ws-connection-provider'; -import { ElectronWebSocketConnectionProvider } from './electron-ws-connection-provider'; -import { ElectronIpcConnectionProvider } from './electron-ipc-connection-provider'; -import { ElectronLocalWebSocketConnectionProvider, getLocalPort } from './electron-local-ws-connection-provider'; +import { ElectronWebSocketConnectionSource } from './electron-ws-connection-source'; +import { ElectronIpcConnectionSource, ElectronMainConnectionProvider } from './electron-ipc-connection-source'; +import { ElectronLocalWebSocketConnectionSource, getLocalPort } from './electron-local-ws-connection-source'; +import { ElectronFrontendIdProvider } from './electron-frontend-id-provider'; +import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider'; +import { ConnectionSource } from '../../browser/messaging/connection-source'; +import { LocalConnectionProvider, RemoteConnectionProvider, ServiceConnectionProvider } from '../../browser/messaging/service-connection-provider'; +import { WebSocketConnectionProvider } from '../../browser'; +import { ConnectionCloseService, connectionCloseServicePath } from '../../common/messaging/connection-management'; +import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source'; + +const backendServiceProvider = Symbol('backendServiceProvider2'); +const localServiceProvider = Symbol('localServiceProvider'); export const messagingFrontendModule = new ContainerModule(bind => { - bind(ElectronWebSocketConnectionProvider).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(ElectronWebSocketConnectionProvider); - bind(WebSocketConnectionProvider).toService(ElectronWebSocketConnectionProvider); - bind(ElectronLocalWebSocketConnectionProvider).toSelf().inSingletonScope(); - bind(LocalWebSocketConnectionProvider).toDynamicValue(ctx => { + bind(ConnectionCloseService).toDynamicValue(ctx => WebSocketConnectionProvider.createProxy(ctx.container, connectionCloseServicePath)).inSingletonScope(); + bind(ElectronWebSocketConnectionSource).toSelf().inSingletonScope(); + bind(WebSocketConnectionSource).toService(ElectronWebSocketConnectionSource); + bind(ElectronIpcConnectionSource).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(ElectronIpcConnectionSource); + bind(ElectronLocalWebSocketConnectionSource).toSelf().inSingletonScope(); + bind(backendServiceProvider).toDynamicValue(ctx => { + const container = ctx.container.createChild(); + container.bind(ServiceConnectionProvider).toSelf().inSingletonScope(); + container.bind(ConnectionSource).toService(ElectronWebSocketConnectionSource); + return container.get(ServiceConnectionProvider); + }).inSingletonScope(); + + bind(localServiceProvider).toDynamicValue(ctx => { + const container = ctx.container.createChild(); + container.bind(ServiceConnectionProvider).toSelf().inSingletonScope(); + container.bind(ConnectionSource).toService(ElectronLocalWebSocketConnectionSource); + return container.get(ServiceConnectionProvider); + }).inSingletonScope(); + + bind(ElectronMainConnectionProvider).toDynamicValue(ctx => { + const container = ctx.container.createChild(); + container.bind(ServiceConnectionProvider).toSelf().inSingletonScope(); + container.bind(ConnectionSource).toService(ElectronIpcConnectionSource); + return container.get(ServiceConnectionProvider); + }).inSingletonScope(); + + bind(LocalConnectionProvider).toDynamicValue(ctx => { const localPort = getLocalPort(); if (localPort) { // Return new web socket provider that connects to local app - return ctx.container.get(ElectronLocalWebSocketConnectionProvider); + return ctx.container.get(localServiceProvider); } else { // Return the usual web socket provider that already established its connection // That way we don't create a second socket connection - return ctx.container.get(WebSocketConnectionProvider); + return ctx.container.get(backendServiceProvider); } }).inSingletonScope(); - bind(ElectronIpcConnectionProvider).toSelf().inSingletonScope(); + bind(RemoteConnectionProvider).toService(backendServiceProvider); + + bind(FrontendApplicationContribution).toService(ElectronWebSocketConnectionSource); + bind(ElectronFrontendIdProvider).toSelf().inSingletonScope(); + bind(FrontendIdProvider).toService(ElectronFrontendIdProvider); + bind(WebSocketConnectionProvider).toSelf().inSingletonScope(); }); diff --git a/packages/core/src/electron-browser/messaging/electron-ws-connection-provider.ts b/packages/core/src/electron-browser/messaging/electron-ws-connection-source.ts similarity index 67% rename from packages/core/src/electron-browser/messaging/electron-ws-connection-provider.ts rename to packages/core/src/electron-browser/messaging/electron-ws-connection-source.ts index a9d0f161ab0df..258bb148d5ea6 100644 --- a/packages/core/src/electron-browser/messaging/electron-ws-connection-provider.ts +++ b/packages/core/src/electron-browser/messaging/electron-ws-connection-source.ts @@ -15,9 +15,8 @@ // ***************************************************************************** import { injectable } from 'inversify'; -import { WebSocketConnectionProvider, WebSocketOptions } from '../../browser/messaging/ws-connection-provider'; import { FrontendApplicationContribution } from '../../browser/frontend-application-contribution'; -import { Channel } from '../../common'; +import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source'; /** * Customized connection provider between the frontend and the backend in electron environment. @@ -25,25 +24,15 @@ import { Channel } from '../../common'; * once the electron-browser window is refreshed. Otherwise, backend resources are not disposed. */ @injectable() -export class ElectronWebSocketConnectionProvider extends WebSocketConnectionProvider implements FrontendApplicationContribution { - - /** - * Do not try to reconnect when the frontend application is stopping. The browser is navigating away from this page. - */ - protected stopping = false; +export class ElectronWebSocketConnectionSource extends WebSocketConnectionSource implements FrontendApplicationContribution { + constructor() { + super(); + } onStop(): void { - this.stopping = true; // Manually close the websocket connections `onStop`. Otherwise, the channels will be closed with 30 sec (`MessagingContribution#checkAliveTimeout`) delay. // https://github.com/eclipse-theia/theia/issues/6499 // `1001` indicates that an endpoint is "going away", such as a server going down or a browser having navigated away from a page. - this.channelMultiplexer?.onUnderlyingChannelClose({ reason: 'The frontend is "going away"', code: 1001 }); + this.socket.close(); } - - override async openChannel(path: string, handler: (channel: Channel) => void, options?: WebSocketOptions): Promise { - if (!this.stopping) { - super.openChannel(path, handler, options); - } - } - } diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index 1ce37cc52b633..c78043cb1fc9c 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -25,7 +25,8 @@ import { CHANNEL_ON_WINDOW_EVENT, CHANNEL_GET_ZOOM_LEVEL, CHANNEL_SET_ZOOM_LEVEL, CHANNEL_IS_FULL_SCREENABLE, CHANNEL_TOGGLE_FULL_SCREEN, CHANNEL_IS_FULL_SCREEN, CHANNEL_SET_MENU_BAR_VISIBLE, CHANNEL_REQUEST_CLOSE, CHANNEL_SET_TITLE_STYLE, CHANNEL_RESTART, CHANNEL_REQUEST_RELOAD, CHANNEL_APP_STATE_CHANGED, CHANNEL_SHOW_ITEM_IN_FOLDER, CHANNEL_READ_CLIPBOARD, CHANNEL_WRITE_CLIPBOARD, - CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR + CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR, + CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE } from '../electron-common/electron-api'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -65,6 +66,7 @@ function convertMenu(menu: MenuDto[] | undefined, handlerMap: Map } const api: TheiaCoreAPI = { + WindowMetadata: { webcontentId: 'none' }, setMenuBarVisible: (visible: boolean, windowName?: string) => ipcRenderer.send(CHANNEL_SET_MENU_BAR_VISIBLE, visible, windowName), setMenu: (menu: MenuDto[] | undefined) => { commandHandlers.delete(mainMenuId); @@ -119,6 +121,17 @@ const api: TheiaCoreAPI = { close: function (): void { ipcRenderer.send(CHANNEL_CLOSE); }, + + onAboutToClose(handler: () => void): Disposable { + const h = (event: Electron.IpcRendererEvent, replyChannel: string) => { + handler(); + event.sender.send(replyChannel); + }; + + ipcRenderer.on(CHANNEL_ABOUT_TO_CLOSE, h); + return Disposable.create(() => ipcRenderer.off(CHANNEL_ABOUT_TO_CLOSE, h)); + }, + onWindowEvent: function (event: WindowEvent, handler: () => void): Disposable { const h = (_event: unknown, evt: WindowEvent) => { if (event === evt) { @@ -227,6 +240,7 @@ export function preload(): void { } } }); + api.WindowMetadata.webcontentId = ipcRenderer.sendSync(CHANNEL_WC_METADATA); contextBridge.exposeInMainWorld('electronTheiaCore', api); } diff --git a/packages/core/src/electron-browser/window/electron-window-module.ts b/packages/core/src/electron-browser/window/electron-window-module.ts index 360ba55aa36e4..b50e21267b4cd 100644 --- a/packages/core/src/electron-browser/window/electron-window-module.ts +++ b/packages/core/src/electron-browser/window/electron-window-module.ts @@ -21,7 +21,7 @@ import { FrontendApplicationContribution } from '../../browser/frontend-applicat import { ElectronClipboardService } from '../electron-clipboard-service'; import { ClipboardService } from '../../browser/clipboard-service'; import { ElectronMainWindowService, electronMainWindowServicePath } from '../../electron-common/electron-main-window-service'; -import { ElectronIpcConnectionProvider } from '../messaging/electron-ipc-connection-provider'; +import { ElectronIpcConnectionProvider } from '../messaging/electron-ipc-connection-source'; import { bindWindowPreferences } from './electron-window-preferences'; import { FrontendApplicationStateService } from '../../browser/frontend-application-state'; import { ElectronFrontendApplicationStateService } from './electron-frontend-application-state'; diff --git a/packages/core/src/electron-browser/window/electron-window-service.ts b/packages/core/src/electron-browser/window/electron-window-service.ts index 44442ed5d4d21..7777063b67e0c 100644 --- a/packages/core/src/electron-browser/window/electron-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-window-service.ts @@ -19,6 +19,8 @@ import { NewWindowOptions, WindowSearchParams } from '../../common/window'; import { DefaultWindowService } from '../../browser/window/default-window-service'; import { ElectronMainWindowService } from '../../electron-common/electron-main-window-service'; import { ElectronWindowPreferences } from './electron-window-preferences'; +import { ConnectionCloseService } from '../../common/messaging/connection-management'; +import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider'; @injectable() export class ElectronWindowService extends DefaultWindowService { @@ -33,12 +35,18 @@ export class ElectronWindowService extends DefaultWindowService { */ protected closeOnUnload: boolean = false; + @inject(FrontendIdProvider) + protected readonly frontendIdProvider: FrontendIdProvider; + @inject(ElectronMainWindowService) protected readonly delegate: ElectronMainWindowService; @inject(ElectronWindowPreferences) protected readonly electronWindowPreferences: ElectronWindowPreferences; + @inject(ConnectionCloseService) + protected readonly connectionCloseService: ConnectionCloseService; + override openNewWindow(url: string, { external }: NewWindowOptions = {}): undefined { this.delegate.openNewWindow(url, { external }); return undefined; @@ -56,6 +64,9 @@ export class ElectronWindowService extends DefaultWindowService { this.updateWindowZoomLevel(); } }); + window.electronTheiaCore.onAboutToClose(() => { + this.connectionCloseService.markForClose(this.frontendIdProvider.getId()); + }); } protected override registerUnloadListeners(): void { diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 9007a41e747e5..5bff4341c68ab 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -41,6 +41,9 @@ export type InternalMenuDto = Omit & { export type WindowEvent = 'maximize' | 'unmaximize' | 'focus'; export interface TheiaCoreAPI { + WindowMetadata: { + webcontentId: string; + } getSecurityToken: () => string; attachSecurityToken: (endpoint: string) => Promise; @@ -63,6 +66,7 @@ export interface TheiaCoreAPI { unMaximize(): void; close(): void; onWindowEvent(event: WindowEvent, handler: () => void): Disposable; + onAboutToClose(handler: () => void): Disposable; setCloseRequestHandler(handler: (reason: StopReason) => Promise): void; setSecondaryWindowCloseRequestHandler(windowName: string, handler: () => Promise): void; @@ -96,6 +100,7 @@ declare global { } } +export const CHANNEL_WC_METADATA = 'WebContentMetadata'; export const CHANNEL_SET_MENU = 'SetMenu'; export const CHANNEL_SET_MENU_BAR_VISIBLE = 'SetMenuBarVisible'; export const CHANNEL_INVOKE_MENU = 'InvokeMenu'; @@ -117,6 +122,8 @@ export const CHANNEL_MINIMIZE = 'Minimize'; export const CHANNEL_MAXIMIZE = 'Maximize'; export const CHANNEL_IS_MAXIMIZED = 'IsMaximized'; +export const CHANNEL_ABOUT_TO_CLOSE = 'AboutToClose'; + export const CHANNEL_UNMAXIMIZE = 'UnMaximize'; export const CHANNEL_ON_WINDOW_EVENT = 'OnWindowEvent'; export const CHANNEL_TOGGLE_DEVTOOLS = 'ToggleDevtools'; diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index 4eeff065ad556..add9606bbdad1 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -51,7 +51,9 @@ import { CHANNEL_TOGGLE_FULL_SCREEN, CHANNEL_IS_MAXIMIZED, CHANNEL_REQUEST_SECONDARY_CLOSE, - CHANNEL_SET_BACKGROUND_COLOR + CHANNEL_SET_BACKGROUND_COLOR, + CHANNEL_WC_METADATA, + CHANNEL_ABOUT_TO_CLOSE } from '../electron-common/electron-api'; import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application'; import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common'; @@ -65,6 +67,10 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { protected readonly openPopups = new Map(); onStart(application: ElectronMainApplication): MaybePromise { + ipcMain.on(CHANNEL_WC_METADATA, event => { + event.returnValue = event.sender.id.toString(); + }); + // electron security token ipcMain.on(CHANNEL_GET_SECURITY_TOKEN, event => { event.returnValue = this.electronSecurityToken.value; @@ -254,6 +260,19 @@ export namespace TheiaRendererAPI { wc.send(CHANNEL_ON_WINDOW_EVENT, event); } + export function sendAboutToClose(wc: WebContents): Promise { + return new Promise(resolve => { + const channelNr = nextReplyChannel++; + const replyChannel = `aboutToClose${channelNr}`; + const l = createDisposableListener(ipcMain, replyChannel, e => { + l.dispose(); + resolve(); + }); + + wc.send(CHANNEL_ABOUT_TO_CLOSE, replyChannel); + }); + } + export function requestClose(wc: WebContents, stopReason: StopReason): Promise { const channelNr = nextReplyChannel++; const confirmChannel = `confirm-${channelNr}`; diff --git a/packages/core/src/electron-main/electron-main-application-module.ts b/packages/core/src/electron-main/electron-main-application-module.ts index 0c789b617d675..0148dbc6fd77c 100644 --- a/packages/core/src/electron-main/electron-main-application-module.ts +++ b/packages/core/src/electron-main/electron-main-application-module.ts @@ -22,12 +22,12 @@ import { ElectronSecurityToken } from '../electron-common/electron-token'; import { ElectronMainWindowService, electronMainWindowServicePath } from '../electron-common/electron-main-window-service'; import { ElectronMainApplication, ElectronMainApplicationContribution, ElectronMainProcessArgv } from './electron-main-application'; import { ElectronMainWindowServiceImpl } from './electron-main-window-service-impl'; -import { ElectronMessagingContribution } from './messaging/electron-messaging-contribution'; -import { ElectronMessagingService } from './messaging/electron-messaging-service'; -import { ElectronConnectionHandler } from '../electron-common/messaging/electron-connection-handler'; -import { ElectronSecurityTokenService } from './electron-security-token-service'; import { TheiaBrowserWindowOptions, TheiaElectronWindow, TheiaElectronWindowFactory, WindowApplicationConfig } from './theia-electron-window'; import { TheiaMainApi } from './electron-api-main'; +import { ElectronMessagingContribution } from './messaging/electron-messaging-contribution'; +import { ElectronSecurityTokenService } from './electron-security-token-service'; +import { ElectronMessagingService } from './messaging/electron-messaging-service'; +import { ElectronConnectionHandler } from './messaging/electron-connection-handler'; const electronSecurityToken: ElectronSecurityToken = { value: v4() }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -36,6 +36,7 @@ const electronSecurityToken: ElectronSecurityToken = { value: v4() }; export default new ContainerModule(bind => { bind(ElectronMainApplication).toSelf().inSingletonScope(); bind(ElectronMessagingContribution).toSelf().inSingletonScope(); + bind(ElectronMainApplicationContribution).toService(ElectronMessagingContribution); bind(ElectronSecurityToken).toConstantValue(electronSecurityToken); bind(ElectronSecurityTokenService).toSelf().inSingletonScope(); @@ -43,7 +44,6 @@ export default new ContainerModule(bind => { bindContributionProvider(bind, ElectronMessagingService.Contribution); bindContributionProvider(bind, ElectronMainApplicationContribution); - bind(ElectronMainApplicationContribution).toService(ElectronMessagingContribution); bind(TheiaMainApi).toSelf().inSingletonScope(); bind(ElectronMainApplicationContribution).toService(TheiaMainApi); diff --git a/packages/core/src/electron-common/messaging/electron-connection-handler.ts b/packages/core/src/electron-main/messaging/electron-connection-handler.ts similarity index 100% rename from packages/core/src/electron-common/messaging/electron-connection-handler.ts rename to packages/core/src/electron-main/messaging/electron-connection-handler.ts diff --git a/packages/core/src/electron-main/messaging/electron-messaging-contribution.ts b/packages/core/src/electron-main/messaging/electron-messaging-contribution.ts index 421d5d01497ad..9a4d5c4f68ccb 100644 --- a/packages/core/src/electron-main/messaging/electron-messaging-contribution.ts +++ b/packages/core/src/electron-main/messaging/electron-messaging-contribution.ts @@ -16,15 +16,15 @@ import { WebContents } from '@theia/electron/shared/electron'; import { inject, injectable, named, postConstruct } from 'inversify'; -import { ContributionProvider } from '../../common/contribution-provider'; -import { MessagingContribution } from '../../node/messaging/messaging-contribution'; -import { ElectronConnectionHandler } from '../../electron-common/messaging/electron-connection-handler'; -import { ElectronMainApplicationContribution } from '../electron-main-application'; -import { ElectronMessagingService } from './electron-messaging-service'; +import { ConnectionHandlers } from '../../node/messaging/default-messaging-service'; import { AbstractChannel, Channel, ChannelMultiplexer, MessageProvider } from '../../common/message-rpc/channel'; -import { ConnectionHandler, Emitter, WriteBuffer } from '../../common'; +import { ConnectionHandler, ContributionProvider, Emitter, WriteBuffer } from '../../common'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer'; import { TheiaRendererAPI } from '../electron-api-main'; +import { MessagingService } from '../../node'; +import { ElectronMessagingService } from './electron-messaging-service'; +import { ElectronConnectionHandler } from './electron-connection-handler'; +import { ElectronMainApplicationContribution } from '../electron-main-application'; /** * This component replicates the role filled by `MessagingContribution` but for Electron. @@ -36,37 +36,54 @@ import { TheiaRendererAPI } from '../electron-api-main'; @injectable() export class ElectronMessagingContribution implements ElectronMainApplicationContribution, ElectronMessagingService { - @inject(ContributionProvider) @named(ElectronMessagingService.Contribution) protected readonly messagingContributions: ContributionProvider; @inject(ContributionProvider) @named(ElectronConnectionHandler) protected readonly connectionHandlers: ContributionProvider; - protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers(); + protected readonly channelHandlers = new ConnectionHandlers(); + /** * Each electron window has a main channel and its own multiplexer to route multiple client messages the same IPC connection. */ - protected readonly windowChannelMultiplexer = new Map(); + protected readonly openChannels = new Map(); @postConstruct() protected init(): void { TheiaRendererAPI.onIpcData((sender, data) => this.handleIpcEvent(sender, data)); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ipcChannel(spec: string, callback: (params: any, channel: Channel) => void): void { + this.channelHandlers.push(spec, callback); + } + + onStart(): void { + for (const contribution of this.messagingContributions.getContributions()) { + contribution.configure(this); + } + for (const connectionHandler of this.connectionHandlers.getContributions()) { + this.channelHandlers.push(connectionHandler.path, (params, channel) => { + connectionHandler.onConnection(channel); + }); + } + } + protected handleIpcEvent(sender: WebContents, data: Uint8Array): void { // Get the multiplexer for a given window id try { - const windowChannelData = this.windowChannelMultiplexer.get(sender.id) ?? this.createWindowChannelData(sender); - windowChannelData!.channel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(data)); + const windowChannel = this.openChannels.get(sender.id) ?? this.createWindowChannel(sender); + windowChannel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(data)); } catch (error) { console.error('IPC: Failed to handle message', { error, data }); } } - // Creates a new multiplexer for a given sender/window - protected createWindowChannelData(sender: Electron.WebContents): { channel: ElectronWebContentChannel, multiplexer: ChannelMultiplexer } { - const mainChannel = this.createWindowMainChannel(sender); + // Creates a new channel for a given sender/window + protected createWindowChannel(sender: Electron.WebContents): ElectronWebContentChannel { + const mainChannel = new ElectronWebContentChannel(sender); + const multiplexer = new ChannelMultiplexer(mainChannel); multiplexer.onDidOpenChannel(openEvent => { const { channel, id } = openEvent; @@ -75,41 +92,26 @@ export class ElectronMessagingContribution implements ElectronMainApplicationCon channel.onClose(() => console.debug(`Closing channel on service path '${id}'.`)); } }); - - sender.once('did-navigate', () => this.disposeMultiplexer(sender.id, multiplexer, 'Window was refreshed')); // When refreshing the browser window. - sender.once('destroyed', () => this.disposeMultiplexer(sender.id, multiplexer, 'Window was closed')); // When closing the browser window. - const data = { channel: mainChannel, multiplexer }; - this.windowChannelMultiplexer.set(sender.id, data); - return data; - } - - /** - * Creates the main channel to a window. - * @param sender The window that the channel should be established to. - */ - protected createWindowMainChannel(sender: WebContents): ElectronWebContentChannel { - return new ElectronWebContentChannel(sender); + sender.once('did-navigate', () => this.deleteChannel(sender.id, 'Window was refreshed')); + sender.once('destroyed', () => this.deleteChannel(sender.id, 'Window was closed')); + this.openChannels.set(sender.id, mainChannel); + return mainChannel; } - protected disposeMultiplexer(windowId: number, multiplexer: ChannelMultiplexer, reason: string): void { - multiplexer.onUnderlyingChannelClose({ reason }); - this.windowChannelMultiplexer.delete(windowId); - } - - onStart(): void { - for (const contribution of this.messagingContributions.getContributions()) { - contribution.configure(this); - } - for (const connectionHandler of this.connectionHandlers.getContributions()) { - this.channelHandlers.push(connectionHandler.path, (params, channel) => { - connectionHandler.onConnection(channel); + protected deleteChannel(senderId: number, reason: string): void { + const channel = this.openChannels.get(senderId); + if (channel) { + this.openChannels.delete(senderId); + channel.onCloseEmitter.fire({ + reason: reason }); } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ipcChannel(spec: string, callback: (params: any, channel: Channel) => void): void { - this.channelHandlers.push(spec, callback); + protected readonly wsHandlers = new ConnectionHandlers(); + + registerConnectionHandler(spec: string, callback: (params: MessagingService.PathParams, channel: Channel) => void): void { + this.wsHandlers.push(spec, callback); } } diff --git a/packages/core/src/electron-main/messaging/electron-messaging-service.ts b/packages/core/src/electron-main/messaging/electron-messaging-service.ts index d2f5aab7c086f..ac4a0d2831ba1 100644 --- a/packages/core/src/electron-main/messaging/electron-messaging-service.ts +++ b/packages/core/src/electron-main/messaging/electron-messaging-service.ts @@ -23,6 +23,7 @@ export interface ElectronMessagingService { */ ipcChannel(path: string, callback: (params: ElectronMessagingService.PathParams, socket: Channel) => void): void; } + export namespace ElectronMessagingService { export interface PathParams { [name: string]: string diff --git a/packages/core/src/electron-main/theia-electron-window.ts b/packages/core/src/electron-main/theia-electron-window.ts index 5c473ffdab2f6..855939e1ca9dc 100644 --- a/packages/core/src/electron-main/theia-electron-window.ts +++ b/packages/core/src/electron-main/theia-electron-window.ts @@ -129,8 +129,9 @@ export class TheiaElectronWindow { }, this.toDispose); } - protected doCloseWindow(): void { + protected async doCloseWindow(): Promise { this.closeIsConfirmed = true; + await TheiaRendererAPI.sendAboutToClose(this._window.webContents); this._window.close(); } @@ -139,13 +140,13 @@ export class TheiaElectronWindow { } protected reload(): void { - this.handleStopRequest(() => { + this.handleStopRequest(async () => { this.applicationState = 'init'; this._window.reload(); }, StopReason.Reload); } - protected async handleStopRequest(onSafeCallback: () => unknown, reason: StopReason): Promise { + protected async handleStopRequest(onSafeCallback: () => Promise, reason: StopReason): Promise { // Only confirm close to windows that have loaded our frontend. // Both the windows's URL and the FS path of the `index.html` should be converted to the "same" format to be able to compare them. (#11226) // Notes: diff --git a/packages/core/src/electron-node/token/electron-token-messaging-contribution.ts b/packages/core/src/electron-node/token/electron-token-messaging-contribution.ts deleted file mode 100644 index 631ba2c99a2c6..0000000000000 --- a/packages/core/src/electron-node/token/electron-token-messaging-contribution.ts +++ /dev/null @@ -1,41 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2020 Ericsson and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -import * as http from 'http'; -import { injectable, inject } from 'inversify'; -import { MessagingContribution } from '../../node/messaging/messaging-contribution'; -import { ElectronTokenValidator } from './electron-token-validator'; - -/** - * Override the browser MessagingContribution class to refuse connections that do not include a specific token. - * @deprecated since 1.8.0 - */ -@injectable() -export class ElectronMessagingContribution extends MessagingContribution { - - @inject(ElectronTokenValidator) - protected readonly tokenValidator: ElectronTokenValidator; - - /** - * Only allow token-bearers. - */ - protected override async allowConnect(request: http.IncomingMessage): Promise { - if (this.tokenValidator.allowRequest(request)) { - return super.allowConnect(request); - } - return false; - } -} diff --git a/packages/core/src/node/messaging/default-messaging-service.ts b/packages/core/src/node/messaging/default-messaging-service.ts new file mode 100644 index 0000000000000..37836a846e650 --- /dev/null +++ b/packages/core/src/node/messaging/default-messaging-service.ts @@ -0,0 +1,129 @@ +// ***************************************************************************** +// Copyright (C) 2018 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable, inject, named, interfaces, Container } from 'inversify'; +import { ContributionProvider, ConnectionHandler, bindContributionProvider, servicesPath } from '../../common'; +import { MessagingService } from './messaging-service'; +import { ConnectionContainerModule } from './connection-container-module'; +import Route = require('route-parser'); +import { Channel, ChannelMultiplexer } from '../../common/message-rpc/channel'; +import { FrontendConnectionService } from './frontend-connection-service'; +import { BackendApplicationContribution } from '../backend-application'; + +export const MessagingContainer = Symbol('MessagingContainer'); +export const MainChannel = Symbol('MainChannel'); + +@injectable() +export class DefaultMessagingService implements MessagingService, BackendApplicationContribution { + @inject(MessagingContainer) + protected readonly container: interfaces.Container; + + @inject(FrontendConnectionService) + protected readonly frontendConnectionService: FrontendConnectionService; + + @inject(ContributionProvider) @named(ConnectionContainerModule) + protected readonly connectionModules: ContributionProvider; + + @inject(ContributionProvider) @named(MessagingService.Contribution) + protected readonly contributions: ContributionProvider; + + protected readonly channelHandlers = new ConnectionHandlers(); + + initialize(): void { + this.registerConnectionHandler(servicesPath, (_, socket) => this.handleConnection(socket)); + for (const contribution of this.contributions.getContributions()) { + contribution.configure(this); + } + } + + registerConnectionHandler(path: string, callback: (params: MessagingService.PathParams, mainChannel: Channel) => void): void { + this.frontendConnectionService.registerConnectionHandler(path, callback); + } + + registerChannelHandler(spec: string, callback: (params: MessagingService.PathParams, channel: Channel) => void): void { + this.channelHandlers.push(spec, (params, channel) => callback(params, channel)); + } + + protected handleConnection(channel: Channel): void { + const multiplexer = new ChannelMultiplexer(channel); + const channelHandlers = this.getConnectionChannelHandlers(channel); + multiplexer.onDidOpenChannel(event => { + if (channelHandlers.route(event.id, event.channel)) { + console.debug(`Opening channel for service path '${event.id}'.`); + event.channel.onClose(() => console.info(`Closing channel on service path '${event.id}'.`)); + } + }); + } + + protected createMainChannelContainer(socket: Channel): Container { + const connectionContainer: Container = this.container.createChild() as Container; + connectionContainer.bind(MainChannel).toConstantValue(socket); + return connectionContainer; + } + + protected getConnectionChannelHandlers(socket: Channel): ConnectionHandlers { + const connectionContainer = this.createMainChannelContainer(socket); + bindContributionProvider(connectionContainer, ConnectionHandler); + connectionContainer.load(...this.connectionModules.getContributions()); + const connectionChannelHandlers = new ConnectionHandlers(this.channelHandlers); + const connectionHandlers = connectionContainer.getNamed>(ContributionProvider, ConnectionHandler); + for (const connectionHandler of connectionHandlers.getContributions(true)) { + connectionChannelHandlers.push(connectionHandler.path, (_, channel) => { + connectionHandler.onConnection(channel); + }); + } + return connectionChannelHandlers; + } + +} + +export class ConnectionHandlers { + protected readonly handlers: ((path: string, connection: T) => string | false)[] = []; + + constructor( + protected readonly parent?: ConnectionHandlers + ) { } + + push(spec: string, callback: (params: MessagingService.PathParams, connection: T) => void): void { + const route = new Route(spec); + const handler = (path: string, channel: T): string | false => { + const params = route.match(path); + if (!params) { + return false; + } + callback(params, channel); + return route.reverse(params); + }; + this.handlers.push(handler); + } + + route(path: string, connection: T): string | false { + for (const handler of this.handlers) { + try { + const result = handler(path, connection); + if (result) { + return result; + } + } catch (e) { + console.error(e); + } + } + if (this.parent) { + return this.parent.route(path, connection); + } + return false; + } +} diff --git a/packages/core/src/node/messaging/frontend-connection-service.ts b/packages/core/src/node/messaging/frontend-connection-service.ts new file mode 100644 index 0000000000000..5657e137b3512 --- /dev/null +++ b/packages/core/src/node/messaging/frontend-connection-service.ts @@ -0,0 +1,24 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + +import { Channel } from '../../common/message-rpc/'; +import { MessagingService } from './messaging-service'; + +export const FrontendConnectionService = Symbol('FrontendConnectionService'); + +export interface FrontendConnectionService { + registerConnectionHandler(path: string, callback: (params: MessagingService.PathParams, mainChannel: Channel) => void): void; +} + diff --git a/packages/core/src/node/messaging/messaging-backend-module.ts b/packages/core/src/node/messaging/messaging-backend-module.ts index 4f549efb75922..baab3a350736e 100644 --- a/packages/core/src/node/messaging/messaging-backend-module.ts +++ b/packages/core/src/node/messaging/messaging-backend-module.ts @@ -15,23 +15,38 @@ // ***************************************************************************** import { ContainerModule } from 'inversify'; -import { bindContributionProvider } from '../../common'; -import { BackendApplicationContribution } from '../backend-application'; -import { MessagingContribution, MessagingContainer } from './messaging-contribution'; +import { ConnectionHandler, RpcConnectionHandler, bindContributionProvider } from '../../common'; +// import { BackendApplicationContribution } from '../backend-application'; +import { DefaultMessagingService, MessagingContainer } from './default-messaging-service'; import { ConnectionContainerModule } from './connection-container-module'; import { MessagingService } from './messaging-service'; import { MessagingListener, MessagingListenerContribution } from './messaging-listeners'; +import { FrontendConnectionService } from './frontend-connection-service'; +import { BackendApplicationContribution } from '../backend-application'; +import { connectionCloseServicePath } from '../../common/messaging/connection-management'; +import { WebsocketFrontendConnectionService } from './websocket-frontend-connection-service'; +import { WebsocketEndpoint } from './websocket-endpoint'; export const messagingBackendModule = new ContainerModule(bind => { bindContributionProvider(bind, ConnectionContainerModule); bindContributionProvider(bind, MessagingService.Contribution); - bind(MessagingService.Identifier).to(MessagingContribution).inSingletonScope(); - bind(MessagingContribution).toDynamicValue(({ container }) => { - const child = container.createChild(); - child.bind(MessagingContainer).toConstantValue(container); - return child.get(MessagingService.Identifier); - }).inSingletonScope(); - bind(BackendApplicationContribution).toService(MessagingContribution); + bind(DefaultMessagingService).toSelf().inSingletonScope(); + bind(MessagingService.Identifier).toService(DefaultMessagingService); + bind(BackendApplicationContribution).toService(DefaultMessagingService); + bind(MessagingContainer).toDynamicValue(({ container }) => container).inSingletonScope(); + bind(WebsocketEndpoint).toSelf().inSingletonScope(); + bind(BackendApplicationContribution).toService(WebsocketEndpoint); + bind(WebsocketFrontendConnectionService).toSelf().inSingletonScope(); + bind(FrontendConnectionService).toService(WebsocketFrontendConnectionService); bind(MessagingListener).toSelf().inSingletonScope(); bindContributionProvider(bind, MessagingListenerContribution); + + bind(ConnectionHandler).toDynamicValue(context => { + const connectionService = context.container.get(FrontendConnectionService); + return new RpcConnectionHandler(connectionCloseServicePath, () => ({ + markForClose: (channelId: string) => { + connectionService.markForClose(channelId); + } + })); + }).inSingletonScope(); }); diff --git a/packages/core/src/node/messaging/messaging-contribution.ts b/packages/core/src/node/messaging/messaging-contribution.ts deleted file mode 100644 index 2e628a1795762..0000000000000 --- a/packages/core/src/node/messaging/messaging-contribution.ts +++ /dev/null @@ -1,197 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2018 TypeFox and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -import * as http from 'http'; -import * as https from 'https'; -import { Server, Socket } from 'socket.io'; -import { injectable, inject, named, postConstruct, interfaces, Container } from 'inversify'; -import { ContributionProvider, ConnectionHandler, bindContributionProvider } from '../../common'; -import { IWebSocket, WebSocketChannel } from '../../common/messaging/web-socket-channel'; -import { BackendApplicationContribution } from '../backend-application'; -import { MessagingService } from './messaging-service'; -import { ConnectionContainerModule } from './connection-container-module'; -import Route = require('route-parser'); -import { WsRequestValidator } from '../ws-request-validators'; -import { MessagingListener } from './messaging-listeners'; -import { Channel, ChannelMultiplexer } from '../../common/message-rpc/channel'; - -export const MessagingContainer = Symbol('MessagingContainer'); - -@injectable() -export class MessagingContribution implements BackendApplicationContribution, MessagingService { - - @inject(MessagingContainer) - protected readonly container: interfaces.Container; - - @inject(ContributionProvider) @named(ConnectionContainerModule) - protected readonly connectionModules: ContributionProvider; - - @inject(ContributionProvider) @named(MessagingService.Contribution) - protected readonly contributions: ContributionProvider; - - @inject(WsRequestValidator) - protected readonly wsRequestValidator: WsRequestValidator; - - @inject(MessagingListener) - protected readonly messagingListener: MessagingListener; - - protected readonly wsHandlers = new MessagingContribution.ConnectionHandlers(); - protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers(); - - @postConstruct() - protected init(): void { - this.ws(WebSocketChannel.wsPath, (_, socket) => this.handleChannels(socket)); - for (const contribution of this.contributions.getContributions()) { - contribution.configure(this); - } - } - - wsChannel(spec: string, callback: (params: MessagingService.PathParams, channel: Channel) => void): void { - this.channelHandlers.push(spec, (params, channel) => callback(params, channel)); - } - - ws(spec: string, callback: (params: MessagingService.PathParams, socket: Socket) => void): void { - this.wsHandlers.push(spec, callback); - } - - protected checkAliveTimeout = 30000; // 30 seconds - protected maxHttpBufferSize = 1e8; // 100 MB - - onStart(server: http.Server | https.Server): void { - const socketServer = new Server(server, { - pingInterval: this.checkAliveTimeout, - pingTimeout: this.checkAliveTimeout * 2, - maxHttpBufferSize: this.maxHttpBufferSize - }); - // Accept every namespace by using /.*/ - socketServer.of(/.*/).on('connection', async socket => { - const request = socket.request; - // Socket.io strips the `origin` header of the incoming request - // We provide a `fix-origin` header in the `WebSocketConnectionProvider` - request.headers.origin = request.headers['fix-origin'] as string; - if (await this.allowConnect(socket.request)) { - await this.handleConnection(socket); - this.messagingListener.onDidWebSocketUpgrade(socket.request, socket); - } else { - socket.disconnect(true); - } - }); - } - - protected async handleConnection(socket: Socket): Promise { - const pathname = socket.nsp.name; - if (pathname && !this.wsHandlers.route(pathname, socket)) { - console.error('Cannot find a ws handler for the path: ' + pathname); - } - } - - protected async allowConnect(request: http.IncomingMessage): Promise { - try { - return this.wsRequestValidator.allowWsUpgrade(request); - } catch (e) { - return false; - } - } - - protected handleChannels(socket: Socket): void { - const socketChannel = new WebSocketChannel(this.toIWebSocket(socket)); - const multiplexer = new ChannelMultiplexer(socketChannel); - const channelHandlers = this.getConnectionChannelHandlers(socket); - multiplexer.onDidOpenChannel(event => { - if (channelHandlers.route(event.id, event.channel)) { - console.debug(`Opening channel for service path '${event.id}'.`); - event.channel.onClose(() => console.debug(`Closing channel on service path '${event.id}'.`)); - } - }); - } - - protected toIWebSocket(socket: Socket): IWebSocket { - return { - close: () => { - socket.removeAllListeners('disconnect'); - socket.removeAllListeners('error'); - socket.removeAllListeners('message'); - socket.disconnect(); - }, - isConnected: () => socket.connected, - onClose: cb => socket.on('disconnect', reason => cb(reason)), - onError: cb => socket.on('error', error => cb(error)), - onMessage: cb => socket.on('message', data => cb(data)), - send: message => socket.emit('message', message) - }; - } - - protected createSocketContainer(socket: Socket): Container { - const connectionContainer: Container = this.container.createChild() as Container; - connectionContainer.bind(Socket).toConstantValue(socket); - return connectionContainer; - } - - protected getConnectionChannelHandlers(socket: Socket): MessagingContribution.ConnectionHandlers { - const connectionContainer = this.createSocketContainer(socket); - bindContributionProvider(connectionContainer, ConnectionHandler); - connectionContainer.load(...this.connectionModules.getContributions()); - const connectionChannelHandlers = new MessagingContribution.ConnectionHandlers(this.channelHandlers); - const connectionHandlers = connectionContainer.getNamed>(ContributionProvider, ConnectionHandler); - for (const connectionHandler of connectionHandlers.getContributions(true)) { - connectionChannelHandlers.push(connectionHandler.path, (_, channel) => { - connectionHandler.onConnection(channel); - }); - } - return connectionChannelHandlers; - } - -} - -export namespace MessagingContribution { - export class ConnectionHandlers { - protected readonly handlers: ((path: string, connection: T) => string | false)[] = []; - - constructor( - protected readonly parent?: ConnectionHandlers - ) { } - - push(spec: string, callback: (params: MessagingService.PathParams, connection: T) => void): void { - const route = new Route(spec); - const handler = (path: string, channel: T): string | false => { - const params = route.match(path); - if (!params) { - return false; - } - callback(params, channel); - return route.reverse(params); - }; - this.handlers.push(handler); - } - - route(path: string, connection: T): string | false { - for (const handler of this.handlers) { - try { - const result = handler(path, connection); - if (result) { - return result; - } - } catch (e) { - console.error(e); - } - } - if (this.parent) { - return this.parent.route(path, connection); - } - return false; - } - } -} diff --git a/packages/core/src/node/messaging/messaging-service.ts b/packages/core/src/node/messaging/messaging-service.ts index 7d4ad45432e4e..38fa34643e2f0 100644 --- a/packages/core/src/node/messaging/messaging-service.ts +++ b/packages/core/src/node/messaging/messaging-service.ts @@ -14,7 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Socket } from 'socket.io'; import { Channel } from '../../common/message-rpc/channel'; export interface MessagingService { @@ -22,7 +21,7 @@ export interface MessagingService { * Accept a web socket channel on the given path. * A path supports the route syntax: https://github.com/rcs/route-parser#what-can-i-use-in-my-routes. */ - wsChannel(path: string, callback: (params: MessagingService.PathParams, channel: Channel) => void): void; + registerChannelHandler(path: string, handler: (params: MessagingService.PathParams, channel: Channel) => void): void; /** * Accept a web socket connection on the given path. * A path supports the route syntax: https://github.com/rcs/route-parser#what-can-i-use-in-my-routes. @@ -31,8 +30,9 @@ export interface MessagingService { * Prefer using web socket channels over establishing new web socket connection. Clients can handle only limited amount of web sockets * and excessive amount can cause performance degradation. All web socket channels share a single web socket connection. */ - ws(path: string, callback: (params: MessagingService.PathParams, socket: Socket) => void): void; + registerConnectionHandler(path: string, callback: (params: MessagingService.PathParams, mainChannel: Channel) => void): void; } + export namespace MessagingService { /** Inversify container identifier for the `MessagingService` component. */ export const Identifier = Symbol('MessagingService'); diff --git a/packages/core/src/node/messaging/test/test-web-socket-channel.ts b/packages/core/src/node/messaging/test/test-web-socket-channel.ts index 65c4ed1e641e9..199d5a3e4c7a1 100644 --- a/packages/core/src/node/messaging/test/test-web-socket-channel.ts +++ b/packages/core/src/node/messaging/test/test-web-socket-channel.ts @@ -1,3 +1,4 @@ +/* eslint-disable @theia/runtime-import-check */ // ***************************************************************************** // Copyright (C) 2018 TypeFox and others. // @@ -17,40 +18,44 @@ import * as http from 'http'; import * as https from 'https'; import { AddressInfo } from 'net'; -import { io, Socket } from 'socket.io-client'; -import { Channel, ChannelMultiplexer } from '../../../common/message-rpc/channel'; -import { IWebSocket, WebSocketChannel } from '../../../common/messaging/web-socket-channel'; +import { servicesPath } from '../../../common'; +import { WebSocketConnectionSource } from '../../../browser/messaging/ws-connection-source'; +import { Container, inject } from 'inversify'; +import { RemoteConnectionProvider, ServiceConnectionProvider } from '../../../browser/messaging/service-connection-provider'; +import { messagingFrontendModule } from '../../../browser/messaging/messaging-frontend-module'; +import { Socket, io } from 'socket.io-client'; + +const websocketUrl = Symbol('testWebsocketUrl'); +class TestWebsocketConnectionSource extends WebSocketConnectionSource { + @inject(websocketUrl) + readonly websocketUrl: string; + + protected override createWebSocketUrl(path: string): string { + return this.websocketUrl; + } + + protected override createWebSocket(url: string): Socket { + return io(url); + } +} export class TestWebSocketChannelSetup { - public readonly multiplexer: ChannelMultiplexer; - public readonly channel: Channel; + public readonly connectionProvider: ServiceConnectionProvider; constructor({ server, path }: { server: http.Server | https.Server, path: string }) { - const socket = io(`ws://localhost:${(server.address() as AddressInfo).port}${WebSocketChannel.wsPath}`); - this.channel = new WebSocketChannel(toIWebSocket(socket)); - this.multiplexer = new ChannelMultiplexer(this.channel); - socket.on('connect', () => { - this.multiplexer.open(path); - }); - socket.connect(); + const address = (server.address() as AddressInfo); + const url = `ws://${address.address}:${address.port}${servicesPath}`; + this.connectionProvider = this.createConnectionProvider(url); } -} -function toIWebSocket(socket: Socket): IWebSocket { - return { - close: () => { - socket.removeAllListeners('disconnect'); - socket.removeAllListeners('error'); - socket.removeAllListeners('message'); - socket.close(); - }, - isConnected: () => socket.connected, - onClose: cb => socket.on('disconnect', reason => cb(reason)), - onError: cb => socket.on('error', reason => cb(reason)), - onMessage: cb => socket.on('message', data => cb(data)), - send: message => socket.emit('message', message) - }; + protected createConnectionProvider(socketUrl: string): ServiceConnectionProvider { + const container = new Container(); + container.bind(websocketUrl).toConstantValue(socketUrl); + container.load(messagingFrontendModule); + container.rebind(WebSocketConnectionSource).to(TestWebsocketConnectionSource); + return container.get(RemoteConnectionProvider); + } } diff --git a/packages/core/src/node/messaging/websocket-endpoint.ts b/packages/core/src/node/messaging/websocket-endpoint.ts new file mode 100644 index 0000000000000..187603c77db6e --- /dev/null +++ b/packages/core/src/node/messaging/websocket-endpoint.ts @@ -0,0 +1,79 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + +import { MessagingService } from './messaging-service'; +import * as http from 'http'; +import * as https from 'https'; +import { inject, injectable } from 'inversify'; +import { Server, Socket } from 'socket.io'; +import { WsRequestValidator } from '../ws-request-validators'; +import { MessagingListener } from './messaging-listeners'; +import { ConnectionHandlers } from './default-messaging-service'; +import { BackendApplicationContribution } from '../backend-application'; + +@injectable() +export class WebsocketEndpoint implements BackendApplicationContribution { + @inject(WsRequestValidator) + protected readonly wsRequestValidator: WsRequestValidator; + + @inject(MessagingListener) + protected readonly messagingListener: MessagingListener; + + protected checkAliveTimeout = 30000; // 30 seconds + protected maxHttpBufferSize = 1e8; // 100 MB + + protected readonly wsHandlers = new ConnectionHandlers(); + + registerConnectionHandler(spec: string, callback: (params: MessagingService.PathParams, socket: Socket) => void): void { + this.wsHandlers.push(spec, callback); + } + + onStart(server: http.Server | https.Server): void { + const socketServer = new Server(server, { + pingInterval: this.checkAliveTimeout, + pingTimeout: this.checkAliveTimeout * 2, + maxHttpBufferSize: this.maxHttpBufferSize + }); + // Accept every namespace by using /.*/ + socketServer.of(/.*/).on('connection', async socket => { + const request = socket.request; + // Socket.io strips the `origin` header of the incoming request + // We provide a `fix-origin` header in the `WebSocketConnectionProvider` + request.headers.origin = request.headers['fix-origin'] as string; + if (await this.allowConnect(socket.request)) { + await this.handleConnection(socket); + this.messagingListener.onDidWebSocketUpgrade(socket.request, socket); + } else { + socket.disconnect(true); + } + }); + } + + protected async allowConnect(request: http.IncomingMessage): Promise { + try { + return this.wsRequestValidator.allowWsUpgrade(request); + } catch (e) { + return false; + } + } + + protected async handleConnection(socket: Socket): Promise { + const pathname = socket.nsp.name; + if (pathname && !this.wsHandlers.route(pathname, socket)) { + console.error('Cannot find a ws handler for the path: ' + pathname); + } + } +} + diff --git a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts new file mode 100644 index 0000000000000..648edd0f14a55 --- /dev/null +++ b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts @@ -0,0 +1,171 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + +import { Channel, WriteBuffer } from '../../common/message-rpc'; +import { MessagingService } from './messaging-service'; +import { inject, injectable } from 'inversify'; +import { Socket } from 'socket.io'; +import { ConnectionHandlers } from './default-messaging-service'; +import { SocketWriteBuffer } from '../../common/messaging/socket-write-buffer'; +import { FrontendConnectionService } from './frontend-connection-service'; +import { AbstractChannel } from '../../common/message-rpc/channel'; +import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer'; +import { BackendApplicationConfigProvider } from '../backend-application-config-provider'; +import { WebsocketEndpoint } from './websocket-endpoint'; +import { ConnectionManagementMessages } from '../../common/messaging/connection-management'; +import { Disposable, DisposableCollection } from '../../common'; + +@injectable() +export class WebsocketFrontendConnectionService implements FrontendConnectionService { + + @inject(WebsocketEndpoint) + protected readonly websocketServer: WebsocketEndpoint; + + protected readonly wsHandlers = new ConnectionHandlers(); + protected readonly connectionsByFrontend = new Map(); + protected readonly closeTimeouts = new Map(); + protected readonly channelsMarkedForClose = new Set(); + + registerConnectionHandler(spec: string, callback: (params: MessagingService.PathParams, channel: Channel) => void): void { + this.websocketServer.registerConnectionHandler(spec, (params, socket) => this.handleConnection(socket, channel => callback(params, channel))); + } + + protected async handleConnection(socket: Socket, channelCreatedHandler: (channel: Channel) => void): Promise { + // eslint-disable-next-line prefer-const + let reconnectListener: (frontEndId: string) => void; + const initialConnectListener = (frontEndId: string) => { + socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); + if (this.connectionsByFrontend.has(frontEndId)) { + this.closeConnection(frontEndId, 'reconnecting same front end'); + } + channelCreatedHandler(this.createConnection(socket, frontEndId)); + socket.emit(ConnectionManagementMessages.INITIAL_CONNECT); + }; + + reconnectListener = (frontEndId: string) => { + socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); + const channel = this.connectionsByFrontend.get(frontEndId); + if (channel) { + console.info(`Reconnecting to front end ${frontEndId}`); + socket.emit(ConnectionManagementMessages.RECONNECT, true); + channel.connect(socket); + const pendingTimeout = this.closeTimeouts.get(frontEndId); + clearTimeout(pendingTimeout); + this.closeTimeouts.delete(frontEndId); + } else { + console.info(`Reconnecting failed for ${frontEndId}`); + socket.emit(ConnectionManagementMessages.RECONNECT, false); + } + }; + socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); + } + + protected closeConnection(frontEndId: string, reason: string): void { + console.info(`closing connection for ${frontEndId}`); + const connection = this.connectionsByFrontend.get(frontEndId)!; // not called when no connection is present + + this.connectionsByFrontend.delete(frontEndId); + + const pendingTimeout = this.closeTimeouts.get(frontEndId); + clearTimeout(pendingTimeout); + this.closeTimeouts.delete(frontEndId); + + connection.onCloseEmitter.fire({ reason }); + connection.close(); + } + + protected createConnection(socket: Socket, frontEndId: string): Channel { + console.info(`creating connection for ${frontEndId}`); + const channel = new ReconnectableSocketChannel(); + channel.connect(socket); + + socket.on('disconnect', evt => { + console.info('socked closed'); + channel.disconnect(); + const timeout = BackendApplicationConfigProvider.get().frontendConnectionTimeout; + const isMarkedForClose = this.channelsMarkedForClose.delete(frontEndId); + if (timeout === 0 || isMarkedForClose) { + this.closeConnection(frontEndId, evt); + } else if (timeout > 0) { + console.info(`setting close timeout for id ${frontEndId} to ${timeout}`); + const handle = setTimeout(() => { + this.closeConnection(frontEndId, evt); + }, timeout); + this.closeTimeouts.set(frontEndId, handle); + } else { + // timeout < 0: never close the back end + } + }); + + this.connectionsByFrontend.set(frontEndId, channel); + return channel; + } + + markForClose(channelId: string): void { + this.channelsMarkedForClose.add(channelId); + } +} + +class ReconnectableSocketChannel extends AbstractChannel { + private socket: Socket | undefined; + private socketBuffer = new SocketWriteBuffer(); + private disposables = new DisposableCollection(); + + connect(socket: Socket): void { + this.disposables = new DisposableCollection(); + this.socket = socket; + const errorHandler = (err: Error) => { + this.onErrorEmitter.fire(err); + }; + this.disposables.push(Disposable.create(() => { + socket.off('error', errorHandler); + })); + socket.on('error', errorHandler); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const dataListener = (data: any) => { + // In the browser context socketIO receives binary messages as ArrayBuffers. + // So we have to convert them to a Uint8Array before delegating the message to the read buffer. + const buffer = data instanceof ArrayBuffer ? new Uint8Array(data) : data; + this.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(buffer)); + }; + this.disposables.push(Disposable.create(() => { + socket.off('message', dataListener); + })); + socket.on('message', dataListener); + this.socketBuffer.flush(socket); + } + + disconnect(): void { + this.disposables.dispose(); + this.socket = undefined; + } + + override getWriteBuffer(): WriteBuffer { + const writeBuffer = new Uint8ArrayWriteBuffer(); + writeBuffer.onCommit(data => { + if (this.socket?.connected) { + this.socket.send(data); + } else { + this.socketBuffer.buffer(data); + } + }); + return writeBuffer; + } +} + diff --git a/packages/debug/src/browser/debug-session-contribution.ts b/packages/debug/src/browser/debug-session-contribution.ts index 04d36c3c645fb..907e64e531aaf 100644 --- a/packages/debug/src/browser/debug-session-contribution.ts +++ b/packages/debug/src/browser/debug-session-contribution.ts @@ -19,7 +19,6 @@ import { MessageClient } from '@theia/core/lib/common'; import { LabelProvider } from '@theia/core/lib/browser'; import { EditorManager } from '@theia/editor/lib/browser'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; -import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider'; import { DebugSession } from './debug-session'; import { BreakpointManager } from './breakpoint/breakpoint-manager'; import { DebugConfigurationSessionOptions, DebugSessionOptions } from './debug-session-options'; @@ -31,6 +30,7 @@ import { ContributionProvider } from '@theia/core/lib/common/contribution-provid import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { DebugContribution } from './debug-contribution'; import { WorkspaceService } from '@theia/workspace/lib/browser'; +import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; /** * DebugSessionContribution symbol for DI. @@ -95,8 +95,8 @@ export interface DebugSessionFactory { @injectable() export class DefaultDebugSessionFactory implements DebugSessionFactory { - @inject(WebSocketConnectionProvider) - protected readonly connectionProvider: WebSocketConnectionProvider; + @inject(RemoteConnectionProvider) + protected readonly connectionProvider: ServiceConnectionProvider; @inject(TerminalService) protected readonly terminalService: TerminalService; @inject(EditorManager) @@ -122,9 +122,9 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory { const connection = new DebugSessionConnection( sessionId, () => new Promise(resolve => - this.connectionProvider.openChannel(`${DebugAdapterPath}/${sessionId}`, wsChannel => { + this.connectionProvider.listen(`${DebugAdapterPath}/${sessionId}`, (_, wsChannel) => { resolve(new ForwardingDebugChannel(wsChannel)); - }, { reconnecting: false }) + }, false) ), this.getTraceOutputChannel()); return new DebugSession( diff --git a/packages/debug/src/node/debug-adapter-session-manager.ts b/packages/debug/src/node/debug-adapter-session-manager.ts index ff4290e73eae7..4f5501b120038 100644 --- a/packages/debug/src/node/debug-adapter-session-manager.ts +++ b/packages/debug/src/node/debug-adapter-session-manager.ts @@ -37,7 +37,7 @@ export class DebugAdapterSessionManager implements MessagingService.Contribution protected readonly debugAdapterFactory: DebugAdapterFactory; configure(service: MessagingService): void { - service.wsChannel(`${DebugAdapterPath}/:id`, ({ id }: { id: string }, wsChannel) => { + service.registerChannelHandler(`${DebugAdapterPath}/:id`, ({ id }: { id: string }, wsChannel) => { const session = this.find(id); if (!session) { wsChannel.close(); diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts index 75beca868c6ec..dcfe7d4a2e722 100644 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ b/packages/plugin-ext/src/common/proxy-handler.ts @@ -48,11 +48,13 @@ export class ClientProxyHandler implements ProxyHandler { } private initializeRpc(): void { + // we need to set the flag to true before waiting for the channel provider. Otherwise `get` might + // get called again and we'll try to open a channel more than once + this.isRpcInitialized = true; const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; this.channelProvider().then(channel => { const rpc = new RpcProtocol(channel, undefined, clientOptions); this.rpcDeferred.resolve(rpc); - this.isRpcInitialized = true; }); } diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 297f9295eac1b..5772914b3ac19 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -573,6 +573,7 @@ export class HostedPluginSupport { if (toDisconnect.disposed) { return undefined; } + this.activationEvents.forEach(event => manager!.$activateByEvent(event)); } return manager; } diff --git a/packages/task/src/node/task-server.slow-spec.ts b/packages/task/src/node/task-server.slow-spec.ts index 03749c9d2f2aa..4c49bd6e8d272 100644 --- a/packages/task/src/node/task-server.slow-spec.ts +++ b/packages/task/src/node/task-server.slow-spec.ts @@ -72,7 +72,7 @@ describe('Task server / back-end', function (): void { taskServer = testContainer.get(TaskServer); taskServer.setClient(taskWatcher.getTaskClient()); backend = testContainer.get(BackendApplication); - server = await backend.start(); + server = await backend.start(3000, 'localhost'); }); afterEach(async () => { @@ -104,11 +104,11 @@ describe('Task server / back-end', function (): void { await new Promise((resolve, reject) => { const setup = new TestWebSocketChannelSetup({ server, path: `${terminalsPath}/${terminalId}` }); const stringBuffer = new StringBufferingStream(); - setup.multiplexer.onDidOpenChannel(event => { - event.channel.onMessage(e => stringBuffer.push(e().readString())); - event.channel.onError(reject); - event.channel.onClose(() => reject(new Error('Channel has been closed'))); - }); + setup.connectionProvider.listen(`${terminalsPath}/${terminalId}`, (path, channel) => { + channel.onMessage(e => stringBuffer.push(e().readString())); + channel.onError(reject); + channel.onClose(() => reject(new Error('Channel has been closed'))); + }, false); stringBuffer.onData(currentMessage => { // Instead of waiting for one message from the terminal, we wait for several ones as the very first message can be something unexpected. // For instance: `nvm is not compatible with the \"PREFIX\" environment variable: currently set to \"/usr/local\"\r\n` diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 5cb3dd8755d22..93bf528ca0cc4 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -19,7 +19,7 @@ import { FitAddon } from 'xterm-addon-fit'; import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core'; import { - Widget, Message, WebSocketConnectionProvider, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer + Widget, Message, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer } from '@theia/core/lib/browser'; import { isOSX } from '@theia/core/lib/common'; import { WorkspaceService } from '@theia/workspace/lib/browser'; @@ -46,6 +46,7 @@ import debounce = require('p-debounce'); import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; import { EnhancedPreviewWidget } from '@theia/core/lib/browser/widgets/enhanced-preview-widget'; import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; +import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; export const TERMINAL_WIDGET_FACTORY_ID = 'terminal'; @@ -88,7 +89,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget override lastCwd = new URI(); @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - @inject(WebSocketConnectionProvider) protected readonly webSocketConnectionProvider: WebSocketConnectionProvider; + @inject(RemoteConnectionProvider) protected readonly conectionProvider: ServiceConnectionProvider; @inject(TerminalWidgetOptions) options: TerminalWidgetOptions; @inject(ShellTerminalServerProxy) protected readonly shellTerminalServer: ShellTerminalServerProxy; @inject(TerminalWatcher) protected readonly terminalWatcher: TerminalWatcher; @@ -629,9 +630,9 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.toDisposeOnConnect.dispose(); this.toDispose.push(this.toDisposeOnConnect); const waitForConnection = this.waitForConnection = new Deferred(); - this.webSocketConnectionProvider.listen({ - path: `${terminalsPath}/${this.terminalId}`, - onConnection: connection => { + this.conectionProvider.listen( + `${terminalsPath}/${this.terminalId}`, + (path, connection) => { connection.onMessage(e => { this.write(e().readString()); }); @@ -652,8 +653,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget if (waitForConnection) { waitForConnection.resolve(connection); } - } - }, { reconnecting: false }); + }, false); } protected async reconnectTerminalProcess(): Promise { if (this.options.isPseudoTerminal) { diff --git a/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts b/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts index cf9defadd94a9..c9b3b6316549f 100644 --- a/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts +++ b/packages/terminal/src/node/terminal-backend-contribution.slow-spec.ts @@ -32,7 +32,7 @@ describe('Terminal Backend Contribution', function (): void { const container = createTerminalTestContainer(); const application = container.get(BackendApplication); shellTerminalServer = container.get(IShellTerminalServer); - server = await application.start(); + server = await application.start(3000, 'localhost'); }); afterEach(() => { @@ -46,16 +46,16 @@ describe('Terminal Backend Contribution', function (): void { const terminalId = await shellTerminalServer.create({}); await new Promise((resolve, reject) => { const path = `${terminalsPath}/${terminalId}`; - const { channel, multiplexer } = new TestWebSocketChannelSetup({ server, path }); - channel.onError(reject); - channel.onClose(event => reject(new Error(`channel is closed with '${event.code}' code and '${event.reason}' reason}`))); + const { connectionProvider } = new TestWebSocketChannelSetup({ server, path }); - multiplexer.onDidOpenChannel(event => { - if (event.id === path) { + connectionProvider.listen(path, (path2, channel) => { + channel.onError(reject); + channel.onClose(event => reject(new Error(`channel is closed with '${event.code}' code and '${event.reason}' reason}`))); + if (path2 === path) { resolve(); channel.close(); } - }); + }, false); }); }); diff --git a/packages/terminal/src/node/terminal-backend-contribution.ts b/packages/terminal/src/node/terminal-backend-contribution.ts index d538ff697bd62..07649685cf377 100644 --- a/packages/terminal/src/node/terminal-backend-contribution.ts +++ b/packages/terminal/src/node/terminal-backend-contribution.ts @@ -32,7 +32,7 @@ export class TerminalBackendContribution implements MessagingService.Contributio protected readonly logger: ILogger; configure(service: MessagingService): void { - service.wsChannel(`${terminalsPath}/:id`, (params: { id: string }, channel) => { + service.registerChannelHandler(`${terminalsPath}/:id`, (params: { id: string }, channel) => { const id = parseInt(params.id, 10); const termProcess = this.processManager.get(id); if (termProcess instanceof TerminalProcess) { From d0a09a98ca6db7ac3c8badd6a2e943af0fbe42b5 Mon Sep 17 00:00:00 2001 From: emilhammarstedtst <89985309+emilhammarstedtst@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:22:34 +0100 Subject: [PATCH 011/441] fix: Cannot quit app when there is a dirty editor (#13164) (#13173) Contributed by STMicroelectronics Signed-off-by: Emil HAMMARSTEDT --- .../browser/common-frontend-contribution.ts | 64 ++++++++++++++++--- packages/core/src/browser/dialogs.ts | 34 ---------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index f436fcf2349b4..dc1532e3315a4 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -57,7 +57,7 @@ import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSe import { AsyncLocalizationProvider } from '../common/i18n/localization'; import { nls } from '../common/nls'; import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter'; -import { ConfirmDialog, confirmExitWithOrWithoutSaving, Dialog } from './dialogs'; +import { ConfirmDialog, confirmExit, ConfirmSaveDialog, Dialog } from './dialogs'; import { WindowService } from './window/window-service'; import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; import { DecorationStyle } from './decoration-style'; @@ -1200,22 +1200,60 @@ export class CommonFrontendContribution implements FrontendApplicationContributi action: async () => { const captionsToSave = this.unsavedTabsCaptions(); const untitledCaptionsToSave = this.unsavedUntitledTabsCaptions(); - const result = await confirmExitWithOrWithoutSaving(captionsToSave, async () => { + const shouldExit = await this.confirmExitWithOrWithoutSaving(captionsToSave, async () => { await this.saveDirty(untitledCaptionsToSave); await this.shell.saveAll(); }); - if (this.shell.canSaveAll()) { - this.shouldPreventClose = true; - return false; - } else { - this.shouldPreventClose = false; - return result; - } + const allSavedOrDoNotSave = ( + shouldExit === true && untitledCaptionsToSave.length === 0 // Should save and cancel if any captions failed to save + ) || shouldExit === false; // Do not save + + this.shouldPreventClose = !allSavedOrDoNotSave; + return allSavedOrDoNotSave; } }; } } + // Asks the user to confirm whether they want to exit with or without saving the changes + private async confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise): Promise { + const div: HTMLElement = document.createElement('div'); + div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them."); + + let result; + if (captionsToSave.length > 0) { + const span = document.createElement('span'); + span.appendChild(document.createElement('br')); + captionsToSave.forEach(cap => { + const b = document.createElement('b'); + b.innerText = cap; + span.appendChild(b); + span.appendChild(document.createElement('br')); + }); + span.appendChild(document.createElement('br')); + div.appendChild(span); + result = await new ConfirmSaveDialog({ + title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length), + msg: div, + dontSave: nls.localizeByDefault("Don't Save"), + save: nls.localizeByDefault('Save All'), + cancel: Dialog.CANCEL + }).open(); + + if (result) { + await performSave(); + } + } else { + // fallback if not passed with an empty caption-list. + result = confirmExit(); + } + if (result !== undefined) { + return result === true; + } else { + return undefined; + }; + + } protected unsavedTabsCaptions(): string[] { return this.shell.widgets .filter(widget => this.saveResourceService.canSave(widget)) @@ -1236,11 +1274,19 @@ export class CommonFrontendContribution implements FrontendApplicationContributi this.windowService.reload(); } } + /** + * saves any dirty widget in toSave + * side effect - will pop all widgets from toSave that was saved + * @param toSave + */ protected async saveDirty(toSave: Widget[]): Promise { for (const widget of toSave) { const saveable = Saveable.get(widget); if (saveable?.dirty) { await this.saveResourceService.save(widget); + if (!this.saveResourceService.canSave(widget)) { + toSave.pop(); + } } } } diff --git a/packages/core/src/browser/dialogs.ts b/packages/core/src/browser/dialogs.ts index 2ab0a1203e976..c72fe33792ea6 100644 --- a/packages/core/src/browser/dialogs.ts +++ b/packages/core/src/browser/dialogs.ts @@ -458,40 +458,6 @@ export class ConfirmSaveDialog extends AbstractDialog { } -// Asks the user to confirm whether they want to exit with or without saving the changes -export async function confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise): Promise { - const div: HTMLElement = document.createElement('div'); - div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them."); - - if (captionsToSave.length > 0) { - const span = document.createElement('span'); - span.appendChild(document.createElement('br')); - captionsToSave.forEach(cap => { - const b = document.createElement('b'); - b.innerText = cap; - span.appendChild(b); - span.appendChild(document.createElement('br')); - }); - span.appendChild(document.createElement('br')); - div.appendChild(span); - const result = await new ConfirmSaveDialog({ - title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length), - msg: div, - dontSave: nls.localizeByDefault("Don't Save"), - save: nls.localizeByDefault('Save All'), - cancel: Dialog.CANCEL - }).open(); - - if (result) { - await performSave(); - } - return result !== undefined; - } else { - // fallback if not passed with an empty caption-list. - return confirmExit(); - } - -} @injectable() export class SingleTextInputDialogProps extends DialogProps { readonly confirmButtonLabel?: string; From 74a91ba1d2231d5173102ed2ca61114dc162fa90 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 19 Dec 2023 15:36:04 +0100 Subject: [PATCH 012/441] Fix compressed navigator indent highlight (#13162) --- .../compressed-tree-widget.tsx | 34 +++++++++++++++---- .../core/src/browser/tree/tree-widget.tsx | 31 +++++++++++------ 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/packages/core/src/browser/tree/tree-compression/compressed-tree-widget.tsx b/packages/core/src/browser/tree/tree-compression/compressed-tree-widget.tsx index ee56f7bf3805b..b9502322dead5 100644 --- a/packages/core/src/browser/tree/tree-compression/compressed-tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-compression/compressed-tree-widget.tsx @@ -58,6 +58,12 @@ export class CompressedTreeWidget extends TreeViewWelcomeWidget { } } + protected override shouldRenderIndent(node: TreeNode): boolean { + return !this.compressionToggle.compress + || !this.compressionService.isCompressionParticipant(node) + || this.compressionService.getCompressionHead(node) === node; + } + protected override shouldDisplayNode(node: TreeNode): boolean { if (this.compressionToggle.compress && this.compressionService.isCompressionParticipant(node) && !this.compressionService.isCompressionHead(node)) { return false; @@ -66,14 +72,18 @@ export class CompressedTreeWidget extends TreeViewWelcomeWidget { } protected override getDepthForNode(node: TreeNode, depths: Map): number { - if (!this.compressionToggle.compress) { return super.getDepthForNode(node, depths); } + if (!this.compressionToggle.compress) { + return super.getDepthForNode(node, depths); + } const parent = this.compressionService.getCompressionHead(node.parent) ?? node.parent; const parentDepth = depths.get(parent); return parentDepth === undefined ? 0 : TreeNode.isVisible(node.parent) ? parentDepth + 1 : parentDepth; } protected override toNodeRow(node: TreeNode, index: number, depth: number): CompressedNodeRow { - if (!this.compressionToggle.compress) { return super.toNodeRow(node, index, depth); } + if (!this.compressionToggle.compress) { + return super.toNodeRow(node, index, depth); + } const row: CompressedNodeRow = { node, index, depth }; if (this.compressionService.isCompressionHead(node)) { row.compressionChain = this.compressionService.getCompressionChain(node); @@ -102,7 +112,9 @@ export class CompressedTreeWidget extends TreeViewWelcomeWidget { } protected override getCaptionChildren(node: TreeNode, props: CompressedNodeProps): React.ReactNode { - if (!this.compressionToggle.compress || !props.compressionChain) { return super.getCaptionChildren(node, props); } + if (!this.compressionToggle.compress || !props.compressionChain) { + return super.getCaptionChildren(node, props); + } return props.compressionChain.map((subNode, index, self) => { const classes = ['theia-tree-compressed-label-part']; if (SelectableTreeNode.isSelected(subNode)) { @@ -129,21 +141,27 @@ export class CompressedTreeWidget extends TreeViewWelcomeWidget { } protected override handleUp(event: KeyboardEvent): void { - if (!this.compressionToggle.compress) { return super.handleUp(event); } + if (!this.compressionToggle.compress) { + return super.handleUp(event); + } const type = this.props.multiSelect && this.hasShiftMask(event) ? TreeSelection.SelectionType.RANGE : undefined; this.model.selectPrevRow(type); this.node.focus(); } protected override handleDown(event: KeyboardEvent): void { - if (!this.compressionToggle.compress) { return super.handleDown(event); } + if (!this.compressionToggle.compress) { + return super.handleDown(event); + } const type = this.props.multiSelect && this.hasShiftMask(event) ? TreeSelection.SelectionType.RANGE : undefined; this.model.selectNextRow(type); this.node.focus(); } protected override async handleLeft(event: KeyboardEvent): Promise { - if (!this.compressionToggle.compress) { return super.handleLeft(event); } + if (!this.compressionToggle.compress) { + return super.handleLeft(event); + } if (Boolean(this.props.multiSelect) && (this.hasCtrlCmdMask(event) || this.hasShiftMask(event))) { return; } @@ -160,7 +178,9 @@ export class CompressedTreeWidget extends TreeViewWelcomeWidget { } protected override async handleRight(event: KeyboardEvent): Promise { - if (!this.compressionToggle.compress) { return super.handleRight(event); } + if (!this.compressionToggle.compress) { + return super.handleRight(event); + } if (Boolean(this.props.multiSelect) && (this.hasCtrlCmdMask(event) || this.hasShiftMask(event))) { return; } diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index d2034c3fd0421..029ff65db4a02 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -894,22 +894,33 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { let current: TreeNode | undefined = node; let depth = props.depth; while (current && depth) { - const classNames: string[] = [TREE_NODE_INDENT_GUIDE_CLASS]; - if (this.needsActiveIndentGuideline(current)) { - classNames.push('active'); - } else { - classNames.push(renderIndentGuides === 'onHover' ? 'hover' : 'always'); + if (this.shouldRenderIndent(current)) { + const classNames: string[] = [TREE_NODE_INDENT_GUIDE_CLASS]; + if (this.needsActiveIndentGuideline(current)) { + classNames.push('active'); + } else { + classNames.push(renderIndentGuides === 'onHover' ? 'hover' : 'always'); + } + const paddingLeft = this.getDepthPadding(depth); + indentDivs.unshift(
); + depth--; } - const paddingLeft = this.getDepthPadding(depth); - indentDivs.unshift(
); current = current.parent; - depth--; } return indentDivs; } + /** + * Determines whether an indentation div should be rendered for the specified tree node. + * If there are multiple tree nodes inside of a single rendered row, + * this method should only return true for the first node. + */ + protected shouldRenderIndent(node: TreeNode): boolean { + return true; + } + protected needsActiveIndentGuideline(node: TreeNode): boolean { const parent = node.parent; if (!parent || !this.isExpandable(parent)) { From cdbfffc837f89551456bef4377982a9995c8ddcd Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 19 Dec 2023 16:17:37 +0100 Subject: [PATCH 013/441] updateWorkspaceFolder may create an untitled workspace if none is defined (#13181) fixes #13100 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 2 ++ packages/workspace/src/browser/workspace-service.ts | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a91d23b8776..412473d665f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) ## Unreleased + - [terminal] Use application shell methods for expanding/collapsing bottom panel for "Terminal: Toggle Terminal" command [#13131](https://github.com/eclipse-theia/theia/pull/13131) +- [workspace] Create an empty workspace if no workspace is active on updateWorkspaceFolders [#13181](https://github.com/eclipse-theia/theia/pull/13181) - contributed on behalf of STMicroelectronics ## v1.44.0 - 11/30/2023 diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts index 81f1ff8c0468e..178bd9cf98141 100644 --- a/packages/workspace/src/browser/workspace-service.ts +++ b/packages/workspace/src/browser/workspace-service.ts @@ -404,8 +404,12 @@ export class WorkspaceService implements FrontendApplicationContribution { } async spliceRoots(start: number, deleteCount?: number, ...rootsToAdd: URI[]): Promise { - if (!this._workspace) { - throw new Error('There is no active workspace'); + if (!this._workspace || this._workspace.isDirectory) { + const untitledWorkspace = await this.getUntitledWorkspace(); + await this.save(untitledWorkspace); + if (!this._workspace) { + throw new Error('Could not create new untitled workspace'); + } } const dedup = new Set(); const roots = this._roots.map(root => (dedup.add(root.resource.toString()), root.resource.toString())); @@ -421,10 +425,7 @@ export class WorkspaceService implements FrontendApplicationContribution { if (!toRemove.length && !toAdd.length) { return []; } - if (this._workspace.isDirectory) { - const untitledWorkspace = await this.getUntitledWorkspace(); - await this.save(untitledWorkspace); - } + const currentData = await this.getWorkspaceDataFromFile(); const newData = WorkspaceData.buildWorkspaceData(roots, currentData); await this.writeWorkspaceFile(this._workspace, newData); From 2527596e59f822660f6f4c81fa5d1b1fead215a9 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 19 Dec 2023 16:53:57 +0100 Subject: [PATCH 014/441] Save untitled files to the last active folder (#13184) --- .../browser/frontend-application-module.ts | 1 + .../user-working-directory-provider.ts | 35 +++++++++++++++++-- .../file-dialog/file-dialog-service.ts | 6 ++-- ...rkspace-user-working-directory-provider.ts | 1 + 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 45e83b311720e..ca1bb7f3ab971 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -445,6 +445,7 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(SaveResourceService).toSelf().inSingletonScope(); bind(UserWorkingDirectoryProvider).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(UserWorkingDirectoryProvider); bind(HoverService).toSelf().inSingletonScope(); diff --git a/packages/core/src/browser/user-working-directory-provider.ts b/packages/core/src/browser/user-working-directory-provider.ts index 002ed8e4bf475..ccc9c63a14bc5 100644 --- a/packages/core/src/browser/user-working-directory-provider.ts +++ b/packages/core/src/browser/user-working-directory-provider.ts @@ -16,14 +16,34 @@ import { inject, injectable } from 'inversify'; import URI from '../common/uri'; -import { MaybePromise, SelectionService, UriSelection } from '../common'; +import { MaybePromise, SelectionService, UNTITLED_SCHEME, UriSelection } from '../common'; import { EnvVariablesServer } from '../common/env-variables'; +import { FrontendApplication } from './frontend-application'; +import { FrontendApplicationContribution } from './frontend-application-contribution'; +import { Widget } from './widgets'; +import { Navigatable } from './navigatable-types'; @injectable() -export class UserWorkingDirectoryProvider { +export class UserWorkingDirectoryProvider implements FrontendApplicationContribution { @inject(SelectionService) protected readonly selectionService: SelectionService; @inject(EnvVariablesServer) protected readonly envVariables: EnvVariablesServer; + protected lastOpenResource: URI | undefined; + + configure(app: FrontendApplication): void { + app.shell.onDidChangeCurrentWidget(e => this.setLastOpenResource(e.newValue ?? undefined)); + this.setLastOpenResource(app.shell.currentWidget); + } + + protected setLastOpenResource(widget?: Widget): void { + if (Navigatable.is(widget)) { + const uri = widget.getResourceUri(); + if (uri && uri.scheme !== UNTITLED_SCHEME) { + this.lastOpenResource = uri; + } + } + } + /** * @returns A {@link URI} that represents a good guess about the directory in which the user is currently operating. * @@ -35,7 +55,16 @@ export class UserWorkingDirectoryProvider { } protected getFromSelection(): MaybePromise { - return this.ensureIsDirectory(UriSelection.getUri(this.selectionService.selection)); + const uri = UriSelection.getUri(this.selectionService.selection); + if (uri?.scheme === UNTITLED_SCHEME) { + // An untitled file is not a valid working directory context. + return undefined; + } + return this.ensureIsDirectory(uri); + } + + protected getFromLastOpenResource(): MaybePromise { + return this.ensureIsDirectory(this.lastOpenResource); } protected getFromUserHome(): MaybePromise { diff --git a/packages/filesystem/src/browser/file-dialog/file-dialog-service.ts b/packages/filesystem/src/browser/file-dialog/file-dialog-service.ts index a6c9f8ce28565..afc42899c64db 100644 --- a/packages/filesystem/src/browser/file-dialog/file-dialog-service.ts +++ b/packages/filesystem/src/browser/file-dialog/file-dialog-service.ts @@ -16,7 +16,7 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; -import { MaybeArray, nls } from '@theia/core/lib/common'; +import { MaybeArray, UNTITLED_SCHEME, nls } from '@theia/core/lib/common'; import { LabelProvider } from '@theia/core/lib/browser'; import { FileStat } from '../../common/files'; import { DirNode } from '../file-tree'; @@ -81,7 +81,9 @@ export class DefaultFileDialogService implements FileDialogService { } protected async getRootNode(folderToOpen?: FileStat): Promise { - const folderExists = folderToOpen && await this.fileService.exists(folderToOpen.resource); + const folderExists = folderToOpen + && folderToOpen.resource.scheme !== UNTITLED_SCHEME + && await this.fileService.exists(folderToOpen.resource); const folder = folderToOpen && folderExists ? folderToOpen : { resource: await this.rootProvider.getUserWorkingDir(), isDirectory: true diff --git a/packages/workspace/src/browser/workspace-user-working-directory-provider.ts b/packages/workspace/src/browser/workspace-user-working-directory-provider.ts index 26d4121f7add8..af8f69d2a2cb5 100644 --- a/packages/workspace/src/browser/workspace-user-working-directory-provider.ts +++ b/packages/workspace/src/browser/workspace-user-working-directory-provider.ts @@ -28,6 +28,7 @@ export class WorkspaceUserWorkingDirectoryProvider extends UserWorkingDirectoryP override async getUserWorkingDir(): Promise { return await this.getFromSelection() + ?? await this.getFromLastOpenResource() ?? await this.getFromWorkspace() ?? this.getFromUserHome(); } From c97a49648e5fbc7becfd5042ff9155953329904b Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Wed, 20 Dec 2023 09:09:12 +0100 Subject: [PATCH 015/441] Add section about custom activation events to custom plugin API docu (#13190) Relates to https://github.com/eclipse-theia/theia/issues/13067 --- .../plugin-ext/doc/how-to-add-new-custom-plugin-api.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md index 8ef05c2e32592..2563edc2a15ad 100644 --- a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md +++ b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md @@ -260,6 +260,16 @@ import * as foo from '@bar/foo'; foo.fooBar.getFoo(); ``` +## Adding custom plugin activation events + +When creating a custom plugin API there may also arise a need to trigger the activation of your plugins at a certain point in time. +The events that trigger the activation of a plugin are simply called `activation events`. +By default Theia supports a set of built-in activation events that contains the [activation events from VS Code](https://code.visualstudio.com/api/references/activation-events) as well as some additional Theia-specific events. +Technically, an activation event is nothing more than a unique string fired at a specific point in time. +To add more flexibility to activations events, Theia allows you to provide additional custom activation events when initializing a plugin host. +These additional events can be specified by adopters through the `ADDITIONAL_ACTIVATION_EVENTS` environment variable. +To fire an activation event, you need to call the plugin hosts `$activateByEvent(eventName)` method. + ## Packaging When bundling our application with the generated `gen-webpack.node.config.js` we need to make sure that our initialization function is bundled as a `commonjs2` library so it can be dynamically loaded. From 7dcce3e993af0e311a964df6aa4a941fb1ab45d0 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 20 Dec 2023 15:27:52 +0100 Subject: [PATCH 016/441] [vscode] Support TestMessage#contextValue (#13176) Also adds the menu mapping for testing/message/context vscode menu extension. contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/plugin-ext/src/common/test-types.ts | 20 +++++++++++ .../menus/plugin-menu-command-adapter.ts | 29 +++++++++++++++ .../menus/vscode-theia-menu-mappings.ts | 3 ++ packages/plugin-ext/src/plugin/tests.ts | 26 +++++++++++++- .../plugin-ext/src/plugin/type-converters.ts | 3 +- packages/plugin-ext/src/plugin/types-impl.ts | 1 + packages/plugin/src/theia.d.ts | 31 ++++++++++++++++ packages/test/src/browser/test-service.ts | 8 +++++ .../browser/view/test-context-key-service.ts | 36 +++++++++++++++++++ .../src/browser/view/test-output-ui-model.ts | 10 +++++- .../test/src/browser/view/test-run-widget.tsx | 11 ++++-- .../browser/view/test-view-frontend-module.ts | 2 ++ 13 files changed, 175 insertions(+), 6 deletions(-) create mode 100644 packages/test/src/browser/view/test-context-key-service.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 412473d665f88..2dbc881f452a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## Unreleased +- [plugin] Support TestMessage.contextValue from vscode API [#13176](https://github.com/eclipse-theia/theia/pull/13176) - contributed on behalf of STMicroelectronics - [terminal] Use application shell methods for expanding/collapsing bottom panel for "Terminal: Toggle Terminal" command [#13131](https://github.com/eclipse-theia/theia/pull/13131) - [workspace] Create an empty workspace if no workspace is active on updateWorkspaceFolders [#13181](https://github.com/eclipse-theia/theia/pull/13181) - contributed on behalf of STMicroelectronics diff --git a/packages/plugin-ext/src/common/test-types.ts b/packages/plugin-ext/src/common/test-types.ts index c1d74d23ba8e5..f73d099266e5d 100644 --- a/packages/plugin-ext/src/common/test-types.ts +++ b/packages/plugin-ext/src/common/test-types.ts @@ -84,6 +84,7 @@ export interface TestMessageDTO { readonly actual?: string; readonly location?: Location; readonly message: string | MarkdownString; + readonly contextValue?: string; } export interface TestItemDTO { @@ -131,3 +132,22 @@ export namespace TestItemReference { } } +export interface TestMessageArg { + testItemReference: TestItemReference | undefined, + testMessage: TestMessageDTO +} + +export namespace TestMessageArg { + export function is(arg: unknown): arg is TestMessageArg { + return isObject(arg) + && isObject(arg.testMessage) + && (MarkdownString.is(arg.testMessage.message) || typeof arg.testMessage.message === 'string'); + } + + export function create(testItemReference: TestItemReference | undefined, testMessageDTO: TestMessageDTO): TestMessageArg { + return { + testItemReference: testItemReference, + testMessage: testMessageDTO + }; + } +} diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index 8dab5d8a80068..0a3cccc5e4593 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -23,10 +23,13 @@ import { ScmRepository } from '@theia/scm/lib/browser/scm-repository'; import { ScmService } from '@theia/scm/lib/browser/scm-service'; import { TimelineItem } from '@theia/timeline/lib/common/timeline-model'; import { ScmCommandArg, TimelineCommandArg, TreeViewItemReference } from '../../../common'; +import { TestItemReference, TestMessageArg } from '../../../common/test-types'; import { PluginScmProvider, PluginScmResource, PluginScmResourceGroup } from '../scm-main'; import { TreeViewWidget } from '../view/tree-view-widget'; import { CodeEditorWidgetUtil, codeToTheiaMappings, ContributionPoint } from './vscode-theia-menu-mappings'; import { TAB_BAR_TOOLBAR_CONTEXT_MENU } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { TestItem, TestMessage } from '@theia/test/lib/browser/test-service'; +import { fromLocation } from '../hierarchy/hierarchy-types-converters'; export type ArgumentAdapter = (...args: unknown[]) => unknown[]; @@ -79,6 +82,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { @postConstruct() protected init(): void { const toCommentArgs: ArgumentAdapter = (...args) => this.toCommentArgs(...args); + const toTestMessageArgs: ArgumentAdapter = (...args) => this.toTestMessageArgs(...args); const firstArgOnly: ArgumentAdapter = (...args) => [args[0]]; const noArgs: ArgumentAdapter = () => []; const toScmArgs: ArgumentAdapter = (...args) => this.toScmArgs(...args); @@ -100,6 +104,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { ['scm/resourceGroup/context', toScmArgs], ['scm/resourceState/context', toScmArgs], ['scm/title', () => [this.toScmArg(this.scmService.selectedRepository)]], + ['testing/message/context', toTestMessageArgs], ['timeline/item/context', (...args) => this.toTimelineArgs(...args)], ['view/item/context', (...args) => this.toTreeArgs(...args)], ['view/title', noArgs], @@ -230,6 +235,30 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { return timelineArgs; } + protected toTestMessageArgs(...args: any[]): any[] { + let testItem: TestItem | undefined; + let testMessage: TestMessage | undefined; + for (const arg of args) { + if (TestItem.is(arg)) { + testItem = arg; + } else if (Array.isArray(arg) && TestMessage.is(arg[0])) { + testMessage = arg[0]; + } + } + if (testMessage) { + const testItemReference = (testItem && testItem.controller) ? TestItemReference.create(testItem.controller.id, testItem.path) : undefined; + const testMessageDTO = { + message: testMessage.message, + actual: testMessage.actual, + expected: testMessage.expected, + contextValue: testMessage.contextValue, + location: testMessage.location ? fromLocation(testMessage.location) : undefined + }; + return [TestMessageArg.create(testItemReference, testMessageDTO)]; + } + return []; + } + protected toTimelineArg(arg: TimelineItem): TimelineCommandArg { return { timelineHandle: arg.handle, diff --git a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts index c7149296b9b43..bf6ea3df16029 100644 --- a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts +++ b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts @@ -32,6 +32,7 @@ import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget'; import { WEBVIEW_CONTEXT_MENU, WebviewWidget } from '../webview/webview'; import { EDITOR_LINENUMBER_CONTEXT_MENU } from '@theia/editor/lib/browser/editor-linenumber-contribution'; import { TEST_VIEW_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-view-contribution'; +import { TEST_RUNS_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-run-view-contribution'; export const PLUGIN_EDITOR_TITLE_MENU = ['plugin_editor/title']; export const PLUGIN_EDITOR_TITLE_RUN_MENU = ['plugin_editor/title/run']; @@ -57,6 +58,7 @@ export const implementedVSCodeContributionPoints = [ 'scm/title', 'timeline/item/context', 'testing/item/context', + 'testing/message/context', 'view/item/context', 'view/title', 'webview/context' @@ -83,6 +85,7 @@ export const codeToTheiaMappings = new Map([ ['scm/resourceState/context', [ScmTreeWidget.RESOURCE_CONTEXT_MENU]], ['scm/title', [PLUGIN_SCM_TITLE_MENU]], ['testing/item/context', [TEST_VIEW_CONTEXT_MENU]], + ['testing/message/context', [TEST_RUNS_CONTEXT_MENU]], ['timeline/item/context', [TIMELINE_ITEM_CONTEXT_MENU]], ['view/item/context', [VIEW_ITEM_CONTEXT_MENU]], ['view/title', [PLUGIN_VIEW_TITLE_MENU]], diff --git a/packages/plugin-ext/src/plugin/tests.ts b/packages/plugin-ext/src/plugin/tests.ts index e58bc432d91da..9f1083651123b 100644 --- a/packages/plugin-ext/src/plugin/tests.ts +++ b/packages/plugin-ext/src/plugin/tests.ts @@ -40,10 +40,11 @@ import { TestItemImpl, TestItemCollection } from './test-item'; import { AccumulatingTreeDeltaEmitter, TreeDelta } from '@theia/test/lib/common/tree-delta'; import { TestItemDTO, TestOutputDTO, TestExecutionState, TestRunProfileDTO, - TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference + TestRunProfileKind, TestRunRequestDTO, TestStateChangeDTO, TestItemReference, TestMessageArg, TestMessageDTO } from '../common/test-types'; import { ChangeBatcher, observableProperty } from '@theia/test/lib/common/collections'; import { TestRunRequest } from './types-impl'; +import { MarkdownString } from '../common/plugin-api-rpc-model'; type RefreshHandler = (token: theia.CancellationToken) => void | theia.Thenable; type ResolveHandler = (item: theia.TestItem | undefined) => theia.Thenable | void; @@ -335,6 +336,8 @@ export class TestingExtImpl implements TestingExt { return this.toTestItem(arg); } else if (Array.isArray(arg)) { return arg.map(param => TestItemReference.is(param) ? this.toTestItem(param) : param); + } else if (TestMessageArg.is(arg)) { + return this.fromTestMessageArg(arg); } else { return arg; } @@ -343,6 +346,27 @@ export class TestingExtImpl implements TestingExt { } + fromTestMessageArg(arg: TestMessageArg): { test?: theia.TestItem, message: theia.TestMessage } { + const testItem = arg.testItemReference ? this.toTestItem(arg.testItemReference) : undefined; + const message = this.toTestMessage(arg.testMessage); + return { + test: testItem, + message: message + }; + } + + toTestMessage(testMessage: TestMessageDTO): theia.TestMessage { + const message = MarkdownString.is(testMessage.message) ? Convert.toMarkdown(testMessage.message) : testMessage.message; + + return { + message: message, + actualOutput: testMessage.actual, + expectedOutput: testMessage.expected, + contextValue: testMessage.contextValue, + location: testMessage.location ? Convert.toLocation(testMessage.location) : undefined + }; + } + toTestItem(ref: TestItemReference): theia.TestItem { const result = this.withController(ref.controllerId).items.find(ref.testPath); if (!result) { diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 405d6873b7050..ba0d8140c3435 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -1644,7 +1644,8 @@ export namespace TestMessage { location: fromLocation(message.location), message: fromMarkdown(message.message)!, expected: message.expectedOutput, - actual: message.actualOutput + actual: message.actualOutput, + contextValue: message.contextValue }]; } } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 5e3f05a21e665..69e74af05355f 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3293,6 +3293,7 @@ export class TestMessage implements theia.TestMessage { public expectedOutput?: string; public actualOutput?: string; public location?: theia.Location; + public contextValue?: string; public static diff(message: string | theia.MarkdownString, expected: string, actual: string): theia.TestMessage { const msg = new TestMessage(message); diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 31847322fd15f..7451907b61cb5 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -16448,6 +16448,37 @@ export module '@theia/plugin' { */ location?: Location; + /** + * Context value of the test item. This can be used to contribute message- + * specific actions to the test peek view. The value set here can be found + * in the `testMessage` property of the following `menus` contribution points: + * + * - `testing/message/context` - context menu for the message in the results tree + * - `testing/message/content` - a prominent button overlaying editor content where + * the message is displayed. + * + * For example: + * + * ```json + * "contributes": { + * "menus": { + * "testing/message/content": [ + * { + * "command": "extension.deleteCommentThread", + * "when": "testMessage == canApplyRichDiff" + * } + * ] + * } + * } + * ``` + * + * The command will be called with an object containing: + * - `test`: the {@link TestItem} the message is associated with, *if* it + * is still present in the {@link TestController.items} collection. + * - `message`: the {@link TestMessage} instance. + */ + contextValue?: string; + /** * Creates a new TestMessage that will present as a diff in the editor. * @param message Message to display to the user. diff --git a/packages/test/src/browser/test-service.ts b/packages/test/src/browser/test-service.ts index e7aba373d23b8..cc0bc6f0dfaa2 100644 --- a/packages/test/src/browser/test-service.ts +++ b/packages/test/src/browser/test-service.ts @@ -58,6 +58,13 @@ export interface TestMessage { readonly actual?: string; readonly location: Location; readonly message: string | MarkdownString; + readonly contextValue?: string; +} + +export namespace TestMessage { + export function is(obj: unknown): obj is TestMessage { + return isObject(obj) && (MarkdownString.is(obj.message) || typeof obj.message === 'string'); + } } export interface TestState { @@ -136,6 +143,7 @@ export interface TestItem { readonly controller: TestController | undefined; readonly canResolveChildren: boolean; resolveChildren(): void; + readonly path: string[]; } export namespace TestItem { diff --git a/packages/test/src/browser/view/test-context-key-service.ts b/packages/test/src/browser/view/test-context-key-service.ts new file mode 100644 index 0000000000000..866b048f747ac --- /dev/null +++ b/packages/test/src/browser/view/test-context-key-service.ts @@ -0,0 +1,36 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service'; + +@injectable() +export class TestContextKeyService { + + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + + protected _contextValue: ContextKey; + get contextValue(): ContextKey { + return this._contextValue; + } + + @postConstruct() + protected init(): void { + this._contextValue = this.contextKeyService.createKey('testMessage', undefined); + } + +} diff --git a/packages/test/src/browser/view/test-output-ui-model.ts b/packages/test/src/browser/view/test-output-ui-model.ts index 87d6f586bb053..f9520d71b337e 100644 --- a/packages/test/src/browser/view/test-output-ui-model.ts +++ b/packages/test/src/browser/view/test-output-ui-model.ts @@ -15,8 +15,9 @@ // ***************************************************************************** import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { TestController, TestOutputItem, TestRun, TestService, TestState, TestStateChangedEvent } from '../test-service'; +import { TestController, TestFailure, TestOutputItem, TestRun, TestService, TestState, TestStateChangedEvent } from '../test-service'; import { Disposable, Emitter, Event } from '@theia/core'; +import { TestContextKeyService } from './test-context-key-service'; export interface ActiveRunEvent { controller: TestController; @@ -41,6 +42,7 @@ interface ActiveTestRunInfo { @injectable() export class TestOutputUIModel { + @inject(TestContextKeyService) protected readonly testContextKeys: TestContextKeyService; @inject(TestService) protected testService: TestService; protected readonly activeRuns = new Map(); @@ -139,6 +141,12 @@ export class TestOutputUIModel { set selectedTestState(element: TestState | undefined) { if (element !== this._selectedTestState) { this._selectedTestState = element; + if (this._selectedTestState && TestFailure.is(this._selectedTestState.state)) { + const message = this._selectedTestState.state.messages[0]; + this.testContextKeys.contextValue.set(message.contextValue); + } else { + this.testContextKeys.contextValue.reset(); + } this.onDidChangeSelectedTestStateEmitter.fire(element); } } diff --git a/packages/test/src/browser/view/test-run-widget.tsx b/packages/test/src/browser/view/test-run-widget.tsx index 296573ffb63e9..c45a6454be7e8 100644 --- a/packages/test/src/browser/view/test-run-widget.tsx +++ b/packages/test/src/browser/view/test-run-widget.tsx @@ -20,7 +20,7 @@ import { ContextMenuRenderer, codicon } from '@theia/core/lib/browser'; import { IconThemeService } from '@theia/core/lib/browser/icon-theme-service'; import { ThemeService } from '@theia/core/lib/browser/theming'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; -import { TestController, TestExecutionState, TestItem, TestOutputItem, TestRun, TestService } from '../test-service'; +import { TestController, TestExecutionState, TestFailure, TestItem, TestMessage, TestOutputItem, TestRun, TestService } from '../test-service'; import * as React from '@theia/core/shared/react'; import { Disposable, DisposableCollection, Event, nls } from '@theia/core'; import { TestExecutionStateManager } from './test-execution-state-manager'; @@ -251,11 +251,16 @@ export class TestRunTreeWidget extends TreeWidget { } } - protected override toContextMenuArgs(node: SelectableTreeNode): (TestRun | TestItem)[] { + protected override toContextMenuArgs(node: SelectableTreeNode): (TestRun | TestItem | TestMessage[])[] { if (node instanceof TestRunNode) { return [node.run]; } else if (node instanceof TestItemNode) { - return [node.item]; + const item = node.item; + const executionState = node.parent.run.getTestState(node.item); + if (TestFailure.is(executionState)) { + return [item, executionState.messages]; + } + return [item]; } return []; } diff --git a/packages/test/src/browser/view/test-view-frontend-module.ts b/packages/test/src/browser/view/test-view-frontend-module.ts index 7040fa6384f01..5ba3d8b860731 100644 --- a/packages/test/src/browser/view/test-view-frontend-module.ts +++ b/packages/test/src/browser/view/test-view-frontend-module.ts @@ -35,10 +35,12 @@ import { TestOutputUIModel } from './test-output-ui-model'; import { TestRunTree, TestRunTreeWidget } from './test-run-widget'; import { TestResultViewContribution } from './test-result-view-contribution'; import { TEST_RUNS_CONTEXT_MENU, TestRunViewContribution } from './test-run-view-contribution'; +import { TestContextKeyService } from './test-context-key-service'; export default new ContainerModule(bind => { bindContributionProvider(bind, TestContribution); + bind(TestContextKeyService).toSelf().inSingletonScope(); bind(TestService).to(DefaultTestService).inSingletonScope(); bind(WidgetFactory).toDynamicValue(({ container }) => ({ From 31743431f237c760a530d178ffba2894140319bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 20 Dec 2023 16:03:37 +0100 Subject: [PATCH 017/441] * Add a cli parameter '--electronUserData` to set the Electron 'userData' path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of #13107 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../electron-main-application.ts | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 3a67acd71ff1b..645048d78012c 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -54,19 +54,13 @@ export interface ElectronMainCommandOptions { */ readonly file?: string; -} + readonly cwd: string; -/** - * Fields related to a launch event. - * - * This kind of event is triggered in two different contexts: - * 1. The app is launched for the first time, `secondInstance` is false. - * 2. The app is already running but user relaunches it, `secondInstance` is true. - */ -export interface ElectronMainExecutionParams { + /** + * If the app is launched for the first time, `secondInstance` is false. + * If the app is already running but user relaunches it, `secondInstance` is true. + */ readonly secondInstance: boolean; - readonly argv: string[]; - readonly cwd: string; } /** @@ -212,21 +206,38 @@ export class ElectronMainApplication { } async start(config: FrontendApplicationConfig): Promise { - const args = this.processArgv.getProcessArgvWithoutBin(process.argv); - this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native'; - this._config = config; - this.hookApplicationEvents(); - this.showInitialWindow(); - const port = await this.startBackend(); - this._backendPort.resolve(port); - await app.whenReady(); - await this.attachElectronSecurityToken(port); - await this.startContributions(); - await this.launch({ - secondInstance: false, - argv: args, - cwd: process.cwd() - }); + const argv = this.processArgv.getProcessArgvWithoutBin(process.argv); + createYargs(argv, process.cwd()) + .command('$0 [file]', false, + cmd => cmd + .option('electronUserData', { + type: 'string', + describe: 'The area where the electron main process puts its data' + }) + .positional('file', { type: 'string' }), + async args => { + if (args.electronUserData) { + console.info(`using electron user data area : '${args.electronUserData}'`); + await fs.mkdir(args.electronUserData, { recursive: true }); + app.setPath('userData', args.electronUserData); + } + this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native'; + this._config = config; + this.hookApplicationEvents(); + this.showInitialWindow(); + const port = await this.startBackend(); + this._backendPort.resolve(port); + await app.whenReady(); + await this.attachElectronSecurityToken(port); + await this.startContributions(); + + this.handleMainCommand({ + file: args.file, + cwd: process.cwd(), + secondInstance: false + }); + }, + ).parse(); } protected getTitleBarStyle(config: FrontendApplicationConfig): 'native' | 'custom' { @@ -288,15 +299,6 @@ export class ElectronMainApplication { } } - protected async launch(params: ElectronMainExecutionParams): Promise { - createYargs(params.argv, params.cwd) - .command('$0 [file]', false, - cmd => cmd - .positional('file', { type: 'string' }), - args => this.handleMainCommand(params, { file: args.file }), - ).parse(); - } - /** * Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it. * @@ -422,15 +424,15 @@ export class ElectronMainApplication { app.quit(); } - protected async handleMainCommand(params: ElectronMainExecutionParams, options: ElectronMainCommandOptions): Promise { - if (params.secondInstance === false) { + protected async handleMainCommand(options: ElectronMainCommandOptions): Promise { + if (options.secondInstance === false) { await this.openWindowWithWorkspace(''); // restore previous workspace. } else if (options.file === undefined) { await this.openDefaultWindow(); } else { let workspacePath: string | undefined; try { - workspacePath = await fs.realpath(path.resolve(params.cwd, options.file)); + workspacePath = await fs.realpath(path.resolve(options.cwd, options.file)); } catch { console.error(`Could not resolve the workspace path. "${options.file}" is not a valid 'file' option. Falling back to the default workspace location.`); } @@ -645,9 +647,8 @@ export class ElectronMainApplication { if (wrapper) { const listener = wrapper.onDidClose(async () => { listener.dispose(); - await this.launch({ + await this.handleMainCommand({ secondInstance: false, - argv: this.processArgv.getProcessArgvWithoutBin(process.argv), cwd: process.cwd() }); this.restarting = false; From 251a5655e0c474ef5878c62493c7f7ed97441ef7 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 20 Dec 2023 17:27:18 +0100 Subject: [PATCH 018/441] Bump API version to 1.84.2 (#13198) Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- dev-packages/application-package/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index 5e86da406d12a..a61f3c2a095e2 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.83.1'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.84.2'; From ea0db7a64147ae4f3143946ed8558498237bc1e0 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 20 Dec 2023 20:34:56 +0100 Subject: [PATCH 019/441] nls: update metadata for vscode API 1.84.2 (#13200) The commit updates the `nls.metadata.json` for the `v1.84.2` release. --- .../src/browser/shell/application-shell.ts | 2 +- .../core/src/common/i18n/nls.metadata.json | 14131 ++++++++-------- .../editor-generated-preference-schema.ts | 6 +- .../src/browser/navigator-widget.tsx | 2 +- 4 files changed, 7213 insertions(+), 6928 deletions(-) diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index b93858eccdb54..3e86fb211fe78 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -2101,7 +2101,7 @@ export namespace ApplicationShell { export const areaLabels: Record = { main: nls.localizeByDefault('Main'), - top: nls.localize('theia/shell-area/top', 'Top'), + top: nls.localizeByDefault('Top'), left: nls.localizeByDefault('Left'), right: nls.localizeByDefault('Right'), bottom: nls.localizeByDefault('Bottom'), diff --git a/packages/core/src/common/i18n/nls.metadata.json b/packages/core/src/common/i18n/nls.metadata.json index d04046e10b5b4..d2f3eae547d4f 100644 --- a/packages/core/src/common/i18n/nls.metadata.json +++ b/packages/core/src/common/i18n/nls.metadata.json @@ -3,6 +3,9 @@ "vs/platform/terminal/node/ptyHostMain": [ "ptyHost" ], + "vs/code/node/cliProcessMain": [ + "cli" + ], "vs/code/electron-main/main": [ "mainLog", "secondInstanceAdmin", @@ -19,9 +22,6 @@ ] } ], - "vs/code/node/cliProcessMain": [ - "cli" - ], "vs/code/node/sharedProcess/sharedProcessMain": [ "sharedLog" ], @@ -84,6 +84,8 @@ "keyboardConfigurationTitle", "touchbar.enabled", "touchbar.ignored", + "security.promptForLocalFileProtocolHandling", + "security.promptForRemoteFileProtocolHandling", "argv.locale", "argv.disableHardwareAcceleration", "argv.forceColorProfile", @@ -92,7 +94,9 @@ "argv.enebleProposedApi", "argv.logLevel", "argv.disableChromiumSandbox", - "argv.force-renderer-accessibility" + "argv.useInMemorySecretStorage", + "argv.force-renderer-accessibility", + "argv.passwordStore" ], "vs/workbench/services/textfile/electron-sandbox/nativeTextFileService": [ "join.textFiles" @@ -146,19 +150,6 @@ "integrity.moreInformation", "integrity.dontShowAgain" ], - "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ - "revealInWindows", - "revealInMac", - "openContainer", - "miShare", - "filesCategory" - ], - "vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService": [ - "voiceTranscription", - "voiceTranscriptionGettingReady", - "voiceTranscriptionRecording", - "voiceTranscriptionError" - ], "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ "join.workingCopyBackups" ], @@ -180,19 +171,18 @@ "restartExtensionHost", "restartExtensionHost.reason" ], - "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ - "runtimeExtension" + "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ + "revealInWindows", + "revealInMac", + "openContainer", + "miShare", + "filesCategory" ], "vs/workbench/contrib/localization/electron-sandbox/localization.contribution": [ "updateLocale", "changeAndRestart", "neverAgain" ], - "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ - "wslFeatureInstalled", - "remote", - "remote.downloadExtensionsLocally" - ], "vs/workbench/contrib/issue/electron-sandbox/issue.contribution": [ { "key": "reportPerformanceIssue", @@ -218,11 +208,22 @@ "stopTracing.title", "stopTracing.detail" ], + "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ + "runtimeExtension" + ], + "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ + "wslFeatureInstalled", + "remote", + "remote.downloadExtensionsLocally" + ], "vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution": [ - "Open Backup folder", "no backups", "download sync activity complete", - "open" + "open", + "Open Backup folder" + ], + "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ + "experimental.rendererProfiling" ], "vs/workbench/contrib/tasks/electron-sandbox/taskService": [ "TaskSystem.runningTask", @@ -240,9 +241,6 @@ ] } ], - "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ - "experimental.rendererProfiling" - ], "vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution": [ "globalConsoleAction", "terminalConfigurationTitle", @@ -354,6 +352,57 @@ ] } ], + "vs/platform/environment/node/argv": [ + "optionsUpperCase", + "extensionsManagement", + "troubleshooting", + "cliDataDir", + "cliDataDir", + "diff", + "merge", + "add", + "goto", + "newWindow", + "reuseWindow", + "wait", + "locale", + "userDataDir", + "profileName", + "help", + "extensionHomePath", + "listExtensions", + "showVersions", + "category", + "installExtension", + "install prerelease", + "uninstallExtension", + "experimentalApis", + "version", + "verbose", + "log", + "status", + "prof-startup", + "disableExtensions", + "disableExtension", + "turn sync", + "inspect-extensions", + "inspect-brk-extensions", + "disableGPU", + "disableChromiumSandbox", + "telemetry", + "deprecated.useInstead", + "paths", + "usage", + "options", + "stdinWindows", + "stdinUnix", + "subcommands", + "unknownVersion", + "unknownCommit" + ], + "vs/platform/terminal/node/ptyService": [ + "terminal-history-restored" + ], "vs/editor/common/config/editorOptions": [ "accessibilitySupport.auto", "accessibilitySupport.on", @@ -460,6 +509,7 @@ "scrollbar.verticalScrollbarSize", "scrollbar.horizontalScrollbarSize", "scrollbar.scrollByPage", + "scrollbar.ignoreHorizontalScrollbarInContentHeight", "unicodeHighlight.nonBasicASCII", "unicodeHighlight.invisibleCharacters", "unicodeHighlight.ambiguousCharacters", @@ -643,6 +693,7 @@ "multiCursorPaste", "multiCursorLimit", "occurrencesHighlight", + "multiDocumentOccurrencesHighlight", "overviewRulerBorder", "peekWidgetDefaultFocus.tree", "peekWidgetDefaultFocus.editor", @@ -731,57 +782,6 @@ "defaultColorDecorators", "tabFocusMode" ], - "vs/platform/environment/node/argv": [ - "optionsUpperCase", - "extensionsManagement", - "troubleshooting", - "cliDataDir", - "cliDataDir", - "diff", - "merge", - "add", - "goto", - "newWindow", - "reuseWindow", - "wait", - "locale", - "userDataDir", - "profileName", - "help", - "extensionHomePath", - "listExtensions", - "showVersions", - "category", - "installExtension", - "install prerelease", - "uninstallExtension", - "experimentalApis", - "version", - "verbose", - "log", - "status", - "prof-startup", - "disableExtensions", - "disableExtension", - "turn sync", - "inspect-extensions", - "inspect-brk-extensions", - "disableGPU", - "disableChromiumSandbox", - "telemetry", - "deprecated.useInstead", - "paths", - "usage", - "options", - "stdinWindows", - "stdinUnix", - "subcommands", - "unknownVersion", - "unknownCommit" - ], - "vs/platform/terminal/node/ptyService": [ - "terminal-history-restored" - ], "vs/base/common/errorMessage": [ "stackTrace.format", "nodeExceptionMessage", @@ -790,21 +790,47 @@ "error.moreErrors", "error.defaultMessage" ], - "vs/code/electron-main/app": [ - { - "key": "open", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "cancel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "confirmOpenMessage", - "confirmOpenDetail" + "vs/platform/extensionManagement/common/extensionManagement": [ + "extensions", + "preferences" + ], + "vs/platform/extensionManagement/common/extensionManagementCLI": [ + "notFound", + "useId", + "listFromLocation", + "installingExtensionsOnLocation", + "installingExtensions", + "alreadyInstalled-checkAndUpdate", + "alreadyInstalled", + "error while installing extensions", + "installation failed", + "successVsixInstall", + "cancelVsixInstall", + "alreadyInstalled", + "updateMessage", + "installing builtin with version", + "installing builtin ", + "installing with version", + "installing", + "successInstall", + "cancelInstall", + "forceDowngrade", + "builtin", + "forceUninstall", + "uninstalling", + "successUninstallFromLocation", + "successUninstall", + "notInstalleddOnLocation", + "notInstalled" + ], + "vs/platform/extensionManagement/common/extensionsScannerService": [ + "fileReadFail", + "jsonParseFail", + "jsonParseInvalidType", + "jsonsParseReportErrors", + "jsonInvalidFormat", + "jsonsParseReportErrors", + "jsonInvalidFormat" ], "vs/platform/files/common/files": [ "unknownError", @@ -814,22 +840,16 @@ "sizeGB", "sizeTB" ], - "vs/platform/environment/node/argvHelper": [ - "multipleValues", - "emptyValue", - "deprecatedArgument", - "unknownSubCommandOption", - "unknownOption", - "gotoValidation" - ], - "vs/platform/files/node/diskFileSystemProvider": [ - "fileExists", - "fileNotExists", - "moveError", - "copyError", - "fileCopyErrorPathCase", - "fileMoveCopyErrorNotFound", - "fileMoveCopyErrorExists" + "vs/platform/extensionManagement/node/extensionManagementService": [ + "incompatible", + "MarketPlaceDisabled", + "Not a Marketplace extension", + "removeError", + "errorDeleting", + "renameError", + "cannot read", + "restartCode", + "restartCode" ], "vs/platform/files/common/fileService": [ "invalidPath", @@ -860,6 +880,18 @@ "err.readonly", "err.readonly" ], + "vs/platform/files/node/diskFileSystemProvider": [ + "fileExists", + "fileNotExists", + "moveError", + "copyError", + "fileCopyErrorPathCase", + "fileMoveCopyErrorNotFound", + "fileMoveCopyErrorExists" + ], + "vs/platform/languagePacks/common/languagePacks": [ + "currentDisplayLanguage" + ], "vs/platform/request/common/request": [ "request", "httpConfigurationTitle", @@ -875,25 +907,74 @@ "systemCertificates", "systemCertificatesV2" ], - "vs/platform/dialogs/common/dialogs": [ - { - "key": "yesButton", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "cancelButton", - "cancelButton", - "cancelButton", - { - "key": "okButton", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "okButton", - "comment": [ + "vs/platform/telemetry/common/telemetryService": [ + "telemetry.telemetryLevelMd", + "telemetry.docsStatement", + "telemetry.docsAndPrivacyStatement", + "telemetry.restart", + "telemetry.crashReports", + "telemetry.errors", + "telemetry.usage", + "telemetry.telemetryLevel.tableDescription", + "telemetry.telemetryLevel.deprecated", + "telemetryConfigurationTitle", + "telemetry.telemetryLevel.default", + "telemetry.telemetryLevel.error", + "telemetry.telemetryLevel.crash", + "telemetry.telemetryLevel.off", + "telemetryConfigurationTitle", + "telemetry.enableTelemetry", + "telemetry.enableTelemetryMd", + "enableTelemetryDeprecated" + ], + "vs/platform/userDataProfile/common/userDataProfile": [ + "defaultProfile" + ], + "vs/code/electron-main/app": [ + { + "key": "open", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "cancel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmOpenMessage", + "confirmOpenDetail", + "doNotAskAgainLocal", + "doNotAskAgainRemote" + ], + "vs/platform/environment/node/argvHelper": [ + "multipleValues", + "emptyValue", + "deprecatedArgument", + "unknownSubCommandOption", + "unknownOption", + "gotoValidation" + ], + "vs/platform/dialogs/common/dialogs": [ + { + "key": "yesButton", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "cancelButton", + "cancelButton", + "cancelButton", + { + "key": "okButton", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "okButton", + "comment": [ "&& denotes a mnemonic" ] }, @@ -920,6 +1001,7 @@ "sendWorkspaceInfo", "sendExtensions", "sendExperiments", + "sendExtensionData", { "key": "reviewGuidanceLabel", "comment": [ @@ -943,6 +1025,8 @@ "details", "descriptionEmptyValidation", "show", + "extensionData", + "show", "show", "show", "show", @@ -983,85 +1067,6 @@ "disabledExtensions", "noCurrentExperiments" ], - "vs/platform/extensionManagement/common/extensionManagement": [ - "extensions", - "preferences" - ], - "vs/platform/extensionManagement/common/extensionsScannerService": [ - "fileReadFail", - "jsonParseFail", - "jsonParseInvalidType", - "jsonsParseReportErrors", - "jsonInvalidFormat", - "jsonsParseReportErrors", - "jsonInvalidFormat" - ], - "vs/platform/languagePacks/common/languagePacks": [ - "currentDisplayLanguage" - ], - "vs/platform/extensionManagement/common/extensionManagementCLI": [ - "notFound", - "useId", - "listFromLocation", - "installingExtensionsOnLocation", - "installingExtensions", - "alreadyInstalled-checkAndUpdate", - "alreadyInstalled", - "error while installing extensions", - "installation failed", - "successVsixInstall", - "cancelVsixInstall", - "alreadyInstalled", - "updateMessage", - "installing builtin with version", - "installing builtin ", - "installing with version", - "installing", - "successInstall", - "cancelInstall", - "forceDowngrade", - "builtin", - "forceUninstall", - "uninstalling", - "successUninstallFromLocation", - "successUninstall", - "notInstalleddOnLocation", - "notInstalled" - ], - "vs/platform/extensionManagement/node/extensionManagementService": [ - "incompatible", - "MarketPlaceDisabled", - "Not a Marketplace extension", - "removeError", - "errorDeleting", - "renameError", - "cannot read", - "restartCode", - "restartCode" - ], - "vs/platform/telemetry/common/telemetryService": [ - "telemetry.telemetryLevelMd", - "telemetry.docsStatement", - "telemetry.docsAndPrivacyStatement", - "telemetry.restart", - "telemetry.crashReports", - "telemetry.errors", - "telemetry.usage", - "telemetry.telemetryLevel.tableDescription", - "telemetry.telemetryLevel.deprecated", - "telemetryConfigurationTitle", - "telemetry.telemetryLevel.default", - "telemetry.telemetryLevel.error", - "telemetry.telemetryLevel.crash", - "telemetry.telemetryLevel.off", - "telemetryConfigurationTitle", - "telemetry.enableTelemetry", - "telemetry.enableTelemetryMd", - "enableTelemetryDeprecated" - ], - "vs/platform/userDataProfile/common/userDataProfile": [ - "defaultProfile" - ], "vs/platform/telemetry/common/telemetryLogAppender": [ "telemetryLog" ], @@ -1072,18 +1077,18 @@ "app.extension.identifier.errorMessage", "settingsSync.ignoredSettings" ], - "vs/platform/userDataSync/common/userDataSyncMachines": [ - "error incompatible" - ], "vs/platform/userDataSync/common/userDataSyncLog": [ "userDataSyncLog" ], - "vs/platform/userDataSync/common/userDataSyncResourceProvider": [ - "incompatible sync data" + "vs/platform/userDataSync/common/userDataSyncMachines": [ + "error incompatible" ], "vs/platform/remoteTunnel/common/remoteTunnel": [ "remoteTunnelLog" ], + "vs/platform/userDataSync/common/userDataSyncResourceProvider": [ + "incompatible sync data" + ], "vs/platform/remoteTunnel/node/remoteTunnelService": [ "remoteTunnelService.building", { @@ -1139,14 +1144,6 @@ "expand mode", "typeNavigationMode2" ], - "vs/platform/markers/common/markers": [ - "sev.error", - "sev.warning", - "sev.info" - ], - "vs/platform/contextkey/browser/contextKeyService": [ - "getContextKeyInfo" - ], "vs/platform/contextkey/common/contextkey": [ "contextkey.parser.error.emptyString", "contextkey.parser.error.emptyString.hint", @@ -1160,10 +1157,29 @@ "contextkey.scanner.errorForLinterWithHint", "contextkey.scanner.errorForLinter" ], + "vs/platform/contextkey/browser/contextKeyService": [ + "getContextKeyInfo" + ], + "vs/platform/markers/common/markers": [ + "sev.error", + "sev.warning", + "sev.info" + ], + "vs/workbench/browser/actions/textInputActions": [ + "undo", + "redo", + "cut", + "copy", + "paste", + "selectAll" + ], "vs/workbench/browser/workbench.contribution": [ "workbench.editor.titleScrollbarSizing.default", "workbench.editor.titleScrollbarSizing.large", "tabScrollbarHeight", + "workbench.editor.showTabs.multiple", + "workbench.editor.showTabs.single", + "workbench.editor.showTabs.none", "showEditorTabs", "wrapTabs", { @@ -1296,6 +1312,9 @@ ], "key": "doubleClickTabToToggleEditorGroupSizes" }, + "workbench.editor.doubleClickTabToToggleEditorGroupSizes.maximize", + "workbench.editor.doubleClickTabToToggleEditorGroupSizes.expand", + "workbench.editor.doubleClickTabToToggleEditorGroupSizes.off", "limitEditorsEnablement", "limitEditorsMaximum", "limitEditorsExcludeDirty", @@ -1324,7 +1343,15 @@ "workbench.panel.opensMaximized.never", "workbench.panel.opensMaximized.preserve", "statusBarVisibility", - "activityBarVisibility", + { + "comment": [ + "This is the description for a setting" + ], + "key": "activityBarLocation" + }, + "workbench.activityBar.location.side", + "workbench.activityBar.location.top", + "workbench.activityBar.location.hide", "activityBarIconClickBehavior", "workbench.activityBar.iconClickBehavior.toggle", "workbench.activityBar.iconClickBehavior.focus", @@ -1418,72 +1445,16 @@ "zenModeConfigurationTitle", "zenMode.fullScreen", "zenMode.centerLayout", - "zenMode.hideTabs", + "zenMode.showTabs", + "zenMode.showTabs.multiple", + "zenMode.showTabs.single", + "zenMode.showTabs.none", "zenMode.hideStatusBar", "zenMode.hideActivityBar", "zenMode.hideLineNumbers", "zenMode.restore", "zenMode.silentNotifications" ], - "vs/workbench/browser/actions/textInputActions": [ - "undo", - "redo", - "cut", - "copy", - "paste", - "selectAll" - ], - "vs/workbench/browser/actions/developerActions": [ - "inspect context keys", - "toggle screencast mode", - { - "key": "logStorage", - "comment": [ - "A developer only action to log the contents of the storage for the current window." - ] - }, - "storageLogDialogMessage", - "storageLogDialogDetails", - { - "key": "logWorkingCopies", - "comment": [ - "A developer only action to log the working copies that exist." - ] - }, - "removeLargeStorageDatabaseEntries", - "largeStorageItemDetail", - "global", - "profile", - "workspace", - "machine", - "user", - "removeLargeStorageEntriesPickerButton", - "removeLargeStorageEntriesPickerPlaceholder", - "removeLargeStorageEntriesPickerDescriptionNoEntries", - "removeLargeStorageEntriesConfirmRemove", - "removeLargeStorageEntriesConfirmRemoveDetail", - { - "key": "removeLargeStorageEntriesButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "startTrackDisposables", - "snapshotTrackedDisposables", - "stopTrackDisposables", - "screencastModeConfigurationTitle", - "screencastMode.location.verticalPosition", - "screencastMode.fontSize", - "screencastMode.keyboardOptions.description", - "screencastMode.keyboardOptions.showKeys", - "screencastMode.keyboardOptions.showKeybindings", - "screencastMode.keyboardOptions.showCommands", - "screencastMode.keyboardOptions.showCommandGroups", - "screencastMode.keyboardOptions.showSingleEditorCursorMoves", - "screencastMode.keyboardOverlayTimeout", - "screencastMode.mouseIndicatorColor", - "screencastMode.mouseIndicatorSize" - ], "vs/workbench/browser/actions/helpActions": [ "keybindingsReference", { @@ -1543,18 +1514,69 @@ ] } ], - "vs/workbench/browser/actions/layoutActions": [ - "menuBarIcon", - "activityBarLeft", - "activityBarRight", - "panelLeft", - "panelLeftOff", - "panelRight", - "panelRightOff", - "panelBottom", - "statusBarIcon", - "panelBottomLeft", - "panelBottomRight", + "vs/workbench/browser/actions/developerActions": [ + "inspect context keys", + "toggle screencast mode", + { + "key": "logStorage", + "comment": [ + "A developer only action to log the contents of the storage for the current window." + ] + }, + "storageLogDialogMessage", + "storageLogDialogDetails", + { + "key": "logWorkingCopies", + "comment": [ + "A developer only action to log the working copies that exist." + ] + }, + "removeLargeStorageDatabaseEntries", + "largeStorageItemDetail", + "global", + "profile", + "workspace", + "machine", + "user", + "removeLargeStorageEntriesPickerButton", + "removeLargeStorageEntriesPickerPlaceholder", + "removeLargeStorageEntriesPickerDescriptionNoEntries", + "removeLargeStorageEntriesConfirmRemove", + "removeLargeStorageEntriesConfirmRemoveDetail", + { + "key": "removeLargeStorageEntriesButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "startTrackDisposables", + "snapshotTrackedDisposables", + "stopTrackDisposables", + "screencastModeConfigurationTitle", + "screencastMode.location.verticalPosition", + "screencastMode.fontSize", + "screencastMode.keyboardOptions.description", + "screencastMode.keyboardOptions.showKeys", + "screencastMode.keyboardOptions.showKeybindings", + "screencastMode.keyboardOptions.showCommands", + "screencastMode.keyboardOptions.showCommandGroups", + "screencastMode.keyboardOptions.showSingleEditorCursorMoves", + "screencastMode.keyboardOverlayTimeout", + "screencastMode.mouseIndicatorColor", + "screencastMode.mouseIndicatorSize" + ], + "vs/workbench/browser/actions/layoutActions": [ + "menuBarIcon", + "activityBarLeft", + "activityBarRight", + "panelLeft", + "panelLeftOff", + "panelRight", + "panelRightOff", + "panelBottom", + "statusBarIcon", + "panelBottomLeft", + "panelBottomRight", "panelBottomCenter", "panelBottomJustify", "fullScreenIcon", @@ -1562,12 +1584,6 @@ "zenModeIcon", "closeSidebar", "toggleActivityBar", - { - "key": "miActivityBar", - "comment": [ - "&& denotes a mnemonic" - ] - }, "toggleCenteredLayout", { "key": "miToggleCenteredLayout", @@ -1633,7 +1649,10 @@ "&& denotes a mnemonic" ] }, - "toggleTabs", + "hideEditorTabs", + "showMultipleEditorTabs", + "showSingleEditorTab", + "tabBar", "toggleSeparatePinnedEditorTabs", "toggleZenMode", { @@ -2026,6 +2045,9 @@ "workspaceConfig.transient", "unknownWorkspaceProperty" ], + "vs/workbench/browser/parts/editor/editorParts": [ + "groupLabel" + ], "vs/workbench/api/browser/viewsExtensionPoint": [ { "key": "vscode.extension.contributes.views.containers.id", @@ -2105,8 +2127,10 @@ "splitDown", "splitLeft", "splitRight", - "toggleTabs", - "toggleSeparatePinnedEditorTabs", + "tabBar", + "multipleTabs", + "singleTab", + "hideTabBar", "close", "closeOthers", "closeRight", @@ -2127,6 +2151,8 @@ "closeAll", "closeAllSaved", "togglePreviewMode", + "maximizeGroup", + "unmaximizeGroup", "lockGroup", "splitEditorRight", "splitEditorDown", @@ -2421,13 +2447,14 @@ ] } ], - "vs/workbench/browser/parts/statusbar/statusbarPart": [ - "hideStatusBar" - ], "vs/workbench/browser/parts/banner/bannerPart": [ "focusBanner" ], + "vs/workbench/browser/parts/statusbar/statusbarPart": [ + "hideStatusBar" + ], "vs/workbench/browser/parts/views/viewsService": [ + "editor", "show view", "toggle view", "show view", @@ -2843,6 +2870,16 @@ "export profile title", "profile name required" ], + "vs/workbench/services/remote/common/remoteExplorerService": [ + "getStartedWalkthrough.id", + "RemoteHelpInformationExtPoint", + "RemoteHelpInformationExtPoint.getStarted", + "RemoteHelpInformationExtPoint.documentation", + "RemoteHelpInformationExtPoint.feedback", + "RemoteHelpInformationExtPoint.feedback.deprecated", + "RemoteHelpInformationExtPoint.reportIssue", + "RemoteHelpInformationExtPoint.issues" + ], "vs/workbench/services/filesConfiguration/common/filesConfigurationService": [ "providerReadonly", { @@ -2976,6 +3013,59 @@ "vs/workbench/services/assignment/common/assignmentService": [ "workbench.enableExperiments" ], + "vs/workbench/services/issue/browser/issueTroubleshoot": [ + "troubleshoot issue", + "detail.start", + { + "key": "msg", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "profile.extensions.disabled", + "empty.profile", + "issue is with configuration", + "issue is in core", + "I cannot reproduce", + "This is Bad", + "Stop", + "troubleshoot issue", + "use insiders", + "troubleshoot issue", + "download insiders", + "report anyway", + "ask to download insiders", + "troubleshoot issue", + "good", + "bad", + "stop", + "ask to reproduce issue", + "troubleshootIssue", + "title.stop" + ], + "vs/workbench/contrib/preferences/browser/keybindingsEditorContribution": [ + "defineKeybinding.kbLayoutErrorMessage", + { + "key": "defineKeybinding.kbLayoutLocalAndUSMessage", + "comment": [ + "Please translate maintaining the stars (*) around the placeholders such that they will be rendered in bold.", + "The placeholders will contain a keyboard combination e.g. Ctrl+Shift+/" + ] + }, + { + "key": "defineKeybinding.kbLayoutLocalMessage", + "comment": [ + "Please translate maintaining the stars (*) around the placeholder such that it will be rendered in bold.", + "The placeholder will contain a keyboard combination e.g. Ctrl+Shift+/" + ] + } + ], + "vs/workbench/contrib/performance/browser/performance.contribution": [ + "show.label", + "cycles", + "insta.trace", + "emitter" + ], "vs/workbench/contrib/preferences/browser/preferences.contribution": [ "settingsEditor2", "keybindingsEditor", @@ -3043,58 +3133,16 @@ ] } ], - "vs/workbench/services/issue/browser/issueTroubleshoot": [ - "troubleshoot issue", - "detail.start", - { - "key": "msg", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "profile.extensions.disabled", - "empty.profile", - "issue is with configuration", - "issue is in core", - "I cannot reproduce", - "This is Bad", - "Stop", - "troubleshoot issue", - "use insiders", - "troubleshoot issue", - "download insiders", - "report anyway", - "ask to download insiders", - "troubleshoot issue", - "good", - "bad", - "stop", - "ask to reproduce issue", - "troubleshootIssue", - "title.stop" - ], - "vs/workbench/contrib/preferences/browser/keybindingsEditorContribution": [ - "defineKeybinding.kbLayoutErrorMessage", - { - "key": "defineKeybinding.kbLayoutLocalAndUSMessage", - "comment": [ - "Please translate maintaining the stars (*) around the placeholders such that they will be rendered in bold.", - "The placeholders will contain a keyboard combination e.g. Ctrl+Shift+/" - ] - }, - { - "key": "defineKeybinding.kbLayoutLocalMessage", - "comment": [ - "Please translate maintaining the stars (*) around the placeholder such that it will be rendered in bold.", - "The placeholder will contain a keyboard combination e.g. Ctrl+Shift+/" - ] - } - ], - "vs/workbench/contrib/performance/browser/performance.contribution": [ - "show.label", - "cycles", - "insta.trace", - "emitter" + "vs/workbench/contrib/chat/browser/chat.contribution": [ + "interactiveSessionConfigurationTitle", + "interactiveSession.editor.fontSize", + "interactiveSession.editor.fontFamily", + "interactiveSession.editor.fontWeight", + "interactiveSession.editor.wordWrap", + "interactiveSession.editor.lineHeight", + "chat", + "chat", + "clear" ], "vs/workbench/contrib/notebook/browser/notebook.contribution": [ "notebook.editorOptions.experimentalCustomization", @@ -3154,17 +3202,6 @@ "notebook.scrolling.anchorToFocusedCell.on.description", "notebook.scrolling.anchorToFocusedCell.off.description" ], - "vs/workbench/contrib/chat/browser/chat.contribution": [ - "interactiveSessionConfigurationTitle", - "interactiveSession.editor.fontSize", - "interactiveSession.editor.fontFamily", - "interactiveSession.editor.fontWeight", - "interactiveSession.editor.wordWrap", - "interactiveSession.editor.lineHeight", - "chat", - "chat", - "clear" - ], "vs/workbench/contrib/testing/browser/testing.contribution": [ "test", { @@ -3173,10 +3210,10 @@ "&& denotes a mnemonic" ] }, - "testResultsPanelName", - "testResultsPanelName", "noTestProvidersRegistered", "searchForAdditionalTestExtensions", + "testResultsPanelName", + "testResultsPanelName", "testExplorer" ], "vs/workbench/contrib/logs/common/logs.contribution": [ @@ -3312,14 +3349,12 @@ "comment": [ "&& denotes a mnemonic" ] - } + }, + "newFolderDescription" ], "vs/workbench/contrib/files/browser/explorerViewlet": [ "explorerViewIcon", "openEditorsIcon", - "folders", - "explore", - "explore", { "key": "miViewExplorer", "comment": [ @@ -3358,7 +3393,10 @@ "comment": [ "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" ] - } + }, + "folders", + "explore", + "explore" ], "vs/workbench/contrib/files/browser/files.contribution": [ "textFileEditor", @@ -3578,6 +3616,7 @@ "&& denotes a mnemonic" ] }, + "refactorPreviewViewIcon", "apply", "cat", "Discard", @@ -3590,35 +3629,10 @@ "cat", "groupByType", "cat", - "refactorPreviewViewIcon", "panel", "panel" ], - "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ - "searchEditor", - "promptOpenWith.searchEditor.displayName", - "search", - "searchEditor.deleteResultBlock", - "search.openNewSearchEditor", - "search.openSearchEditor", - "search.openNewEditorToSide", - "search.openResultsInEditor", - "search.rerunSearchInEditor", - "search.action.focusQueryEditorWidget", - "search.action.focusFilesToInclude", - "search.action.focusFilesToExclude", - "searchEditor.action.toggleSearchEditorCaseSensitive", - "searchEditor.action.toggleSearchEditorWholeWord", - "searchEditor.action.toggleSearchEditorRegex", - "searchEditor.action.toggleSearchEditorContextLines", - "searchEditor.action.increaseSearchEditorContextLines", - "searchEditor.action.decreaseSearchEditorContextLines", - "searchEditor.action.selectAllSearchEditorMatches", - "search.openNewEditor" - ], "vs/workbench/contrib/search/browser/search.contribution": [ - "name", - "search", { "key": "miViewSearch", "comment": [ @@ -3699,10 +3713,107 @@ "scm.defaultViewMode.list", "search.defaultViewMode", "search.experimental.closedNotebookResults", - "search.experimental.quickAccess.preserveInput" + "search.experimental.quickAccess.preserveInput", + "search", + "search" ], - "vs/workbench/contrib/search/browser/searchView": [ - "searchCanceled", + "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ + "searchEditor", + "promptOpenWith.searchEditor.displayName", + "search", + "searchEditor.deleteResultBlock", + "search.openNewSearchEditor", + "search.openSearchEditor", + "search.openNewEditorToSide", + "search.openResultsInEditor", + "search.rerunSearchInEditor", + "search.action.focusQueryEditorWidget", + "search.action.focusFilesToInclude", + "search.action.focusFilesToExclude", + "searchEditor.action.toggleSearchEditorCaseSensitive", + "searchEditor.action.toggleSearchEditorWholeWord", + "searchEditor.action.toggleSearchEditorRegex", + "searchEditor.action.toggleSearchEditorContextLines", + "searchEditor.action.increaseSearchEditorContextLines", + "searchEditor.action.decreaseSearchEditorContextLines", + "searchEditor.action.selectAllSearchEditorMatches", + "search.openNewEditor" + ], + "vs/workbench/contrib/sash/browser/sash.contribution": [ + "sashSize", + "sashHoverDelay" + ], + "vs/workbench/contrib/scm/browser/scm.contribution": [ + "sourceControlViewIcon", + "source control", + "no open repo", + "no open repo in an untrusted workspace", + "manageWorkspaceTrustAction", + { + "key": "miViewSCM", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "scmConfigurationTitle", + "scm.diffDecorations.all", + "scm.diffDecorations.gutter", + "scm.diffDecorations.overviewRuler", + "scm.diffDecorations.minimap", + "scm.diffDecorations.none", + "diffDecorations", + "diffGutterWidth", + "scm.diffDecorationsGutterVisibility.always", + "scm.diffDecorationsGutterVisibility.hover", + "scm.diffDecorationsGutterVisibility", + "scm.diffDecorationsGutterAction.diff", + "scm.diffDecorationsGutterAction.none", + "scm.diffDecorationsGutterAction", + "diffGutterPattern", + "diffGutterPatternAdded", + "diffGutterPatternModifed", + "scm.diffDecorationsIgnoreTrimWhitespace.true", + "scm.diffDecorationsIgnoreTrimWhitespace.false", + "scm.diffDecorationsIgnoreTrimWhitespace.inherit", + "diffDecorationsIgnoreTrimWhitespace", + "alwaysShowActions", + "scm.countBadge.all", + "scm.countBadge.focused", + "scm.countBadge.off", + "scm.countBadge", + "scm.providerCountBadge.hidden", + "scm.providerCountBadge.auto", + "scm.providerCountBadge.visible", + "scm.providerCountBadge", + "scm.defaultViewMode.tree", + "scm.defaultViewMode.list", + "scm.defaultViewMode", + "scm.defaultViewSortKey.name", + "scm.defaultViewSortKey.path", + "scm.defaultViewSortKey.status", + "scm.defaultViewSortKey", + "autoReveal", + "inputFontFamily", + "inputFontSize", + "alwaysShowRepository", + "scm.repositoriesSortOrder.discoveryTime", + "scm.repositoriesSortOrder.name", + "scm.repositoriesSortOrder.path", + "repositoriesSortOrder", + "providersVisible", + "showActionButton", + "showSyncView", + "scm accept", + "scm view next commit", + "scm view previous commit", + "open in external terminal", + "open in integrated terminal", + "source control", + "source control repositories", + "source control sync" + ], + "vs/workbench/contrib/search/browser/searchView": [ + "searchCanceled", "moreSearch", "searchScope.includes", "placeholder.includes", @@ -3732,7 +3843,6 @@ "removeAll.occurrences.files.confirmation.message", "replaceAll.occurrences.files.confirmation.message", "emptySearch", - "ariaSearchResultsClearStatus", "searchPathNotFoundError", "noOpenEditorResultsIncludesExcludes", "noOpenEditorResultsIncludes", @@ -3764,9 +3874,9 @@ "searchWithoutFolder", "openFolder" ], - "vs/workbench/contrib/sash/browser/sash.contribution": [ - "sashSize", - "sashHoverDelay" + "vs/workbench/contrib/debug/browser/debugEditorContribution": [ + "editor.inlineValuesForeground", + "editor.inlineValuesBackground" ], "vs/workbench/contrib/debug/browser/debug.contribution": [ "debugCategory", @@ -3775,16 +3885,6 @@ "tasksQuickAccessPlaceholder", "tasksQuickAccessHelp", "terminateThread", - { - "comment": [ - "Debug is a noun in this context, not a verb." - ], - "key": "debugFocusConsole" - }, - "jumpToCursor", - "SetNextStatement", - "inlineBreakpoint", - "terminateThread", "restartFrame", "copyStackTrace", "viewMemory", @@ -3800,7 +3900,6 @@ "copyValue", "viewMemory", "removeWatchExpression", - "run", { "key": "mRun", "comment": [ @@ -3879,36 +3978,18 @@ "&& denotes a mnemonic" ] }, - { - "comment": [ - "Debug is a noun in this context, not a verb." - ], - "key": "debugPanel" - }, - { - "comment": [ - "Debug is a noun in this context, not a verb." - ], - "key": "debugPanel" - }, { "key": "miToggleDebugConsole", "comment": [ "&& denotes a mnemonic" ] }, - "run and debug", { "key": "miViewRun", "comment": [ "&& denotes a mnemonic" ] }, - "variables", - "watch", - "callStack", - "breakpoints", - "loadedScripts", "disassembly", "debugConfigurationTitle", { @@ -4006,7 +4087,46 @@ "debug.confirmOnExit.always", "debug.disassemblyView.showSourceCode", "debug.autoExpandLazyVariables", - "debug.enableStatusBarColor" + "debug.enableStatusBarColor", + { + "comment": [ + "This is the description for a setting" + ], + "key": "debug.hideLauncherWhileDebugging" + }, + "terminateThread", + { + "comment": [ + "Debug is a noun in this context, not a verb." + ], + "key": "debugFocusConsole" + }, + "jumpToCursor", + "SetNextStatement", + "inlineBreakpoint", + "run", + { + "comment": [ + "Debug is a noun in this context, not a verb." + ], + "key": "debugPanel" + }, + { + "comment": [ + "Debug is a noun in this context, not a verb." + ], + "key": "debugPanel" + }, + "run and debug", + "variables", + "watch", + "callStack", + "breakpoints", + "loadedScripts" + ], + "vs/workbench/contrib/debug/browser/callStackEditorContribution": [ + "topStackFrameLineHighlight", + "focusedStackFrameLineHighlight" ], "vs/workbench/contrib/debug/browser/breakpointEditorContribution": [ "logPoint", @@ -4063,78 +4183,26 @@ "debugIcon.breakpointCurrentStackframeForeground", "debugIcon.breakpointStackframeForeground" ], - "vs/workbench/contrib/scm/browser/scm.contribution": [ - "sourceControlViewIcon", - "source control", - "no open repo", - "no open repo in an untrusted workspace", - "manageWorkspaceTrustAction", - "source control", + "vs/workbench/contrib/debug/browser/debugViewlet": [ { - "key": "miViewSCM", + "key": "miOpenConfigurations", "comment": [ "&& denotes a mnemonic" ] }, - "source control repositories", - "source control sync", - "scmConfigurationTitle", - "scm.diffDecorations.all", - "scm.diffDecorations.gutter", - "scm.diffDecorations.overviewRuler", - "scm.diffDecorations.minimap", - "scm.diffDecorations.none", - "diffDecorations", - "diffGutterWidth", - "scm.diffDecorationsGutterVisibility.always", - "scm.diffDecorationsGutterVisibility.hover", - "scm.diffDecorationsGutterVisibility", - "scm.diffDecorationsGutterAction.diff", - "scm.diffDecorationsGutterAction.none", - "scm.diffDecorationsGutterAction", - "diffGutterPattern", - "diffGutterPatternAdded", - "diffGutterPatternModifed", - "scm.diffDecorationsIgnoreTrimWhitespace.true", - "scm.diffDecorationsIgnoreTrimWhitespace.false", - "scm.diffDecorationsIgnoreTrimWhitespace.inherit", - "diffDecorationsIgnoreTrimWhitespace", - "alwaysShowActions", - "scm.countBadge.all", - "scm.countBadge.focused", - "scm.countBadge.off", - "scm.countBadge", - "scm.providerCountBadge.hidden", - "scm.providerCountBadge.auto", - "scm.providerCountBadge.visible", - "scm.providerCountBadge", - "scm.defaultViewMode.tree", - "scm.defaultViewMode.list", - "scm.defaultViewMode", - "scm.defaultViewSortKey.name", - "scm.defaultViewSortKey.path", - "scm.defaultViewSortKey.status", - "scm.defaultViewSortKey", - "autoReveal", - "inputFontFamily", - "inputFontSize", - "alwaysShowRepository", - "scm.repositoriesSortOrder.discoveryTime", - "scm.repositoriesSortOrder.name", - "scm.repositoriesSortOrder.path", - "repositoriesSortOrder", - "providersVisible", - "showActionButton", - "showSyncView", - "scm accept", - "scm view next commit", - "scm view previous commit", - "open in external terminal", - "open in integrated terminal" + { + "key": "selectWorkspaceFolder", + "comment": [ + "User picks a workspace folder or a workspace configuration file here. Workspace configuration files can contain settings and thus a launch.json configuration can be written into one." + ] + }, + "debugPanel", + "startAdditionalSession" ], - "vs/workbench/contrib/debug/browser/debugEditorContribution": [ - "editor.inlineValuesForeground", - "editor.inlineValuesBackground" + "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ + "name", + "diffAlgorithm.legacy", + "diffAlgorithm.advanced" ], "vs/workbench/contrib/debug/browser/repl": [ { @@ -4156,16 +4224,11 @@ "actions.repl.copyAll", "selectRepl", "clearRepl", - "debugConsoleCleared", "collapse", "paste", "copyAll", "copy" ], - "vs/workbench/contrib/debug/browser/callStackEditorContribution": [ - "topStackFrameLineHighlight", - "focusedStackFrameLineHighlight" - ], "vs/workbench/contrib/markers/browser/markers.contribution": [ "markersViewIcon", { @@ -4211,27 +4274,6 @@ "manyProblems", "totalProblems" ], - "vs/workbench/contrib/debug/browser/debugViewlet": [ - { - "key": "miOpenConfigurations", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "selectWorkspaceFolder", - "comment": [ - "User picks a workspace folder or a workspace configuration file here. Workspace configuration files can contain settings and thus a launch.json configuration can be written into one." - ] - }, - "debugPanel", - "startAdditionalSession" - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ - "name", - "diffAlgorithm.legacy", - "diffAlgorithm.advanced" - ], "vs/workbench/contrib/commands/common/commands.contribution": [ "runCommands", "runCommands.description", @@ -4239,6 +4281,11 @@ "runCommands.invalidArgs", "runCommands.noCommandsToRun" ], + "vs/workbench/contrib/url/browser/url.contribution": [ + "openUrl", + "urlToOpen", + "workbench.trustedDomains.promptInTrustedWorkspace" + ], "vs/workbench/contrib/comments/browser/comments.contribution": [ "commentsConfigurationTitle", "openComments", @@ -4270,18 +4317,118 @@ "submitComment", "submitCommentNoKb" ], - "vs/workbench/contrib/url/browser/url.contribution": [ - "openUrl", - "urlToOpen", - "workbench.trustedDomains.promptInTrustedWorkspace" + "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ + "webview.editor.label" ], "vs/workbench/contrib/webview/browser/webview.contribution": [ "cut", "copy", "paste" ], - "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ - "webview.editor.label" + "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ + { + "key": "remote", + "comment": [ + "Remote as in remote machine" + ] + }, + "installed", + "select and install local extensions", + "install remote in local", + "searchExtensions", + "extensionFoundInSection", + "extensionFound", + "extensionsFoundInSection", + "extensionsFound", + "suggestProxyError", + "open user settings", + "extensionToUpdate", + "extensionsToUpdate", + "extensionToReload", + "extensionsToReload", + "malicious warning", + "reloadNow", + "popularExtensions", + "recommendedExtensions", + "enabledExtensions", + "disabledExtensions", + "marketPlace", + "installed", + "recently updated", + "enabled", + "disabled", + "availableUpdates", + "builtin", + "workspaceUnsupported", + "workspaceRecommendedExtensions", + "otherRecommendedExtensions", + "builtinFeatureExtensions", + "builtInThemesExtensions", + "builtinProgrammingLanguageExtensions", + "untrustedUnsupportedExtensions", + "untrustedPartiallySupportedExtensions", + "virtualUnsupportedExtensions", + "virtualPartiallySupportedExtensions", + "deprecated" + ], + "vs/workbench/contrib/output/browser/outputView": [ + "output model title", + "channel", + "output", + "outputViewAriaLabel" + ], + "vs/workbench/contrib/output/browser/output.contribution": [ + "outputViewIcon", + { + "key": "miToggleOutput", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "switchBetweenOutputs.label", + "switchToOutput.label", + "selectOutput", + "outputScrollOff", + "outputScrollOn", + "extensionLogs", + "selectlog", + "logFile", + "selectlogFile", + "output", + "output.smartScroll.enabled", + "output", + "output", + "showOutputChannels", + "output", + "clearOutput.label", + "toggleAutoScroll", + "openActiveLogOutputFile", + "showLogs", + "openLogFile" + ], + "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ + "scopedConsoleAction.Integrated", + "scopedConsoleAction.external", + "scopedConsoleAction.wt" + ], + "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ + "relaunchSettingMessage", + "relaunchSettingMessageWeb", + "relaunchSettingDetail", + "relaunchSettingDetailWeb", + { + "key": "restart", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "restartWeb", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "restartExtensionHost.reason" ], "vs/workbench/contrib/extensions/browser/extensions.contribution": [ "manageExtensionsQuickAccessPlaceholder", @@ -4436,112 +4583,6 @@ "extensions", "extensions" ], - "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ - { - "key": "remote", - "comment": [ - "Remote as in remote machine" - ] - }, - "installed", - "select and install local extensions", - "install remote in local", - "popularExtensions", - "recommendedExtensions", - "enabledExtensions", - "disabledExtensions", - "marketPlace", - "installed", - "recently updated", - "enabled", - "disabled", - "availableUpdates", - "builtin", - "workspaceUnsupported", - "workspaceRecommendedExtensions", - "otherRecommendedExtensions", - "builtinFeatureExtensions", - "builtInThemesExtensions", - "builtinProgrammingLanguageExtensions", - "untrustedUnsupportedExtensions", - "untrustedPartiallySupportedExtensions", - "virtualUnsupportedExtensions", - "virtualPartiallySupportedExtensions", - "deprecated", - "searchExtensions", - "extensionFoundInSection", - "extensionFound", - "extensionsFoundInSection", - "extensionsFound", - "suggestProxyError", - "open user settings", - "extensionToUpdate", - "extensionsToUpdate", - "extensionToReload", - "extensionsToReload", - "malicious warning", - "reloadNow" - ], - "vs/workbench/contrib/output/browser/output.contribution": [ - "outputViewIcon", - "output", - "output", - { - "key": "miToggleOutput", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "switchBetweenOutputs.label", - "switchToOutput.label", - "showOutputChannels", - "output", - "selectOutput", - "clearOutput.label", - "outputCleared", - "toggleAutoScroll", - "outputScrollOff", - "outputScrollOn", - "openActiveLogOutputFile", - "showLogs", - "extensionLogs", - "selectlog", - "openLogFile", - "logFile", - "selectlogFile", - "output", - "output.smartScroll.enabled" - ], - "vs/workbench/contrib/output/browser/outputView": [ - "output model title", - "channel", - "output", - "outputViewAriaLabel" - ], - "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ - "scopedConsoleAction.Integrated", - "scopedConsoleAction.external", - "scopedConsoleAction.wt" - ], - "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ - "relaunchSettingMessage", - "relaunchSettingMessageWeb", - "relaunchSettingDetail", - "relaunchSettingDetailWeb", - { - "key": "restart", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "restartWeb", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "restartExtensionHost.reason" - ], "vs/workbench/contrib/tasks/browser/task.contribution": [ "building", "status.runningTasks", @@ -4689,14 +4730,14 @@ "snippetSchema.json", "snippetSchema.json.scope" ], + "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ + "toggleKeybindingsLog" + ], "vs/workbench/contrib/folding/browser/folding.contribution": [ "null", "nullFormatterDescription", "formatter.default" ], - "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ - "toggleKeybindingsLog" - ], "vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution": [ "status.button.configure", "colorDecoratorsStatusItem.name", @@ -4736,6 +4777,17 @@ ] } ], + "vs/workbench/contrib/surveys/browser/nps.contribution": [ + "surveyQuestion", + "takeSurvey", + "remindLater", + "neverAgain" + ], + "vs/workbench/contrib/surveys/browser/ces.contribution": [ + "cesSurveyQuestion", + "giveFeedback", + "remindLater" + ], "vs/workbench/contrib/themes/browser/themes.contribution": [ "manageExtensionIcon", "themes.selectMarketplaceTheme", @@ -4794,23 +4846,12 @@ ] } ], - "vs/workbench/contrib/surveys/browser/nps.contribution": [ - "surveyQuestion", - "takeSurvey", - "remindLater", - "neverAgain" - ], "vs/workbench/contrib/surveys/browser/languageSurveys.contribution": [ "helpUs", "takeShortSurvey", "remindLater", "neverAgain" ], - "vs/workbench/contrib/surveys/browser/ces.contribution": [ - "cesSurveyQuestion", - "giveFeedback", - "remindLater" - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution": [ "miWelcome", "welcome", @@ -4877,21 +4918,6 @@ "miNewFileWithName", "miNewFile2" ], - "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ - "document" - ], - "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ - "editorHasTypeHierarchyProvider", - "typeHierarchyVisible", - "typeHierarchyDirection", - "no.item", - "error", - "title", - "title.supertypes", - "title.subtypes", - "title.refocusTypeHierarchy", - "close" - ], "vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution": [ "editorHasCallHierarchyProvider", "callHierarchyVisible", @@ -4906,16 +4932,23 @@ "title.refocus", "close" ], - "vs/workbench/contrib/languageDetection/browser/languageDetection.contribution": [ - "status.autoDetectLanguage", - "langDetection.name", - "langDetection.aria", - "detectlang", - "noDetection" + "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ + "editorHasTypeHierarchyProvider", + "typeHierarchyVisible", + "typeHierarchyDirection", + "no.item", + "error", + "title", + "title.supertypes", + "title.subtypes", + "title.refocusTypeHierarchy", + "close" + ], + "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ + "document" ], "vs/workbench/contrib/outline/browser/outline.contribution": [ "outlineViewIcon", - "name", "outlineConfigurationTitle", "outline.showIcons", "outline.initialState", @@ -4949,7 +4982,25 @@ "filteredTypes.struct", "filteredTypes.event", "filteredTypes.operator", - "filteredTypes.typeParameter" + "filteredTypes.typeParameter", + "name" + ], + "vs/workbench/contrib/languageDetection/browser/languageDetection.contribution": [ + "status.autoDetectLanguage", + "langDetection.name", + "langDetection.aria", + "detectlang", + "noDetection" + ], + "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ + "langStatus.name", + "langStatus.aria", + "pin", + "unpin", + "aria.1", + "aria.2", + "name.pattern", + "reset" ], "vs/workbench/contrib/userDataSync/browser/userDataSync.contribution": [ { @@ -4977,6 +5028,16 @@ "settings sync", "show sync logs" ], + "vs/workbench/contrib/timeline/browser/timeline.contribution": [ + "timelineViewIcon", + "timelineOpenIcon", + "timelineConfigurationTitle", + "timeline.pageSize", + "timeline.pageOnScroll", + "files.openTimeline", + "timelineFilter", + "filterTimeline" + ], "vs/workbench/contrib/editSessions/browser/editSessions.contribution": [ "continue working on", "continue edit session in local folder", @@ -5032,26 +5093,6 @@ "continueOnCloudChanges", "cloudChangesPartialMatchesEnabled" ], - "vs/workbench/contrib/timeline/browser/timeline.contribution": [ - "timelineViewIcon", - "timelineOpenIcon", - "timelineConfigurationTitle", - "timeline.pageSize", - "timeline.pageOnScroll", - "files.openTimeline", - "timelineFilter", - "filterTimeline" - ], - "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ - "langStatus.name", - "langStatus.aria", - "pin", - "unpin", - "aria.1", - "aria.2", - "name.pattern", - "reset" - ], "vs/workbench/contrib/workspaces/browser/workspaces.contribution": [ "workspaceFound", "openWorkspace", @@ -5061,12 +5102,6 @@ "openWorkspace", "alreadyOpen" ], - "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ - "bracketPairColorizer.notification", - "bracketPairColorizer.notification.action.uninstall", - "bracketPairColorizer.notification.action.enableNative", - "bracketPairColorizer.notification.action.showMoreInfo" - ], "vs/workbench/contrib/workspace/browser/workspace.contribution": [ "openLooseFileWorkspaceDetails", "openLooseFileWindowDetails", @@ -5186,6 +5221,15 @@ "workspace.trust.untrustedFiles.newWindow", "workspace.trust.emptyWindow.description" ], + "vs/workbench/contrib/share/browser/share.contribution": [ + "share", + "generating link", + "shareTextSuccess", + "shareSuccess", + "close", + "open link", + "experimental.share.enabled" + ], "vs/workbench/contrib/audioCues/browser/audioCues.contribution": [ "audioCues.enabled.auto", "audioCues.enabled.on", @@ -5210,27 +5254,28 @@ "audioCues.notebookCellFailed", "audioCues.chatRequestSent", "audioCues.chatResponsePending", - "audioCues.chatResponseReceived" - ], - "vs/workbench/contrib/share/browser/share.contribution": [ - "share", - "generating link", - "shareTextSuccess", - "shareSuccess", - "close", - "open link", - "experimental.share.enabled" + "audioCues.chatResponseReceived", + "audioCues.clear", + "audioCues.save", + "audioCues.save.userGesture", + "audioCues.save.always", + "audioCues.save.never", + "audioCues.format", + "audioCues.format.userGesture", + "audioCues.format.always", + "audioCues.format.never" + ], + "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution": [ + "workbench.accounts.showEntitlements" ], "vs/workbench/browser/workbench": [ "loaderErrorNative" ], - "vs/workbench/services/configuration/browser/configurationService": [ - "configurationDefaults.description", - "experimental", - "setting description" - ], - "vs/platform/workspace/common/workspace": [ - "codeWorkspace" + "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ + "bracketPairColorizer.notification", + "bracketPairColorizer.notification.action.uninstall", + "bracketPairColorizer.notification.action.enableNative", + "bracketPairColorizer.notification.action.showMoreInfo" ], "vs/workbench/electron-sandbox/window": [ "restart", @@ -5290,21 +5335,21 @@ "loaderCycle", "runningAsRoot", "appRootWarning.banner", - "windows32eolmessage", - "windowseolBannerLearnMore", - "windowseolarialabel", - "learnMore", "macoseolmessage", - "macoseolBannerLearnMore", - "macoseolarialabel", "learnMore", "resolveShellEnvironment", "learnMore" ], - "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ - "devTools", - "directUrl", - "connectionError" + "vs/platform/workspace/common/workspace": [ + "codeWorkspace" + ], + "vs/workbench/services/configuration/browser/configurationService": [ + "configurationDefaults.description", + "experimental", + "setting description" + ], + "vs/workbench/services/log/electron-sandbox/logService": [ + "rendererLog" ], "vs/platform/workspace/common/workspaceTrust": [ "trusted", @@ -5315,8 +5360,16 @@ "profiles", "profile" ], - "vs/workbench/services/log/electron-sandbox/logService": [ - "rendererLog" + "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ + "devTools", + "directUrl", + "connectionError" + ], + "vs/workbench/electron-sandbox/actions/developerActions": [ + "toggleDevTools", + "configureRuntimeArguments", + "reloadWindowWithExtensionsDisabled", + "openUserDataFolder" ], "vs/platform/configuration/common/configurationRegistry": [ "defaultLanguageConfigurationOverrides.title", @@ -5330,12 +5383,6 @@ "config.property.duplicate", "config.policy.duplicate" ], - "vs/workbench/electron-sandbox/actions/developerActions": [ - "toggleDevTools", - "configureRuntimeArguments", - "reloadWindowWithExtensionsDisabled", - "openUserDataFolder" - ], "vs/workbench/electron-sandbox/actions/windowActions": [ "closeWindow", { @@ -5371,7 +5418,14 @@ "windowDirtyAriaLabel", "current", "switchWindow", - "quickSwitchWindow" + "quickSwitchWindow", + "splitWindow", + { + "key": "miSplitWindow", + "comment": [ + "&& denotes a mnemonic" + ] + } ], "vs/platform/contextkey/common/contextkeys": [ "isMac", @@ -5384,13 +5438,12 @@ "productQualityType", "inputFocus" ], - "vs/workbench/common/configuration": [ - "applicationConfigurationTitle", - "workbenchConfigurationTitle", - "securityConfigurationTitle", - "security.allowedUNCHosts.patternErrorMessage", - "security.allowedUNCHosts", - "security.restrictUNCAccess" + "vs/workbench/electron-sandbox/actions/installActions": [ + "shellCommand", + "install", + "successIn", + "uninstall", + "successFrom" ], "vs/workbench/common/contextkeys": [ "workbenchState", @@ -5426,10 +5479,13 @@ "splitEditorsVertically", "editorAreaVisible", "editorTabsVisible", + "editorGroupMaximized", "sideBarVisible", "sideBarFocus", "activeViewlet", "statusBarFocused", + "titleBarStyle", + "titleBarVisible", "bannerFocused", "notificationFocus", "notificationCenterVisible", @@ -5454,6 +5510,39 @@ "resourceSet", "isFileSystemResource" ], + "vs/workbench/common/configuration": [ + "applicationConfigurationTitle", + "workbenchConfigurationTitle", + "securityConfigurationTitle", + "security.allowedUNCHosts.patternErrorMessage", + "security.allowedUNCHosts", + "security.restrictUNCAccess" + ], + "vs/workbench/browser/parts/dialogs/dialogHandler": [ + "aboutDetail", + { + "key": "copy", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "ok" + ], + "vs/workbench/electron-sandbox/parts/dialogs/dialogHandler": [ + { + "key": "aboutDetail", + "comment": [ + "Electron, Chromium, Node.js and V8 are product names that need no translation" + ] + }, + { + "key": "copy", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "okButton" + ], "vs/workbench/services/dialogs/browser/abstractFileDialogService": [ "saveChangesDetail", "saveChangesMessage", @@ -5486,13 +5575,6 @@ "allFiles", "noExt" ], - "vs/workbench/electron-sandbox/actions/installActions": [ - "shellCommand", - "install", - "successIn", - "uninstall", - "successFrom" - ], "vs/workbench/services/textfile/browser/textFileService": [ "textFileCreate.source", "textFileOverwrite.source", @@ -5502,38 +5584,21 @@ "deleted", "fileBinaryError", "confirmOverwrite", - "irreversible", + "overwriteIrreversible", { "key": "replaceButtonLabel", "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/browser/parts/dialogs/dialogHandler": [ - "aboutDetail", - { - "key": "copy", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "ok" - ], - "vs/workbench/electron-sandbox/parts/dialogs/dialogHandler": [ - { - "key": "aboutDetail", - "comment": [ - "Electron, Chromium, Node.js and V8 are product names that need no translation" - ] }, + "confirmMakeWriteable", + "confirmMakeWriteableDetail", { - "key": "copy", + "key": "makeWriteableButtonLabel", "comment": [ "&& denotes a mnemonic" ] - }, - "okButton" + } ], "vs/workbench/common/theme": [ "tabActiveBackground", @@ -5687,6 +5752,7 @@ "textLinkForeground", "textLinkActiveForeground", "textPreformatForeground", + "textPreformatBackground", "textBlockQuoteBackground", "textBlockQuoteBorder", "textCodeBlockBackground", @@ -5903,11 +5969,6 @@ "errorInvalidTaskConfiguration", "openWorkspaceConfigurationFile" ], - "vs/platform/keyboardLayout/common/keyboardConfig": [ - "keyboardConfigurationTitle", - "dispatch", - "mapAltGrToCtrlAlt" - ], "vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService": [ "commandVariable.noStringType", "inputVariable.noInputSection", @@ -5917,6 +5978,11 @@ "inputVariable.unknownType", "inputVariable.undefinedVariable" ], + "vs/platform/keyboardLayout/common/keyboardConfig": [ + "keyboardConfigurationTitle", + "dispatch", + "mapAltGrToCtrlAlt" + ], "vs/workbench/services/extensionManagement/common/extensionManagementService": [ "singleDependentError", "twoDependentsError", @@ -5960,57 +6026,10 @@ "non web extensions detail", "non web extensions" ], - "vs/workbench/services/workingCopy/common/workingCopyHistoryService": [ - "default.source", - "moved.source", - "renamed.source", - "join.workingCopyHistory" - ], "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService": [ "notFoundReleaseExtension", "notFoundCompatibleDependency" ], - "vs/editor/common/editorContextKeys": [ - "editorTextFocus", - "editorFocus", - "textInputFocus", - "editorReadonly", - "inDiffEditor", - "isEmbeddedDiffEditor", - "comparingMovedCode", - "accessibleDiffViewerVisible", - "diffEditorRenderSideBySideInlineBreakpointReached", - "editorColumnSelection", - "editorHasSelection", - "editorHasMultipleSelections", - "editorTabMovesFocus", - "editorHoverVisible", - "editorHoverFocused", - "stickyScrollFocused", - "stickyScrollVisible", - "standaloneColorPickerVisible", - "standaloneColorPickerFocused", - "inCompositeEditor", - "editorLangId", - "editorHasCompletionItemProvider", - "editorHasCodeActionsProvider", - "editorHasCodeLensProvider", - "editorHasDefinitionProvider", - "editorHasDeclarationProvider", - "editorHasImplementationProvider", - "editorHasTypeDefinitionProvider", - "editorHasHoverProvider", - "editorHasDocumentHighlightProvider", - "editorHasDocumentSymbolProvider", - "editorHasReferenceProvider", - "editorHasRenameProvider", - "editorHasSignatureHelpProvider", - "editorHasInlayHintsProvider", - "editorHasDocumentFormattingProvider", - "editorHasDocumentSelectionFormattingProvider", - "editorHasMultipleDocumentFormattingProvider", - "editorHasMultipleDocumentSelectionFormattingProvider" - ], "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupTracker": [ "backupTrackerBackupFailed", "backupTrackerConfirmFailed", @@ -6021,11 +6040,11 @@ "revertBeforeShutdown", "discardBackupsBeforeShutdown" ], - "vs/workbench/common/editor": [ - "promptOpenWith.defaultEditor.displayName", - "builtinProviderDisplayName", - "openLargeFile", - "configureEditorLargeFileConfirmation" + "vs/workbench/services/workingCopy/common/workingCopyHistoryService": [ + "default.source", + "moved.source", + "renamed.source", + "join.workingCopyHistory" ], "vs/platform/action/common/actionCommonCategories": [ "view", @@ -6052,13 +6071,65 @@ "extensionService.crash", "restart" ], + "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ + "extensionCache.invalid", + "reloadWindow" + ], + "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService": [ + "unableToOpenWindowError", + "unableToOpenWindow", + "unableToOpenWindowDetail", + { + "key": "retry", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ "openLogsFolder", "openExtensionLogsFolder" ], - "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ - "extensionCache.invalid", - "reloadWindow" + "vs/editor/common/editorContextKeys": [ + "editorTextFocus", + "editorFocus", + "textInputFocus", + "editorReadonly", + "inDiffEditor", + "isEmbeddedDiffEditor", + "comparingMovedCode", + "accessibleDiffViewerVisible", + "diffEditorRenderSideBySideInlineBreakpointReached", + "editorColumnSelection", + "editorHasSelection", + "editorHasMultipleSelections", + "editorTabMovesFocus", + "editorHoverVisible", + "editorHoverFocused", + "stickyScrollFocused", + "stickyScrollVisible", + "standaloneColorPickerVisible", + "standaloneColorPickerFocused", + "inCompositeEditor", + "editorLangId", + "editorHasCompletionItemProvider", + "editorHasCodeActionsProvider", + "editorHasCodeLensProvider", + "editorHasDefinitionProvider", + "editorHasDeclarationProvider", + "editorHasImplementationProvider", + "editorHasTypeDefinitionProvider", + "editorHasHoverProvider", + "editorHasDocumentHighlightProvider", + "editorHasDocumentSymbolProvider", + "editorHasReferenceProvider", + "editorHasRenameProvider", + "editorHasSignatureHelpProvider", + "editorHasInlayHintsProvider", + "editorHasDocumentFormattingProvider", + "editorHasDocumentSelectionFormattingProvider", + "editorHasMultipleDocumentFormattingProvider", + "editorHasMultipleDocumentSelectionFormattingProvider" ], "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ "extensionHost.startupFailDebug", @@ -6066,18 +6137,48 @@ "reloadWindow", "join.extensionDevelopment" ], + "vs/workbench/common/editor": [ + "promptOpenWith.defaultEditor.displayName", + "builtinProviderDisplayName", + "openLargeFile", + "configureEditorLargeFileConfirmation" + ], + "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ + "showLanguagePackExtensions", + "searchMarketplace", + "installAndRestartMessage", + "installAndRestart" + ], + "vs/workbench/contrib/localization/common/localization.contribution": [ + "vscode.extension.contributes.localizations", + "vscode.extension.contributes.localizations.languageId", + "vscode.extension.contributes.localizations.languageName", + "vscode.extension.contributes.localizations.languageNameLocalized", + "vscode.extension.contributes.localizations.translations", + "vscode.extension.contributes.localizations.translations.id", + "vscode.extension.contributes.localizations.translations.id.pattern", + "vscode.extension.contributes.localizations.translations.path" + ], "vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard": [ "actions.pasteSelectionClipboard" ], - "vs/workbench/browser/editor": [ - "preview", - "pinned" - ], "vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate": [ "startDebugTextMate" ], - "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ - "extensionsInputName" + "vs/workbench/contrib/issue/common/issue.contribution": [ + { + "key": "reportIssueInEnglish", + "comment": [ + "Translate this to \"Report Issue in English\" in all languages please!" + ] + }, + { + "key": "miReportIssue", + "comment": [ + "&& denotes a mnemonic", + "Translate this to \"Report Issue in English\" in all languages please!" + ] + } ], "vs/workbench/contrib/extensions/electron-sandbox/runtimeExtensionsEditor": [ "extensionHostProfileStart", @@ -6086,6 +6187,10 @@ "saveprofile.dialogTitle", "saveprofile.saveButton" ], + "vs/workbench/browser/editor": [ + "preview", + "pinned" + ], "vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction": [ "debugExtensionHost", "restart1", @@ -6102,6 +6207,9 @@ "openExtensionsFolder", "cleanUpExtensionsFolder" ], + "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ + "extensionsInputName" + ], "vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService": [ "status.profiler", "profilingExtensionHost", @@ -6121,21 +6229,14 @@ "unresponsive-exthost", "show" ], - "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ - "showLanguagePackExtensions", - "searchMarketplace", - "installAndRestartMessage", - "installAndRestart" - ], - "vs/workbench/contrib/localization/common/localization.contribution": [ - "vscode.extension.contributes.localizations", - "vscode.extension.contributes.localizations.languageId", - "vscode.extension.contributes.localizations.languageName", - "vscode.extension.contributes.localizations.languageNameLocalized", - "vscode.extension.contributes.localizations.translations", - "vscode.extension.contributes.localizations.translations.id", - "vscode.extension.contributes.localizations.translations.id.pattern", - "vscode.extension.contributes.localizations.translations.path" + "vs/workbench/contrib/terminal/common/terminal": [ + "vscode.extension.contributes.terminal", + "vscode.extension.contributes.terminal.profiles", + "vscode.extension.contributes.terminal.profiles.id", + "vscode.extension.contributes.terminal.profiles.title", + "vscode.extension.contributes.terminal.types.icon", + "vscode.extension.contributes.terminal.types.icon.light", + "vscode.extension.contributes.terminal.types.icon.dark" ], "vs/workbench/services/dialogs/browser/simpleFileDialog": [ "openLocalFile", @@ -6158,30 +6259,6 @@ "remoteFileDialog.validateFileOnly", "remoteFileDialog.validateFolderOnly" ], - "vs/workbench/contrib/issue/common/issue.contribution": [ - { - "key": "reportIssueInEnglish", - "comment": [ - "Translate this to \"Report Issue in English\" in all languages please!" - ] - }, - { - "key": "miReportIssue", - "comment": [ - "&& denotes a mnemonic", - "Translate this to \"Report Issue in English\" in all languages please!" - ] - } - ], - "vs/workbench/contrib/terminal/common/terminal": [ - "vscode.extension.contributes.terminal", - "vscode.extension.contributes.terminal.profiles", - "vscode.extension.contributes.terminal.profiles.id", - "vscode.extension.contributes.terminal.profiles.title", - "vscode.extension.contributes.terminal.types.icon", - "vscode.extension.contributes.terminal.types.icon.light", - "vscode.extension.contributes.terminal.types.icon.dark" - ], "vs/editor/common/languages": [ "Array", "Boolean", @@ -6220,15 +6297,22 @@ "ui state label", "profiles", "workspace state label", - "sync category", "syncViewIcon", - "download sync activity title" + "download sync activity title", + "sync category" ], "vs/workbench/contrib/tasks/common/tasks": [ "tasks.taskRunningContext", "tasksCategory", "TaskDefinition.missingRequiredProperty" ], + "vs/workbench/contrib/tasks/common/taskService": [ + "tasks.customExecutionSupported", + "tasks.shellExecutionSupported", + "tasks.taskCommandsRegistered", + "tasks.processExecutionSupported", + "tasks.serverlessWebContext" + ], "vs/workbench/contrib/performance/electron-sandbox/startupProfiler": [ "prof.message", "prof.detail", @@ -6248,63 +6332,158 @@ ] } ], - "vs/workbench/contrib/tasks/common/taskService": [ - "tasks.customExecutionSupported", - "tasks.shellExecutionSupported", - "tasks.taskCommandsRegistered", - "tasks.processExecutionSupported", - "tasks.serverlessWebContext" - ], "vs/workbench/common/views": [ + "views log", "defaultViewIcon", "duplicateId", "treeView.notRegistered" ], - "vs/workbench/contrib/tasks/browser/abstractTaskService": [ - "ConfigureTaskRunnerAction.label", - "tasks", - "TaskService.pickBuildTaskForLabel", - "runTask.arg", - "runTask.label", - "runTask.type", - "runTask.task", - "taskServiceOutputPrompt", - "showOutput", - "TaskServer.folderIgnored", - "TaskService.providerUnavailable", - "TaskService.noTestTask1", - "TaskService.noTestTask2", - "TaskService.noBuildTask1", - "TaskService.noBuildTask2", - "TaskServer.noTask", - "TaskService.associate", - "TaskService.attachProblemMatcher.continueWithout", - "TaskService.attachProblemMatcher.never", - "TaskService.attachProblemMatcher.neverType", - "TaskService.attachProblemMatcher.learnMoreAbout", - "selectProblemMatcher", - "customizeParseErrors", - "tasksJsonComment", - "moreThanOneBuildTask", - "TaskSystem.saveBeforeRun.prompt.title", - "detail", - { - "key": "saveBeforeRun.save", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "saveBeforeRun.dontSave", - "TaskSystem.activeSame.noBackground", - "terminateTask", - "restartTask", - "TaskSystem.active", - "TaskSystem.restartFailed", - "unexpectedTaskType", - "TaskService.providerUnavailable", - "TaskService.noConfiguration", - "TaskSystem.configurationErrors", - { + "vs/workbench/contrib/terminal/common/terminalContextKey": [ + "terminalFocusContextKey", + "terminalFocusInAnyContextKey", + "terminalEditorFocusContextKey", + "terminalCountContextKey", + "terminalTabsFocusContextKey", + "terminalShellTypeContextKey", + "terminalAltBufferActive", + "terminalSuggestWidgetVisible", + "terminalViewShowing", + "terminalTextSelectedContextKey", + "terminalTextSelectedInFocusedContextKey", + "terminalProcessSupportedContextKey", + "terminalTabsSingularSelectedContextKey", + "isSplitTerminalContextKey", + "inTerminalRunCommandPickerContextKey", + "terminalShellIntegrationEnabled" + ], + "vs/platform/audioCues/browser/audioCueService": [ + "audioCues.lineHasError.name", + "audioCues.lineHasWarning.name", + "audioCues.lineHasFoldedArea.name", + "audioCues.lineHasBreakpoint.name", + "audioCues.lineHasInlineSuggestion.name", + "audioCues.terminalQuickFix.name", + "audioCues.onDebugBreak.name", + "audioCues.noInlayHints", + "audioCues.taskCompleted", + "audioCues.taskFailed", + "audioCues.terminalCommandFailed", + "audioCues.terminalBell", + "audioCues.notebookCellCompleted", + "audioCues.notebookCellFailed", + "audioCues.diffLineInserted", + "audioCues.diffLineDeleted", + "audioCues.diffLineModified", + "audioCues.chatRequestSent", + "audioCues.chatResponseReceived", + "audioCues.chatResponsePending", + "audioCues.clear", + "audioCues.save", + "audioCues.format" + ], + "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ + "openToolsLabel", + "iframeWebviewAlert" + ], + "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ + "revealInWindows", + "revealInMac", + "openContainer" + ], + "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ + "mergeEditor", + "merge.dev.openState", + "mergeEditor.enterJSON", + "merge.dev.openSelectionInTemporaryMergeEditor" + ], + "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ + "TerminalTaskSystem.unknownError", + "TerminalTaskSystem.taskLoadReporting", + "dependencyCycle", + "dependencyFailed", + "TerminalTaskSystem.nonWatchingMatcher", + { + "key": "task.executingInFolder", + "comment": [ + "The workspace folder the task is running in", + "The task command line or label" + ] + }, + { + "key": "task.executing.shellIntegration", + "comment": [ + "The task command line or label" + ] + }, + { + "key": "task.executingInFolder", + "comment": [ + "The workspace folder the task is running in", + "The task command line or label" + ] + }, + { + "key": "task.executing.shell-integration", + "comment": [ + "The task command line or label" + ] + }, + { + "key": "task.executing", + "comment": [ + "The task command line or label" + ] + }, + "TerminalTaskSystem", + "unknownProblemMatcher", + "closeTerminal", + "reuseTerminal" + ], + "vs/workbench/contrib/tasks/browser/abstractTaskService": [ + "ConfigureTaskRunnerAction.label", + "tasks", + "TaskService.pickBuildTaskForLabel", + "runTask.arg", + "runTask.label", + "runTask.type", + "runTask.task", + "taskServiceOutputPrompt", + "showOutput", + "TaskServer.folderIgnored", + "TaskService.providerUnavailable", + "TaskService.noTestTask1", + "TaskService.noTestTask2", + "TaskService.noBuildTask1", + "TaskService.noBuildTask2", + "TaskServer.noTask", + "TaskService.associate", + "TaskService.attachProblemMatcher.continueWithout", + "TaskService.attachProblemMatcher.never", + "TaskService.attachProblemMatcher.neverType", + "TaskService.attachProblemMatcher.learnMoreAbout", + "selectProblemMatcher", + "customizeParseErrors", + "tasksJsonComment", + "moreThanOneBuildTask", + "TaskSystem.saveBeforeRun.prompt.title", + "detail", + { + "key": "saveBeforeRun.save", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "saveBeforeRun.dontSave", + "TaskSystem.activeSame.noBackground", + "terminateTask", + "restartTask", + "TaskSystem.active", + "TaskSystem.restartFailed", + "unexpectedTaskType", + "TaskService.providerUnavailable", + "TaskService.noConfiguration", + "TaskSystem.configurationErrors", + { "key": "TaskSystem.invalidTaskJsonOther", "comment": [ "Message notifies of an error in one of several places there is tasks related json, not necessarily in a file named tasks.json" @@ -6360,104 +6539,6 @@ "taskService.openDiff", "taskService.openDiffs" ], - "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ - "TerminalTaskSystem.unknownError", - "TerminalTaskSystem.taskLoadReporting", - "dependencyCycle", - "dependencyFailed", - "TerminalTaskSystem.nonWatchingMatcher", - { - "key": "task.executingInFolder", - "comment": [ - "The workspace folder the task is running in", - "The task command line or label" - ] - }, - { - "key": "task.executing.shellIntegration", - "comment": [ - "The task command line or label" - ] - }, - { - "key": "task.executingInFolder", - "comment": [ - "The workspace folder the task is running in", - "The task command line or label" - ] - }, - { - "key": "task.executing.shell-integration", - "comment": [ - "The task command line or label" - ] - }, - { - "key": "task.executing", - "comment": [ - "The task command line or label" - ] - }, - "TerminalTaskSystem", - "unknownProblemMatcher", - "closeTerminal", - "reuseTerminal" - ], - "vs/platform/audioCues/browser/audioCueService": [ - "audioCues.lineHasError.name", - "audioCues.lineHasWarning.name", - "audioCues.lineHasFoldedArea.name", - "audioCues.lineHasBreakpoint.name", - "audioCues.lineHasInlineSuggestion.name", - "audioCues.terminalQuickFix.name", - "audioCues.onDebugBreak.name", - "audioCues.noInlayHints", - "audioCues.taskCompleted", - "audioCues.taskFailed", - "audioCues.terminalCommandFailed", - "audioCues.terminalBell", - "audioCues.notebookCellCompleted", - "audioCues.notebookCellFailed", - "audioCues.diffLineInserted", - "audioCues.diffLineDeleted", - "audioCues.diffLineModified", - "audioCues.chatRequestSent", - "audioCues.chatResponseReceived", - "audioCues.chatResponsePending" - ], - "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ - "openToolsLabel", - "iframeWebviewAlert" - ], - "vs/workbench/contrib/terminal/common/terminalContextKey": [ - "terminalFocusContextKey", - "terminalFocusInAnyContextKey", - "terminalEditorFocusContextKey", - "terminalCountContextKey", - "terminalTabsFocusContextKey", - "terminalShellTypeContextKey", - "terminalAltBufferActive", - "terminalSuggestWidgetVisible", - "terminalViewShowing", - "terminalTextSelectedContextKey", - "terminalTextSelectedInFocusedContextKey", - "terminalProcessSupportedContextKey", - "terminalTabsSingularSelectedContextKey", - "isSplitTerminalContextKey", - "inTerminalRunCommandPickerContextKey", - "terminalShellIntegrationEnabled" - ], - "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ - "revealInWindows", - "revealInMac", - "openContainer" - ], - "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ - "mergeEditor", - "merge.dev.openState", - "mergeEditor.enterJSON", - "merge.dev.openSelectionInTemporaryMergeEditor" - ], "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions": [ "voiceChatGettingReady", "voiceChatInProgress", @@ -6465,24 +6546,25 @@ "inlineVoiceChatInProgress", "voiceChatInViewInProgress", "voiceChatInEditorInProgress", + "listening", "workbench.action.chat.voiceChatInView.label", "workbench.action.chat.inlineVoiceChat", "workbench.action.chat.quickVoiceChat.label", - "workbench.action.chat.startVoiceChat", - "workbench.action.chat.stopVoiceChat.label", - "workbench.action.chat.stopVoiceChatInChatView.label", - "workbench.action.chat.stopVoiceChatInChatEditor.label", - "workbench.action.chat.stopQuickVoiceChat.label", - "workbench.action.chat.stopInlineVoiceChat.label", - "workbench.action.chat.stopAndAcceptVoiceChat.label" + "workbench.action.chat.startVoiceChat.label", + "workbench.action.chat.stopListening.label", + "workbench.action.chat.stopListeningInChatView.label", + "workbench.action.chat.stopListeningInChatEditor.label", + "workbench.action.chat.stopListeningInQuickChat.label", + "workbench.action.chat.stopListeningInInlineChat.label", + "workbench.action.chat.stopListeningAndSubmit.label" + ], + "vs/workbench/api/common/extHostTelemetry": [ + "extensionTelemetryLog" ], "vs/workbench/api/common/extHostExtensionService": [ "extensionTestError1", "extensionTestError" ], - "vs/workbench/api/common/extHostTelemetry": [ - "extensionTelemetryLog" - ], "vs/workbench/api/common/extHostWorkspace": [ "updateerror" ], @@ -6498,21 +6580,80 @@ "worker", "local" ], - "vs/workbench/api/node/extHostDebugService": [ - "debug.terminal.title" - ], "vs/platform/terminal/node/terminalProcess": [ "launchFail.cwdNotDirectory", "launchFail.cwdDoesNotExist", "launchFail.executableDoesNotExist", "launchFail.executableIsNotFileOrSymlink" ], - "vs/platform/shell/node/shellEnv": [ - "resolveShellEnvTimeout", - "resolveShellEnvError", - "resolveShellEnvExitError" + "vs/workbench/api/node/extHostDebugService": [ + "debug.terminal.title" ], - "vs/platform/dialogs/electron-main/dialogMainService": [ + "vs/platform/extensions/common/extensionValidator": [ + "extensionDescription.publisher", + "extensionDescription.name", + "extensionDescription.version", + "extensionDescription.engines", + "extensionDescription.engines.vscode", + "extensionDescription.extensionDependencies", + "extensionDescription.activationEvents1", + "extensionDescription.activationEvents2", + "extensionDescription.extensionKind", + "extensionDescription.main1", + "extensionDescription.main2", + "extensionDescription.browser1", + "extensionDescription.browser2", + "notSemver", + "versionSyntax", + "versionSpecificity1", + "versionSpecificity2", + "versionMismatch" + ], + "vs/platform/extensionManagement/common/extensionNls": [ + "missingNLSKey" + ], + "vs/base/common/jsonErrorMessages": [ + "error.invalidSymbol", + "error.invalidNumberFormat", + "error.propertyNameExpected", + "error.valueExpected", + "error.colonExpected", + "error.commaExpected", + "error.closeBraceExpected", + "error.closeBracketExpected", + "error.endOfFileExpected" + ], + "vs/base/node/zip": [ + "invalid file", + "incompleteExtract", + "notFound" + ], + "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ + "MarketPlaceDisabled", + "malicious extension", + "notFoundDeprecatedReplacementExtension", + "incompatible platform", + "notFoundReleaseExtension", + "notFoundCompatibleDependency", + "singleDependentError", + "twoDependentsError", + "multipleDependentsError", + "singleIndirectDependentError", + "twoIndirectDependentsError", + "multipleIndirectDependentsError" + ], + "vs/platform/extensionManagement/node/extensionManagementUtil": [ + "invalidManifest" + ], + "vs/platform/files/common/io": [ + "fileTooLargeError" + ], + "vs/platform/shell/node/shellEnv": [ + "resolveShellEnvTimeout", + "resolveShellEnvError", + "resolveShellEnvExitError" + ], + "vs/platform/dialogs/electron-main/dialogMainService": [ "open", "openFolder", "openFile", @@ -6524,6 +6665,10 @@ ] } ], + "vs/platform/files/electron-main/diskFileSystemProviderServer": [ + "binFailed", + "trashFailed" + ], "vs/platform/externalTerminal/node/externalTerminalService": [ "console.title", "mac.terminal.script.failed", @@ -6532,10 +6677,6 @@ "linux.term.failed", "ext.term.app.not.found" ], - "vs/platform/files/electron-main/diskFileSystemProviderServer": [ - "binFailed", - "trashFailed" - ], "vs/platform/issue/electron-main/issueMainService": [ "local", "issueReporter", @@ -6586,6 +6727,24 @@ "cantUninstall", "sourceMissing" ], + "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ + "newWindow", + "newWindowDesc", + "recentFoldersAndWorkspaces", + "recentFolders", + "untitledWorkspace", + "workspaceName" + ], + "vs/platform/workspaces/electron-main/workspacesManagementMainService": [ + { + "key": "ok", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "workspaceOpenedMessage", + "workspaceOpenedDetail" + ], "vs/platform/windows/electron-main/windowsMainService": [ { "key": "ok", @@ -6619,83 +6778,9 @@ "confirmOpenDetail", "doNotAskAgain" ], - "vs/platform/workspaces/electron-main/workspacesManagementMainService": [ - { - "key": "ok", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "workspaceOpenedMessage", - "workspaceOpenedDetail" - ], - "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ - "newWindow", - "newWindowDesc", - "recentFoldersAndWorkspaces", - "recentFolders", - "untitledWorkspace", - "workspaceName" - ], - "vs/platform/files/common/io": [ - "fileTooLargeError" - ], "vs/base/browser/ui/button/button": [ "button dropdown more actions" ], - "vs/platform/extensions/common/extensionValidator": [ - "extensionDescription.publisher", - "extensionDescription.name", - "extensionDescription.version", - "extensionDescription.engines", - "extensionDescription.engines.vscode", - "extensionDescription.extensionDependencies", - "extensionDescription.activationEvents1", - "extensionDescription.activationEvents2", - "extensionDescription.extensionKind", - "extensionDescription.main1", - "extensionDescription.main2", - "extensionDescription.browser1", - "extensionDescription.browser2", - "notSemver", - "versionSyntax", - "versionSpecificity1", - "versionSpecificity2", - "versionMismatch" - ], - "vs/base/common/jsonErrorMessages": [ - "error.invalidSymbol", - "error.invalidNumberFormat", - "error.propertyNameExpected", - "error.valueExpected", - "error.colonExpected", - "error.commaExpected", - "error.closeBraceExpected", - "error.closeBracketExpected", - "error.endOfFileExpected" - ], - "vs/platform/extensionManagement/common/extensionNls": [ - "missingNLSKey" - ], - "vs/base/node/zip": [ - "invalid file", - "incompleteExtract", - "notFound" - ], - "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ - "MarketPlaceDisabled", - "malicious extension", - "notFoundDeprecatedReplacementExtension", - "incompatible platform", - "notFoundReleaseExtension", - "notFoundCompatibleDependency", - "singleDependentError", - "twoDependentsError", - "multipleDependentsError", - "singleIndirectDependentError", - "twoIndirectDependentsError", - "multipleIndirectDependentsError" - ], "vs/base/common/date": [ "date.fromNow.in", "date.fromNow.now", @@ -6752,9 +6837,6 @@ "date.fromNow.years.plural.fullWord", "date.fromNow.years.plural" ], - "vs/platform/extensionManagement/node/extensionManagementUtil": [ - "invalidManifest" - ], "vs/platform/userDataSync/common/keybindingsSync": [ "errorInvalidSettings", "errorInvalidSettings" @@ -6865,6 +6947,11 @@ "editorUnicodeHighlight.border", "editorUnicodeHighlight.background" ], + "vs/editor/browser/coreCommands": [ + "stickydesc", + "stickydesc", + "removedCursor" + ], "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ "toggleCollapseUnchangedRegions", "toggleShowMovedCodeBlocks", @@ -6881,6 +6968,10 @@ "Open Accessible Diff Viewer", "editor.action.accessibleDiffViewer.prev" ], + "vs/editor/browser/widget/codeEditorWidget": [ + "cursors.maximum", + "goToSetting" + ], "vs/platform/contextkey/common/scanner": [ "contextkey.scanner.hint.didYouMean1", "contextkey.scanner.hint.didYouMean2", @@ -6888,11 +6979,6 @@ "contextkey.scanner.hint.didYouForgetToOpenOrCloseQuote", "contextkey.scanner.hint.didYouForgetToEscapeSlash" ], - "vs/editor/browser/coreCommands": [ - "stickydesc", - "stickydesc", - "removedCursor" - ], "vs/editor/contrib/anchorSelect/browser/anchorSelect": [ "selectionAnchor", "anchorSet", @@ -6901,10 +6987,6 @@ "selectFromAnchorToCursor", "cancelSelectionAnchor" ], - "vs/editor/contrib/caretOperations/browser/caretOperations": [ - "caret.moveLeft", - "caret.moveRight" - ], "vs/editor/contrib/bracketMatching/browser/bracketMatching": [ "overviewRulerBracketMatchForeground", "smartSelect.jumpBracket", @@ -6917,9 +6999,9 @@ ] } ], - "vs/editor/browser/widget/codeEditorWidget": [ - "cursors.maximum", - "goToSetting" + "vs/editor/contrib/caretOperations/browser/caretOperations": [ + "caret.moveLeft", + "caret.moveRight" ], "vs/editor/contrib/caretOperations/browser/transpose": [ "transposeLetters.label" @@ -6961,7 +7043,7 @@ ], "vs/editor/contrib/codeAction/browser/codeActionContributions": [ "showCodeActionHeaders", - "includeNearbyQuickfixes" + "includeNearbyQuickFixes" ], "vs/editor/contrib/codelens/browser/codelensController": [ "showLensOnLine", @@ -7029,6 +7111,26 @@ "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution": [ "defaultProviderDescription" ], + "vs/editor/contrib/folding/browser/folding": [ + "unfoldAction.label", + "unFoldRecursivelyAction.label", + "foldAction.label", + "toggleFoldAction.label", + "foldRecursivelyAction.label", + "foldAllBlockComments.label", + "foldAllMarkerRegions.label", + "unfoldAllMarkerRegions.label", + "foldAllExcept.label", + "unfoldAllExcept.label", + "foldAllAction.label", + "unfoldAllAction.label", + "gotoParentFold.label", + "gotoPreviousFold.label", + "gotoNextFold.label", + "createManualFoldRange.label", + "removeManualFoldingRanges.label", + "foldLevelAction.label" + ], "vs/editor/contrib/find/browser/findController": [ "too.large.for.replaceall", "startFindAction", @@ -7066,26 +7168,6 @@ "EditorFontZoomOut.label", "EditorFontZoomReset.label" ], - "vs/editor/contrib/folding/browser/folding": [ - "unfoldAction.label", - "unFoldRecursivelyAction.label", - "foldAction.label", - "toggleFoldAction.label", - "foldRecursivelyAction.label", - "foldAllBlockComments.label", - "foldAllMarkerRegions.label", - "unfoldAllMarkerRegions.label", - "foldAllExcept.label", - "unfoldAllExcept.label", - "foldAllAction.label", - "unfoldAllAction.label", - "gotoParentFold.label", - "gotoPreviousFold.label", - "gotoNextFold.label", - "createManualFoldRange.label", - "removeManualFoldingRanges.label", - "foldLevelAction.label" - ], "vs/editor/contrib/format/browser/formatActions": [ "formatDocument.label", "formatSelection.label" @@ -7179,25 +7261,6 @@ ] } ], - "vs/editor/contrib/indentation/browser/indentation": [ - "indentationToSpaces", - "indentationToTabs", - "configuredTabSize", - "defaultTabSize", - "currentTabSize", - { - "key": "selectTabWidth", - "comment": [ - "Tab corresponds to the tab key" - ] - }, - "indentUsingTabs", - "indentUsingSpaces", - "changeTabDisplaySize", - "detectIndentation", - "editor.reindentlines", - "editor.reindentselectedlines" - ], "vs/editor/contrib/hover/browser/hover": [ { "key": "showOrFocusHover", @@ -7264,28 +7327,31 @@ ] } ], - "vs/editor/contrib/lineSelection/browser/lineSelection": [ - "expandLineSelection" - ], - "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ - "linkedEditing.label", - "editorLinkedEditingBackground" + "vs/editor/contrib/indentation/browser/indentation": [ + "indentationToSpaces", + "indentationToTabs", + "configuredTabSize", + "defaultTabSize", + "currentTabSize", + { + "key": "selectTabWidth", + "comment": [ + "Tab corresponds to the tab key" + ] + }, + "indentUsingTabs", + "indentUsingSpaces", + "changeTabDisplaySize", + "detectIndentation", + "editor.reindentlines", + "editor.reindentselectedlines" ], "vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace": [ "InPlaceReplaceAction.previous.label", "InPlaceReplaceAction.next.label" ], - "vs/editor/contrib/links/browser/links": [ - "invalid.url", - "missing.url", - "links.navigate.executeCmd", - "links.navigate.follow", - "links.navigate.kb.meta.mac", - "links.navigate.kb.meta", - "links.navigate.kb.alt.mac", - "links.navigate.kb.alt", - "tooltip.explanation", - "label" + "vs/editor/contrib/lineSelection/browser/lineSelection": [ + "expandLineSelection" ], "vs/editor/contrib/linesOperations/browser/linesOperations": [ "lines.copyUp", @@ -7343,6 +7409,22 @@ "editor.transformToCamelcase", "editor.transformToKebabcase" ], + "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ + "linkedEditing.label", + "editorLinkedEditingBackground" + ], + "vs/editor/contrib/links/browser/links": [ + "invalid.url", + "missing.url", + "links.navigate.executeCmd", + "links.navigate.follow", + "links.navigate.kb.meta.mac", + "links.navigate.kb.meta", + "links.navigate.kb.alt.mac", + "links.navigate.kb.alt", + "tooltip.explanation", + "label" + ], "vs/editor/contrib/multicursor/browser/multicursor": [ "cursorAdded", "cursorsAdded", @@ -7434,6 +7516,9 @@ "hasPrevTabstop", "next" ], + "vs/editor/contrib/tokenization/browser/tokenization": [ + "forceRetokenize" + ], "vs/editor/contrib/suggest/browser/suggestController": [ "aria.alert.snippet", "suggest.trigger.label", @@ -7446,9 +7531,6 @@ "detail.less", "suggest.reset.label" ], - "vs/editor/contrib/tokenization/browser/tokenization": [ - "forceRetokenize" - ], "vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode": [ { "key": "toggle.tabMovesFocus", @@ -7459,18 +7541,6 @@ "toggle.tabMovesFocus.on", "toggle.tabMovesFocus.off" ], - "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ - "unusualLineTerminators.title", - "unusualLineTerminators.message", - "unusualLineTerminators.detail", - { - "key": "unusualLineTerminators.fix", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "unusualLineTerminators.ignore" - ], "vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter": [ "warningIcon", "unicodeHighlighting.thisDocumentHasManyNonBasicAsciiUnicodeCharacters", @@ -7497,6 +7567,18 @@ "unicodeHighlight.allowCommonCharactersInLanguage", "unicodeHighlight.configureUnicodeHighlightOptions" ], + "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ + "unusualLineTerminators.title", + "unusualLineTerminators.message", + "unusualLineTerminators.detail", + { + "key": "unusualLineTerminators.fix", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "unusualLineTerminators.ignore" + ], "vs/editor/contrib/wordHighlighter/browser/wordHighlighter": [ "wordHighlight.next.label", "wordHighlight.previous.label", @@ -7529,6 +7611,12 @@ "tabFocusModeOffMsg", "tabFocusModeOffMsgNoKb", "showAccessibilityHelpAction", + "saveAudioCueDisabled", + "saveAudioCueAlways", + "saveAudioCueUserGesture", + "formatAudioCueDisabled", + "formatAudioCueAlways", + "formatAudioCueUserGesture", "inspectTokens", "gotoLineActionLabel", "helpQuickAccess", @@ -7541,6 +7629,74 @@ "toggleHighContrast", "bulkEditServiceSummary" ], + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ + "toggleAuxiliaryIconRight", + "toggleAuxiliaryIconRightOn", + "toggleAuxiliaryIconLeft", + "toggleAuxiliaryIconLeftOn", + "toggleAuxiliaryBar", + "secondary sidebar", + { + "key": "secondary sidebar mnemonic", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "focusAuxiliaryBar", + "toggleSecondarySideBar", + "toggleSecondarySideBar", + "hideAuxiliaryBar" + ], + "vs/workbench/browser/parts/panel/panelActions": [ + "maximizeIcon", + "restoreIcon", + "closeIcon", + "togglePanelOffIcon", + "togglePanelOnIcon", + "togglePanelVisibility", + "toggle panel", + { + "key": "toggle panel mnemonic", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "focusPanel", + "focusPanel", + "positionPanelLeft", + "positionPanelLeftShort", + "positionPanelRight", + "positionPanelRightShort", + "positionPanelBottom", + "positionPanelBottomShort", + "alignPanelLeft", + "alignPanelLeftShort", + "alignPanelRight", + "alignPanelRightShort", + "alignPanelCenter", + "alignPanelCenterShort", + "alignPanelJustify", + "alignPanelJustifyShort", + "positionPanel", + "alignPanel", + "previousPanelView", + "nextPanelView", + "toggleMaximizedPanel", + "maximizePanel", + "minimizePanel", + "panelMaxNotSupported", + "closePanel", + "closeSecondarySideBar", + "togglePanel", + "hidePanel", + "movePanelToSecondarySideBar", + "movePanelToSecondarySideBar", + "moveSidePanelToPanel", + "moveSidePanelToPanel" + ], + "vs/workbench/browser/quickaccess": [ + "inQuickOpen" + ], "vs/workbench/api/common/jsonValidationExtensionPoint": [ "contributes.jsonValidation", "contributes.jsonValidation.fileMatch", @@ -7552,6 +7708,21 @@ "invalid.url.fileschema", "invalid.url.schema" ], + "vs/workbench/services/themes/common/iconExtensionPoint": [ + "contributes.icons", + "contributes.icon.id", + "contributes.icon.id.format", + "contributes.icon.description", + "contributes.icon.default.fontPath", + "contributes.icon.default.fontCharacter", + "contributes.icon.default", + "invalid.icons.configuration", + "invalid.icons.id.format", + "invalid.icons.description", + "invalid.icons.default.fontPath.extension", + "invalid.icons.default.fontPath.path", + "invalid.icons.default" + ], "vs/workbench/services/themes/common/colorExtensionPoint": [ "contributes.color", "contributes.color.id", @@ -7570,21 +7741,6 @@ "invalid.defaults.highContrast", "invalid.defaults.highContrastLight" ], - "vs/workbench/services/themes/common/iconExtensionPoint": [ - "contributes.icons", - "contributes.icon.id", - "contributes.icon.id.format", - "contributes.icon.description", - "contributes.icon.default.fontPath", - "contributes.icon.default.fontCharacter", - "contributes.icon.default", - "invalid.icons.configuration", - "invalid.icons.id.format", - "invalid.icons.description", - "invalid.icons.default.fontPath.extension", - "invalid.icons.default.fontPath.path", - "invalid.icons.default" - ], "vs/workbench/services/themes/common/tokenClassificationExtensionPoint": [ "contributes.semanticTokenTypes", "contributes.semanticTokenTypes.id", @@ -7838,74 +7994,6 @@ ] } ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ - "toggleAuxiliaryIconRight", - "toggleAuxiliaryIconRightOn", - "toggleAuxiliaryIconLeft", - "toggleAuxiliaryIconLeftOn", - "toggleAuxiliaryBar", - "secondary sidebar", - { - "key": "secondary sidebar mnemonic", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "focusAuxiliaryBar", - "toggleSecondarySideBar", - "toggleSecondarySideBar", - "hideAuxiliaryBar" - ], - "vs/workbench/browser/parts/panel/panelActions": [ - "maximizeIcon", - "restoreIcon", - "closeIcon", - "togglePanelOffIcon", - "togglePanelOnIcon", - "togglePanelVisibility", - "toggle panel", - { - "key": "toggle panel mnemonic", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "focusPanel", - "focusPanel", - "positionPanelLeft", - "positionPanelLeftShort", - "positionPanelRight", - "positionPanelRightShort", - "positionPanelBottom", - "positionPanelBottomShort", - "alignPanelLeft", - "alignPanelLeftShort", - "alignPanelRight", - "alignPanelRightShort", - "alignPanelCenter", - "alignPanelCenterShort", - "alignPanelJustify", - "alignPanelJustifyShort", - "positionPanel", - "alignPanel", - "previousPanelView", - "nextPanelView", - "toggleMaximizedPanel", - "maximizePanel", - "minimizePanel", - "panelMaxNotSupported", - "closePanel", - "closeSecondarySideBar", - "togglePanel", - "hidePanel", - "movePanelToSecondarySideBar", - "movePanelToSecondarySideBar", - "moveSidePanelToPanel", - "moveSidePanelToPanel" - ], - "vs/workbench/browser/quickaccess": [ - "inQuickOpen" - ], "vs/workbench/services/extensions/common/extensionsRegistry": [ "ui", "workspace", @@ -7993,18 +8081,32 @@ "vscode.extension.pricing", "product.extensionEnabledApiProposals" ], - "vs/workbench/contrib/debug/common/debug": [ - "debugType", - "debugConfigurationType", - "debugState", - "debugUX", - "hasDebugged", - "inDebugMode", - "inDebugRepl", - "breakpointWidgetVisibile", - "inBreakpointWidget", - "breakpointsFocused", - "watchExpressionsFocused", + "vs/workbench/browser/parts/titlebar/windowTitle": [ + "userIsAdmin", + "userIsSudo", + "devExtensionWindowTitlePrefix" + ], + "vs/workbench/browser/parts/views/treeView": [ + "no-dataprovider", + "treeView.enableCollapseAll", + "treeView.enableRefresh", + "refresh", + "collapseAll", + "treeView.toggleCollapseAll", + "command-error" + ], + "vs/workbench/contrib/debug/common/debug": [ + "debugType", + "debugConfigurationType", + "debugState", + "debugUX", + "hasDebugged", + "inDebugMode", + "inDebugRepl", + "breakpointWidgetVisibile", + "inBreakpointWidget", + "breakpointsFocused", + "watchExpressionsFocused", "watchExpressionsExist", "variablesFocused", "expressionSelected", @@ -8048,6 +8150,14 @@ "debuggerDisabled", "internalConsoleOptions" ], + "vs/workbench/browser/parts/views/viewPaneContainer": [ + "views", + "viewMoveUp", + "viewMoveLeft", + "viewMoveDown", + "viewMoveRight", + "viewsMove" + ], "vs/workbench/contrib/files/common/files": [ "explorerViewletVisible", "foldersViewVisible", @@ -8064,14 +8174,6 @@ "explorerViewletCompressedLastFocus", "viewHasSomeCollapsibleItem" ], - "vs/workbench/browser/parts/views/viewPaneContainer": [ - "views", - "viewMoveUp", - "viewMoveLeft", - "viewMoveDown", - "viewMoveRight", - "viewsMove" - ], "vs/workbench/contrib/remote/browser/remoteExplorer": [ "remoteNoPorts", "noRemoteNoPorts", @@ -8092,24 +8194,15 @@ "remote.tunnelsView.makePublic", "remote.tunnelsView.elevationButton" ], - "vs/workbench/browser/parts/views/treeView": [ - "no-dataprovider", - "treeView.enableCollapseAll", - "treeView.enableRefresh", - "refresh", - "collapseAll", - "treeView.toggleCollapseAll", - "command-error" - ], "vs/workbench/common/editor/sideBySideEditorInput": [ "sideBySideLabels" ], - "vs/workbench/common/editor/diffEditorInput": [ - "sideBySideLabels" - ], "vs/workbench/browser/parts/editor/sideBySideEditor": [ "sideBySideEditor" ], + "vs/workbench/common/editor/diffEditorInput": [ + "sideBySideLabels" + ], "vs/workbench/browser/parts/editor/textDiffEditor": [ "textDiffEditor", "fileTooLargeForHeapErrorWithSize", @@ -8185,6 +8278,28 @@ "pickEncodingForReopen", "pickEncodingForSave" ], + "vs/workbench/browser/parts/editor/editorCommands": [ + "editorCommand.activeEditorMove.description", + "editorCommand.activeEditorMove.arg.name", + "editorCommand.activeEditorMove.arg.description", + "editorCommand.activeEditorCopy.description", + "editorCommand.activeEditorCopy.arg.name", + "editorCommand.activeEditorCopy.arg.description", + "compare.nextChange", + "compare.previousChange", + "toggleInlineView", + "compare", + "splitEditorInGroup", + "joinEditorInGroup", + "toggleJoinEditorInGroup", + "toggleSplitEditorInGroupLayout", + "focusLeftSideEditor", + "focusRightSideEditor", + "focusOtherSideEditor", + "toggleEditorGroupLock", + "lockEditorGroup", + "unlockEditorGroup" + ], "vs/workbench/browser/parts/editor/editorActions": [ "splitEditor", "splitEditorOrthogonal", @@ -8226,7 +8341,8 @@ "minimizeOtherEditorGroups", "evenEditorGroups", "toggleEditorWidths", - "maximizeEditor", + "maximizeEditorHideSidebar", + "toggleMaximizeEditorGroup", "openNextEditor", "openPreviousEditor", "nextEditorInGroup", @@ -8321,7 +8437,14 @@ "newGroupAbove", "newGroupBelow", "toggleEditorType", - "reopenTextEditor" + "reopenTextEditor", + "popEditorOut", + { + "key": "miPopEditorOut", + "comment": [ + "&& denotes a mnemonic" + ] + } ], "vs/editor/browser/editorExtensions": [ { @@ -8346,28 +8469,6 @@ }, "selectAll" ], - "vs/workbench/browser/parts/editor/editorCommands": [ - "editorCommand.activeEditorMove.description", - "editorCommand.activeEditorMove.arg.name", - "editorCommand.activeEditorMove.arg.description", - "editorCommand.activeEditorCopy.description", - "editorCommand.activeEditorCopy.arg.name", - "editorCommand.activeEditorCopy.arg.description", - "compare.nextChange", - "compare.previousChange", - "toggleInlineView", - "compare", - "splitEditorInGroup", - "joinEditorInGroup", - "toggleJoinEditorInGroup", - "toggleSplitEditorInGroupLayout", - "focusLeftSideEditor", - "focusRightSideEditor", - "focusOtherSideEditor", - "toggleEditorGroupLock", - "lockEditorGroup", - "unlockEditorGroup" - ], "vs/workbench/browser/parts/editor/editorQuickAccess": [ "noViewResults", "entryAriaLabelWithGroupDirty", @@ -8383,41 +8484,19 @@ "editor.editorAssociations", "editorLargeFileSizeConfirmation" ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ - "move second side bar left", - "move second side bar right", - "hide second side bar" - ], - "vs/workbench/browser/parts/activitybar/activitybarPart": [ - "accountsViewBarIcon", - "menu", - "hideMenu", - "accounts", - "hideActivitBar", - "resetLocation", - "resetLocation", - "manage", - "accounts", - "manage", - "accounts" - ], "vs/workbench/browser/parts/panel/panelPart": [ - "resetLocation", - "resetLocation", - "panel.emptyMessage", - "moreActions", "panel position", "align panel", "hidePanel" ], - "vs/workbench/browser/parts/editor/editorGroupView": [ - "ariaLabelGroupActions", - "emptyEditorGroup", - "groupLabel", - "groupAriaLabel" + "vs/workbench/browser/parts/sidebar/sidebarPart": [ + "manage", + "accounts" ], - "vs/workbench/browser/parts/editor/editorDropTarget": [ - "dropIntoEditorPrompt" + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ + "move second side bar left", + "move second side bar right", + "hide second side bar" ], "vs/workbench/browser/parts/statusbar/statusbarActions": [ "hide", @@ -8441,19 +8520,16 @@ "dialogPendingMessage", "dialogClose" ], - "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ - "keybindingsInputName" - ], "vs/workbench/services/preferences/common/preferencesEditorInput": [ "settingsEditor2InputName" ], + "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ + "keybindingsInputName" + ], "vs/workbench/services/preferences/common/preferencesModels": [ "commonlyUsed", "defaultKeybindingsHeader" ], - "vs/workbench/services/editor/common/editorResolverService": [ - "editor.editorAssociations" - ], "vs/workbench/services/textfile/common/textFileEditorModel": [ "textFileCreate.source" ], @@ -8579,10 +8655,50 @@ ] } ], + "vs/platform/keybinding/common/abstractKeybindingService": [ + "first.chord", + "next.chord", + "missing.chord", + "missing.chord" + ], + "vs/workbench/services/editor/common/editorResolverService": [ + "editor.editorAssociations" + ], + "vs/workbench/services/themes/common/colorThemeData": [ + "error.cannotparsejson", + "error.invalidformat", + { + "key": "error.invalidformat.colors", + "comment": [ + "{0} will be replaced by a path. Values in quotes should not be translated." + ] + }, + { + "key": "error.invalidformat.tokenColors", + "comment": [ + "{0} will be replaced by a path. Values in quotes should not be translated." + ] + }, + { + "key": "error.invalidformat.semanticTokenColors", + "comment": [ + "{0} will be replaced by a path. Values in quotes should not be translated." + ] + }, + "error.plist.invalidformat", + "error.cannotparse", + "error.cannotload" + ], "vs/workbench/services/themes/common/fileIconThemeSchema": [ "schema.folderExpanded", "schema.folder", "schema.file", + "schema.rootFolder", + "schema.rootFolderExpanded", + "schema.rootFolderNames", + "schema.folderName", + "schema.rootFolderNamesExpanded", + "schema.rootFolderNameExpanded", "schema.folderNames", "schema.folderName", "schema.folderNamesExpanded", @@ -8618,37 +8734,6 @@ "error.cannotparseicontheme", "error.invalidformat" ], - "vs/workbench/services/themes/common/colorThemeData": [ - "error.cannotparsejson", - "error.invalidformat", - { - "key": "error.invalidformat.colors", - "comment": [ - "{0} will be replaced by a path. Values in quotes should not be translated." - ] - }, - { - "key": "error.invalidformat.tokenColors", - "comment": [ - "{0} will be replaced by a path. Values in quotes should not be translated." - ] - }, - { - "key": "error.invalidformat.semanticTokenColors", - "comment": [ - "{0} will be replaced by a path. Values in quotes should not be translated." - ] - }, - "error.plist.invalidformat", - "error.cannotparse", - "error.cannotload" - ], - "vs/platform/keybinding/common/abstractKeybindingService": [ - "first.chord", - "next.chord", - "missing.chord", - "missing.chord" - ], "vs/workbench/services/themes/common/colorThemeSchema": [ "schema.token.settings", "schema.token.foreground", @@ -8697,16 +8782,6 @@ "error.icon.font", "error.icon.fontCharacter" ], - "vs/workbench/services/themes/common/productIconThemeSchema": [ - "schema.id", - "schema.id.formatError", - "schema.src", - "schema.font-path", - "schema.font-format", - "schema.font-weight", - "schema.font-style", - "schema.iconDefinitions" - ], "vs/workbench/services/themes/common/themeConfiguration": [ "colorTheme", "colorThemeError", @@ -8780,6 +8855,16 @@ "editorColors.semanticHighlighting.rules", "semanticTokenColors" ], + "vs/workbench/services/themes/common/productIconThemeSchema": [ + "schema.id", + "schema.id.formatError", + "schema.src", + "schema.font-path", + "schema.font-format", + "schema.font-weight", + "schema.font-style", + "schema.iconDefinitions" + ], "vs/workbench/services/extensionManagement/browser/extensionBisect": [ "I cannot reproduce", "This is Bad", @@ -8845,8 +8930,8 @@ "snippets", "exclude" ], - "vs/workbench/services/userDataProfile/browser/globalStateResource": [ - "globalState" + "vs/workbench/services/userDataProfile/browser/tasksResource": [ + "tasks" ], "vs/workbench/services/userDataProfile/browser/extensionsResource": [ "extensions", @@ -8854,12 +8939,15 @@ "exclude", "exclude" ], - "vs/workbench/services/userDataProfile/browser/tasksResource": [ - "tasks" + "vs/workbench/services/userDataProfile/browser/globalStateResource": [ + "globalState" ], "vs/workbench/services/userDataProfile/common/userDataProfileIcons": [ "settingsViewBarIcon" ], + "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ + "saveParticipants" + ], "vs/workbench/services/remote/common/tunnelModel": [ "tunnel.forwardedPortsViewEnabled", "tunnel.source.user", @@ -8867,12 +8955,6 @@ "remote.localPortMismatch.single", "tunnel.staticallyForwarded" ], - "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ - "saveParticipants" - ], - "vs/workbench/services/views/common/viewContainerModel": [ - "views log" - ], "vs/workbench/services/hover/browser/hoverWidget": [ "hoverhint" ], @@ -8889,13 +8971,25 @@ "invalid.tokenTypes", "invalid.path.1" ], - "vs/editor/contrib/suggest/browser/suggest": [ - "suggestWidgetHasSelection", - "suggestWidgetDetailsVisible", - "suggestWidgetMultipleSuggestions", - "suggestionMakesTextEdit", - "acceptSuggestionOnEnter", - "suggestionHasInsertAndReplaceRange", + "vs/workbench/contrib/preferences/browser/keybindingWidgets": [ + "defineKeybinding.initial", + "defineKeybinding.oneExists", + "defineKeybinding.existing", + "defineKeybinding.chordsTo" + ], + "vs/workbench/contrib/performance/browser/perfviewEditor": [ + "name" + ], + "vs/workbench/contrib/speech/common/speechService": [ + "hasSpeechProvider" + ], + "vs/editor/contrib/suggest/browser/suggest": [ + "suggestWidgetHasSelection", + "suggestWidgetDetailsVisible", + "suggestWidgetMultipleSuggestions", + "suggestionMakesTextEdit", + "acceptSuggestionOnEnter", + "suggestionHasInsertAndReplaceRange", "suggestionInsertMode", "suggestionCanResolve" ], @@ -8934,11 +9028,6 @@ "noWhen", "keyboard shortcuts aria label" ], - "vs/workbench/contrib/preferences/browser/preferencesActions": [ - "configureLanguageBasedSettings", - "languageDescriptionConfigured", - "pickLanguage" - ], "vs/workbench/contrib/preferences/browser/preferencesIcons": [ "settingsScopeDropDownIcon", "settingsMoreActionIcon", @@ -8953,6 +9042,18 @@ "settingsFilter", "preferencesOpenSettings" ], + "vs/workbench/contrib/preferences/browser/preferencesActions": [ + "configureLanguageBasedSettings", + "languageDescriptionConfigured", + "pickLanguage" + ], + "vs/workbench/contrib/preferences/common/preferencesContribution": [ + "splitSettingsEditorLabel", + "enableNaturalLanguageSettingsSearch", + "settingsSearchTocBehavior.hide", + "settingsSearchTocBehavior.filter", + "settingsSearchTocBehavior" + ], "vs/workbench/contrib/preferences/browser/settingsEditor2": [ "SearchSettings.AriaLabel", "clearInput", @@ -8967,21 +9068,160 @@ "turnOnSyncButton", "lastSyncedLabel" ], - "vs/workbench/contrib/preferences/common/preferencesContribution": [ - "splitSettingsEditorLabel", - "enableNaturalLanguageSettingsSearch", - "settingsSearchTocBehavior.hide", - "settingsSearchTocBehavior.filter", - "settingsSearchTocBehavior" + "vs/workbench/contrib/chat/browser/actions/chatActions": [ + "chat.category", + "quickChat", + { + "key": "actions.chat.acceptInput", + "comment": [ + "Apply input from the chat input box" + ] + }, + { + "key": "actions.chat.submitSecondaryAgent", + "comment": [ + "Send input from the chat input box to the secondary agent" + ] + }, + "interactiveSession.clearHistory.label", + "actions.interactiveSession.focus", + "interactiveSession.focusInput.label", + "interactiveSession.open", + "interactiveSession.history.label", + "interactiveSession.history.delete", + "interactiveSession.history.pick" ], - "vs/workbench/contrib/preferences/browser/keybindingWidgets": [ - "defineKeybinding.initial", - "defineKeybinding.oneExists", - "defineKeybinding.existing", - "defineKeybinding.chordsTo" + "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ + "interactive.copyCodeBlock.label", + "interactive.insertCodeBlock.label", + "interactive.insertIntoNewFile.label", + "interactive.runInTerminal.label", + "interactive.nextCodeBlock.label", + "interactive.previousCodeBlock.label" ], - "vs/workbench/contrib/performance/browser/perfviewEditor": [ - "name" + "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ + "interactive.copyAll.label", + "interactive.copyItem.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ + "interactive.submit.label", + "interactive.cancel.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ + "chat.openInChatView.label", + "chat.closeQuickChat.label", + "quickChat", + "toggle.desc", + "toggle.query", + "toggle.isPartialQuery", + "toggle.query", + "interactiveSession.open" + ], + "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ + "interactive.helpful.label", + "interactive.unhelpful.label", + "interactive.insertIntoNotebook.label", + "chat.remove.label" + ], + "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ + "vscode.extension.contributes.interactiveSession", + "vscode.extension.contributes.interactiveSession.id", + "vscode.extension.contributes.interactiveSession.label", + "vscode.extension.contributes.interactiveSession.icon", + "vscode.extension.contributes.interactiveSession.when", + "chat.viewContainer.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ + "chat.file.label", + "chat.export.label", + "chat.import.label" + ], + "vs/workbench/contrib/chat/browser/chatEditorInput": [ + "chatEditorName" + ], + "vs/workbench/contrib/chat/common/chatServiceImpl": [ + "emptyResponse" + ], + "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ + "chat.openInEditor.label", + "interactiveSession.openInEditor.label", + "interactiveSession.openInSidebar.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ + "interactiveSession.clear.label", + "interactiveSession.clear.label", + "interactiveSession.clear.label" + ], + "vs/workbench/contrib/accessibility/browser/accessibleView": [ + "symbolLabel", + "symbolLabelAria", + "disableAccessibilityHelp", + "openDoc", + "exit", + "ariaAccessibleViewActionsBottom", + "ariaAccessibleViewActions", + "accessibility-help", + "accessible-view", + "accessible-view-hint", + "accessibility-help-hint", + "accessibleHelpToolbar", + "accessibleViewToolbar", + "toolbar", + "intro", + "accessibleViewNextPreviousHint", + "chatAccessibleViewNextPreviousHintNoKb", + "acessibleViewDisableHint", + "accessibleViewDisableHintNoKb", + "goToSymbolHint", + "goToSymbolHintNoKb", + "acessibleViewHint", + "acessibleViewHintNoKbEither", + "accessibleViewSymbolQuickPickPlaceholder", + "accessibleViewSymbolQuickPickTitle" + ], + "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ + "notification.accessibleViewSrc", + "notification.accessibleView", + "clearNotification", + "clearNotification" + ], + "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ + "editor.action.accessibleViewNext", + "editor.action.accessibleViewPrevious", + "editor.action.accessibleViewGoToSymbol", + "editor.action.accessibilityHelp", + "editor.action.accessibleView", + "editor.action.accessibleViewDisableHint", + "editor.action.accessibleViewAcceptInlineCompletionAction" + ], + "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ + "interactive.nextFileTree.label", + "interactive.previousFileTree.label" + ], + "vs/workbench/contrib/chat/common/chatContextKeys": [ + "interactiveSessionResponseHasProviderId", + "interactiveSessionResponseVote", + "chatResponseFiltered", + "interactiveSessionRequestInProgress", + "chatResponse", + "chatRequest", + "interactiveInputHasText", + "inInteractiveInput", + "inChat", + "hasChatProvider" + ], + "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ + "pickFileLabel" + ], + "vs/workbench/contrib/chat/common/chatColors": [ + "chat.requestBorder", + "chat.slashCommandBackground", + "chat.slashCommandForeground", + "chat.avatarBackground", + "chat.avatarForeground" + ], + "vs/workbench/contrib/notebook/common/notebookEditorInput": [ + "vetoExtHostRestart" ], "vs/workbench/contrib/notebook/browser/notebookEditor": [ "fail.noEditor", @@ -8991,23 +9231,20 @@ "notebookOpenAsText", "notebookOpenInTextEditor" ], - "vs/workbench/contrib/notebook/common/notebookEditorInput": [ - "vetoExtHostRestart" - ], "vs/workbench/contrib/notebook/browser/services/notebookServiceImpl": [ "notebookOpenInstallMissingViewType" ], "vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor": [ "notebookTreeAriaLabel" ], + "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ + "notebookRunTrust" + ], "vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl": [ "disableOtherKeymapsConfirmation", "yes", "no" ], - "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ - "notebookRunTrust" - ], "vs/editor/common/languages/modesRegistry": [ "plainText.alias" ], @@ -9031,80 +9268,22 @@ "notebook.cell.quitEditNoKb", "notebook.cell.focusInOutput", "notebook.cell.focusInOutputNoKb", + "notebook.focusNextEditor", + "notebook.focusNextEditorNoKb", + "notebook.focusPreviousEditor", + "notebook.focusPreviousEditorNoKb", "notebook.cellNavigation", "notebook.cell.executeAndFocusContainer", "notebook.cell.executeAndFocusContainerNoKb", "notebook.cell.insertCodeCellBelowAndFocusContainer", "notebook.changeCellType" ], - "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ - "editor.action.accessibleViewNext", - "editor.action.accessibleViewPrevious", - "editor.action.accessibleViewGoToSymbol", - "editor.action.accessibilityHelp", - "editor.action.accessibleView", - "editor.action.accessibleViewDisableHint", - "editor.action.accessibleViewAcceptInlineCompletionAction" - ], - "vs/workbench/contrib/accessibility/browser/accessibleView": [ - "symbolLabel", - "symbolLabelAria", - "disableAccessibilityHelp", - "openDoc", - "exit", - "ariaAccessibleViewActionsBottom", - "ariaAccessibleViewActions", - "accessibility-help", - "accessible-view", - "accessible-view-hint", - "accessibility-help-hint", - "accessibleHelpToolbar", - "accessibleViewToolbar", - "toolbar", - "intro", - "accessibleViewNextPreviousHint", - "chatAccessibleViewNextPreviousHintNoKb", - "acessibleViewDisableHint", - "accessibleViewDisableHintNoKb", - "goToSymbolHint", - "goToSymbolHintNoKb", - "acessibleViewHint", - "acessibleViewHintNoKbEither", - "accessibleViewSymbolQuickPickPlaceholder", - "accessibleViewSymbolQuickPickTitle" - ], "vs/workbench/contrib/notebook/browser/controller/coreActions": [ "notebookActions.category", "notebookMenu.insertCell", "notebookMenu.cellTitle", "miShare" ], - "vs/workbench/contrib/notebook/browser/controller/insertCellActions": [ - "notebookActions.insertCodeCellAbove", - "notebookActions.insertCodeCellAboveAndFocusContainer", - "notebookActions.insertCodeCellBelow", - "notebookActions.insertCodeCellBelowAndFocusContainer", - "notebookActions.insertMarkdownCellAbove", - "notebookActions.insertMarkdownCellBelow", - "notebookActions.insertCodeCellAtTop", - "notebookActions.insertMarkdownCellAtTop", - "notebookActions.menu.insertCode", - "notebookActions.menu.insertCode.tooltip", - "notebookActions.menu.insertCode.minimalToolbar", - "notebookActions.menu.insertCode.tooltip", - "notebookActions.menu.insertCode.ontoolbar", - "notebookActions.menu.insertCode.tooltip", - "notebookActions.menu.insertCode", - "notebookActions.menu.insertCode.tooltip", - "notebookActions.menu.insertCode.minimaltoolbar", - "notebookActions.menu.insertCode.tooltip", - "notebookActions.menu.insertMarkdown", - "notebookActions.menu.insertMarkdown.tooltip", - "notebookActions.menu.insertMarkdown.ontoolbar", - "notebookActions.menu.insertMarkdown.tooltip", - "notebookActions.menu.insertMarkdown", - "notebookActions.menu.insertMarkdown.tooltip" - ], "vs/workbench/contrib/notebook/browser/controller/executeActions": [ "notebookActions.renderMarkdown", "notebookActions.executeNotebook", @@ -9128,8 +9307,31 @@ "revealLastFailedCell", "revealLastFailedCellShort" ], - "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ - "notebookActions.copyOutput" + "vs/workbench/contrib/notebook/browser/controller/insertCellActions": [ + "notebookActions.insertCodeCellAbove", + "notebookActions.insertCodeCellAboveAndFocusContainer", + "notebookActions.insertCodeCellBelow", + "notebookActions.insertCodeCellBelowAndFocusContainer", + "notebookActions.insertMarkdownCellAbove", + "notebookActions.insertMarkdownCellBelow", + "notebookActions.insertCodeCellAtTop", + "notebookActions.insertMarkdownCellAtTop", + "notebookActions.menu.insertCode", + "notebookActions.menu.insertCode.tooltip", + "notebookActions.menu.insertCode.minimalToolbar", + "notebookActions.menu.insertCode.tooltip", + "notebookActions.menu.insertCode.ontoolbar", + "notebookActions.menu.insertCode.tooltip", + "notebookActions.menu.insertCode", + "notebookActions.menu.insertCode.tooltip", + "notebookActions.menu.insertCode.minimaltoolbar", + "notebookActions.menu.insertCode.tooltip", + "notebookActions.menu.insertMarkdown", + "notebookActions.menu.insertMarkdown.tooltip", + "notebookActions.menu.insertMarkdown.ontoolbar", + "notebookActions.menu.insertMarkdown.tooltip", + "notebookActions.menu.insertMarkdown", + "notebookActions.menu.insertMarkdown.tooltip" ], "vs/workbench/contrib/notebook/browser/controller/layoutActions": [ "workbench.notebook.layout.select.label", @@ -9165,23 +9367,14 @@ "detectLanguage", "noDetection" ], + "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ + "notebookActions.copyOutput" + ], "vs/workbench/contrib/notebook/browser/controller/foldingController": [ "fold.cell", "unfold.cell", "fold.cell" ], - "vs/workbench/contrib/notebook/browser/contrib/format/formatting": [ - "format.title", - "label", - "formatCell.label", - "formatCells.label" - ], - "vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted": [ - "workbench.notebook.layout.gettingStarted.label" - ], - "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions": [ - "notebook.toggleCellToolbarPosition" - ], "vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard": [ "notebookActions.copy", "notebookActions.cut", @@ -9193,19 +9386,11 @@ "notebookActions.hideFind", "notebookActions.findInNotebook" ], - "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow": [ - "cursorMoveDown", - "cursorMoveUp", - "focusFirstCell", - "focusLastCell", - "focusOutput", - "focusOutputOut", - "notebookActions.centerActiveCell", - "cursorPageUp", - "cursorPageUpSelect", - "cursorPageDown", - "cursorPageDownSelect", - "notebook.navigation.allowNavigateToSurroundingCells" + "vs/workbench/contrib/notebook/browser/contrib/format/formatting": [ + "format.title", + "label", + "formatCell.label", + "formatCells.label" ], "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants": [ "notebookFormatSave.formatting", @@ -9223,6 +9408,26 @@ }, "codeAction.apply" ], + "vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted": [ + "workbench.notebook.layout.gettingStarted.label" + ], + "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions": [ + "notebook.toggleCellToolbarPosition" + ], + "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow": [ + "cursorMoveDown", + "cursorMoveUp", + "focusFirstCell", + "focusLastCell", + "focusOutput", + "focusOutputOut", + "notebookActions.centerActiveCell", + "cursorPageUp", + "cursorPageUpSelect", + "cursorPageDown", + "cursorPageDownSelect", + "notebook.navigation.allowNavigateToSurroundingCells" + ], "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ "outline.showCodeCells", "breadcrumbs.showCodeCells", @@ -9254,11 +9459,6 @@ "notebook.multiActiveCellIndicator", "notebook.singleActiveCellIndicator" ], - "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ - "workbench.notebook.toggleLayoutTroubleshoot", - "workbench.notebook.inspectLayout", - "workbench.notebook.clearNotebookEdtitorTypeCache" - ], "vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands": [ "notebookActions.moveCellUp", "notebookActions.moveCellDown", @@ -9295,125 +9495,10 @@ "notebook.diff.ignoreMetadata", "notebook.diff.ignoreOutputs" ], - "vs/workbench/contrib/chat/browser/actions/chatActions": [ - "chat.category", - "quickChat", - { - "key": "actions.chat.acceptInput", - "comment": [ - "Apply input from the chat input box" - ] - }, - "interactiveSession.clearHistory.label", - "actions.interactiveSession.focus", - "interactiveSession.focusInput.label", - "interactiveSession.open", - "interactiveSession.history.label", - "interactiveSession.history.delete", - "interactiveSession.history.pick" - ], - "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ - "interactive.copyAll.label", - "interactive.copyItem.label" - ], - "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ - "interactive.submit.label", - "interactive.cancel.label" - ], - "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ - "interactive.helpful.label", - "interactive.unhelpful.label", - "interactive.insertIntoNotebook.label", - "chat.remove.label" - ], - "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ - "chat.openInChatView.label", - "chat.closeQuickChat.label", - "quickChat", - "interactiveSession.open" - ], - "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ - "interactive.copyCodeBlock.label", - "interactive.insertCodeBlock.label", - "interactive.insertIntoNewFile.label", - "interactive.runInTerminal.label", - "interactive.nextCodeBlock.label", - "interactive.previousCodeBlock.label" - ], - "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ - "chat.file.label", - "chat.export.label", - "chat.import.label" - ], - "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ - "vscode.extension.contributes.interactiveSession", - "vscode.extension.contributes.interactiveSession.id", - "vscode.extension.contributes.interactiveSession.label", - "vscode.extension.contributes.interactiveSession.icon", - "vscode.extension.contributes.interactiveSession.when", - "chat.viewContainer.label" - ], - "vs/workbench/contrib/chat/browser/chatEditorInput": [ - "chatEditorName" - ], - "vs/workbench/contrib/chat/common/chatServiceImpl": [ - "emptyResponse" - ], - "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ - "chat.openInEditor.label", - "interactiveSession.openInEditor.label", - "interactiveSession.openInSidebar.label" - ], - "vs/workbench/contrib/chat/common/chatContextKeys": [ - "interactiveSessionResponseHasProviderId", - "interactiveSessionResponseVote", - "chatResponseFiltered", - "interactiveSessionRequestInProgress", - "chatResponse", - "chatRequest", - "interactiveInputHasText", - "inInteractiveInput", - "inChat", - "hasChatProvider" - ], - "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ - "interactiveSession.clear.label", - "interactiveSession.clear.label", - "interactiveSession.clear.label" - ], - "vs/workbench/contrib/chat/common/chatViewModel": [ - "thinking" - ], - "vs/workbench/contrib/chat/common/chatSlashCommands": [ - "command", - "details", - "vscode.extension.contributes.slashes", - "invalid" - ], - "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ - "interactive.nextFileTree.label", - "interactive.previousFileTree.label" - ], - "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ - "notification.accessibleViewSrc", - "notification.accessibleView", - "clearNotification", - "clearNotification" - ], - "vs/workbench/contrib/chat/common/chatAgents": [ - "agent", - "details", - "vscode.extension.contributes.slashes", - "invalid" - ], - "vs/workbench/contrib/chat/common/chatColors": [ - "chat.requestBorder", - "chat.slashCommandBackground", - "chat.slashCommandForeground" - ], - "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ - "interactive.input.placeholderWithCommands", - "interactive.input.placeholderNoCommands" + "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ + "workbench.notebook.toggleLayoutTroubleshoot", + "workbench.notebook.inspectLayout", + "workbench.notebook.clearNotebookEdtitorTypeCache" ], "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ "welcome.1", @@ -9424,9 +9509,9 @@ "default.placeholder", "default.placeholder.history", "thinking", - "empty", - "markdownResponseMessage", "editResponseMessage", + "empty", + "empty", "err.apply", "err.discard" ], @@ -9464,7 +9549,8 @@ "mode.livePreview", "mode.preview", "mode.live", - "showDiff" + "showDiff", + "showGutterIcon" ], "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ "run", @@ -9508,6 +9594,10 @@ "expandMessage", "contractMessage" ], + "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations": [ + "startInlineChatIcon", + "toggleShowGutterIcon" + ], "vs/workbench/contrib/files/browser/fileConstants": [ "saveAs", "save", @@ -9516,47 +9606,6 @@ "removeFolderFromWorkspace", "newUntitledFile" ], - "vs/workbench/contrib/testing/browser/icons": [ - "testViewIcon", - "testingResultsIcon", - "testingRunIcon", - "testingRerunIcon", - "testingRunAllIcon", - "testingDebugAllIcon", - "testingDebugIcon", - "testingCancelIcon", - "filterIcon", - "hiddenIcon", - "testingShowAsList", - "testingShowAsTree", - "testingUpdateProfiles", - "testingRefreshTests", - "testingTurnContinuousRunOn", - "testingTurnContinuousRunOff", - "testingTurnContinuousRunIsOn", - "testingCancelRefreshTests", - "testingErrorIcon", - "testingFailedIcon", - "testingPassedIcon", - "testingQueuedIcon", - "testingSkippedIcon", - "testingUnsetIcon" - ], - "vs/workbench/contrib/testing/browser/testingDecorations": [ - "peekTestOutout", - "expected.title", - "actual.title", - "testing.gutterMsg.contextMenu", - "testing.gutterMsg.debug", - "testing.gutterMsg.run", - "run test", - "debug test", - "testing.runUsing", - "peek failure", - "reveal test", - "run all test", - "debug all test" - ], "vs/workbench/contrib/testing/browser/testingProgressUiService": [ "testProgress.runningInitial", "testProgress.running", @@ -9564,30 +9613,32 @@ "testProgress.completed", "testProgressWithSkip.completed" ], - "vs/workbench/contrib/testing/browser/testingExplorerView": [ - "defaultTestProfile", - "selectDefaultConfigs", - "configureTestProfiles", - "noResults", - "testingContinuousBadge", - "testingCountBadgePassed", - "testingCountBadgeSkipped", - "testingCountBadgeFailed", - "testingNoTest", - "testingFindExtension", - { - "key": "testing.treeElementLabelDuration", - "comment": [ - "{0} is the original label in testing.treeElementLabel, {1} is a duration" - ] - }, - { - "key": "testing.treeElementLabelOutdated", - "comment": [ - "{0} is the original label in testing.treeElementLabel" - ] - }, - "testExplorer" + "vs/workbench/contrib/testing/browser/testingOutputPeek": [ + "testing.markdownPeekError", + "testOutputTitle", + "testingOutputExpected", + "testingOutputActual", + "runNoOutputForPast", + "runNoOutput", + "close", + "testUnnamedTask", + "messageMoreLinesN", + "messageMoreLines1", + "testingPeekLabel", + "testing.showResultOutput", + "testing.showResultOutput", + "testing.reRunLastRun", + "testing.debugLastRun", + "testing.showResultOutput", + "testing.revealInExplorer", + "run test", + "debug test", + "testing.goToFile", + "testing.goToError", + "testing.goToNextMessage", + "testing.goToPreviousMessage", + "testing.openMessageInEditor", + "testing.toggleTestingPeekHistory" ], "vs/workbench/contrib/testing/browser/testingViewPaneContainer": [ "testing" @@ -9620,35 +9671,20 @@ "testing.openTesting", "testing.alwaysRevealTestOnStateChange" ], - "vs/workbench/contrib/testing/browser/testingOutputPeek": [ - "testing.markdownPeekError", - "testOutputTitle", - "testingOutputExpected", - "testingOutputActual", - "runNoOutputForPast", - "runNoOutput", - "close", - "testUnnamedTask", - "messageMoreLinesN", - "messageMoreLines1", - "testingPeekLabel", - "testing.showResultOutput", - "testing.showResultOutput", - "testing.reRunLastRun", - "testing.debugLastRun", - "testing.showResultOutput", - "testing.revealInExplorer", + "vs/workbench/contrib/testing/browser/testingDecorations": [ + "peekTestOutout", + "expected.title", + "actual.title", + "testing.gutterMsg.contextMenu", + "testing.gutterMsg.debug", + "testing.gutterMsg.run", "run test", "debug test", - "testing.goToFile", - "testing.goToError", - "testing.goToNextMessage", - "testing.goToPreviousMessage", - "testing.openMessageInEditor", - "testing.toggleTestingPeekHistory" - ], - "vs/workbench/contrib/testing/common/testingContentProvider": [ - "runNoOutout" + "testing.runUsing", + "peek failure", + "reveal test", + "run all test", + "debug all test" ], "vs/workbench/contrib/testing/common/testServiceImpl": [ "testTrust", @@ -9656,6 +9692,35 @@ "testTrust", "testError" ], + "vs/workbench/contrib/testing/common/testingContentProvider": [ + "runNoOutout" + ], + "vs/workbench/contrib/testing/browser/icons": [ + "testViewIcon", + "testingResultsIcon", + "testingRunIcon", + "testingRerunIcon", + "testingRunAllIcon", + "testingDebugAllIcon", + "testingDebugIcon", + "testingCancelIcon", + "filterIcon", + "hiddenIcon", + "testingShowAsList", + "testingShowAsTree", + "testingUpdateProfiles", + "testingRefreshTests", + "testingTurnContinuousRunOn", + "testingTurnContinuousRunOff", + "testingTurnContinuousRunIsOn", + "testingCancelRefreshTests", + "testingErrorIcon", + "testingFailedIcon", + "testingPassedIcon", + "testingQueuedIcon", + "testingSkippedIcon", + "testingUnsetIcon" + ], "vs/workbench/contrib/testing/common/testingContextKeys": [ "testing.canRefresh", "testing.isRefreshing", @@ -9680,27 +9745,6 @@ "testConfigurationUi.pick", "updateTestConfiguration" ], - "vs/workbench/contrib/logs/common/logsActions": [ - "setLogLevel", - "all", - "extensionLogs", - "loggers", - "selectlog", - "selectLogLevelFor", - "selectLogLevel", - "resetLogLevel", - "trace", - "debug", - "info", - "warn", - "err", - "off", - "default", - "openSessionLogFile", - "current", - "sessions placeholder", - "log placeholder" - ], "vs/workbench/contrib/testing/browser/testExplorerActions": [ "hideTest", "unhideTest", @@ -9753,6 +9797,27 @@ "testing.refreshTests", "testing.cancelTestRefresh" ], + "vs/workbench/contrib/logs/common/logsActions": [ + "setLogLevel", + "all", + "extensionLogs", + "loggers", + "selectlog", + "selectLogLevelFor", + "selectLogLevel", + "resetLogLevel", + "trace", + "debug", + "info", + "warn", + "err", + "off", + "default", + "openSessionLogFile", + "current", + "sessions placeholder", + "log placeholder" + ], "vs/editor/contrib/peekView/browser/peekView": [ "inReferenceSearchEditor", "label.close", @@ -9772,6 +9837,65 @@ "peekViewEditorMatchHighlight", "peekViewEditorMatchHighlightBorder" ], + "vs/workbench/contrib/testing/browser/testingExplorerView": [ + "defaultTestProfile", + "selectDefaultConfigs", + "configureTestProfiles", + "noResults", + "testingContinuousBadge", + "testingCountBadgePassed", + "testingCountBadgeSkipped", + "testingCountBadgeFailed", + "testingNoTest", + "testingFindExtension", + { + "key": "testing.treeElementLabelDuration", + "comment": [ + "{0} is the original label in testing.treeElementLabel, {1} is a duration" + ] + }, + { + "key": "testing.treeElementLabelOutdated", + "comment": [ + "{0} is the original label in testing.treeElementLabel" + ] + }, + "testExplorer" + ], + "vs/workbench/contrib/interactive/browser/interactiveEditor": [ + "interactiveInputPlaceHolder" + ], + "vs/platform/quickinput/browser/helpQuickAccess": [ + "helpPickAriaLabel" + ], + "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ + "noViewResults", + "views", + "panels", + "secondary side bar", + "terminalTitle", + "terminals", + "debugConsoles", + "channels", + "openView", + "quickOpenView" + ], + "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ + "noCommandResults", + "configure keybinding", + "askXInChat", + "commandWithCategory", + "showTriggerActions", + "clearCommandHistory", + "confirmClearMessage", + "confirmClearDetail", + { + "key": "clearButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/workbench/contrib/notebook/browser/notebookIcons": [ "selectKernelIcon", "executeIcon", @@ -9800,97 +9924,22 @@ "previousChangeIcon", "nextChangeIcon" ], - "vs/workbench/contrib/interactive/browser/interactiveEditor": [ - "interactiveInputPlaceHolder" - ], - "vs/platform/quickinput/browser/helpQuickAccess": [ - "helpPickAriaLabel" - ], - "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ - "noCommandResults", - "configure keybinding", - "askXInChat", - "commandWithCategory", - "showTriggerActions", - "clearCommandHistory", - "confirmClearMessage", - "confirmClearDetail", + "vs/workbench/contrib/files/browser/fileActions": [ + "newFile", + "newFolder", + "rename", + "delete", + "copyFile", + "pasteFile", + "download", + "upload", + "deleteButtonLabelRecycleBin", { - "key": "clearButtonLabel", + "key": "deleteButtonLabelTrash", "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ - "noViewResults", - "views", - "panels", - "secondary side bar", - "terminalTitle", - "terminals", - "debugConsoles", - "channels", - "openView", - "quickOpenView" - ], - "vs/workbench/contrib/files/browser/fileCommands": [ - "modifiedLabel", - { - "key": "genericSaveError", - "comment": [ - "{0} is the resource that failed to save and {1} the error message" - ] - }, - "retry", - "discard", - "genericRevertError", - "newFileCommand.saveLabel" - ], - "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ - "userGuide", - "staleSaveError", - "readonlySaveErrorAdmin", - "readonlySaveErrorSudo", - "readonlySaveError", - "permissionDeniedSaveError", - "permissionDeniedSaveErrorSudo", - { - "key": "genericSaveError", - "comment": [ - "{0} is the resource that failed to save and {1} the error message" - ] - }, - "learnMore", - "dontShowAgain", - "compareChanges", - "saveConflictDiffLabel", - "overwriteElevated", - "overwriteElevatedSudo", - "saveElevated", - "saveElevatedSudo", - "retry", - "discard", - "overwrite", - "overwrite", - "configure" - ], - "vs/workbench/contrib/files/browser/fileActions": [ - "newFile", - "newFolder", - "rename", - "delete", - "copyFile", - "pasteFile", - "download", - "upload", - "deleteButtonLabelRecycleBin", - { - "key": "deleteButtonLabelTrash", - "comment": [ - "&& denotes a mnemonic" - ] - }, + }, { "key": "deleteButtonLabel", "comment": [ @@ -9961,7 +10010,6 @@ "confirmOverwrite", "replaceButtonLabel", "globalCompareFile", - "toggleAutoSave", "saveAllInGroup", "closeGroup", "focusFilesExplorer", @@ -10034,10 +10082,50 @@ "setActiveEditorReadonlyInSession", "setActiveEditorWriteableInSession", "toggleActiveEditorReadonlyInSession", - "resetActiveEditorReadonlyInSession" + "resetActiveEditorReadonlyInSession", + "toggleAutoSave", + "toggleAutoSaveDescription" + ], + "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ + "userGuide", + "staleSaveError", + "readonlySaveErrorAdmin", + "readonlySaveErrorSudo", + "readonlySaveError", + "permissionDeniedSaveError", + "permissionDeniedSaveErrorSudo", + { + "key": "genericSaveError", + "comment": [ + "{0} is the resource that failed to save and {1} the error message" + ] + }, + "learnMore", + "dontShowAgain", + "compareChanges", + "saveConflictDiffLabel", + "overwriteElevated", + "overwriteElevatedSudo", + "saveElevated", + "saveElevatedSudo", + "retry", + "discard", + "overwrite", + "overwrite", + "configure" ], - "vs/workbench/contrib/files/browser/views/emptyView": [ - "noWorkspace" + "vs/workbench/contrib/files/browser/fileCommands": [ + "modifiedLabel", + { + "key": "genericSaveError", + "comment": [ + "{0} is the resource that failed to save and {1} the error message" + ] + }, + "retry", + "discard", + "genericRevertError", + "newFileCommand.saveLabel" ], "vs/workbench/contrib/files/browser/views/explorerView": [ "explorerSection", @@ -10047,12 +10135,6 @@ "collapseExplorerFolders" ], "vs/workbench/contrib/files/browser/views/openEditorsView": [ - { - "key": "openEditors", - "comment": [ - "Open is an adjective" - ] - }, "dirtyCounter", "openEditors", "flipLayout", @@ -10063,11 +10145,23 @@ "&& denotes a mnemonic" ] }, - "newUntitledFile" + "newUntitledFile", + { + "key": "openEditors", + "comment": [ + "Open is an adjective" + ] + } ], "vs/workbench/contrib/files/browser/editors/binaryFileEditor": [ "binaryFileEditor" ], + "vs/workbench/contrib/files/browser/workspaceWatcher": [ + "enospcError", + "learnMore", + "eshutdownError", + "reload" + ], "vs/editor/common/config/editorConfigurationSchema": [ "editorConfigurationTitle", "tabSize", @@ -10121,12 +10215,6 @@ "dirtyFile", "dirtyFiles" ], - "vs/workbench/contrib/files/browser/workspaceWatcher": [ - "enospcError", - "learnMore", - "eshutdownError", - "reload" - ], "vs/workbench/contrib/files/browser/editors/textFileEditor": [ "textFileEditor", "openFolder", @@ -10152,74 +10240,6 @@ "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ "default" ], - "vs/workbench/contrib/search/browser/searchActionsBase": [ - "search" - ], - "vs/workbench/contrib/search/browser/searchIcons": [ - "searchDetailsIcon", - "searchShowContextIcon", - "searchHideReplaceIcon", - "searchShowReplaceIcon", - "searchReplaceAllIcon", - "searchReplaceIcon", - "searchRemoveIcon", - "searchRefreshIcon", - "searchCollapseAllIcon", - "searchExpandAllIcon", - "searchShowAsTree", - "searchShowAsList", - "searchClearIcon", - "searchStopIcon", - "searchViewIcon", - "searchNewEditorIcon", - "searchOpenInFile" - ], - "vs/workbench/contrib/searchEditor/browser/searchEditor": [ - "moreSearch", - "searchScope.includes", - "label.includes", - "searchScope.excludes", - "label.excludes", - "runSearch", - "searchResultItem", - "searchEditor", - "textInputBoxBorder" - ], - "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ - "searchTitle.withQuery", - "searchTitle.withQuery", - "searchTitle" - ], - "vs/workbench/contrib/search/browser/patternInputWidget": [ - "defaultLabel", - "onlySearchInOpenEditors", - "useExcludesAndIgnoreFilesDescription" - ], - "vs/workbench/contrib/search/browser/searchMessage": [ - "unable to open trust", - "unable to open" - ], - "vs/workbench/browser/parts/views/viewPane": [ - "viewPaneContainerExpandedIcon", - "viewPaneContainerCollapsedIcon", - "viewToolbarAriaLabel" - ], - "vs/workbench/contrib/search/browser/searchResultsView": [ - "searchFolderMatch.other.label", - "searchFolderMatch.other.label", - "searchFileMatches", - "searchFileMatch", - "searchMatches", - "searchMatch", - "lineNumStr", - "numLinesStr", - "search", - "folderMatchAriaLabel", - "otherFilesAriaLabel", - "fileMatchAriaLabel", - "replacePreviewResultAria", - "searchResultAria" - ], "vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess": [ "cannotRunGotoLine", "gotoLineColumnLabel", @@ -10227,19 +10247,6 @@ "gotoLineLabelEmptyWithLimit", "gotoLineLabelEmpty" ], - "vs/workbench/contrib/search/browser/searchWidget": [ - "search.action.replaceAll.disabled.label", - "search.action.replaceAll.enabled.label", - "search.replace.toggle.button.title", - "label.Search", - "search.placeHolder", - "showContext", - "label.Replace", - "search.replace.placeHolder" - ], - "vs/workbench/services/search/common/queryBuilder": [ - "search.noWorkspaceWithName" - ], "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess": [ "empty", "gotoSymbol", @@ -10253,11 +10260,6 @@ "gotoSymbolQuickAccess", "gotoSymbolByCategoryQuickAccess" ], - "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ - "noSymbolResults", - "openToSide", - "openToBottom" - ], "vs/workbench/contrib/search/browser/anythingQuickAccess": [ "noAnythingResults", "recentlyOpenedSeparator", @@ -10280,6 +10282,40 @@ "closeEditor", "filePickAriaLabelDirty" ], + "vs/workbench/contrib/search/browser/searchIcons": [ + "searchDetailsIcon", + "searchShowContextIcon", + "searchHideReplaceIcon", + "searchShowReplaceIcon", + "searchReplaceAllIcon", + "searchReplaceIcon", + "searchRemoveIcon", + "searchRefreshIcon", + "searchCollapseAllIcon", + "searchExpandAllIcon", + "searchShowAsTree", + "searchShowAsList", + "searchClearIcon", + "searchStopIcon", + "searchViewIcon", + "searchNewEditorIcon", + "searchOpenInFile" + ], + "vs/workbench/contrib/search/browser/searchWidget": [ + "search.action.replaceAll.disabled.label", + "search.action.replaceAll.enabled.label", + "search.replace.toggle.button.title", + "label.Search", + "search.placeHolder", + "showContext", + "label.Replace", + "search.replace.placeHolder" + ], + "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ + "noSymbolResults", + "openToSide", + "openToBottom" + ], "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess": [ "QuickSearchSeeMoreFiles", "QuickSearchOpenInFile", @@ -10331,16 +10367,6 @@ "file.replaceAll.label", "file.replaceAll.label" ], - "vs/workbench/contrib/search/browser/searchActionsSymbol": [ - "showTriggerActions", - "showTriggerActions", - { - "key": "miGotoSymbolInWorkspace", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], "vs/workbench/contrib/search/browser/searchActionsTopBar": [ "clearSearchHistoryLabel", "CancelSearchAction.label", @@ -10351,117 +10377,182 @@ "ViewAsTreeAction.label", "ViewAsListAction.label" ], - "vs/workbench/contrib/search/browser/searchActionsTextQuickAccess": [ - "quickTextSearch" - ], - "vs/workbench/contrib/debug/browser/debugColors": [ - "debugToolBarBackground", - "debugToolBarBorder", - "debugIcon.startForeground", - "debugIcon.pauseForeground", - "debugIcon.stopForeground", - "debugIcon.disconnectForeground", - "debugIcon.restartForeground", - "debugIcon.stepOverForeground", - "debugIcon.stepIntoForeground", - "debugIcon.stepOutForeground", - "debugIcon.continueForeground", - "debugIcon.stepBackForeground" - ], - "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ - "workbench.action.debug.startDebug" - ], - "vs/workbench/contrib/debug/browser/callStackView": [ + "vs/workbench/contrib/search/browser/searchActionsSymbol": [ + "showTriggerActions", + "showTriggerActions", { - "key": "running", + "key": "miGotoSymbolInWorkspace", "comment": [ - "indicates state" + "&& denotes a mnemonic" ] - }, - "showMoreStackFrames2", + } + ], + "vs/workbench/contrib/search/browser/searchActionsTextQuickAccess": [ + "quickTextSearch" + ], + "vs/workbench/contrib/search/browser/searchActionsBase": [ + "search" + ], + "vs/workbench/contrib/searchEditor/browser/searchEditor": [ + "moreSearch", + "searchScope.includes", + "label.includes", + "searchScope.excludes", + "label.excludes", + "runSearch", + "searchResultItem", + "searchEditor", + "textInputBoxBorder" + ], + "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ + "searchTitle.withQuery", + "searchTitle.withQuery", + "searchTitle" + ], + "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ + "changes", + "change", + "multiChanges", + "multiChange", + "label.close", + "show previous change", + "show next change", { - "key": "session", + "key": "miGotoNextChange", "comment": [ - "Session is a noun" + "&& denotes a mnemonic" ] }, { - "key": "running", + "key": "miGotoPreviousChange", "comment": [ - "indicates state" + "&& denotes a mnemonic" ] }, - "restartFrame", - "loadAllStackFrames", - "showMoreAndOrigin", - "showMoreStackFrames", + "move to previous change", + "move to next change", + "editorGutterModifiedBackground", + "editorGutterAddedBackground", + "editorGutterDeletedBackground", + "minimapGutterModifiedBackground", + "minimapGutterAddedBackground", + "minimapGutterDeletedBackground", + "overviewRulerModifiedForeground", + "overviewRulerAddedForeground", + "overviewRulerDeletedForeground" + ], + "vs/workbench/contrib/scm/browser/activity": [ + "status.scm", + "scmPendingChangesBadge" + ], + "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ + "source control" + ], + "vs/workbench/contrib/scm/browser/scmViewPane": [ + "scm", + "input", + "sortAction", + "repositories", + "setListViewMode", + "setTreeViewMode", + "repositorySortByDiscoveryTime", + "repositorySortByName", + "repositorySortByPath", + "sortChangesByName", + "sortChangesByPath", + "sortChangesByStatus", + "collapse all", + "expand all", + "label.close" + ], + "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ + "scm" + ], + "vs/workbench/contrib/workspace/common/workspace": [ + "workspaceTrustEnabledCtx", + "workspaceTrustedCtx" + ], + "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ + "scmSync", + "incoming", + "outgoing", + "refresh", + "setListViewMode", + "setTreeViewMode" + ], + "vs/workbench/browser/parts/views/viewPane": [ + "viewPaneContainerExpandedIcon", + "viewPaneContainerCollapsedIcon", + "viewToolbarAriaLabel" + ], + "vs/workbench/contrib/search/browser/patternInputWidget": [ + "defaultLabel", + "onlySearchInOpenEditors", + "useExcludesAndIgnoreFilesDescription" + ], + "vs/workbench/contrib/search/browser/searchMessage": [ + "unable to open trust", + "unable to open" + ], + "vs/workbench/contrib/search/browser/searchResultsView": [ + "searchFolderMatch.other.label", + "searchFolderMatch.other.label", + "searchFileMatches", + "searchFileMatch", + "searchMatches", + "searchMatch", + "lineNumStr", + "numLinesStr", + "search", + "folderMatchAriaLabel", + "otherFilesAriaLabel", + "fileMatchAriaLabel", + "replacePreviewResultAria", + "searchResultAria" + ], + "vs/workbench/services/search/common/queryBuilder": [ + "search.noWorkspaceWithName" + ], + "vs/workbench/contrib/debug/browser/debugHover": [ { - "key": "pausedOn", + "key": "quickTip", "comment": [ - "indicates reason for program being paused" + "\"switch to editor language hover\" means to show the programming language hover widget instead of the debug hover" ] }, - "paused", + "treeAriaLabel", { + "key": "variableAriaLabel", "comment": [ - "Debug is a noun in this context, not a verb." - ], - "key": "callStackAriaLabel" - }, + "Do not translate placeholders. Placeholders are name and value of a variable." + ] + } + ], + "vs/workbench/contrib/debug/browser/exceptionWidget": [ + "debugExceptionWidgetBorder", + "debugExceptionWidgetBackground", + "exceptionThrownWithId", + "exceptionThrown", + "close" + ], + "vs/workbench/contrib/debug/common/debugModel": [ + "invalidVariableAttributes", + "startDebugFirst", + "notAvailable", { - "key": "threadAriaLabel", + "key": "pausedOn", "comment": [ - "Placeholders stand for the thread name and the thread state.For example \"Thread 1\" and \"Stopped" + "indicates reason for program being paused" ] }, - "stackFrameAriaLabel", + "paused", { "key": "running", "comment": [ "indicates state" ] }, - { - "key": "sessionLabel", - "comment": [ - "Placeholders stand for the session name and the session state. For example \"Launch Program\" and \"Running\"" - ] - }, - "showMoreStackFrames", - "collapse" - ], - "vs/workbench/contrib/debug/browser/debugCommands": [ - "debug", - "restartDebug", - "stepOverDebug", - "stepIntoDebug", - "stepIntoTargetDebug", - "stepOutDebug", - "pauseDebug", - "disconnect", - "disconnectSuspend", - "stop", - "continueDebug", - "focusSession", - "selectAndStartDebugging", - "openLaunchJson", - "startDebug", - "startWithoutDebugging", - "nextDebugConsole", - "prevDebugConsole", - "openLoadedScript", - "callStackTop", - "callStackBottom", - "callStackUp", - "callStackDown", - "selectDebugConsole", - "selectDebugSession", - "chooseLocation", - "noExecutableCode", - "jumpToCursor", - "editor.debug.action.stepIntoTargets.none", - "addConfiguration", - "addInlineBreakpoint" + "breakpointDirtydHover" ], "vs/workbench/contrib/debug/browser/breakpointsView": [ "unverifiedExceptionBreakpoint", @@ -10537,62 +10628,114 @@ "editBreakpoint", "editHitCount" ], - "vs/workbench/contrib/debug/browser/debugIcons": [ - "debugConsoleViewIcon", - "runViewIcon", - "variablesViewIcon", - "watchViewIcon", - "callStackViewIcon", - "breakpointsViewIcon", - "loadedScriptsViewIcon", - "debugBreakpoint", - "debugBreakpointDisabled", - "debugBreakpointUnverified", - "debugBreakpointFunction", - "debugBreakpointFunctionDisabled", - "debugBreakpointFunctionUnverified", - "debugBreakpointConditional", - "debugBreakpointConditionalDisabled", - "debugBreakpointConditionalUnverified", - "debugBreakpointData", - "debugBreakpointDataDisabled", - "debugBreakpointDataUnverified", - "debugBreakpointLog", - "debugBreakpointLogDisabled", - "debugBreakpointLogUnverified", - "debugBreakpointHint", - "debugBreakpointUnsupported", - "debugStackframe", - "debugStackframeFocused", - "debugGripper", - "debugRestartFrame", - "debugStop", - "debugDisconnect", - "debugRestart", - "debugStepOver", - "debugStepInto", - "debugStepOut", - "debugStepBack", - "debugPause", - "debugContinue", - "debugReverseContinue", - "debugRun", - "debugStart", - "debugConfigure", - "debugConsole", - "debugRemoveConfig", - "debugCollapseAll", - "callstackViewSession", - "debugConsoleClearAll", - "watchExpressionsRemoveAll", - "watchExpressionRemove", - "watchExpressionsAdd", - "watchExpressionsAddFuncBreakpoint", - "breakpointsRemoveAll", - "breakpointsActivate", - "debugConsoleEvaluationInput", - "debugConsoleEvaluationPrompt", - "debugInspectMemory" + "vs/workbench/contrib/debug/browser/callStackView": [ + { + "key": "running", + "comment": [ + "indicates state" + ] + }, + "showMoreStackFrames2", + { + "key": "session", + "comment": [ + "Session is a noun" + ] + }, + { + "key": "running", + "comment": [ + "indicates state" + ] + }, + "restartFrame", + "loadAllStackFrames", + "showMoreAndOrigin", + "showMoreStackFrames", + { + "key": "pausedOn", + "comment": [ + "indicates reason for program being paused" + ] + }, + "paused", + { + "comment": [ + "Debug is a noun in this context, not a verb." + ], + "key": "callStackAriaLabel" + }, + { + "key": "threadAriaLabel", + "comment": [ + "Placeholders stand for the thread name and the thread state.For example \"Thread 1\" and \"Stopped" + ] + }, + "stackFrameAriaLabel", + { + "key": "running", + "comment": [ + "indicates state" + ] + }, + { + "key": "sessionLabel", + "comment": [ + "Placeholders stand for the session name and the session state. For example \"Launch Program\" and \"Running\"" + ] + }, + "showMoreStackFrames", + "collapse" + ], + "vs/workbench/contrib/debug/browser/debugColors": [ + "debugToolBarBackground", + "debugToolBarBorder", + "debugIcon.startForeground", + "debugIcon.pauseForeground", + "debugIcon.stopForeground", + "debugIcon.disconnectForeground", + "debugIcon.restartForeground", + "debugIcon.stepOverForeground", + "debugIcon.stepIntoForeground", + "debugIcon.stepOutForeground", + "debugIcon.continueForeground", + "debugIcon.stepBackForeground" + ], + "vs/workbench/contrib/debug/browser/debugCommands": [ + "debug", + "restartDebug", + "stepOverDebug", + "stepIntoDebug", + "stepIntoTargetDebug", + "stepOutDebug", + "pauseDebug", + "disconnect", + "disconnectSuspend", + "stop", + "continueDebug", + "focusSession", + "selectAndStartDebugging", + "openLaunchJson", + "startDebug", + "startWithoutDebugging", + "nextDebugConsole", + "prevDebugConsole", + "openLoadedScript", + "callStackTop", + "callStackBottom", + "callStackUp", + "callStackDown", + "selectDebugConsole", + "selectDebugSession", + "chooseLocation", + "noExecutableCode", + "jumpToCursor", + "editor.debug.action.stepIntoTargets.none", + "addConfiguration", + "addInlineBreakpoint" + ], + "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ + "workbench.action.debug.startDebug" ], "vs/workbench/contrib/debug/browser/debugEditorActions": [ "toggleBreakpointAction", @@ -10637,9 +10780,6 @@ "&& denotes a mnemonic" ] }, - "runToCursor", - "evaluateInDebugConsole", - "addToWatch", "showDebugHover", "editor.debug.action.stepIntoTargets.notAvailable", { @@ -10650,7 +10790,87 @@ }, "goToNextBreakpoint", "goToPreviousBreakpoint", - "closeExceptionWidget" + "closeExceptionWidget", + "runToCursor", + "evaluateInDebugConsole", + "addToWatch" + ], + "vs/workbench/contrib/debug/browser/debugIcons": [ + "debugConsoleViewIcon", + "runViewIcon", + "variablesViewIcon", + "watchViewIcon", + "callStackViewIcon", + "breakpointsViewIcon", + "loadedScriptsViewIcon", + "debugBreakpoint", + "debugBreakpointDisabled", + "debugBreakpointUnverified", + "debugBreakpointFunction", + "debugBreakpointFunctionDisabled", + "debugBreakpointFunctionUnverified", + "debugBreakpointConditional", + "debugBreakpointConditionalDisabled", + "debugBreakpointConditionalUnverified", + "debugBreakpointData", + "debugBreakpointDataDisabled", + "debugBreakpointDataUnverified", + "debugBreakpointLog", + "debugBreakpointLogDisabled", + "debugBreakpointLogUnverified", + "debugBreakpointHint", + "debugBreakpointUnsupported", + "debugStackframe", + "debugStackframeFocused", + "debugGripper", + "debugRestartFrame", + "debugStop", + "debugDisconnect", + "debugRestart", + "debugStepOver", + "debugStepInto", + "debugStepOut", + "debugStepBack", + "debugPause", + "debugContinue", + "debugReverseContinue", + "debugRun", + "debugStart", + "debugConfigure", + "debugConsole", + "debugRemoveConfig", + "debugCollapseAll", + "callstackViewSession", + "debugConsoleClearAll", + "watchExpressionsRemoveAll", + "watchExpressionRemove", + "watchExpressionsAdd", + "watchExpressionsAddFuncBreakpoint", + "breakpointsRemoveAll", + "breakpointsActivate", + "debugConsoleEvaluationInput", + "debugConsoleEvaluationPrompt", + "debugInspectMemory" + ], + "vs/workbench/contrib/debug/browser/debugQuickAccess": [ + "noDebugResults", + "customizeLaunchConfig", + { + "key": "contributed", + "comment": [ + "contributed is lower case because it looks better like that in UI. Nothing preceeds it. It is a name of the grouping of debug configurations." + ] + }, + "removeLaunchConfig", + { + "key": "providerAriaLabel", + "comment": [ + "Placeholder stands for the provider label. For example \"NodeJS\"." + ] + }, + "configure", + "addConfigTo", + "addConfiguration" ], "vs/workbench/contrib/debug/browser/debugService": [ "1activeSession", @@ -10691,36 +10911,16 @@ "breakpointAdded", "breakpointRemoved" ], - "vs/workbench/contrib/debug/browser/debugQuickAccess": [ - "noDebugResults", - "customizeLaunchConfig", - { - "key": "contributed", - "comment": [ - "contributed is lower case because it looks better like that in UI. Nothing preceeds it. It is a name of the grouping of debug configurations." - ] - }, - "removeLaunchConfig", - { - "key": "providerAriaLabel", - "comment": [ - "Placeholder stands for the provider label. For example \"NodeJS\"." - ] - }, - "configure", - "addConfigTo", - "addConfiguration" + "vs/workbench/contrib/debug/browser/debugStatus": [ + "status.debug", + "debugTarget", + "selectAndStartDebug" ], "vs/workbench/contrib/debug/browser/debugToolBar": [ "notebook.moreRunActionsLabel", "stepBackDebug", "reverseContinue" ], - "vs/workbench/contrib/debug/browser/debugStatus": [ - "status.debug", - "debugTarget", - "selectAndStartDebug" - ], "vs/workbench/contrib/debug/browser/disassemblyView": [ "instructionNotAvailable", "disassemblyTableColumnLabel", @@ -10781,10 +10981,27 @@ "addWatchExpression", "removeAllWatchExpressions" ], - "vs/workbench/contrib/debug/browser/welcomeView": [ - "run", + "vs/workbench/contrib/debug/common/debugContentProvider": [ + "unable", + "canNotResolveSourceWithError", + "canNotResolveSource" + ], + "vs/workbench/contrib/debug/common/debugLifecycle": [ + "debug.debugSessionCloseConfirmationSingular", + "debug.debugSessionCloseConfirmationPlural", { - "key": "openAFileWhichCanBeDebugged", + "key": "debug.stop", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], + "vs/workbench/contrib/debug/common/disassemblyViewInput": [ + "disassemblyInputName" + ], + "vs/workbench/contrib/debug/browser/welcomeView": [ + { + "key": "openAFileWhichCanBeDebugged", "comment": [ "Please do not translate the word \"command\", it is part of our internal syntax which must not change", "{Locked=\"](command:{0})\"}" @@ -10806,25 +11023,8 @@ "{Locked=\"](command:{0})\"}" ] }, - "allDebuggersDisabled" - ], - "vs/workbench/contrib/debug/common/debugContentProvider": [ - "unable", - "canNotResolveSourceWithError", - "canNotResolveSource" - ], - "vs/workbench/contrib/debug/common/disassemblyViewInput": [ - "disassemblyInputName" - ], - "vs/workbench/contrib/debug/common/debugLifecycle": [ - "debug.debugSessionCloseConfirmationSingular", - "debug.debugSessionCloseConfirmationPlural", - { - "key": "debug.stop", - "comment": [ - "&& denotes a mnemonic" - ] - } + "allDebuggersDisabled", + "run" ], "vs/workbench/contrib/debug/browser/breakpointWidget": [ "breakpointWidgetLogMessagePlaceholder", @@ -10835,134 +11035,77 @@ "logMessage", "breakpointType" ], - "vs/workbench/contrib/scm/browser/activity": [ - "status.scm", - "scmPendingChangesBadge" - ], - "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ - "source control" - ], - "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ - "changes", - "change", - "multiChanges", - "multiChange", - "label.close", - "show previous change", - "show next change", - { - "key": "miGotoNextChange", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "miGotoPreviousChange", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "move to previous change", - "move to next change", - "editorGutterModifiedBackground", - "editorGutterAddedBackground", - "editorGutterDeletedBackground", - "minimapGutterModifiedBackground", - "minimapGutterAddedBackground", - "minimapGutterDeletedBackground", - "overviewRulerModifiedForeground", - "overviewRulerAddedForeground", - "overviewRulerDeletedForeground" - ], - "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ - "scm" - ], - "vs/workbench/contrib/workspace/common/workspace": [ - "workspaceTrustEnabledCtx", - "workspaceTrustedCtx" - ], - "vs/workbench/contrib/scm/browser/scmViewPane": [ - "scm", - "input", - "sortAction", - "repositories", - "setListViewMode", - "setTreeViewMode", - "repositorySortByDiscoveryTime", - "repositorySortByName", - "repositorySortByPath", - "sortChangesByName", - "sortChangesByPath", - "sortChangesByStatus", - "collapse all", - "expand all", - "label.close", - "scm.providerBorder" + "vs/platform/actions/browser/menuEntryActionViewItem": [ + "titleAndKb", + "titleAndKb", + "titleAndKbAndAlt" ], - "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ - "scmSync", - "incoming", - "outgoing", - "refresh", - "setListViewMode", - "setTreeViewMode" + "vs/workbench/contrib/debug/browser/debugActionViewItems": [ + "debugLaunchConfigurations", + "noConfigurations", + "addConfigTo", + "addConfiguration", + "debugSession" ], - "vs/workbench/contrib/debug/browser/exceptionWidget": [ - "debugExceptionWidgetBorder", - "debugExceptionWidgetBackground", - "exceptionThrownWithId", - "exceptionThrown", - "close" + "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ + "mergeEditor", + "merge.dev.copyState", + "mergeEditor.name", + "mergeEditor.noActiveMergeEditor", + "mergeEditor.name", + "mergeEditor.successfullyCopiedMergeEditorContents", + "merge.dev.saveContentsToFolder", + "mergeEditor.name", + "mergeEditor.noActiveMergeEditor", + "mergeEditor.selectFolderToSaveTo", + "mergeEditor.name", + "mergeEditor.successfullySavedMergeEditorContentsToFolder", + "merge.dev.loadContentsFromFolder", + "mergeEditor.selectFolderToSaveTo" ], - "vs/workbench/contrib/debug/browser/debugHover": [ - { - "key": "quickTip", - "comment": [ - "\"switch to editor language hover\" means to show the programming language hover widget instead of the debug hover" - ] - }, - "treeAriaLabel", + "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ + "title", + "layout.mixed", + "layout.column", + "showNonConflictingChanges", + "layout.showBase", + "layout.showBaseTop", + "layout.showBaseCenter", + "mergeEditor", + "openfile", + "merge.goToNextUnhandledConflict", + "merge.goToPreviousUnhandledConflict", + "merge.toggleCurrentConflictFromLeft", + "merge.toggleCurrentConflictFromRight", + "mergeEditor.compareInput1WithBase", + "mergeEditor.compareWithBase", + "mergeEditor.compareInput2WithBase", + "mergeEditor.compareWithBase", + "merge.openBaseEditor", + "merge.acceptAllInput1", + "merge.acceptAllInput2", + "mergeEditor.resetResultToBaseAndAutoMerge", + "mergeEditor.resetResultToBaseAndAutoMerge.short", + "mergeEditor.resetChoice", + "mergeEditor.acceptMerge", + "mergeEditor.acceptMerge.unhandledConflicts.message", + "mergeEditor.acceptMerge.unhandledConflicts.detail", { - "key": "variableAriaLabel", + "key": "mergeEditor.acceptMerge.unhandledConflicts.accept", "comment": [ - "Do not translate placeholders. Placeholders are name and value of a variable." + "&& denotes a mnemonic" ] } ], - "vs/workbench/contrib/debug/common/debugModel": [ - "invalidVariableAttributes", - "startDebugFirst", - "notAvailable", - { - "key": "pausedOn", - "comment": [ - "indicates reason for program being paused" - ] - }, - "paused", - { - "key": "running", - "comment": [ - "indicates state" - ] - }, - "breakpointDirtydHover" + "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ + "name" + ], + "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ + "mergeEditor" ], "vs/platform/history/browser/contextScopedHistoryWidget": [ "suggestWidgetVisible" ], - "vs/workbench/contrib/debug/browser/debugActionViewItems": [ - "debugLaunchConfigurations", - "noConfigurations", - "addConfigTo", - "addConfiguration", - "debugSession" - ], - "vs/platform/actions/browser/menuEntryActionViewItem": [ - "titleAndKb", - "titleAndKb", - "titleAndKbAndAlt" - ], "vs/workbench/contrib/debug/browser/linkDetector": [ "followForwardedLink", "followLink", @@ -10971,9 +11114,6 @@ "fileLinkMac", "fileLink" ], - "vs/workbench/contrib/debug/common/replModel": [ - "consoleCleared" - ], "vs/workbench/contrib/debug/browser/replViewer": [ "debugConsole", "replVariableAriaLabel", @@ -10986,6 +11126,9 @@ "replRawObjectAriaLabel", "replGroup" ], + "vs/workbench/contrib/debug/common/replModel": [ + "consoleCleared" + ], "vs/workbench/contrib/markers/browser/markersView": [ "showing filtered problems", "No problems filtered", @@ -11002,7 +11145,6 @@ "problems.panel.configuration.compareOrder", "problems.panel.configuration.compareOrder.severity", "problems.panel.configuration.compareOrder.position", - "markers.panel.title.problems", "markers.panel.no.problems.build", "markers.panel.no.problems.activeFile.build", "markers.panel.no.problems.filters", @@ -11039,72 +11181,46 @@ "problems.tree.aria.label.marker", "problems.tree.aria.label.marker.nosource", "problems.tree.aria.label.relatedinfo.message", - "errors.warnings.show.label" + "errors.warnings.show.label", + "markers.panel.title.problems" ], "vs/workbench/browser/parts/views/viewFilter": [ "more filters" ], - "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ - "title", - "layout.mixed", - "layout.column", - "showNonConflictingChanges", - "layout.showBase", - "layout.showBaseTop", - "layout.showBaseCenter", - "mergeEditor", - "openfile", - "merge.goToNextUnhandledConflict", - "merge.goToPreviousUnhandledConflict", - "merge.toggleCurrentConflictFromLeft", - "merge.toggleCurrentConflictFromRight", - "mergeEditor.compareInput1WithBase", - "mergeEditor.compareWithBase", - "mergeEditor.compareInput2WithBase", - "mergeEditor.compareWithBase", - "merge.openBaseEditor", - "merge.acceptAllInput1", - "merge.acceptAllInput2", - "mergeEditor.resetResultToBaseAndAutoMerge", - "mergeEditor.resetResultToBaseAndAutoMerge.short", - "mergeEditor.resetChoice", - "mergeEditor.acceptMerge", - "mergeEditor.acceptMerge.unhandledConflicts.message", - "mergeEditor.acceptMerge.unhandledConflicts.detail", - { - "key": "mergeEditor.acceptMerge.unhandledConflicts.accept", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], "vs/workbench/contrib/markers/browser/markersFileDecorations": [ "label", "tooltip.1", "tooltip.N", "markers.showOnFile" ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ - "name" + "vs/workbench/contrib/url/browser/trustedDomains": [ + "trustedDomain.manageTrustedDomain", + "trustedDomain.trustDomain", + "trustedDomain.trustAllPorts", + "trustedDomain.trustSubDomain", + "trustedDomain.trustAllDomains", + "trustedDomain.manageTrustedDomains" ], - "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ - "mergeEditor", - "merge.dev.copyState", - "mergeEditor.name", - "mergeEditor.noActiveMergeEditor", - "mergeEditor.name", - "mergeEditor.successfullyCopiedMergeEditorContents", - "merge.dev.saveContentsToFolder", - "mergeEditor.name", - "mergeEditor.noActiveMergeEditor", - "mergeEditor.selectFolderToSaveTo", - "mergeEditor.name", - "mergeEditor.successfullySavedMergeEditorContentsToFolder", - "merge.dev.loadContentsFromFolder", - "mergeEditor.selectFolderToSaveTo" - ], - "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ - "mergeEditor" + "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ + "openExternalLinkAt", + { + "key": "open", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "copy", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "configureTrustedDomains", + "comment": [ + "&& denotes a mnemonic" + ] + } ], "vs/workbench/contrib/comments/common/commentContextKeys": [ "hasCommentingRange", @@ -11117,14 +11233,6 @@ "commentController", "commentFocused" ], - "vs/workbench/contrib/url/browser/trustedDomains": [ - "trustedDomain.manageTrustedDomain", - "trustedDomain.trustDomain", - "trustedDomain.trustAllPorts", - "trustedDomain.trustSubDomain", - "trustedDomain.trustAllDomains", - "trustedDomain.manageTrustedDomains" - ], "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ "comments.nextCommentingRange", "comments.previousCommentingRange", @@ -11135,27 +11243,6 @@ "comments.expandAll", "comments.expandUnresolved" ], - "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ - "openExternalLinkAt", - { - "key": "open", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "copy", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "configureTrustedDomains", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], "vs/workbench/contrib/webviewPanel/browser/webviewCommands": [ "editor.action.webvieweditor.showFind", "editor.action.webvieweditor.hideFind", @@ -11166,11 +11253,8 @@ "vs/workbench/contrib/webviewPanel/browser/webviewEditor": [ "context.activeWebviewId" ], - "vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService": [ - "selectOpenerDefaultLabel.web", - "selectOpenerDefaultLabel", - "selectOpenerConfigureTitle", - "selectOpenerPlaceHolder" + "vs/workbench/contrib/customEditor/common/customEditor": [ + "context.customEditor" ], "vs/workbench/contrib/externalUriOpener/common/configuration": [ "externalUriOpeners", @@ -11178,148 +11262,56 @@ "externalUriOpeners.uri", "externalUriOpeners.defaultId" ], - "vs/workbench/contrib/customEditor/common/customEditor": [ - "context.customEditor" + "vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService": [ + "selectOpenerDefaultLabel.web", + "selectOpenerDefaultLabel", + "selectOpenerConfigureTitle", + "selectOpenerPlaceHolder" ], "vs/workbench/contrib/extensions/common/extensionsInput": [ "extensionsInputName" ], - "vs/workbench/contrib/extensions/common/extensionsUtils": [ - "disableOtherKeymapsConfirmation", - "yes", - "no" - ], - "vs/workbench/contrib/extensions/browser/extensionEditor": [ - "extension version", - "preRelease", - "name", - "preview", - "preview", - "builtin", - "publisher", - "install count", - "rating", - "details", - "detailstooltip", - "contributions", - "contributionstooltip", - "changelog", - "changelogtooltip", - "dependencies", - "dependenciestooltip", - "extensionpack", - "extensionpacktooltip", - "runtimeStatus", - "runtimeStatus description", - "noReadme", - "Readme title", - "extension pack", - "noReadme", - "Readme title", - "categories", - "Marketplace", - "repository", - "license", - "resources", - "Marketplace Info", - "published", - "last released", - "last updated", - "id", - "noChangelog", - "Changelog title", - "noContributions", - "noContributions", - "noDependencies", - "activation reason", - "startup", - "activation time", - "activatedBy", - "not yet activated", - "uncaught errors", - "messages", - "noStatus", - "settings", - "setting name", - "description", - "default", - "debuggers", - "debugger name", - "debugger type", - "viewContainers", - "view container id", - "view container title", - "view container location", - "views", - "view id", - "view name", - "view location", - "localizations", - "localizations language id", - "localizations language name", - "localizations localized language name", - "customEditors", - "customEditors view type", - "customEditors priority", - "customEditors filenamePattern", - "codeActions", - "codeActions.title", - "codeActions.kind", - "codeActions.description", - "codeActions.languages", - "authentication", - "authentication.label", - "authentication.id", - "colorThemes", - "iconThemes", - "productThemes", - "colors", - "colorId", - "description", - "defaultDark", - "defaultLight", - "defaultHC", - "JSON Validation", - "fileMatch", - "schema", - "commands", - "command name", - "command title", - "keyboard shortcuts", - "menuContexts", - "languages", - "language id", - "language name", - "file extensions", - "grammar", - "snippets", - "activation events", - "Notebooks", - "Notebook id", - "Notebook name", - "NotebookRenderers", - "Notebook renderer name", - "Notebook mimetypes", - "find", - "find next", - "find previous" - ], - "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ - "app.extensions.json.title", - "app.extensions.json.recommendations", - "app.extension.identifier.errorMessage", - "app.extensions.json.unwantedRecommendations", - "app.extension.identifier.errorMessage" + "vs/workbench/contrib/extensions/browser/extensionsViews": [ + "extensions", + "offline error", + "error", + "no extensions found", + "suggestProxyError", + "open user settings", + "no local extensions", + "extension.arialabel.verifiedPublisher", + "extension.arialabel.publisher", + "extension.arialabel.deprecated", + "extension.arialabel.rating" ], - "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ - "activation" + "vs/workbench/contrib/extensions/browser/extensionsIcons": [ + "extensionsViewIcon", + "manageExtensionIcon", + "clearSearchResultsIcon", + "refreshIcon", + "filterIcon", + "installLocalInRemoteIcon", + "installWorkspaceRecommendedIcon", + "configureRecommendedIcon", + "syncEnabledIcon", + "syncIgnoredIcon", + "remoteIcon", + "installCountIcon", + "ratingIcon", + "verifiedPublisher", + "preReleaseIcon", + "sponsorIcon", + "starFullIcon", + "starHalfIcon", + "starEmptyIcon", + "errorIcon", + "warningIcon", + "infoIcon", + "trustIcon", + "activationtimeIcon" ], - "vs/workbench/contrib/extensions/browser/extensionsDependencyChecker": [ - "extensions", - "auto install missing deps", - "finished installing missing deps", - "reload", - "no missing deps" + "vs/platform/dnd/browser/dnd": [ + "fileTooLarge" ], "vs/workbench/contrib/extensions/browser/extensionsActions": [ "VS Code for Web", @@ -11501,12 +11493,202 @@ "extensionButtonProminentForeground", "extensionButtonProminentHoverBackground" ], - "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ - "type", - "searchFor", - "install", - "manage" - ], + "vs/workbench/contrib/terminal/browser/terminal.contribution": [ + "tasksQuickAccessPlaceholder", + "tasksQuickAccessHelp", + { + "key": "miToggleIntegratedTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "terminal", + "terminal" + ], + "vs/workbench/contrib/terminal/browser/terminalView": [ + "terminal.useMonospace", + "terminal.monospaceOnly", + "terminals", + "terminalConnectingLabel" + ], + "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ + "workbench.action.terminal.focusAccessibleBuffer", + "workbench.action.terminal.accessibleBufferGoToNextCommand", + "workbench.action.terminal.accessibleBufferGoToPreviousCommand" + ], + "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ + "workbench.action.terminal.showTextureAtlas", + "workbench.action.terminal.writeDataToTerminal", + "workbench.action.terminal.writeDataToTerminal.prompt", + "workbench.action.terminal.restartPtyHost" + ], + "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ + "workbench.action.terminal.showEnvironmentContributions", + "envChanges", + "extension", + "ScopedEnvironmentContributionInfo" + ], + "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ + "workbench.action.terminal.focusFind", + "workbench.action.terminal.hideFind", + "workbench.action.terminal.toggleFindRegex", + "workbench.action.terminal.toggleFindWholeWord", + "workbench.action.terminal.toggleFindCaseSensitive", + "workbench.action.terminal.findNext", + "workbench.action.terminal.findPrevious", + "workbench.action.terminal.searchWorkspace" + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ + "workbench.action.terminal.openDetectedLink", + "workbench.action.terminal.openLastUrlLink", + "workbench.action.terminal.openLastLocalFileLink" + ], + "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ + "workbench.action.terminal.showQuickFixes" + ], + "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ + "app.extensions.json.title", + "app.extensions.json.recommendations", + "app.extension.identifier.errorMessage", + "app.extensions.json.unwantedRecommendations", + "app.extension.identifier.errorMessage" + ], + "vs/workbench/contrib/extensions/browser/extensionEditor": [ + "extension version", + "preRelease", + "name", + "preview", + "preview", + "builtin", + "publisher", + "install count", + "rating", + "details", + "detailstooltip", + "contributions", + "contributionstooltip", + "changelog", + "changelogtooltip", + "dependencies", + "dependenciestooltip", + "extensionpack", + "extensionpacktooltip", + "runtimeStatus", + "runtimeStatus description", + "noReadme", + "Readme title", + "extension pack", + "noReadme", + "Readme title", + "categories", + "Marketplace", + "repository", + "license", + "resources", + "Marketplace Info", + "published", + "last released", + "last updated", + "id", + "noChangelog", + "Changelog title", + "noContributions", + "noContributions", + "noDependencies", + "activation reason", + "startup", + "activation time", + "activatedBy", + "not yet activated", + "uncaught errors", + "messages", + "noStatus", + "settings", + "setting name", + "description", + "default", + "debuggers", + "debugger name", + "debugger type", + "viewContainers", + "view container id", + "view container title", + "view container location", + "views", + "view id", + "view name", + "view location", + "localizations", + "localizations language id", + "localizations language name", + "localizations localized language name", + "customEditors", + "customEditors view type", + "customEditors priority", + "customEditors filenamePattern", + "codeActions", + "codeActions.title", + "codeActions.kind", + "codeActions.description", + "codeActions.languages", + "authentication", + "authentication.label", + "authentication.id", + "colorThemes", + "iconThemes", + "productThemes", + "colors", + "colorId", + "description", + "defaultDark", + "defaultLight", + "defaultHC", + "JSON Validation", + "fileMatch", + "schema", + "commands", + "command name", + "command title", + "keyboard shortcuts", + "menuContexts", + "languages", + "language id", + "language name", + "file extensions", + "grammar", + "snippets", + "activation events", + "Notebooks", + "Notebook id", + "Notebook name", + "NotebookRenderers", + "Notebook renderer name", + "Notebook mimetypes", + "find", + "find next", + "find previous" + ], + "vs/workbench/contrib/extensions/common/extensionsUtils": [ + "disableOtherKeymapsConfirmation", + "yes", + "no" + ], + "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ + "activation" + ], + "vs/workbench/contrib/extensions/browser/extensionsDependencyChecker": [ + "extensions", + "auto install missing deps", + "finished installing missing deps", + "reload", + "no missing deps" + ], + "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ + "type", + "searchFor", + "install", + "manage" + ], "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService": [ "neverShowAgain", "ignoreExtensionRecommendations", @@ -11533,9 +11715,6 @@ "install and do no sync", "show recommendations" ], - "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ - "restartExtensionHost.reason" - ], "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ "Manifest is not found", "postUninstallTooltip", @@ -11558,31 +11737,13 @@ "twoDependentsError", "multipleDependentsError" ], - "vs/workbench/contrib/extensions/browser/extensionsIcons": [ - "extensionsViewIcon", - "manageExtensionIcon", - "clearSearchResultsIcon", - "refreshIcon", - "filterIcon", - "installLocalInRemoteIcon", - "installWorkspaceRecommendedIcon", - "configureRecommendedIcon", - "syncEnabledIcon", - "syncIgnoredIcon", - "remoteIcon", - "installCountIcon", - "ratingIcon", - "verifiedPublisher", - "preReleaseIcon", - "sponsorIcon", - "starFullIcon", - "starHalfIcon", - "starEmptyIcon", - "errorIcon", - "warningIcon", - "infoIcon", - "trustIcon", - "activationtimeIcon" + "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ + "exampleExtension" + ], + "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ + "deprecated extensions", + "showDeprecated", + "neverShowAgain" ], "vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor": [ { @@ -11635,88 +11796,6 @@ "disable", "showRuntimeExtensions" ], - "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ - "exampleExtension" - ], - "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ - "deprecated extensions", - "showDeprecated", - "neverShowAgain" - ], - "vs/workbench/contrib/extensions/browser/extensionsViews": [ - "extensions", - "offline error", - "error", - "no extensions found", - "suggestProxyError", - "open user settings", - "no local extensions", - "extension.arialabel.verifiedPublisher", - "extension.arialabel.publisher", - "extension.arialabel.deprecated", - "extension.arialabel.rating" - ], - "vs/platform/dnd/browser/dnd": [ - "fileTooLarge" - ], - "vs/workbench/contrib/terminal/browser/terminal.contribution": [ - "tasksQuickAccessPlaceholder", - "tasksQuickAccessHelp", - "terminal", - "terminal", - { - "key": "miToggleIntegratedTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ - "workbench.action.terminal.showTextureAtlas", - "workbench.action.terminal.writeDataToTerminal", - "workbench.action.terminal.writeDataToTerminal.prompt", - "workbench.action.terminal.restartPtyHost" - ], - "vs/workbench/contrib/terminal/browser/terminalView": [ - "terminal.useMonospace", - "terminal.monospaceOnly", - "terminals", - "terminalConnectingLabel" - ], - "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ - "workbench.action.terminal.focusAccessibleBuffer", - "workbench.action.terminal.accessibleBufferGoToNextCommand", - "workbench.action.terminal.accessibleBufferGoToPreviousCommand" - ], - "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ - "workbench.action.terminal.showEnvironmentContributions", - "envChanges", - "extension", - "ScopedEnvironmentContributionInfo" - ], - "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ - "workbench.action.terminal.openDetectedLink", - "workbench.action.terminal.openLastUrlLink", - "workbench.action.terminal.openLastLocalFileLink" - ], - "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ - "workbench.action.terminal.focusFind", - "workbench.action.terminal.hideFind", - "workbench.action.terminal.toggleFindRegex", - "workbench.action.terminal.toggleFindWholeWord", - "workbench.action.terminal.toggleFindCaseSensitive", - "workbench.action.terminal.findNext", - "workbench.action.terminal.findPrevious", - "workbench.action.terminal.searchWorkspace" - ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ - "workbench.action.terminal.showQuickFixes" - ], - "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ - "workbench.action.tasks.manageAutomaticRunning", - "workbench.action.tasks.allowAutomaticTasks", - "workbench.action.tasks.disallowAutomaticTasks" - ], "vs/workbench/contrib/tasks/common/problemMatcher": [ "ProblemPatternParser.problemPattern.missingRegExp", "ProblemPatternParser.loopProperty.notLast", @@ -11787,6 +11866,11 @@ "eslint-stylish", "go" ], + "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ + "workbench.action.tasks.manageAutomaticRunning", + "workbench.action.tasks.allowAutomaticTasks", + "workbench.action.tasks.disallowAutomaticTasks" + ], "vs/workbench/contrib/tasks/common/jsonSchema_v2": [ "JsonSchema.shell", "JsonSchema.tasks.isShellCommand.deprecated", @@ -11871,16 +11955,8 @@ "JsonSchema.mac", "JsonSchema.linux" ], - "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ - "noTaskResults", - "TaskService.pickRunTask" - ], - "vs/workbench/contrib/tasks/common/taskDefinitionRegistry": [ - "TaskDefinition.description", - "TaskDefinition.properties", - "TaskDefinition.when", - "TaskTypeConfiguration.noType", - "TaskDefinitionExtPoint" + "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ + "restartExtensionHost.reason" ], "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ "JsonSchema.version.deprecated", @@ -11892,25 +11968,27 @@ "JsonSchema.linux", "JsonSchema.shell" ], + "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ + "noTaskResults", + "TaskService.pickRunTask" + ], + "vs/workbench/contrib/tasks/common/taskDefinitionRegistry": [ + "TaskDefinition.description", + "TaskDefinition.properties", + "TaskDefinition.when", + "TaskTypeConfiguration.noType", + "TaskDefinitionExtPoint" + ], "vs/workbench/contrib/remote/browser/tunnelFactory": [ "tunnelPrivacy.private", "tunnelPrivacy.public" ], "vs/workbench/contrib/remote/browser/remote": [ - "getStartedWalkthrough.id", - "RemoteHelpInformationExtPoint", - "RemoteHelpInformationExtPoint.getStarted", - "RemoteHelpInformationExtPoint.documentation", - "RemoteHelpInformationExtPoint.feedback", - "RemoteHelpInformationExtPoint.feedback.deprecated", - "RemoteHelpInformationExtPoint.reportIssue", - "RemoteHelpInformationExtPoint.issues", "remote.help.getStarted", "remote.help.documentation", "remote.help.issues", "remote.help.report", "pickRemoteExtension", - "remote.help", "remotehelp", "remote.explorer", "remote.explorer", @@ -11926,16 +12004,8 @@ "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ - "expandAbbreviationAction", - { - "key": "miEmmetExpandAbbreviation", - "comment": [ - "&& denotes a mnemonic" - ] - } + }, + "remote.help" ], "vs/workbench/contrib/remote/browser/remoteIndicator": [ "remote.category", @@ -11982,97 +12052,15 @@ "remoteActions", "remote.startActions.installingExtension" ], - "vs/workbench/contrib/format/browser/formatActionsNone": [ - "formatDocument.label.multiple", - "too.large", - "no.provider", + "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ + "expandAbbreviationAction", { - "key": "install.formatter", + "key": "miEmmetExpandAbbreviation", "comment": [ "&& denotes a mnemonic" ] } ], - "vs/workbench/contrib/format/browser/formatModified": [ - "formatChanges" - ], - "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ - "global.scope", - "global.1", - "detail.label", - "name", - "bad_name1", - "bad_name2", - "bad_name3", - "openSnippet.label", - "userSnippets", - { - "key": "miOpenSnippets", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "new.global_scope", - "new.global", - "new.workspace_scope", - "new.folder", - "group.global", - "new.global.sep", - "new.global.sep", - "openSnippet.pickLanguage" - ], - "vs/workbench/contrib/format/browser/formatActionsMultiple": [ - "null", - "nullFormatterDescription", - "miss", - "config.needed", - "config.bad", - "miss.1", - { - "key": "do.config", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "do.config.notification", - "select", - "do.config.command", - "summary", - "formatter", - "formatter.default", - "def", - "config", - "format.placeHolder", - "select", - "formatDocument.label.multiple", - "formatSelection.label.multiple" - ], - "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ - "label", - "placeholder" - ], - "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ - "label" - ], - "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ - "snippet.suggestions.label" - ], - "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ - "codeAction", - "overflow.start.title", - "title" - ], - "vs/workbench/contrib/snippets/browser/snippetsService": [ - "invalid.path.0", - "invalid.language.0", - "invalid.language", - "invalid.path.1", - "vscode.extension.contributes.snippets", - "vscode.extension.contributes.snippets-language", - "vscode.extension.contributes.snippets-path", - "badVariableUse", - "badFile" - ], "vs/workbench/contrib/codeEditor/browser/accessibility/accessibility": [ "toggleScreenReaderMode" ], @@ -12102,11 +12090,6 @@ "inspectEditorTokens", "inspectTMScopesWidget.loading" ], - "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ - "gotoLine", - "gotoLineQuickAccessPlaceholder", - "gotoLineQuickAccess" - ], "vs/workbench/contrib/codeEditor/browser/saveParticipants": [ { "key": "formatting2", @@ -12141,14 +12124,10 @@ ] } ], - "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ - "toggleRenderWhitespace", - { - "key": "miToggleRenderWhitespace", - "comment": [ - "&& denotes a mnemonic" - ] - } + "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ + "gotoLine", + "gotoLineQuickAccessPlaceholder", + "gotoLineQuickAccess" ], "vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier": [ "toggleLocation", @@ -12165,6 +12144,15 @@ ] } ], + "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ + "toggleRenderWhitespace", + { + "key": "miToggleRenderWhitespace", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/workbench/contrib/codeEditor/browser/toggleWordWrap": [ "editorWordWrap", "toggle.wordwrap", @@ -12196,46 +12184,137 @@ "defaultHintAriaLabel", "disableHint" ], - "vs/workbench/contrib/update/browser/update": [ - "update.noReleaseNotesOnline", - "read the release notes", - "releaseNotes", - "update service disabled", - "learn more", - "updateIsReady", - "checkingForUpdates", - "downloading", - "updating", - "update service", - "noUpdatesAvailable", - "thereIsUpdateAvailable", - "download update", - "later", - "releaseNotes", - "updateAvailable", - "installUpdate", - "later", - "releaseNotes", - "updateNow", - "later", - "releaseNotes", - "updateAvailableAfterRestart", - "checkForUpdates", - "checkingForUpdates", - "download update_1", - "DownloadingUpdate", - "installUpdate...", - "installingUpdate", - "showUpdateReleaseNotes", - "restartToUpdate", - "switchToInsiders", - "switchToStable", - "relaunchMessage", - "relaunchDetailInsiders", - "relaunchDetailStable", - { - "key": "reload", - "comment": [ + "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ + "global.scope", + "global.1", + "detail.label", + "name", + "bad_name1", + "bad_name2", + "bad_name3", + "openSnippet.label", + "userSnippets", + { + "key": "miOpenSnippets", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "new.global_scope", + "new.global", + "new.workspace_scope", + "new.folder", + "group.global", + "new.global.sep", + "new.global.sep", + "openSnippet.pickLanguage" + ], + "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ + "label", + "placeholder" + ], + "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ + "snippet.suggestions.label" + ], + "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ + "label" + ], + "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ + "codeAction", + "overflow.start.title", + "title" + ], + "vs/workbench/contrib/snippets/browser/snippetsService": [ + "invalid.path.0", + "invalid.language.0", + "invalid.language", + "invalid.path.1", + "vscode.extension.contributes.snippets", + "vscode.extension.contributes.snippets-language", + "vscode.extension.contributes.snippets-path", + "badVariableUse", + "badFile" + ], + "vs/workbench/contrib/format/browser/formatActionsNone": [ + "formatDocument.label.multiple", + "too.large", + "no.provider", + { + "key": "install.formatter", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], + "vs/workbench/contrib/format/browser/formatActionsMultiple": [ + "null", + "nullFormatterDescription", + "miss", + "config.needed", + "config.bad", + "miss.1", + { + "key": "do.config", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "do.config.notification", + "select", + "do.config.command", + "summary", + "formatter", + "formatter.default", + "def", + "config", + "format.placeHolder", + "select", + "formatDocument.label.multiple", + "formatSelection.label.multiple" + ], + "vs/workbench/contrib/format/browser/formatModified": [ + "formatChanges" + ], + "vs/workbench/contrib/update/browser/update": [ + "update.noReleaseNotesOnline", + "read the release notes", + "releaseNotes", + "update service disabled", + "learn more", + "updateIsReady", + "checkingForUpdates", + "downloading", + "updating", + "update service", + "noUpdatesAvailable", + "thereIsUpdateAvailable", + "download update", + "later", + "releaseNotes", + "updateAvailable", + "installUpdate", + "later", + "releaseNotes", + "updateNow", + "later", + "releaseNotes", + "updateAvailableAfterRestart", + "checkForUpdates", + "checkingForUpdates", + "download update_1", + "DownloadingUpdate", + "installUpdate...", + "installingUpdate", + "showUpdateReleaseNotes", + "restartToUpdate", + "switchToInsiders", + "switchToStable", + "relaunchMessage", + "relaunchDetailInsiders", + "relaunchDetailStable", + { + "key": "reload", + "comment": [ "&& denotes a mnemonic" ] }, @@ -12261,19 +12340,6 @@ "welcome.displayName", "startupPage.markdownPreviewError" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ - "gettingStartedUnchecked", - "gettingStartedChecked" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ - "builtin", - "developer", - "resetWelcomePageWalkthroughProgress" - ], - "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ - "walkThrough.unboundCommand", - "walkThrough.gitNotFound" - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted": [ "welcomeAriaLabel", "pickWalkthroughs", @@ -12322,6 +12388,19 @@ ] } ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ + "builtin", + "developer", + "resetWelcomePageWalkthroughProgress" + ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ + "gettingStartedUnchecked", + "gettingStartedChecked" + ], + "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ + "walkThrough.unboundCommand", + "walkThrough.gitNotFound" + ], "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ "editorWalkThrough.title", "editorWalkThrough" @@ -12339,11 +12418,12 @@ "contributes.viewsWelcome.view.group", "contributes.viewsWelcome.view.enablement" ], - "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree": [ - "title.template", - "1.problem", - "N.problem", - "deep.problem" + "vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek": [ + "callFrom", + "callsTo", + "title.loading", + "empt.callsFrom", + "empt.callsTo" ], "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek": [ "supertypes", @@ -12352,12 +12432,11 @@ "empt.supertypes", "empt.subtypes" ], - "vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek": [ - "callFrom", - "callsTo", - "title.loading", - "empt.callsFrom", - "empt.callsTo" + "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree": [ + "title.template", + "1.problem", + "N.problem", + "deep.problem" ], "vs/workbench/contrib/outline/browser/outlinePane": [ "no-editor", @@ -12373,42 +12452,6 @@ "sortByName", "sortByKind" ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ - "create temporary profile", - "rename profile", - "select profile to rename", - "profileExists", - "current", - "rename specific profile", - "pick profile to rename", - "mange", - "cleanup profile", - "reset workspaces" - ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ - "profiles", - "switchProfile", - "selectProfile", - "edit profile", - "show profile contents", - "export profile", - "export profile in share", - "import profile", - "import from url", - "import from file", - "templates", - "import profile quick pick title", - "import profile placeholder", - "profile import error", - "import profile dialog", - "import profile share", - "save profile as", - "create profile", - "delete profile", - "current", - "delete specific profile", - "pick profile to delete" - ], "vs/workbench/contrib/userDataSync/browser/userDataSync": [ "stop sync", "configure sync", @@ -12498,41 +12541,41 @@ "download sync activity complete", "workbench.actions.syncData.reset" ], - "vs/workbench/contrib/editSessions/common/editSessions": [ - "cloud changes", - "cloud changes", - "editSessionViewIcon" + "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ + "profiles", + "switchProfile", + "selectProfile", + "edit profile", + "show profile contents", + "export profile", + "export profile in share", + "import profile", + "import from url", + "import from file", + "templates", + "import profile quick pick title", + "import profile placeholder", + "profile import error", + "import profile dialog", + "import profile share", + "save profile as", + "create profile", + "delete profile", + "current", + "delete specific profile", + "pick profile to delete" ], - "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ - "cloudChangesLog" - ], - "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ - "choose account read placeholder", - "choose account placeholder", - "signed in", - "others", - "sign in using account", - "sign in", - "sign in badge", - "reset auth.v3", - "sign out of cloud changes clear data prompt", - "delete all cloud changes" - ], - "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ - "noStoredChanges", - "storeWorkingChangesTitle", - "workbench.editSessions.actions.resume.v2", - "workbench.editSessions.actions.store.v2", - "workbench.editSessions.actions.delete.v2", - "confirm delete.v2", - "confirm delete detail.v2", - "workbench.editSessions.actions.deleteAll", - "confirm delete all", - "confirm delete all detail", - "compare changes", - "local copy", - "cloud changes", - "open file" + "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ + "create temporary profile", + "rename profile", + "select profile to rename", + "profileExists", + "current", + "rename specific profile", + "pick profile to rename", + "mange", + "cleanup profile", + "reset workspaces" ], "vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint": [ "contributes.codeActions", @@ -12562,7 +12605,6 @@ "vs/workbench/contrib/timeline/browser/timelinePane": [ "timeline.loadingMore", "timeline.loadMore", - "timeline", "timeline.editorCannotProvideTimeline", "timeline.noTimelineSourcesEnabled", "timeline.noLocalHistoryYet", @@ -12581,6 +12623,7 @@ "timeline.toggleFollowActiveEditorCommand.follow", "timeline", "timeline.toggleFollowActiveEditorCommand.unfollow", + "timeline", "timeline" ], "vs/workbench/contrib/localHistory/browser/localHistoryTimeline": [ @@ -12636,14 +12679,41 @@ "localHistoryCompareToFileEditorLabel", "localHistoryCompareToPreviousEditorLabel" ], - "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ - "workspaceTrustEditorInputName" + "vs/workbench/contrib/editSessions/common/editSessions": [ + "cloud changes", + "editSessionViewIcon", + "cloud changes" ], - "vs/workbench/contrib/audioCues/browser/commands": [ - "audioCues.help", - "disabled", - "audioCues.help.settings", - "audioCues.help.placeholder" + "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ + "cloudChangesLog" + ], + "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ + "choose account read placeholder", + "choose account placeholder", + "signed in", + "others", + "sign in using account", + "sign in", + "sign in badge", + "reset auth.v3", + "sign out of cloud changes clear data prompt", + "delete all cloud changes" + ], + "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ + "noStoredChanges", + "storeWorkingChangesTitle", + "workbench.editSessions.actions.resume.v2", + "workbench.editSessions.actions.store.v2", + "workbench.editSessions.actions.delete.v2", + "confirm delete.v2", + "confirm delete detail.v2", + "workbench.editSessions.actions.deleteAll", + "confirm delete all", + "confirm delete all detail", + "compare changes", + "local copy", + "cloud changes", + "open file" ], "vs/workbench/contrib/workspace/browser/workspaceTrustEditor": [ "shieldIcon", @@ -12749,6 +12819,9 @@ "untrustedFolderReason", "trustedForcedReason" ], + "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ + "workspaceTrustEditorInputName" + ], "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ "accessibilityConfigurationTitle", "verbosity.terminal.description", @@ -12762,8 +12835,22 @@ "verbosity.notification", "verbosity.emptyEditorHint", "verbosity.comments", + "alert.save", + "alert.save.userGesture", + "alert.save.always", + "alert.save.never", + "alert.format", + "alert.format.userGesture", + "alert.format.always", + "alert.format.never", "dimUnfocusedEnabled", - "dimUnfocusedOpacity" + "dimUnfocusedOpacity", + "accessibility.hideAccessibleView" + ], + "vs/workbench/contrib/accessibility/browser/accessibleNotificationService": [ + "cleared", + "saved", + "formatted" ], "vs/workbench/contrib/accessibility/browser/accessibilityStatus": [ "screenReaderDetectedExplanation.question", @@ -12772,6 +12859,12 @@ "screenReaderDetected", "status.editor.screenReaderMode" ], + "vs/workbench/contrib/audioCues/browser/commands": [ + "audioCues.help", + "disabled", + "audioCues.help.settings", + "audioCues.help.placeholder" + ], "vs/workbench/contrib/share/browser/shareService": [ "shareProviderCount", "type to filter" @@ -12782,6 +12875,11 @@ "notificationsToolbar", "notificationsCenterWidgetAriaLabel" ], + "vs/workbench/browser/parts/notifications/notificationsAlerts": [ + "alertErrorMessage", + "alertWarningMessage", + "alertInfoMessage" + ], "vs/workbench/browser/parts/notifications/notificationsStatus": [ "status.notifications", "status.notifications", @@ -12817,6 +12915,10 @@ }, "status.message" ], + "vs/workbench/browser/parts/notifications/notificationsToasts": [ + "notificationAriaLabel", + "notificationWithSourceAriaLabel" + ], "vs/workbench/browser/parts/notifications/notificationsCommands": [ "notifications", "showNotifications", @@ -12826,15 +12928,6 @@ "toggleDoNotDisturbMode", "focusNotificationToasts" ], - "vs/workbench/browser/parts/notifications/notificationsToasts": [ - "notificationAriaLabel", - "notificationWithSourceAriaLabel" - ], - "vs/workbench/browser/parts/notifications/notificationsAlerts": [ - "alertErrorMessage", - "alertWarningMessage", - "alertInfoMessage" - ], "vs/workbench/services/configuration/common/configurationEditing": [ "fsError", "openTasksConfiguration", @@ -12879,9 +12972,6 @@ "workspaceTarget", "folderTarget" ], - "vs/workbench/common/editor/textEditorModel": [ - "languageAutoDetected" - ], "vs/workbench/services/textfile/common/textFileEditorModelManager": [ { "key": "genericSaveError", @@ -12890,6 +12980,9 @@ ] } ], + "vs/workbench/common/editor/textEditorModel": [ + "languageAutoDetected" + ], "vs/workbench/browser/parts/titlebar/titlebarPart": [ "focusTitleBar", "toggle.commandCenter", @@ -12915,24 +13008,25 @@ "vs/workbench/services/workingCopy/common/workingCopyHistoryTracker": [ "undoRedo.source" ], + "vs/workbench/services/extensions/common/extensionHostManager": [ + "measureExtHostLatency" + ], "vs/workbench/services/extensions/common/extensionsUtil": [ "overwritingExtension", "overwritingExtension", "extensionUnderDevelopment" ], - "vs/workbench/services/extensions/common/extensionHostManager": [ - "measureExtHostLatency" - ], - "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ - "reportExtensionIssue" - ], "vs/workbench/contrib/localization/common/localizationsActions": [ - "configureLocale", "chooseLocale", "installed", "available", "moreInfo", - "clearDisplayLanguage" + "clearDisplayLanguage", + "configureLocale", + "configureLocaleDescription" + ], + "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ + "reportExtensionIssue" ], "vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions": [ "cmd.reportOrShow", @@ -12946,30 +13040,27 @@ "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote": [ "workbench.action.terminal.newLocal" ], - "vs/workbench/contrib/tasks/common/taskTemplates": [ - "dotnetCore", - "msbuild", - "externalCommand", - "Maven" + "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ + "ptyHostStatus", + "ptyHostStatus.short", + "nonResponsivePtyHost", + "ptyHostStatus.ariaLabel" ], - "vs/workbench/contrib/tasks/browser/taskQuickPick": [ - "taskQuickPick.showAll", - "configureTaskIcon", - "removeTaskIcon", - "configureTask", - "contributedTasks", - "taskType", - "removeRecent", - "recentlyUsed", - "configured", - "configured", - "TaskQuickPick.changeSettingDetails", - "TaskQuickPick.changeSettingNo", - "TaskService.pickRunTask", - "TaskQuickPick.changeSettingsOptions", - "TaskQuickPick.goBack", - "TaskQuickPick.noTasksForType", - "noProviderForTask" + "vs/workbench/contrib/localHistory/browser/localHistory": [ + "localHistoryIcon", + "localHistoryRestore" + ], + "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ + "taskTerminalStatus.active", + "taskTerminalStatus.succeeded", + "taskTerminalStatus.succeededInactive", + "taskTerminalStatus.errors", + "taskTerminalStatus.errorsInactive", + "taskTerminalStatus.warnings", + "taskTerminalStatus.warningsInactive", + "taskTerminalStatus.infos", + "taskTerminalStatus.infosInactive", + "task.watchFirstError" ], "vs/workbench/contrib/tasks/common/taskConfiguration": [ "ConfigurationParser.invalidCWD", @@ -12994,27 +13085,30 @@ ] } ], - "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ - "taskTerminalStatus.active", - "taskTerminalStatus.succeeded", - "taskTerminalStatus.succeededInactive", - "taskTerminalStatus.errors", - "taskTerminalStatus.errorsInactive", - "taskTerminalStatus.warnings", - "taskTerminalStatus.warningsInactive", - "taskTerminalStatus.infos", - "taskTerminalStatus.infosInactive", - "task.watchFirstError" - ], - "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ - "ptyHostStatus", - "ptyHostStatus.short", - "nonResponsivePtyHost", - "ptyHostStatus.ariaLabel" + "vs/workbench/contrib/tasks/common/taskTemplates": [ + "dotnetCore", + "msbuild", + "externalCommand", + "Maven" ], - "vs/workbench/contrib/localHistory/browser/localHistory": [ - "localHistoryIcon", - "localHistoryRestore" + "vs/workbench/contrib/tasks/browser/taskQuickPick": [ + "taskQuickPick.showAll", + "configureTaskIcon", + "removeTaskIcon", + "configureTask", + "contributedTasks", + "taskType", + "removeRecent", + "recentlyUsed", + "configured", + "configured", + "TaskQuickPick.changeSettingDetails", + "TaskQuickPick.changeSettingNo", + "TaskService.pickRunTask", + "TaskQuickPick.changeSettingsOptions", + "TaskQuickPick.goBack", + "TaskQuickPick.noTasksForType", + "noProviderForTask" ], "vs/workbench/contrib/debug/common/abstractDebugAdapter": [ "timeout" @@ -13213,6 +13307,9 @@ "terminal.integrated.defaultProfile.osx", "terminal.integrated.defaultProfile.windows" ], + "vs/base/browser/ui/findinput/findInput": [ + "defaultLabel" + ], "vs/base/browser/ui/inputbox/inputBox": [ "alertErrorMessage", "alertWarningMessage", @@ -13225,14 +13322,6 @@ }, "clearedInput" ], - "vs/base/browser/ui/findinput/findInput": [ - "defaultLabel" - ], - "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ - "preferredcodeActionWithKb", - "codeActionWithKb", - "codeAction" - ], "vs/editor/contrib/codeAction/browser/codeActionCommands": [ "args.schema.kind", "args.schema.apply", @@ -13268,6 +13357,11 @@ "hideMoreActions", "showMoreActions" ], + "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ + "preferredcodeActionWithKb", + "codeActionWithKb", + "codeAction" + ], "vs/base/browser/ui/actionbar/actionViewItems": [ { "key": "titleLabel", @@ -13299,6 +13393,14 @@ "postDropWidgetTitle", "dropIntoEditorProgress" ], + "vs/editor/contrib/folding/browser/foldingDecorations": [ + "foldBackgroundBackground", + "editorGutter.foldingControlForeground", + "foldingExpandedIcon", + "foldingCollapsedIcon", + "foldingManualCollapedIcon", + "foldingManualExpandedIcon" + ], "vs/editor/contrib/find/browser/findWidget": [ "findSelectionIcon", "findCollapsedIcon", @@ -13328,23 +13430,6 @@ "ariaSearchNoResultWithLineNumNoCurrentMatch", "ctrlEnter.keybindingChanged" ], - "vs/editor/contrib/folding/browser/foldingDecorations": [ - "foldBackgroundBackground", - "editorGutter.foldingControlForeground", - "foldingExpandedIcon", - "foldingCollapsedIcon", - "foldingManualCollapedIcon", - "foldingManualExpandedIcon" - ], - "vs/editor/contrib/format/browser/format": [ - "hint11", - "hintn1", - "hint1n", - "hintnn" - ], - "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ - "inlineSuggestionFollows" - ], "vs/editor/contrib/inlineCompletions/browser/commands": [ "action.inlineSuggest.showNext", "action.inlineSuggest.showPrevious", @@ -13358,6 +13443,9 @@ "action.inlineSuggest.hide", "action.inlineSuggest.alwaysShowToolbar" ], + "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ + "inlineSuggestionFollows" + ], "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController": [ "showAccessibleViewHint" ], @@ -13366,11 +13454,6 @@ "labelLoading", "metaTitle.N" ], - "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ - "hasSymbols", - "location.kb", - "location" - ], "vs/editor/contrib/gotoSymbol/browser/referencesModel": [ "aria.oneReference", { @@ -13386,6 +13469,11 @@ "aria.result.n1", "aria.result.nm" ], + "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ + "hasSymbols", + "location.kb", + "location" + ], "vs/editor/contrib/message/browser/messageController": [ "messageVisible" ], @@ -13405,16 +13493,6 @@ "editorMarkerNavigationInfoHeaderBackground", "editorMarkerNavigationBackground" ], - "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ - "hint.dbl", - "links.navigate.kb.meta.mac", - "links.navigate.kb.meta", - "links.navigate.kb.alt.mac", - "links.navigate.kb.alt", - "hint.defAndCommand", - "hint.def", - "hint.cmd" - ], "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ "modesContentHover.loading", "stopped rendering", @@ -13440,6 +13518,16 @@ "previous", "next" ], + "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ + "hint.dbl", + "links.navigate.kb.meta.mac", + "links.navigate.kb.meta", + "links.navigate.kb.alt.mac", + "links.navigate.kb.alt", + "hint.defAndCommand", + "hint.def", + "hint.cmd" + ], "vs/editor/contrib/wordHighlighter/browser/highlightDecorations": [ "wordHighlight", "wordHighlightStrong", @@ -13590,30 +13678,35 @@ "expandAll" ], "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ - "comments.view.title", "commentsCount", "commentCount", "imageWithLabel", "image", "commentLine", "commentRange", - "lastReplyFrom" + "lastReplyFrom", + "comments.view.title" ], "vs/workbench/contrib/testing/common/testResult": [ "runFinished" ], - "vs/workbench/browser/parts/compositeBarActions": [ - "titleKeybinding", - "badgeTitle", - "additionalViews", - "numberBadge", - "manageExtension", - "hide", - "keep", - "hideBadge", - "showBadge", - "toggle", - "toggleBadge" + "vs/workbench/browser/parts/editor/editorDropTarget": [ + "dropIntoEditorPrompt" + ], + "vs/workbench/browser/parts/editor/editorGroupView": [ + "ariaLabelGroupActions", + "emptyEditorGroup", + "groupLabelLong", + "groupLabel", + "groupAriaLabelLong", + "groupAriaLabel" + ], + "vs/base/browser/ui/tree/treeDefaults": [ + "collapse all" + ], + "vs/workbench/browser/parts/views/checkbox": [ + "checked", + "unchecked" ], "vs/base/browser/ui/splitview/paneview": [ "viewSection" @@ -13645,7 +13738,6 @@ "tunnelPrivacy.unknown", "tunnelPrivacy.private", "tunnel.focusContext", - "remote.tunnel", "tunnelView", "remote.tunnel.label", "remote.tunnelsView.labelPlaceholder", @@ -13678,7 +13770,8 @@ "remote.tunnel.protocolHttps", "tunnelContext.privacyMenu", "tunnelContext.protocolMenu", - "portWithRunningProcess.foreground" + "portWithRunningProcess.foreground", + "remote.tunnel" ], "vs/workbench/contrib/remote/browser/remoteIcons": [ "getStartedIcon", @@ -13699,13 +13792,6 @@ "forwardedPortWithoutProcessIcon", "forwardedPortWithProcessIcon" ], - "vs/base/browser/ui/tree/treeDefaults": [ - "collapse all" - ], - "vs/workbench/browser/parts/views/checkbox": [ - "checked", - "unchecked" - ], "vs/workbench/browser/parts/editor/textCodeEditor": [ "textEditor" ], @@ -13714,145 +13800,49 @@ "binaryError", "openAnyway" ], - "vs/workbench/browser/parts/activitybar/activitybarActions": [ - "loading", - "authProviderUnavailable", - "manageTrustedExtensions", - "signOut", - "noAccounts", - "hideAccounts", - "manage", - "previousSideBarView", - "nextSideBarView", - "focusActivityBar" - ], - "vs/workbench/browser/parts/compositeBar": [ - "activityBarAriaLabel" + "vs/workbench/browser/parts/paneCompositePart": [ + "pane.emptyMessage", + "moreActions", + "views" ], - "vs/workbench/browser/parts/titlebar/menubarControl": [ - { - "key": "mFile", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mEdit", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mSelection", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mView", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mGoto", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mHelp", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mPreferences", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "menubar.customTitlebarAccessibilityNotification", - "goToSetting", - "focusMenu", + "vs/workbench/browser/parts/activitybar/activitybarPart": [ + "menu", + "hideMenu", + "activity bar position", + "positionActivityBarSide", { - "key": "checkForUpdates", + "key": "miSideActivityBar", "comment": [ "&& denotes a mnemonic" ] }, - "checkingForUpdates", + "side", + "positionActivityBarTop", { - "key": "download now", + "key": "miTopActivityBar", "comment": [ "&& denotes a mnemonic" ] }, - "DownloadingUpdate", + "top", + "hideActivityBar", { - "key": "installUpdate...", + "key": "miHideActivityBar", "comment": [ "&& denotes a mnemonic" ] }, - "installingUpdate", - { - "key": "restartToUpdate", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/browser/parts/compositePart": [ - "ariaCompositeToolbarLabel", - "viewsAndMoreActions", - "titleTooltip" + "hide", + "positionActivituBar", + "positionActivituBar", + "positionActivituBar", + "previousSideBarView", + "nextSideBarView", + "focusActivityBar" ], "vs/workbench/browser/parts/sidebar/sidebarActions": [ "focusSideBar" ], - "vs/base/browser/ui/toolbar/toolbar": [ - "moreActions" - ], - "vs/workbench/browser/parts/editor/editorPanes": [ - "editorOpenErrorDialog", - { - "key": "ok", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/browser/parts/editor/editorGroupWatermark": [ - "watermark.showCommands", - "watermark.quickAccess", - "watermark.openFile", - "watermark.openFolder", - "watermark.openFileFolder", - "watermark.openRecent", - "watermark.newUntitledFile", - "watermark.findInFiles", - { - "key": "watermark.toggleTerminal", - "comment": [ - "toggle is a verb here" - ] - }, - "watermark.startDebugging", - { - "key": "watermark.toggleFullscreen", - "comment": [ - "toggle is a verb here" - ] - }, - "watermark.showSettings" - ], "vs/base/browser/ui/iconLabel/iconLabelHover": [ "iconLabel.loading" ], @@ -13940,6 +13930,18 @@ "vscode.extension.contributes.grammars.balancedBracketScopes", "vscode.extension.contributes.grammars.unbalancedBracketScopes" ], + "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ + "userSettings", + "userSettingsRemote", + "workspaceSettings", + "folderSettings", + "settingsSwitcherBarAriaLabel", + "userSettings", + "userSettingsRemote", + "workspaceSettings", + "userSettings", + "workspaceSettings" + ], "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ "unbound" ], @@ -13984,17 +13986,28 @@ "manage workspace trust", "unsupportedProperty" ], - "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ - "userSettings", - "userSettingsRemote", - "workspaceSettings", - "folderSettings", - "settingsSwitcherBarAriaLabel", - "userSettings", - "userSettingsRemote", - "workspaceSettings", - "userSettings", - "workspaceSettings" + "vs/base/browser/ui/toolbar/toolbar": [ + "moreActions" + ], + "vs/workbench/contrib/preferences/browser/settingsTree": [ + "extensions", + "modified", + "settingsContextMenuTitle", + "newExtensionsButtonLabel", + "editInSettingsJson", + "editLanguageSettingLabel", + "settings.Default", + "modified", + "showExtension", + "resetSettingLabel", + "validationError", + "validationError", + "settings.Modified", + "settings", + "copySettingIdLabel", + "copySettingAsJSONLabel", + "stopSyncingSetting", + "applyToAllProfiles" ], "vs/workbench/contrib/preferences/browser/settingsLayout": [ "commonlyUsed", @@ -14046,26 +14059,6 @@ "security", "workspace" ], - "vs/workbench/contrib/preferences/browser/settingsTree": [ - "extensions", - "modified", - "settingsContextMenuTitle", - "newExtensionsButtonLabel", - "editInSettingsJson", - "editLanguageSettingLabel", - "settings.Default", - "modified", - "showExtension", - "resetSettingLabel", - "validationError", - "validationError", - "settings.Modified", - "settings", - "copySettingIdLabel", - "copySettingAsJSONLabel", - "stopSyncingSetting", - "applyToAllProfiles" - ], "vs/workbench/contrib/preferences/browser/tocTree": [ { "key": "settingsTOC", @@ -14091,23 +14084,91 @@ "policySettingsSearch", "policySettingsSearchTooltip" ], - "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView": [ - "notebookActions.selectKernel", - "notebookActions.selectKernel.args" - ], - "vs/workbench/contrib/notebook/browser/notebookExtensionPoint": [ - "contributes.notebook.provider", - "contributes.notebook.provider.viewType", - "contributes.notebook.provider.displayName", - "contributes.notebook.provider.selector", - "contributes.notebook.provider.selector.filenamePattern", - "contributes.notebook.selector.provider.excludeFileNamePattern", - "contributes.priority", - "contributes.priority.default", - "contributes.priority.option", - "contributes.notebook.renderer", - "contributes.notebook.renderer.viewType", - "contributes.notebook.renderer.displayName", + "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ + "chat.overview", + "chat.requestHistory", + "chat.inspectResponse", + "chat.inspectResponseNoKb", + "chat.announcement", + "workbench.action.chat.focus", + "workbench.action.chat.focusNoKb", + "workbench.action.chat.focusInput", + "workbench.action.interactiveSession.focusInputNoKb", + "workbench.action.chat.nextCodeBlock", + "workbench.action.chat.nextCodeBlockNoKb", + "workbench.action.chat.nextFileTree", + "workbench.action.chat.nextFileTreeNoKb", + "workbench.action.chat.clear", + "workbench.action.chat.clearNoKb", + "inlineChat.overview", + "inlineChat.access", + "inlineChat.requestHistory", + "inlineChat.inspectResponse", + "inlineChat.inspectResponseNoKb", + "inlineChat.contextActions", + "inlineChat.fix", + "inlineChat.diff", + "inlineChat.diffNoKb", + "inlineChat.toolbar", + "chat.audioCues" + ], + "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ + "notebookActions.joinSelectedCells", + "notebookActions.joinSelectedCells.label" + ], + "vs/workbench/contrib/chat/browser/chatInputPart": [ + "actions.chat.accessibiltyHelp", + "chatInput.accessibilityHelpNoKb", + "chatInput" + ], + "vs/workbench/contrib/chat/browser/chatListRenderer": [ + "usedAgent", + "usingAgent", + "thinking", + "usedReferencesPlural", + "usedReferencesSingular", + "usedReferencesExpanded", + "usedReferencesCollapsed", + "chat", + "commandFollowUpInfo", + "commandFollowUpInfoMany", + "singleFileTreeHint", + "multiFileTreeHint", + "noCodeBlocksHint", + "noCodeBlocks", + "singleCodeBlockHint", + "singleCodeBlock", + "multiCodeBlockHint", + "multiCodeBlock", + "treeAriaLabel" + ], + "vs/platform/actions/browser/toolbar": [ + "hide", + "resetThisMenu" + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ + "inlineSuggestionVisible", + "inlineSuggestionHasIndentation", + "inlineSuggestionHasIndentationLessThanTabSize", + "suppressSuggestions" + ], + "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView": [ + "notebookActions.selectKernel", + "notebookActions.selectKernel.args" + ], + "vs/workbench/contrib/notebook/browser/notebookExtensionPoint": [ + "contributes.notebook.provider", + "contributes.notebook.provider.viewType", + "contributes.notebook.provider.displayName", + "contributes.notebook.provider.selector", + "contributes.notebook.provider.selector.filenamePattern", + "contributes.notebook.selector.provider.excludeFileNamePattern", + "contributes.priority", + "contributes.priority.default", + "contributes.priority.option", + "contributes.notebook.renderer", + "contributes.notebook.renderer.viewType", + "contributes.notebook.renderer.displayName", "contributes.notebook.renderer.hardDependencies", "contributes.notebook.renderer.optionalDependencies", "contributes.notebook.renderer.requiresMessaging.always", @@ -14177,18 +14238,22 @@ "readonly", "deleted", "confirmOverwrite", - "irreversible", + "overwriteIrreversible", { "key": "replaceButtonLabel", "comment": [ "&& denotes a mnemonic" ] + }, + "confirmMakeWriteable", + "confirmMakeWriteableDetail", + { + "key": "makeWriteableButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] } ], - "vs/platform/actions/browser/toolbar": [ - "hide", - "resetThisMenu" - ], "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy": [ "current1", "current2", @@ -14207,10 +14272,6 @@ "kernels.detecting", "select" ], - "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ - "notebookActions.joinSelectedCells", - "notebookActions.joinSelectedCells.label" - ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget": [ "ariaSearchNoResultEmpty", "ariaSearchNoResult", @@ -14219,76 +14280,27 @@ "vs/editor/contrib/codeAction/browser/codeAction": [ "applyCodeActionFailed" ], - "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ - "chat.overview", - "chat.requestHistory", - "chat.inspectResponse", - "chat.inspectResponseNoKb", - "chat.announcement", - "workbench.action.chat.focus", - "workbench.action.chat.focusNoKb", - "workbench.action.chat.focusInput", - "workbench.action.interactiveSession.focusInputNoKb", - "workbench.action.chat.nextCodeBlock", - "workbench.action.chat.nextCodeBlockNoKb", - "workbench.action.chat.nextFileTree", - "workbench.action.chat.nextFileTreeNoKb", - "workbench.action.chat.clear", - "workbench.action.chat.clearNoKb", - "inlineChat.overview", - "inlineChat.access", - "inlineChat.requestHistory", - "inlineChat.inspectResponse", - "inlineChat.inspectResponseNoKb", - "inlineChat.contextActions", - "inlineChat.fix", - "inlineChat.diff", - "inlineChat.diffNoKb", - "inlineChat.toolbar", - "chat.audioCues" - ], - "vs/workbench/contrib/chat/browser/chatInputPart": [ - "actions.chat.accessibiltyHelp", - "chatInput.accessibilityHelpNoKb", - "chatInput" - ], - "vs/workbench/contrib/chat/browser/chatListRenderer": [ - "chat", - "commandFollowUpInfo", - "commandFollowUpInfoMany", - "singleFileTreeHint", - "multiFileTreeHint", - "noCodeBlocksHint", - "noCodeBlocks", - "singleCodeBlockHint", - "singleCodeBlock", - "multiCodeBlockHint", - "multiCodeBlock", - "chat.codeBlockHelp", - "chat.codeBlock.toolbarVerbose", - "chat.codeBlock.toolbar", - "chat.codeBlockLabel", - "treeAriaLabel" - ], "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ "lines.0", "lines.1", "lines.N" ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ - "inlineSuggestionVisible", - "inlineSuggestionHasIndentation", - "inlineSuggestionHasIndentationLessThanTabSize", - "suppressSuggestions" - ], "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ "aria-label", "original", "modified", "inlineChat.accessibilityHelp", "inlineChat.accessibilityHelpNoKb", + "slashCommandUsed", "inlineChatClosed" ], + "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ + "terminal.integrated.copySelection.noSelection", + "yes", + "no", + "dontShowAgain", + "terminal.slowRendering" + ], "vs/workbench/contrib/testing/browser/theme": [ "testing.iconFailed", "testing.iconErrored", @@ -14304,6 +14316,29 @@ "testing.message.info.decorationForeground", "testing.message.info.marginBackground" ], + "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ + "terminal.background", + "terminal.foreground", + "terminalCursor.foreground", + "terminalCursor.background", + "terminal.selectionBackground", + "terminal.inactiveSelectionBackground", + "terminal.selectionForeground", + "terminalCommandDecoration.defaultBackground", + "terminalCommandDecoration.successBackground", + "terminalCommandDecoration.errorBackground", + "terminalOverviewRuler.cursorForeground", + "terminal.border", + "terminal.findMatchBackground", + "terminal.findMatchHighlightBorder", + "terminal.findMatchBorder", + "terminal.findMatchHighlightBackground", + "terminal.findMatchHighlightBorder", + "terminalOverviewRuler.findMatchHighlightForeground", + "terminal.dragAndDropBackground", + "terminal.tab.activeBorder", + "terminal.ansiColor" + ], "vs/workbench/contrib/testing/common/constants": [ "testState.errored", "testState.failed", @@ -14334,36 +14369,6 @@ "testing.filters.showExcludedTests", "testing.filters.removeTestExclusions" ], - "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ - "terminal.integrated.copySelection.noSelection", - "yes", - "no", - "dontShowAgain", - "terminal.slowRendering" - ], - "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ - "terminal.background", - "terminal.foreground", - "terminalCursor.foreground", - "terminalCursor.background", - "terminal.selectionBackground", - "terminal.inactiveSelectionBackground", - "terminal.selectionForeground", - "terminalCommandDecoration.defaultBackground", - "terminalCommandDecoration.successBackground", - "terminalCommandDecoration.errorBackground", - "terminalOverviewRuler.cursorForeground", - "terminal.border", - "terminal.findMatchBackground", - "terminal.findMatchHighlightBorder", - "terminal.findMatchBorder", - "terminal.findMatchHighlightBackground", - "terminal.findMatchHighlightBorder", - "terminalOverviewRuler.findMatchHighlightForeground", - "terminal.dragAndDropBackground", - "terminal.tab.activeBorder", - "terminal.ansiColor" - ], "vs/platform/quickinput/browser/commandsQuickAccess": [ "recentlyUsed", "suggested", @@ -14438,6 +14443,12 @@ ] } ], + "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ + "canNotResolve", + "symbolicLlink", + "unknown", + "label" + ], "vs/workbench/contrib/files/browser/views/explorerViewer": [ "treeAriaLabel", "fileInputAriaLabel", @@ -14459,21 +14470,6 @@ "numberOfFolders", "numberOfFiles" ], - "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ - "canNotResolve", - "symbolicLlink", - "unknown", - "label" - ], - "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ - "invalidQueryStringError", - "numFiles", - "oneFile", - "numResults", - "oneResult", - "noResults", - "searchMaxResultsWarning" - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree": [ "bulkEdit", "aria.renameAndEdit", @@ -14492,9 +14488,6 @@ "detail.del", "title" ], - "vs/workbench/contrib/search/browser/searchFindInput": [ - "searchFindInputNotebookFilter.label" - ], "vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess": [ "cannotRunGotoSymbolWithoutEditor", "cannotRunGotoSymbolWithoutSymbolProvider", @@ -14534,9 +14527,31 @@ "searchReplace.source", "fileReplaceChanges" ], + "vs/workbench/contrib/search/browser/searchFindInput": [ + "searchFindInputNotebookFilter.label" + ], + "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ + "invalidQueryStringError", + "numFiles", + "oneFile", + "numResults", + "oneResult", + "noResults", + "searchMaxResultsWarning" + ], + "vs/workbench/contrib/scm/browser/dirtyDiffSwitcher": [ + "remotes", + "quickDiff.base.switch" + ], + "vs/workbench/contrib/scm/browser/menus": [ + "miShare" + ], "vs/workbench/contrib/debug/browser/baseDebugView": [ "debug.lazyButton.tooltip" ], + "vs/workbench/contrib/debug/common/debugSource": [ + "unknownSource" + ], "vs/workbench/contrib/debug/browser/debugSessionPicker": [ "moveFocusedView.selectView", "workbench.action.debug.startDebug", @@ -14571,38 +14586,6 @@ "workspace", "user settings" ], - "vs/workbench/contrib/debug/browser/debugTaskRunner": [ - "preLaunchTaskErrors", - "preLaunchTaskError", - "preLaunchTaskExitCode", - "preLaunchTaskTerminated", - { - "key": "debugAnyway", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "showErrors", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "abort", - "remember", - { - "key": "debugAnyway", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "rememberTask", - "invalidTaskReference", - "DebugTaskNotFoundWithTaskId", - "DebugTaskNotFound", - "taskNotTrackedWithTaskId", - "taskNotTracked" - ], "vs/workbench/contrib/debug/browser/debugSession": [ "noDebugAdapter", "noDebugAdapter", @@ -14641,32 +14624,45 @@ "noDebugAdapter", "noDebugAdapter", "noDebugAdapter", + "debuggingStartedNoDebug", "debuggingStarted", "debuggingStopped" ], - "vs/workbench/contrib/debug/common/debugSource": [ - "unknownSource" - ], - "vs/workbench/contrib/scm/browser/menus": [ - "miShare" - ], - "vs/workbench/contrib/scm/browser/dirtyDiffSwitcher": [ - "remotes", - "quickDiff.base.switch" - ], - "vs/base/browser/ui/findinput/replaceInput": [ - "defaultLabel", - "label.preserveCaseToggle" + "vs/workbench/contrib/debug/browser/debugTaskRunner": [ + "preLaunchTaskErrors", + "preLaunchTaskError", + "preLaunchTaskExitCode", + "preLaunchTaskTerminated", + { + "key": "debugAnyway", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "showErrors", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "abort", + "remember", + { + "key": "debugAnyway", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "rememberTask", + "invalidTaskReference", + "DebugTaskNotFoundWithTaskId", + "DebugTaskNotFound", + "taskNotTrackedWithTaskId", + "taskNotTracked" ], "vs/base/browser/ui/dropdown/dropdownActionViewItem": [ "moreActions" ], - "vs/workbench/contrib/markers/browser/markersTable": [ - "codeColumnLabel", - "messageColumnLabel", - "fileColumnLabel", - "sourceColumnLabel" - ], "vs/workbench/contrib/mergeEditor/common/mergeEditor": [ "is", "isr", @@ -14677,13 +14673,6 @@ "baseUri", "resultUri" ], - "vs/workbench/contrib/markers/browser/markersTreeViewer": [ - "problemsView", - "expandedIcon", - "collapsedIcon", - "single line", - "multi line" - ], "vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel": [ "messageN", "message1", @@ -14760,14 +14749,16 @@ }, "noMoreWarn" ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ - "base", - "compareWith", - "compareWithTooltip" - ], "vs/workbench/contrib/mergeEditor/browser/view/viewModel": [ "noConflictMessage" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ + "result", + "mergeEditor.remainingConflicts", + "mergeEditor.remainingConflict", + "goToNextConflict", + "allConflictHandled" + ], "vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView": [ "input1", "input2", @@ -14796,12 +14787,22 @@ "mergeEditor.conflict.input1.background", "mergeEditor.conflict.input2.background" ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ - "result", - "mergeEditor.remainingConflicts", - "mergeEditor.remainingConflict", - "goToNextConflict", - "allConflictHandled" + "vs/base/browser/ui/findinput/replaceInput": [ + "defaultLabel", + "label.preserveCaseToggle" + ], + "vs/workbench/contrib/markers/browser/markersTreeViewer": [ + "problemsView", + "expandedIcon", + "collapsedIcon", + "single line", + "multi line" + ], + "vs/workbench/contrib/markers/browser/markersTable": [ + "codeColumnLabel", + "messageColumnLabel", + "fileColumnLabel", + "sourceColumnLabel" ], "vs/workbench/contrib/comments/browser/commentsController": [ "commentRange", @@ -14811,72 +14812,18 @@ "hasCommentRanges", "pickCommentService" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ + "base", + "compareWith", + "compareWithTooltip" + ], "vs/workbench/contrib/customEditor/common/contributedCustomEditors": [ "builtinProviderDisplayName" ], - "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ - "ratedLabel", - "sponsor", - "remote extension title", - "syncingore.label", - "activation", - "startup", - "pre-release-label", - "sponsor", - "publisher verified tooltip", - "updateRequired", - "activation", - "startup", - "uncaught error", - "uncaught errors", - "message", - "messages", - "dependencies", - "Show prerelease version", - "has prerelease", - "recommendationHasBeenIgnored", - "extensionIconStarForeground", - "extensionIconVerifiedForeground", - "extensionPreReleaseForeground", - "extensionIcon.sponsorForeground" - ], - "vs/workbench/contrib/extensions/browser/extensionsViewer": [ - "error", - "Unknown Extension", - "extensions" - ], - "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ - "workspaceRecommendation" - ], - "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ - "fileBasedRecommendation", - "languageName" - ], - "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ - "exeBasedRecommendation" - ], - "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ - "exeBasedRecommendation" - ], - "vs/workbench/contrib/extensions/browser/webRecommendations": [ - "reason" - ], "vs/platform/files/browser/htmlFileSystemProvider": [ "fileSystemRenameError", "fileSystemNotAllowedError" ], - "vs/workbench/contrib/terminal/browser/terminalService": [ - "terminalService.terminalCloseConfirmationSingular", - "terminalService.terminalCloseConfirmationPlural", - { - "key": "terminate", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "localTerminalVirtualWorkspace", - "localTerminalRemote" - ], "vs/workbench/contrib/terminal/browser/terminalActions": [ "showTerminalTabs", "workbench.action.terminal.newWorkspacePlaceholder", @@ -14963,6 +14910,18 @@ "workbench.action.terminal.newWithProfilePlus", "renameTerminal" ], + "vs/workbench/contrib/terminal/browser/terminalService": [ + "terminalService.terminalCloseConfirmationSingular", + "terminalService.terminalCloseConfirmationPlural", + { + "key": "terminate", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "localTerminalVirtualWorkspace", + "localTerminalRemote" + ], "vs/workbench/contrib/terminal/common/terminalConfiguration": [ "cwd", "cwdFolder", @@ -15114,7 +15073,8 @@ "terminal.integrated.focusAfterRun", "terminal.integrated.focusAfterRun.terminal", "terminal.integrated.focusAfterRun.accessible-buffer", - "terminal.integrated.focusAfterRun.none" + "terminal.integrated.focusAfterRun.none", + "terminal.integrated.accessibleViewPreserveCursorPosition" ], "vs/workbench/contrib/terminal/browser/terminalMenus": [ { @@ -15190,6 +15150,8 @@ "doNotShowAgain", "currentSessionCategory", "previousSessionCategory", + "task", + "local", "terminalCategory", "workbench.action.terminal.focus", "workbench.action.terminal.focusAndHideAccessibleBuffer", @@ -15232,6 +15194,7 @@ "vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp": [ "focusAccessibleBuffer", "focusAccessibleBufferNoKb", + "preserveCursor", "commandPromptMigration", "shellIntegration", "goToNextCommand", @@ -15250,8 +15213,16 @@ "openDetectedLinkNoKb", "newWithProfile", "newWithProfileNoKb", - "focusAfterRun", - "accessibilitySettings" + "focusAfterRun" + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ + "terminalLinkHandler.followLinkAlt.mac", + "terminalLinkHandler.followLinkAlt", + "terminalLinkHandler.followLinkCmd", + "terminalLinkHandler.followLinkCtrl", + "followLink", + "followForwardedLink", + "followLinkUrl" ], "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick": [ "terminal.integrated.urlLinks", @@ -15264,15 +15235,6 @@ "terminal.integrated.localFolderLinks", "terminal.integrated.searchLinks" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ - "terminalLinkHandler.followLinkAlt.mac", - "terminalLinkHandler.followLinkAlt", - "terminalLinkHandler.followLinkCmd", - "terminalLinkHandler.followLinkCtrl", - "followLink", - "followForwardedLink", - "followLinkUrl" - ], "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ "quickFix.command", "quickFix.opener", @@ -15290,44 +15252,91 @@ "vscode.extension.contributes.terminalQuickFixes.commandExitResult", "vscode.extension.contributes.terminalQuickFixes.kind" ], - "vs/workbench/contrib/tasks/common/jsonSchemaCommon": [ - "JsonSchema.options", - "JsonSchema.options.cwd", - "JsonSchema.options.env", - "JsonSchema.tasks.matcherError", - "JsonSchema.tasks.matcherError", - "JsonSchema.shellConfiguration", - "JsonSchema.shell.executable", - "JsonSchema.shell.args", - "JsonSchema.command", - "JsonSchema.tasks.args", - "JsonSchema.tasks.taskName", - "JsonSchema.command", - "JsonSchema.tasks.args", - "JsonSchema.tasks.windows", - "JsonSchema.tasks.matchers", - "JsonSchema.tasks.mac", - "JsonSchema.tasks.matchers", - "JsonSchema.tasks.linux", - "JsonSchema.tasks.matchers", - "JsonSchema.tasks.suppressTaskName", - "JsonSchema.tasks.showOutput", - "JsonSchema.echoCommand", - "JsonSchema.tasks.watching.deprecation", - "JsonSchema.tasks.watching", - "JsonSchema.tasks.background", - "JsonSchema.tasks.promptOnClose", - "JsonSchema.tasks.build", - "JsonSchema.tasks.test", - "JsonSchema.tasks.matchers", - "JsonSchema.command", - "JsonSchema.args", - "JsonSchema.showOutput", - "JsonSchema.watching.deprecation", - "JsonSchema.watching", - "JsonSchema.background", - "JsonSchema.promptOnClose", - "JsonSchema.echoCommand", + "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ + "ratedLabel", + "sponsor", + "remote extension title", + "syncingore.label", + "activation", + "startup", + "pre-release-label", + "sponsor", + "publisher verified tooltip", + "updateRequired", + "activation", + "startup", + "uncaught error", + "uncaught errors", + "message", + "messages", + "dependencies", + "Show prerelease version", + "has prerelease", + "recommendationHasBeenIgnored", + "extensionIconStarForeground", + "extensionIconVerifiedForeground", + "extensionPreReleaseForeground", + "extensionIcon.sponsorForeground" + ], + "vs/workbench/contrib/extensions/browser/extensionsViewer": [ + "error", + "Unknown Extension", + "extensions" + ], + "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ + "exeBasedRecommendation" + ], + "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ + "workspaceRecommendation" + ], + "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ + "fileBasedRecommendation", + "languageName" + ], + "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ + "exeBasedRecommendation" + ], + "vs/workbench/contrib/extensions/browser/webRecommendations": [ + "reason" + ], + "vs/workbench/contrib/tasks/common/jsonSchemaCommon": [ + "JsonSchema.options", + "JsonSchema.options.cwd", + "JsonSchema.options.env", + "JsonSchema.tasks.matcherError", + "JsonSchema.tasks.matcherError", + "JsonSchema.shellConfiguration", + "JsonSchema.shell.executable", + "JsonSchema.shell.args", + "JsonSchema.command", + "JsonSchema.tasks.args", + "JsonSchema.tasks.taskName", + "JsonSchema.command", + "JsonSchema.tasks.args", + "JsonSchema.tasks.windows", + "JsonSchema.tasks.matchers", + "JsonSchema.tasks.mac", + "JsonSchema.tasks.matchers", + "JsonSchema.tasks.linux", + "JsonSchema.tasks.matchers", + "JsonSchema.tasks.suppressTaskName", + "JsonSchema.tasks.showOutput", + "JsonSchema.echoCommand", + "JsonSchema.tasks.watching.deprecation", + "JsonSchema.tasks.watching", + "JsonSchema.tasks.background", + "JsonSchema.tasks.promptOnClose", + "JsonSchema.tasks.build", + "JsonSchema.tasks.test", + "JsonSchema.tasks.matchers", + "JsonSchema.command", + "JsonSchema.args", + "JsonSchema.showOutput", + "JsonSchema.watching.deprecation", + "JsonSchema.watching", + "JsonSchema.background", + "JsonSchema.promptOnClose", + "JsonSchema.echoCommand", "JsonSchema.suppressTaskName", "JsonSchema.taskSelector", "JsonSchema.matchers", @@ -15355,8 +15364,7 @@ "JsonSchema.input.command.args" ], "vs/workbench/contrib/remote/browser/explorerViewItems": [ - "remotes", - "remote.explorer.switch" + "switchRemote.label" ], "vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions": [ "snippets" @@ -15380,6 +15388,11 @@ "snippetSuggest.longLabel", "snippetSuggest.longLabel" ], + "vs/workbench/contrib/update/browser/releaseNotesEditor": [ + "releaseNotesInputName", + "unassigned", + "showOnUpdate" + ], "vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent": [ "getting-started-setup-icon", "getting-started-beginner-icon", @@ -15406,12 +15419,12 @@ "gettingStarted.topLevelOpenTunnel.description", "gettingStarted.setup.title", "gettingStarted.setup.description", - "gettingStarted.pickColor.title", - "gettingStarted.pickColor.description.interpolated", - "titleID", "gettingStarted.settingsSync.title", "gettingStarted.settingsSync.description.interpolated", "enableSync", + "gettingStarted.pickColor.title", + "gettingStarted.pickColor.description.interpolated", + "titleID", "gettingStarted.commandPalette.title", "gettingStarted.commandPalette.description.interpolated", "commandPalette", @@ -15432,12 +15445,12 @@ "quickOpen", "gettingStarted.setupWeb.title", "gettingStarted.setupWeb.description", - "gettingStarted.pickColor.title", - "gettingStarted.pickColor.description.interpolated", - "titleID", "gettingStarted.settingsSync.title", "gettingStarted.settingsSync.description.interpolated", "enableSync", + "gettingStarted.pickColor.title", + "gettingStarted.pickColor.description.interpolated", + "titleID", "gettingStarted.commandPalette.title", "gettingStarted.commandPalette.description.interpolated", "commandPalette", @@ -15516,10 +15529,17 @@ "gettingStarted.notebookProfile.title", "gettingStarted.notebookProfile.description" ], - "vs/workbench/contrib/update/browser/releaseNotesEditor": [ - "releaseNotesInputName", - "unassigned", - "showOnUpdate" + "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ + "gettingStarted.featuredTitle" + ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ + "welcomePage.background", + "welcomePage.tileBackground", + "welcomePage.tileHoverBackground", + "welcomePage.tileBorder", + "welcomePage.progress.background", + "welcomePage.progress.foreground", + "walkthrough.stepTitle.foreground" ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint": [ "title", @@ -15563,17 +15583,15 @@ "vs/workbench/contrib/welcomeWalkthrough/common/walkThroughUtils": [ "walkThrough.embeddedEditorBackground" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ - "gettingStarted.featuredTitle" + "vs/workbench/contrib/callHierarchy/browser/callHierarchyTree": [ + "tree.aria", + "from", + "to" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ - "welcomePage.background", - "welcomePage.tileBackground", - "welcomePage.tileHoverBackground", - "welcomePage.tileBorder", - "welcomePage.progress.background", - "welcomePage.progress.foreground", - "walkthrough.stepTitle.foreground" + "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree": [ + "tree.aria", + "supertypes", + "subtypes" ], "vs/editor/contrib/symbolIcons/browser/symbolIcons": [ "symbolIcon.arrayForeground", @@ -15610,24 +15628,9 @@ "symbolIcon.unitForeground", "symbolIcon.variableForeground" ], - "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree": [ - "tree.aria", - "supertypes", - "subtypes" - ], - "vs/workbench/contrib/callHierarchy/browser/callHierarchyTree": [ - "tree.aria", - "from", - "to" - ], "vs/workbench/contrib/userDataSync/browser/userDataSyncViews": [ - "conflicts", - "synced machines", "workbench.actions.sync.editMachineName", "workbench.actions.sync.turnOffSyncOnMachine", - "remote sync activity title", - "local sync activity title", - "downloaded sync activity title", "workbench.actions.sync.loadActivity", "select sync activity file", "workbench.actions.sync.resolveResourceRef", @@ -15652,7 +15655,6 @@ "A confirmation message to replace current user data (settings, extensions, keybindings, snippets) with selected version" ] }, - "troubleshoot", "reset", "sideBySideLabels", { @@ -15693,7 +15695,13 @@ "comment": [ "Represents current log file" ] - } + }, + "conflicts", + "synced machines", + "remote sync activity title", + "local sync activity title", + "downloaded sync activity title", + "troubleshoot" ], "vs/workbench/browser/parts/notifications/notificationsList": [ "notificationAccessibleViewHint", @@ -15724,6 +15732,86 @@ "vs/workbench/services/textfile/common/textFileSaveParticipant": [ "saveParticipants" ], + "vs/workbench/browser/parts/titlebar/menubarControl": [ + { + "key": "mFile", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mEdit", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mSelection", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mView", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mGoto", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mHelp", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mPreferences", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "menubar.customTitlebarAccessibilityNotification", + "goToSetting", + "focusMenu", + { + "key": "checkForUpdates", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "checkingForUpdates", + { + "key": "download now", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "DownloadingUpdate", + { + "key": "installUpdate...", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "installingUpdate", + { + "key": "restartToUpdate", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/workbench/browser/parts/titlebar/commandCenterControl": [ "label.dfl", "label1", @@ -15732,10 +15820,19 @@ "title2", "title3" ], - "vs/workbench/browser/parts/titlebar/windowTitle": [ - "userIsAdmin", - "userIsSudo", - "devExtensionWindowTitlePrefix" + "vs/workbench/browser/parts/globalCompositeBar": [ + "accountsViewBarIcon", + "hideAccounts", + "manage", + "accounts", + "accounts", + "loading", + "authProviderUnavailable", + "manageTrustedExtensions", + "signOut", + "noAccounts", + "manage", + "manage profile" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopy": [ "staleSaveError", @@ -15764,14 +15861,14 @@ "vs/platform/terminal/common/terminalProfiles": [ "terminalAutomaticProfile" ], + "vs/workbench/contrib/webview/browser/webviewElement": [ + "fatalErrorMessage" + ], "vs/platform/quickinput/browser/quickPickPin": [ "terminal.commands.pinned", "pinCommand", "pinnedCommand" ], - "vs/workbench/contrib/webview/browser/webviewElement": [ - "fatalErrorMessage" - ], "vs/workbench/api/common/extHostDiagnostics": [ { "key": "limitHit", @@ -15802,6 +15899,9 @@ "emptyResponse", "errorResponse" ], + "vs/workbench/api/common/extHostChatAgents2": [ + "errorResponse" + ], "vs/base/browser/ui/findinput/findInputToggles": [ "caseDescription", "wordsDescription", @@ -15838,12 +15938,6 @@ "insertLine", "deleteLine" ], - "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ - "codeMovedToWithChanges", - "codeMovedFromWithChanges", - "codeMovedTo", - "codeMovedFrom" - ], "vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature": [ "foldUnchanged", "diff.hiddenLines.top", @@ -15852,6 +15946,12 @@ "hiddenLines", "diff.hiddenLines.expandAll" ], + "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ + "codeMovedToWithChanges", + "codeMovedFromWithChanges", + "codeMovedTo", + "codeMovedFrom" + ], "vs/editor/browser/widget/diffEditor/diffEditorEditors": [ "diff-aria-navigation-tip" ], @@ -15866,15 +15966,6 @@ "accessibilityOffAriaLabelNoKb", "accessibilityOffAriaLabelNoKbs" ], - "vs/platform/actionWidget/browser/actionWidget": [ - "actionBar.toggledBackground", - "codeActionMenuVisible", - "hideCodeActionWidget.title", - "selectPrevCodeAction.title", - "selectNextCodeAction.title", - "acceptSelected.title", - "previewSelected.title" - ], "vs/editor/contrib/codeAction/browser/codeActionMenu": [ "codeAction.widget.id.more", "codeAction.widget.id.quickfix", @@ -15885,6 +15976,15 @@ "codeAction.widget.id.surround", "codeAction.widget.id.source" ], + "vs/platform/actionWidget/browser/actionWidget": [ + "actionBar.toggledBackground", + "codeActionMenuVisible", + "hideCodeActionWidget.title", + "selectPrevCodeAction.title", + "selectNextCodeAction.title", + "acceptSelected.title", + "previewSelected.title" + ], "vs/editor/contrib/colorPicker/browser/colorPickerWidget": [ "clickToToggleColorOptions", "closeIcon" @@ -15968,17 +16068,6 @@ "comments", "resolved" ], - "vs/workbench/browser/parts/editor/editorPlaceholder": [ - "trustRequiredEditor", - "requiresFolderTrustText", - "requiresWorkspaceTrustText", - "manageTrust", - "errorEditor", - "unavailableResourceErrorEditorText", - "unknownErrorEditorTextWithError", - "unknownErrorEditorTextWithoutError", - "retry" - ], "vs/workbench/contrib/comments/browser/commentColors": [ "resolvedCommentIcon", "unresolvedCommentIcon", @@ -15987,35 +16076,60 @@ "commentThreadRangeBackground", "commentThreadActiveRangeBackground" ], - "vs/base/browser/ui/menu/menubar": [ - "mAppMenu", - "mMore" - ], - "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ - "ariaLabelTabActions" - ], - "vs/workbench/browser/parts/editor/breadcrumbsControl": [ - "separatorIcon", - "breadcrumbsPossible", - "breadcrumbsVisible", - "breadcrumbsActive", - "empty", - "cmd.toggle", + "vs/workbench/browser/parts/editor/editorPanes": [ + "editorUnsupportedInAuxWindow", + "openFolder", + "editorOpenErrorDialog", { - "key": "miBreadcrumbs", + "key": "ok", "comment": [ "&& denotes a mnemonic" ] + } + ], + "vs/workbench/browser/parts/editor/editorGroupWatermark": [ + "watermark.showCommands", + "watermark.quickAccess", + "watermark.openFile", + "watermark.openFolder", + "watermark.openFileFolder", + "watermark.openRecent", + "watermark.newUntitledFile", + "watermark.findInFiles", + { + "key": "watermark.toggleTerminal", + "comment": [ + "toggle is a verb here" + ] }, - "cmd.toggle2", + "watermark.startDebugging", { - "key": "miBreadcrumbs2", + "key": "watermark.toggleFullscreen", "comment": [ - "&& denotes a mnemonic" + "toggle is a verb here" ] }, - "cmd.focusAndSelect", - "cmd.focus" + "watermark.showSettings" + ], + "vs/workbench/browser/parts/editor/editorPlaceholder": [ + "trustRequiredEditor", + "requiresFolderTrustText", + "requiresWorkspaceTrustText", + "manageTrust", + "errorEditor", + "unavailableResourceErrorEditorText", + "unknownErrorEditorTextWithError", + "unknownErrorEditorTextWithoutError", + "retry" + ], + "vs/workbench/browser/parts/compositePart": [ + "ariaCompositeToolbarLabel", + "viewsAndMoreActions", + "titleTooltip" + ], + "vs/workbench/browser/parts/paneCompositeBar": [ + "resetLocation", + "resetLocation" ], "vs/platform/quickinput/browser/quickInput": [ "quickInput.back", @@ -16110,6 +16224,12 @@ "objectKeyHeader", "objectValueHeader" ], + "vs/workbench/contrib/chat/browser/codeBlockPart": [ + "chat.codeBlockHelp", + "chat.codeBlock.toolbarVerbose", + "chat.codeBlock.toolbar", + "chat.codeBlockLabel" + ], "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer": [ "cellExecutionOrderCountLabel" ], @@ -16153,11 +16273,24 @@ "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ "empty" ], + "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ + "exited slash command mode" + ], "vs/platform/actions/browser/buttonbar": [ "labelWithKeybinding" ], - "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ - "exited slash command mode" + "vs/workbench/contrib/debug/common/debugger": [ + "cannot.find.da", + "launch.config.comment1", + "launch.config.comment2", + "launch.config.comment3", + "debugType", + "debugTypeNotRecognised", + "node2NotSupported", + "debugRequest", + "debugWindowsConfiguration", + "debugOSXConfiguration", + "debugLinuxConfiguration" ], "vs/workbench/contrib/terminal/browser/xterm/decorationAddon": [ "terminal.rerunCommand", @@ -16177,19 +16310,6 @@ "gutter", "overviewRuler" ], - "vs/workbench/contrib/debug/common/debugger": [ - "cannot.find.da", - "launch.config.comment1", - "launch.config.comment2", - "launch.config.comment3", - "debugType", - "debugTypeNotRecognised", - "node2NotSupported", - "debugRequest", - "debugWindowsConfiguration", - "debugOSXConfiguration", - "debugLinuxConfiguration" - ], "vs/workbench/contrib/debug/common/debugSchemas": [ "vscode.extension.contributes.debuggers", "vscode.extension.contributes.debuggers.type", @@ -16233,10 +16353,6 @@ "app.launch.json.compound.stopAll", "compoundPrelaunchTask" ], - "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ - "conflictingLine", - "conflictingLines" - ], "vs/workbench/contrib/debug/browser/rawDebugSession": [ "noDebugAdapterStart", "canNotStart", @@ -16253,6 +16369,10 @@ "setInputHandled", "undoMarkAsHandled" ], + "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ + "conflictingLine", + "conflictingLines" + ], "vs/workbench/contrib/mergeEditor/browser/view/conflictActions": [ "accept", "acceptTooltip", @@ -16283,10 +16403,6 @@ "editorGutterCommentGlyphForeground", "editorGutterCommentUnresolvedGlyphForeground" ], - "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ - "useWslExtension.title", - "install" - ], "vs/workbench/contrib/customEditor/common/extensionPoint": [ "contributes.customEditors", "contributes.viewType", @@ -16297,9 +16413,24 @@ "contributes.priority.default", "contributes.priority.option" ], + "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ + "useWslExtension.title", + "install" + ], + "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ + "terminal.integrated.selectProfileToCreate", + "terminal.integrated.chooseDefaultProfile", + "enterTerminalProfileName", + "terminalProfileAlreadyExists", + "terminalProfiles", + "ICreateContributedTerminalProfileOptions", + "terminalProfiles.detected", + "unsafePathWarning", + "yes", + "cancel", + "createQuickLaunchProfile" + ], "vs/workbench/contrib/terminal/browser/terminalInstance": [ - "terminalTypeTask", - "terminalTypeLocal", "terminal.integrated.a11yPromptLabel", "terminal.integrated.useAccessibleBuffer", "terminal.integrated.useAccessibleBufferNoKb", @@ -16337,19 +16468,6 @@ "terminated.exitCodeOnly", "launchFailed.errorMessage" ], - "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ - "terminal.integrated.selectProfileToCreate", - "terminal.integrated.chooseDefaultProfile", - "enterTerminalProfileName", - "terminalProfileAlreadyExists", - "terminalProfiles", - "ICreateContributedTerminalProfileOptions", - "terminalProfiles.detected", - "unsafePathWarning", - "yes", - "cancel", - "createQuickLaunchProfile" - ], "vs/workbench/contrib/terminal/browser/terminalTabsList": [ "terminalInputAriaLabel", "terminal.tabs", @@ -16371,13 +16489,6 @@ }, "label" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ - "searchWorkspace", - "openFile", - "focusFolder", - "openFolder", - "followLink" - ], "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget": [ "label.find", "placeholder.find", @@ -16390,6 +16501,13 @@ "ariaSearchNoResultWithLineNumNoCurrentMatch", "simpleFindWidget.sashBorder" ], + "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ + "searchWorkspace", + "openFile", + "focusFolder", + "openFolder", + "followLink" + ], "vs/workbench/contrib/terminal/browser/xterm/decorationStyles": [ "terminalPromptContextMenu", "terminalPromptCommandFailed", @@ -16403,6 +16521,11 @@ "HighContrastLight", "seeMore" ], + "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ + "default", + "jupyter", + "colab" + ], "vs/workbench/contrib/userDataSync/browser/userDataSyncConflictsView": [ "explanation", { @@ -16423,11 +16546,6 @@ "Theirs", "Yours" ], - "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ - "default", - "jupyter", - "colab" - ], "vs/workbench/browser/parts/notifications/notificationsViewer": [ "executeCommand", "notificationActions", @@ -16438,6 +16556,28 @@ "close", "find" ], + "vs/base/browser/ui/menu/menubar": [ + "mAppMenu", + "mMore" + ], + "vs/workbench/browser/parts/compositeBarActions": [ + "titleKeybinding", + "badgeTitle", + "additionalViews", + "numberBadge", + "manageExtension", + "hide", + "keep", + "hideBadge", + "showBadge", + "toggle", + "toggleBadge" + ], + "vs/editor/browser/widget/diffEditor/decorations": [ + "diffInsertIcon", + "diffRemoveIcon", + "revertChangeHoverMessage" + ], "vs/editor/common/viewLayout/viewLineRenderer": [ "showMore", "overflow.chars" @@ -16451,11 +16591,6 @@ "diff.clipboard.copyChangedLineContent.label", "diff.inline.revertChange.label" ], - "vs/editor/browser/widget/diffEditor/decorations": [ - "diffInsertIcon", - "diffRemoveIcon", - "revertChangeHoverMessage" - ], "vs/platform/actionWidget/browser/actionList": [ { "key": "label-preview", @@ -16487,55 +16622,34 @@ "referenceCount", "treeAriaLabel" ], - "vs/workbench/browser/parts/editor/editorTabsControl": [ - "ariaLabelEditorActions", - "draggedEditorGroup" + "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ + "ariaLabelTabActions" ], - "vs/workbench/browser/parts/editor/breadcrumbs": [ - "title", - "enabled", - "filepath", - "filepath.on", - "filepath.off", - "filepath.last", - "symbolpath", - "symbolpath.on", - "symbolpath.off", - "symbolpath.last", - "symbolSortOrder", - "symbolSortOrder.position", - "symbolSortOrder.name", - "symbolSortOrder.type", - "icons", - "filteredTypes.file", - "filteredTypes.module", - "filteredTypes.namespace", - "filteredTypes.package", - "filteredTypes.class", - "filteredTypes.method", - "filteredTypes.property", - "filteredTypes.field", - "filteredTypes.constructor", - "filteredTypes.enum", - "filteredTypes.interface", - "filteredTypes.function", - "filteredTypes.variable", - "filteredTypes.constant", - "filteredTypes.string", - "filteredTypes.number", - "filteredTypes.boolean", - "filteredTypes.array", - "filteredTypes.object", - "filteredTypes.key", - "filteredTypes.null", - "filteredTypes.enumMember", - "filteredTypes.struct", - "filteredTypes.event", - "filteredTypes.operator", - "filteredTypes.typeParameter" + "vs/workbench/browser/parts/editor/breadcrumbsControl": [ + "separatorIcon", + "breadcrumbsPossible", + "breadcrumbsVisible", + "breadcrumbsActive", + "empty", + "cmd.toggle", + { + "key": "miBreadcrumbs", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "cmd.toggle2", + { + "key": "miBreadcrumbs2", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "cmd.focusAndSelect", + "cmd.focus" ], - "vs/workbench/browser/parts/editor/breadcrumbsPicker": [ - "breadcrumbs" + "vs/workbench/browser/parts/compositeBar": [ + "activityBarAriaLabel" ], "vs/platform/quickinput/browser/quickInputUtils": [ "executeCommand" @@ -16562,6 +16676,11 @@ "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar": [ "notebook.moreRunActionsLabel" ], + "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ + "cellOutputsCollapsedMsg", + "cellExpandOutputButtonLabelWithDoubleClick", + "cellExpandOutputButtonLabel" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ "hiddenCellsLabel", "hiddenCellsLabelPlural" @@ -16570,11 +16689,6 @@ "cellExpandInputButtonLabelWithDoubleClick", "cellExpandInputButtonLabel" ], - "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ - "cellOutputsCollapsedMsg", - "cellExpandOutputButtonLabelWithDoubleClick", - "cellExpandOutputButtonLabel" - ], "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ "suggest", "label.full", @@ -16587,6 +16701,10 @@ "commentLabelWithKeybinding", "commentLabelWithKeybindingNoKeybinding" ], + "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ + "killportfailure", + "ptyHostRelaunch" + ], "vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick": [ "removeCommand", "viewCommandOutput", @@ -16596,9 +16714,55 @@ "selectRecentDirectoryMac", "selectRecentDirectory" ], - "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ - "killportfailure", - "ptyHostRelaunch" + "vs/workbench/browser/parts/editor/editorTabsControl": [ + "ariaLabelEditorActions", + "draggedEditorGroup" + ], + "vs/workbench/browser/parts/editor/breadcrumbs": [ + "title", + "enabled", + "filepath", + "filepath.on", + "filepath.off", + "filepath.last", + "symbolpath", + "symbolpath.on", + "symbolpath.off", + "symbolpath.last", + "symbolSortOrder", + "symbolSortOrder.position", + "symbolSortOrder.name", + "symbolSortOrder.type", + "icons", + "filteredTypes.file", + "filteredTypes.module", + "filteredTypes.namespace", + "filteredTypes.package", + "filteredTypes.class", + "filteredTypes.method", + "filteredTypes.property", + "filteredTypes.field", + "filteredTypes.constructor", + "filteredTypes.enum", + "filteredTypes.interface", + "filteredTypes.function", + "filteredTypes.variable", + "filteredTypes.constant", + "filteredTypes.string", + "filteredTypes.number", + "filteredTypes.boolean", + "filteredTypes.array", + "filteredTypes.object", + "filteredTypes.key", + "filteredTypes.null", + "filteredTypes.enumMember", + "filteredTypes.struct", + "filteredTypes.event", + "filteredTypes.operator", + "filteredTypes.typeParameter" + ], + "vs/workbench/browser/parts/editor/breadcrumbsPicker": [ + "breadcrumbs" ], "vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput": [ "empty", @@ -16674,6 +16838,9 @@ "vs/platform/terminal/node/ptyHostMain": [ "Pty Host" ], + "vs/code/node/cliProcessMain": [ + "CLI" + ], "vs/code/electron-main/main": [ "Main", "Another instance of {0} is already running as administrator.", @@ -16685,9 +16852,6 @@ "{0}\n\nPlease make sure the following directories are writeable:\n\n{1}", "&&Close" ], - "vs/code/node/cliProcessMain": [ - "CLI" - ], "vs/code/node/sharedProcess/sharedProcessMain": [ "Shared" ], @@ -16745,6 +16909,8 @@ "Keyboard", "Enables the macOS touchbar buttons on the keyboard if available.", "A set of identifiers for entries in the touchbar that should not show up (for example `workbench.action.navigateBack`).", + "If enabled, a dialog will ask for confirmation whenever a local file or workspace is about to open through a protocol handler.", + "If enabled, a dialog will ask for confirmation whenever a remote file or workspace is about to open through a protocol handler.", "The display Language to use. Picking a different language requires the associated language pack to be installed.", "Disables hardware acceleration. ONLY change this option if you encounter graphic issues.", "Allows to override the color profile to use. If you experience colors appear badly, try to set this to `srgb` and restart.", @@ -16753,7 +16919,9 @@ "Enable proposed APIs for a list of extension ids (such as `vscode.git`). Proposed APIs are unstable and subject to breaking without warning at any time. This should only be set for extension development and testing purposes.", "Log level to use. Default is 'info'. Allowed values are 'error', 'warn', 'info', 'debug', 'trace', 'off'.", "Disables the Chromium sandbox. This is useful when running VS Code as elevated on Linux and running under Applocker on Windows.", - "Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on." + "Ensures that an in-memory store will be used for secret storage instead of using the OS's credential store. This is often used when running VS Code extension tests or when you're experiencing difficulties with the credential store.", + "Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.", + "Configures the backend used to store secrets on Linux. This argument is ignored on Windows & macOS." ], "vs/workbench/services/textfile/electron-sandbox/nativeTextFileService": [ "Saving text files" @@ -16792,19 +16960,6 @@ "More Information", "Don't Show Again" ], - "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ - "Reveal in File Explorer", - "Reveal in Finder", - "Open Containing Folder", - "Share", - "File" - ], - "vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService": [ - "Voice Transcription", - "Getting microphone ready...", - "Recording from microphone...", - "Voice transcription failed: {0}" - ], "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ "Backup working copies" ], @@ -16826,19 +16981,18 @@ "Restart Extension Host", "Restarting extension host on explicit request." ], - "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ - "Running Extensions" + "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ + "Reveal in File Explorer", + "Reveal in Finder", + "Open Containing Folder", + "Share", + "File" ], "vs/workbench/contrib/localization/electron-sandbox/localization.contribution": [ "Would you like to change {0}'s display language to {1} and restart?", "Change Language and Restart", "Don't Show Again" ], - "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ - "Whether the platform has the WSL feature installed", - "Remote", - "When enabled extensions are downloaded locally and installed on remote." - ], "vs/workbench/contrib/issue/electron-sandbox/issue.contribution": [ "Report Performance Issue...", "Open Process Explorer", @@ -16849,11 +17003,22 @@ "Creating trace file...", "This can take up to one minute to complete." ], + "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ + "Running Extensions" + ], + "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ + "Whether the platform has the WSL feature installed", + "Remote", + "When enabled extensions are downloaded locally and installed on remote." + ], "vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution": [ - "Open Local Backups Folder", "Local backups folder does not exist", "Successfully downloaded Settings Sync activity.", - "Open Folder" + "Open Folder", + "Open Local Backups Folder" + ], + "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ + "When enabled slow renderers are automatically profiled" ], "vs/workbench/contrib/tasks/electron-sandbox/taskService": [ "There is a task running. Do you want to terminate it?", @@ -16861,9 +17026,6 @@ "The launched task doesn't exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.", "&&Exit Anyways" ], - "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ - "When enabled slow renderers are automatically profiled" - ], "vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution": [ "Open New External Terminal", "External Terminal", @@ -16925,10 +17087,61 @@ "vs/base/common/platform": [ "_" ], + "vs/platform/environment/node/argv": [ + "Options", + "Extensions Management", + "Troubleshooting", + "Directory where CLI metadata should be stored.", + "Directory where CLI metadata should be stored.", + "Compare two files with each other.", + "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.", + "Add folder(s) to the last active window.", + "Open a file at the path on the specified line and character position.", + "Force to open a new window.", + "Force to open a file or folder in an already opened window.", + "Wait for the files to be closed before returning.", + "The locale to use (e.g. en-US or zh-TW).", + "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.", + "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created.", + "Print usage.", + "Set the root path for extensions.", + "List the installed extensions.", + "Show versions of installed extensions, when using --list-extensions.", + "Filters installed extensions by provided category, when using --list-extensions.", + "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: 'vscode.csharp@1.2.3'.", + "Installs the pre-release version of the extension, when using --install-extension", + "Uninstalls an extension.", + "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.", + "Print version.", + "Print verbose output (implies --wait).", + "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.", + "Print process usage and diagnostics information.", + "Run CPU profiler during startup.", + "Disable all installed extensions. This option is not persisted and is effective only when the command opens a new window.", + "Disable the provided extension. This option is not persisted and is effective only when the command opens a new window.", + "Turn sync on or off.", + "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.", + "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.", + "Disable GPU hardware acceleration.", + "Use this option only when there is requirement to launch the application as sudo user on Linux or when running as an elevated user in an applocker environment on Windows.", + "Shows all telemetry events which VS code collects.", + "Use {0} instead.", + "paths", + "Usage", + "options", + "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", + "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", + "Subcommands", + "Unknown version", + "Unknown commit" + ], + "vs/platform/terminal/node/ptyService": [ + "History restored" + ], "vs/editor/common/config/editorOptions": [ - "Use platform APIs to detect when a Screen Reader is attached", - "Optimize for usage with a Screen Reader", - "Assume a screen reader is not attached", + "Use platform APIs to detect when a Screen Reader is attached.", + "Optimize for usage with a Screen Reader.", + "Assume a screen reader is not attached.", "Controls if the UI should run in a mode where it is optimized for screen readers.", "Controls whether a space character is inserted when commenting.", "Controls if empty lines should be ignored with toggle, add or remove actions for line comments.", @@ -16971,7 +17184,7 @@ "Controls whether the hover is shown.", "Controls the delay in milliseconds after which the hover is shown.", "Controls whether the hover should remain visible when mouse is moved over it.", - "Controls the delay in milliseconds after thich the hover is hidden. Requires `editor.hover.sticky` to be enabled.", + "Controls the delay in milliseconds after which the hover is hidden. Requires `editor.hover.sticky` to be enabled.", "Prefer showing hovers above the line, if there's space.", "Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width.", "Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.", @@ -16980,7 +17193,7 @@ "Shows the nested current scopes during the scroll at the top of the editor.", "Defines the maximum number of sticky lines to show.", "Defines the model to use for determining which lines to stick. If the outline model does not exist, it will fall back on the folding provider model which falls back on the indentation model. This order is respected in all three cases.", - "Enable scrolling of the sticky scroll widget with the editor's horizontal scrollbar.", + "Enable scrolling of Sticky Scroll with the editor's horizontal scrollbar.", "Enables the inlay hints in the editor.", "Inlay hints are enabled", "Inlay hints are showing by default and hide when holding {0}", @@ -17031,6 +17244,7 @@ "The width of the vertical scrollbar.", "The height of the horizontal scrollbar.", "Controls whether clicks scroll by page or jump to click position.", + "When set, the horizontal scrollbar will not increase the size of the editor's content.", "Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII.", "Controls whether characters that just reserve space or have no width at all are highlighted.", "Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale.", @@ -17208,6 +17422,7 @@ "Controls pasting when the line count of the pasted text matches the cursor count.", "Controls the max number of cursors that can be in an active editor at once.", "Controls whether the editor should highlight semantic symbol occurrences.", + "Experimental: Controls whether the editor should highlight word occurrences accross multiple open editors.", "Controls whether a border should be drawn around the overview ruler.", "Focus the tree when opening peek", "Focus the editor when opening peek", @@ -17273,57 +17488,6 @@ "Controls whether inline color decorations should be shown using the default document color provider", "Controls whether the editor receives tabs or defers them to the workbench for navigation." ], - "vs/platform/environment/node/argv": [ - "Options", - "Extensions Management", - "Troubleshooting", - "Directory where CLI metadata should be stored.", - "Directory where CLI metadata should be stored.", - "Compare two files with each other.", - "Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results.", - "Add folder(s) to the last active window.", - "Open a file at the path on the specified line and character position.", - "Force to open a new window.", - "Force to open a file or folder in an already opened window.", - "Wait for the files to be closed before returning.", - "The locale to use (e.g. en-US or zh-TW).", - "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.", - "Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created. A folder or workspace must be provided for the profile to take effect.", - "Print usage.", - "Set the root path for extensions.", - "List the installed extensions.", - "Show versions of installed extensions, when using --list-extensions.", - "Filters installed extensions by provided category, when using --list-extensions.", - "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: 'vscode.csharp@1.2.3'.", - "Installs the pre-release version of the extension, when using --install-extension", - "Uninstalls an extension.", - "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.", - "Print version.", - "Print verbose output (implies --wait).", - "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.", - "Print process usage and diagnostics information.", - "Run CPU profiler during startup.", - "Disable all installed extensions. This option is not persisted and is effective only when the command opens a new window.", - "Disable the provided extension. This option is not persisted and is effective only when the command opens a new window.", - "Turn sync on or off.", - "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.", - "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.", - "Disable GPU hardware acceleration.", - "Use this option only when there is requirement to launch the application as sudo user on Linux or when running as an elevated user in an applocker environment on Windows.", - "Shows all telemetry events which VS code collects.", - "Use {0} instead.", - "paths", - "Usage", - "options", - "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", - "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", - "Subcommands", - "Unknown version", - "Unknown commit" - ], - "vs/platform/terminal/node/ptyService": [ - "History restored" - ], "vs/base/common/errorMessage": [ "{0}: {1}", "A system error occurred ({0})", @@ -17332,11 +17496,47 @@ "{0} ({1} errors in total)", "An unknown error occurred. Please consult the log for more details." ], - "vs/code/electron-main/app": [ - "&&Yes", - "&&No", - "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", - "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'" + "vs/platform/extensionManagement/common/extensionManagement": [ + "Extensions", + "Preferences" + ], + "vs/platform/extensionManagement/common/extensionManagementCLI": [ + "Extension '{0}' not found.", + "Make sure you use the full extension ID, including the publisher, e.g.: {0}", + "Extensions installed on {0}:", + "Installing extensions on {0}...", + "Installing extensions...", + "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", + "Extension '{0}' is already installed.", + "Error while installing extensions: {0}", + "Failed Installing Extensions: {0}", + "Extension '{0}' was successfully installed.", + "Cancelled installing extension '{0}'.", + "Extension '{0}' is already installed.", + "Updating the extension '{0}' to the version {1}", + "Installing builtin extension '{0}' v{1}...", + "Installing builtin extension '{0}'...", + "Installing extension '{0}' v{1}...", + "Installing extension '{0}'...", + "Extension '{0}' v{1} was successfully installed.", + "Cancelled installing extension '{0}'.", + "A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", + "Extension '{0}' is a Built-in extension and cannot be uninstalled", + "Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.", + "Uninstalling {0}...", + "Extension '{0}' was successfully uninstalled from {1}!", + "Extension '{0}' was successfully uninstalled!", + "Extension '{0}' is not installed on {1}.", + "Extension '{0}' is not installed." + ], + "vs/platform/extensionManagement/common/extensionsScannerService": [ + "Cannot read file {0}: {1}.", + "Failed to parse {0}: [{1}, {2}] {3}.", + "Invalid manifest file {0}: Not an JSON object.", + "Failed to parse {0}: {1}.", + "Invalid format {0}: JSON object expected.", + "Failed to parse {0}: {1}.", + "Invalid format {0}: JSON object expected." ], "vs/platform/files/common/files": [ "Unknown Error", @@ -17346,22 +17546,16 @@ "{0}GB", "{0}TB" ], - "vs/platform/environment/node/argvHelper": [ - "Option '{0}' is defined more than once. Using value '{1}'.", - "Option '{0}' requires a non empty value. Ignoring the option.", - "Option '{0}' is deprecated: {1}", - "Warning: '{0}' is not in the list of known options for subcommand '{1}'", - "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", - "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`." - ], - "vs/platform/files/node/diskFileSystemProvider": [ - "File already exists", - "File does not exist", - "Unable to move '{0}' into '{1}' ({2}).", - "Unable to copy '{0}' into '{1}' ({2}).", - "File cannot be copied to same path with different path case", - "File to move/copy does not exist", - "File at target already exists and thus will not be moved/copied to unless overwrite is specified" + "vs/platform/extensionManagement/node/extensionManagementService": [ + "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", + "Marketplace is not enabled", + "Only Marketplace Extensions can be reinstalled", + "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", + "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", + "Unknown error while renaming {0} to {1}", + "Cannot read the extension from {0}", + "Please restart VS Code before reinstalling {0}.", + "Please restart VS Code before reinstalling {0}." ], "vs/platform/files/common/fileService": [ "Unable to resolve filesystem provider with relative file path '{0}'", @@ -17392,6 +17586,18 @@ "Unable to modify read-only file '{0}'", "Unable to modify read-only file '{0}'" ], + "vs/platform/files/node/diskFileSystemProvider": [ + "File already exists", + "File does not exist", + "Unable to move '{0}' into '{1}' ({2}).", + "Unable to copy '{0}' into '{1}' ({2}).", + "File cannot be copied to same path with different path case", + "File to move/copy does not exist", + "File at target already exists and thus will not be moved/copied to unless overwrite is specified" + ], + "vs/platform/languagePacks/common/languagePacks": [ + " (Current)" + ], "vs/platform/request/common/request": [ "Network Requests", "HTTP", @@ -17407,6 +17613,45 @@ "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS, a reload of the window is required after turning this off.)", "Controls whether experimental loading of CA certificates from the OS should be enabled. This uses a more general approach than the default implemenation." ], + "vs/platform/telemetry/common/telemetryService": [ + "Controls {0} telemetry, first-party extension telemetry, and participating third-party extension telemetry. Some third party extensions might not respect this setting. Consult the specific extension's documentation to be sure. Telemetry helps us better understand how {0} is performing, where improvements need to be made, and how features are being used.", + "Read more about the [data we collect]({0}).", + "Read more about the [data we collect]({0}) and our [privacy statement]({1}).", + "A full restart of the application is necessary for crash reporting changes to take effect.", + "Crash Reports", + "Error Telemetry", + "Usage Data", + "The following table outlines the data sent with each setting:", + "****Note:*** If this setting is 'off', no telemetry will be sent regardless of other telemetry settings. If this setting is set to anything except 'off' and telemetry is disabled with deprecated settings, no telemetry will be sent.*", + "Telemetry", + "Sends usage data, errors, and crash reports.", + "Sends general error telemetry and crash reports.", + "Sends OS level crash reports.", + "Disables all product telemetry.", + "Telemetry", + "Enable diagnostic data to be collected. This helps us to better understand how {0} is performing and where improvements need to be made.", + "Enable diagnostic data to be collected. This helps us to better understand how {0} is performing and where improvements need to be made. [Read more]({1}) about what we collect and our privacy statement.", + "If this setting is false, no telemetry will be sent regardless of the new setting's value. Deprecated in favor of the {0} setting." + ], + "vs/platform/userDataProfile/common/userDataProfile": [ + "Default" + ], + "vs/code/electron-main/app": [ + "&&Yes", + "&&No", + "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", + "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'", + "Allow opening local paths without asking", + "Allow opening remote paths without asking" + ], + "vs/platform/environment/node/argvHelper": [ + "Option '{0}' is defined more than once. Using value '{1}'.", + "Option '{0}' requires a non empty value. Ignoring the option.", + "Option '{0}' is deprecated: {1}", + "Warning: '{0}' is not in the list of known options for subcommand '{1}'", + "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", + "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`." + ], "vs/platform/dialogs/common/dialogs": [ "&&Yes", "Cancel", @@ -17437,6 +17682,7 @@ "Include my workspace metadata", "Include my enabled extensions", "Include A/B experiment info", + "Include additional extension info", "Before you report an issue here please review the guidance we provide.", "Please complete the form in English.", "This is a", @@ -17454,6 +17700,8 @@ "Please enter details.", "A description is required.", "show", + "Extension does not have additional data to include.", + "show", "show", "show", "show", @@ -17494,85 +17742,6 @@ "Extensions are disabled", "No current experiments." ], - "vs/platform/extensionManagement/common/extensionManagement": [ - "Extensions", - "Preferences" - ], - "vs/platform/extensionManagement/common/extensionsScannerService": [ - "Cannot read file {0}: {1}.", - "Failed to parse {0}: [{1}, {2}] {3}.", - "Invalid manifest file {0}: Not an JSON object.", - "Failed to parse {0}: {1}.", - "Invalid format {0}: JSON object expected.", - "Failed to parse {0}: {1}.", - "Invalid format {0}: JSON object expected." - ], - "vs/platform/languagePacks/common/languagePacks": [ - " (Current)" - ], - "vs/platform/extensionManagement/common/extensionManagementCLI": [ - "Extension '{0}' not found.", - "Make sure you use the full extension ID, including the publisher, e.g.: {0}", - "Extensions installed on {0}:", - "Installing extensions on {0}...", - "Installing extensions...", - "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", - "Extension '{0}' is already installed.", - "Error while installing extensions: {0}", - "Failed Installing Extensions: {0}", - "Extension '{0}' was successfully installed.", - "Cancelled installing extension '{0}'.", - "Extension '{0}' is already installed.", - "Updating the extension '{0}' to the version {1}", - "Installing builtin extension '{0}' v{1}...", - "Installing builtin extension '{0}'...", - "Installing extension '{0}' v{1}...", - "Installing extension '{0}'...", - "Extension '{0}' v{1} was successfully installed.", - "Cancelled installing extension '{0}'.", - "A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", - "Extension '{0}' is a Built-in extension and cannot be uninstalled", - "Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.", - "Uninstalling {0}...", - "Extension '{0}' was successfully uninstalled from {1}!", - "Extension '{0}' was successfully uninstalled!", - "Extension '{0}' is not installed on {1}.", - "Extension '{0}' is not installed." - ], - "vs/platform/extensionManagement/node/extensionManagementService": [ - "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", - "Marketplace is not enabled", - "Only Marketplace Extensions can be reinstalled", - "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", - "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", - "Unknown error while renaming {0} to {1}", - "Cannot read the extension from {0}", - "Please restart VS Code before reinstalling {0}.", - "Please restart VS Code before reinstalling {0}." - ], - "vs/platform/telemetry/common/telemetryService": [ - "Controls {0} telemetry, first-party extension telemetry, and participating third-party extension telemetry. Some third party extensions might not respect this setting. Consult the specific extension's documentation to be sure. Telemetry helps us better understand how {0} is performing, where improvements need to be made, and how features are being used.", - "Read more about the [data we collect]({0}).", - "Read more about the [data we collect]({0}) and our [privacy statement]({1}).", - "A full restart of the application is necessary for crash reporting changes to take effect.", - "Crash Reports", - "Error Telemetry", - "Usage Data", - "The following table outlines the data sent with each setting:", - "****Note:*** If this setting is 'off', no telemetry will be sent regardless of other telemetry settings. If this setting is set to anything except 'off' and telemetry is disabled with deprecated settings, no telemetry will be sent.*", - "Telemetry", - "Sends usage data, errors, and crash reports.", - "Sends general error telemetry and crash reports.", - "Sends OS level crash reports.", - "Disables all product telemetry.", - "Telemetry", - "Enable diagnostic data to be collected. This helps us to better understand how {0} is performing and where improvements need to be made.", - "Enable diagnostic data to be collected. This helps us to better understand how {0} is performing and where improvements need to be made. [Read more]({1}) about what we collect and our privacy statement.", - "If this setting is false, no telemetry will be sent regardless of the new setting's value. Deprecated in favor of the {0} setting." - ], - "vs/platform/userDataProfile/common/userDataProfile": [ - "Default" - ], "vs/platform/telemetry/common/telemetryLogAppender": [ "Telemetry{0}" ], @@ -17583,18 +17752,18 @@ "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.", "Configure settings to be ignored while synchronizing." ], - "vs/platform/userDataSync/common/userDataSyncMachines": [ - "Cannot read machines data as the current version is incompatible. Please update {0} and try again." - ], "vs/platform/userDataSync/common/userDataSyncLog": [ "Settings Sync" ], - "vs/platform/userDataSync/common/userDataSyncResourceProvider": [ - "Cannot parse sync data as it is not compatible with the current version." + "vs/platform/userDataSync/common/userDataSyncMachines": [ + "Cannot read machines data as the current version is incompatible. Please update {0} and try again." ], "vs/platform/remoteTunnel/common/remoteTunnel": [ "Remote Tunnel Service" ], + "vs/platform/userDataSync/common/userDataSyncResourceProvider": [ + "Cannot parse sync data as it is not compatible with the current version." + ], "vs/platform/remoteTunnel/node/remoteTunnelService": [ "Building CLI from sources", "Connecting as {0} ({1})", @@ -17629,14 +17798,6 @@ "Controls how tree folders are expanded when clicking the folder names. Note that some trees and lists might choose to ignore this setting if it is not applicable.", "Controls how type navigation works in lists and trees in the workbench. When set to `trigger`, type navigation begins once the `list.triggerTypeNavigation` command is run." ], - "vs/platform/markers/common/markers": [ - "Error", - "Warning", - "Info" - ], - "vs/platform/contextkey/browser/contextKeyService": [ - "A command that returns information about context keys" - ], "vs/platform/contextkey/common/contextkey": [ "Empty context key expression", "Did you forget to write an expression? You can also put 'false' or 'true' to always evaluate to false or true, respectively.", @@ -17650,14 +17811,33 @@ "Unexpected token. Hint: {0}", "Unexpected token." ], + "vs/platform/contextkey/browser/contextKeyService": [ + "A command that returns information about context keys" + ], + "vs/platform/markers/common/markers": [ + "Error", + "Warning", + "Info" + ], + "vs/workbench/browser/actions/textInputActions": [ + "Undo", + "Redo", + "Cut", + "Copy", + "Paste", + "Select All" + ], "vs/workbench/browser/workbench.contribution": [ "The default size.", "Increases the size, so it can be grabbed more easily with the mouse.", "Controls the height of the scrollbars used for tabs and breadcrumbs in the editor title area.", - "Controls whether opened editors should show in tabs or not.", - "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is disabled.", - "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is disabled.", - "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when `#workbench.editor.showTabs#` is disabled.", + "Each editor is displayed as a tab in the editor title area.", + "The active editor is displayed as a single large tab in the editor title area.", + "The editor title area is not displayed.", + "Controls whether opened editors should show as individual tabs, one single large tab or if the title area should not be shown.", + "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", "Controls whether editor file decorations should use badges.", "Controls whether editor file decorations should use colors.", "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active.", @@ -17675,19 +17855,19 @@ "When enabled, shows a Status bar Quick Fix when the editor language doesn't match detected content language.", "Show in untitled text editors", "Show in notebook editors", - "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is disabled.", + "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", "Always keep tabs large enough to show the full editor label.", "Allow tabs to get smaller when the available space is not enough to show all tabs at once.", "Make all tabs the same size, while allowing them to get smaller when the available space is not enough to show all tabs at once.", - "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.", + "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", "Controls the minimum width of tabs when `#workbench.editor.tabSizing#` size is set to `fixed`.", "Controls the maximum width of tabs when `#workbench.editor.tabSizing#` size is set to `fixed`.", - "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is disabled.", + "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is not set to `multiple`.", "A pinned tab inherits the look of non pinned tabs.", "A pinned tab will show in a compact form with only icon or first letter of the editor name.", "A pinned tab shrinks to a compact fixed size showing parts of the editor name.", - "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is disabled.", - "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when `#workbench.editor.showTabs#` is disabled.", + "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", "Always prevent closing the pinned editor when using mouse middle click or keyboard.", "Prevent closing the pinned editor when using the keyboard.", "Prevent closing the pinned editor when using mouse middle click.", @@ -17720,7 +17900,10 @@ "Editors are positioned from left to right.", "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.", "Controls whether the centered layout tries to maintain constant width when the window is resized.", - "Controls whether to maximize/restore the editor group when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is disabled.", + "Controls how the editor group is resized when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "All other editor groups are hidden and the current editor group is maximized to take up the entire editor area.", + "The editor group takes as much space as possible by making all other editor groups as small as possible.", + "No editor group is resized when double clicking on a tab.", "Controls if the number of opened editors should be limited or not. When enabled, less recently used editors will close to make space for newly opening editors.", "Controls the maximum number of opened editors. Use the {0} setting to control this limit per editor group or across all groups.", "Controls if the maximum number of opened editors should exclude dirty editors for counting towards the configured limit.", @@ -17749,7 +17932,10 @@ "Never maximize the panel when opening it. The panel will open un-maximized.", "Open the panel to the state that it was in, before it was closed.", "Controls the visibility of the status bar at the bottom of the workbench.", - "Controls the visibility of the activity bar in the workbench.", + "Controls the location of the activity bar. It can either show to the `side` or `top` (requires {0} set to {1}) of the primary side bar or `hidden`.", + "Show the activity bar to the side of the primary side bar.", + "Show the activity bar on top of the primary side bar.", + "Hide the activity bar.", "Controls the behavior of clicking an activity bar icon in the workbench.", "Hide the side bar if the clicked item is already visible.", "Focus side bar if the clicked item is already visible.", @@ -17828,31 +18014,45 @@ "Zen Mode", "Controls whether turning on Zen Mode also puts the workbench into full screen mode.", "Controls whether turning on Zen Mode also centers the layout.", - "Controls whether turning on Zen Mode also hides workbench tabs.", + "Controls whether turning on Zen Mode should show multiple editor tabs, a single editor tab or hide the editor title area completely.", + "Each editor is displayed as a tab in the editor title area.", + "The active editor is displayed as a single large tab in the editor title area.", + "The editor title area is not displayed.", "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.", "Controls whether turning on Zen Mode also hides the activity bar either at the left or right of the workbench.", "Controls whether turning on Zen Mode also hides the editor line numbers.", "Controls whether a window should restore to Zen Mode if it was exited in Zen Mode.", "Controls whether notifications do not disturb mode should be enabled while in Zen Mode. If true, only error notifications will pop out." ], - "vs/workbench/browser/actions/textInputActions": [ - "Undo", - "Redo", - "Cut", - "Copy", - "Paste", - "Select All" - ], - "vs/workbench/browser/actions/developerActions": [ - "Inspect Context Keys", - "Toggle Screencast Mode", - "Log Storage Database Contents", - "The storage database contents have been logged to the developer tools.", - "Open developer tools from the menu and select the Console tab.", - "Log Working Copies", - "Remove Large Storage Database Entries...", - "Scope: {0}, Target: {1}", - "Global", + "vs/workbench/browser/actions/helpActions": [ + "Keyboard Shortcuts Reference", + "&&Keyboard Shortcuts Reference", + "Video Tutorials", + "&&Video Tutorials", + "Tips and Tricks", + "Tips and Tri&&cks", + "Documentation", + "&&Documentation", + "Signup for the VS Code Newsletter", + "Join Us on YouTube", + "&&Join Us on YouTube", + "Search Feature Requests", + "&&Search Feature Requests", + "View License", + "View &&License", + "Privacy Statement", + "Privac&&y Statement" + ], + "vs/workbench/browser/actions/developerActions": [ + "Inspect Context Keys", + "Toggle Screencast Mode", + "Log Storage Database Contents", + "The storage database contents have been logged to the developer tools.", + "Open developer tools from the menu and select the Console tab.", + "Log Working Copies", + "Remove Large Storage Database Entries...", + "Scope: {0}, Target: {1}", + "Global", "Profile", "Workspace", "Machine", @@ -17879,25 +18079,6 @@ "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.", "Controls the size (in pixels) of the mouse indicator in screencast mode." ], - "vs/workbench/browser/actions/helpActions": [ - "Keyboard Shortcuts Reference", - "&&Keyboard Shortcuts Reference", - "Video Tutorials", - "&&Video Tutorials", - "Tips and Tricks", - "Tips and Tri&&cks", - "Documentation", - "&&Documentation", - "Signup for the VS Code Newsletter", - "Join Us on YouTube", - "&&Join Us on YouTube", - "Search Feature Requests", - "&&Search Feature Requests", - "View License", - "View &&License", - "Privacy Statement", - "Privac&&y Statement" - ], "vs/workbench/browser/actions/layoutActions": [ "Represents the menu bar", "Represents the activity bar in the left position", @@ -17917,7 +18098,6 @@ "Represents zen mode", "Close Primary Side Bar", "Toggle Activity Bar Visibility", - "&&Activity Bar", "Toggle Centered Layout", "&&Centered Layout", "Move Primary Side Bar Right", @@ -17948,7 +18128,10 @@ "Toggle Primary Side Bar", "Toggle Status Bar Visibility", "S&&tatus Bar", - "Toggle Editor Tab Visibility", + "Hide Editor Tabs", + "Show Multiple Editor Tabs", + "Show Single Editor Tab", + "Tab Bar", "Separate Pinned Editor Tabs", "Toggle Zen Mode", "Zen Mode", @@ -18241,6 +18424,9 @@ "A transient workspace will disappear when restarting or reloading.", "Unknown workspace configuration property" ], + "vs/workbench/browser/parts/editor/editorParts": [ + "Window {0}" + ], "vs/workbench/api/browser/viewsExtensionPoint": [ "Unique id used to identify the container in which views can be contributed using 'views' contribution point", "Human readable string used to render the container", @@ -18315,8 +18501,10 @@ "Split Down", "Split Left", "Split Right", - "Editor Tabs", - "Separate Pinned Editor Tabs", + "Tab Bar", + "Multiple Tabs", + "Single Tab", + "Hide", "Close", "Close Others", "Close to the Right", @@ -18337,6 +18525,8 @@ "Close All", "Close Saved", "Enable Preview Editors", + "Maximize Group", + "Unmaximize Group", "Lock Group", "Split Editor Right", "Split Editor Down", @@ -18426,13 +18616,14 @@ "Group &&Below", "Switch &&Group" ], - "vs/workbench/browser/parts/statusbar/statusbarPart": [ - "Hide Status Bar" - ], "vs/workbench/browser/parts/banner/bannerPart": [ "Focus Banner" ], + "vs/workbench/browser/parts/statusbar/statusbarPart": [ + "Hide Status Bar" + ], "vs/workbench/browser/parts/views/viewsService": [ + "Text Editor", "Show {0}", "Toggle {0}", "Show {0}", @@ -18713,6 +18904,16 @@ "Export Profile", "Profile name must be provided." ], + "vs/workbench/services/remote/common/remoteExplorerService": [ + "The ID of a Get Started walkthrough to open.", + "Contributes help information for Remote", + "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension", + "The url, or a command that returns the url, to your project's documentation page", + "The url, or a command that returns the url, to your project's feedback reporter", + "Use {0} instead", + "The url, or a command that returns the url, to your project's issue reporter", + "The url, or a command that returns the url, to your project's issues list" + ], "vs/workbench/services/filesConfiguration/common/filesConfigurationService": [ "Editor is read-only because the file system of the file is read-only.", "Editor is read-only because the file was set read-only in this session. [Click here](command:{0}) to set writeable.", @@ -18773,6 +18974,42 @@ "vs/workbench/services/assignment/common/assignmentService": [ "Fetches experiments to run from a Microsoft online service." ], + "vs/workbench/services/issue/browser/issueTroubleshoot": [ + "Troubleshoot Issue", + "Issue troubleshooting is a process to help you identify the cause for an issue. The cause for an issue can be a misconfiguration, due to an extension, or be {0} itself.\n\nDuring the process the window reloads repeatedly. Each time you must confirm if you are still seeing the issue.", + "&&Troubleshoot Issue", + "Issue troubleshooting is active and has temporarily disabled all installed extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", + "Issue troubleshooting is active and has temporarily reset your configurations to defaults. Check if you can still reproduce the problem and proceed by selecting from these options.", + "Issue troubleshooting has identified that the issue is caused by your configurations. Please report the issue by exporting your configurations using \"Export Profile\" command and share the file in the issue report.", + "Issue troubleshooting has identified that the issue is with {0}.", + "I Can't Reproduce", + "I Can Reproduce", + "Stop", + "Troubleshoot Issue", + "This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use {0} insiders until the new stable version is available.", + "Troubleshoot Issue", + "Download {0} Insiders", + "Report Issue Anyway", + "Please try to download and reproduce the issue in {0} insiders.", + "Troubleshoot Issue", + "I can't reproduce", + "I can reproduce", + "Stop", + "Please try to reproduce the issue in {0} insiders and confirm if the issue exists there.", + "Troubleshoot Issue...", + "Stop Troubleshoot Issue" + ], + "vs/workbench/contrib/preferences/browser/keybindingsEditorContribution": [ + "You won't be able to produce this key combination under your current keyboard layout.", + "**{0}** for your current keyboard layout (**{1}** for US standard).", + "**{0}** for your current keyboard layout." + ], + "vs/workbench/contrib/performance/browser/performance.contribution": [ + "Startup Performance", + "Print Service Cycles", + "Print Service Traces", + "Print Emitter Profiles" + ], "vs/workbench/contrib/preferences/browser/preferences.contribution": [ "Settings Editor 2", "Keybindings Editor", @@ -18820,41 +19057,16 @@ "Open Settings (JSON)", "&&Preferences" ], - "vs/workbench/services/issue/browser/issueTroubleshoot": [ - "Troubleshoot Issue", - "Issue troubleshooting is a process to help you identify the cause for an issue. The cause for an issue can be a misconfiguration, due to an extension, or be {0} itself.\n\nDuring the process the window reloads repeatedly. Each time you must confirm if you are still seeing the issue.", - "&&Troubleshoot Issue", - "Issue troubleshooting is active and has temporarily disabled all installed extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", - "Issue troubleshooting is active and has temporarily reset your configurations to defaults. Check if you can still reproduce the problem and proceed by selecting from these options.", - "Issue troubleshooting has identified that the issue is caused by your configurations. Please report the issue by exporting your configurations using \"Export Profile\" command and share the file in the issue report.", - "Issue troubleshooting has identified that the issue is with {0}.", - "I Can't Reproduce", - "I Can Reproduce", - "Stop", - "Troubleshoot Issue", - "This likely means that the issue has been addressed already and will be available in an upcoming release. You can safely use {0} insiders until the new stable version is available.", - "Troubleshoot Issue", - "Download {0} Insiders", - "Report Issue Anyway", - "Please try to download and reproduce the issue in {0} insiders.", - "Troubleshoot Issue", - "I can't reproduce", - "I can reproduce", - "Stop", - "Please try to reproduce the issue in {0} insiders and confirm if the issue exists there.", - "Troubleshoot Issue...", - "Stop Troubleshoot Issue" - ], - "vs/workbench/contrib/preferences/browser/keybindingsEditorContribution": [ - "You won't be able to produce this key combination under your current keyboard layout.", - "**{0}** for your current keyboard layout (**{1}** for US standard).", - "**{0}** for your current keyboard layout." - ], - "vs/workbench/contrib/performance/browser/performance.contribution": [ - "Startup Performance", - "Print Service Cycles", - "Print Service Traces", - "Print Emitter Profiles" + "vs/workbench/contrib/chat/browser/chat.contribution": [ + "Chat", + "Controls the font size in pixels in chat codeblocks.", + "Controls the font family in chat codeblocks.", + "Controls the font weight in chat codeblocks.", + "Controls whether lines should wrap in chat codeblocks.", + "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size.", + "Chat", + "Chat", + "Clear the session" ], "vs/workbench/contrib/notebook/browser/notebook.contribution": [ "Settings for code editors used in notebooks. This can be used to customize most editor.* settings.", @@ -18893,10 +19105,10 @@ "Line height of the output text within notebook cells.\n - When set to 0, editor line height is used.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values.", "Font size for the output text within notebook cells. When set to 0, {0} is used.", "The font family of the output text within notebook cells. When set to empty, the {0} is used.", - "Initially render notebook outputs in a scrollable region when longer than the limit", + "Initially render notebook outputs in a scrollable region when longer than the limit.", "Controls whether the lines in output should wrap.", "Format a notebook on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down.", - "Run a series of CodeActions for a notebook on save. CodeActions must be specified, the file must not be saved after delay, and the editor must not be shutting down. Example: `\"notebook.source.organizeImports\": \"explicit\"`", + "Run a series of Code Actions for a notebook on save. Code Actions must be specified, the file must not be saved after delay, and the editor must not be shutting down. Example: `\"notebook.source.organizeImports\": \"explicit\"`", "Triggers Code Actions only when explicitly saved.", "Never triggers Code Actions on save.", "Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of \"explicit\".", @@ -18914,24 +19126,13 @@ "Always anchor the viewport to the focused cell.", "The focused cell may shift around as cells resize." ], - "vs/workbench/contrib/chat/browser/chat.contribution": [ - "Chat", - "Controls the font size in pixels in chat codeblocks.", - "Controls the font family in chat codeblocks.", - "Controls the font weight in chat codeblocks.", - "Controls whether lines should wrap in chat codeblocks.", - "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size.", - "Chat", - "Chat", - "Clear the session" - ], "vs/workbench/contrib/testing/browser/testing.contribution": [ "Testing", "T&&esting", - "Test Results", - "Test Results", "No tests have been found in this workspace yet.", "Install Additional Test Extensions...", + "Test Results", + "Test Results", "Test Explorer" ], "vs/workbench/contrib/logs/common/logs.contribution": [ @@ -19007,14 +19208,12 @@ "A&&uto Save", "Re&&vert File", "&&Close Editor", - "Go to &&File..." + "Go to &&File...", + "Create a new folder or directory" ], "vs/workbench/contrib/files/browser/explorerViewlet": [ "View icon of the explorer view.", "View icon of the open editors view.", - "Folders", - "Explorer", - "Explorer", "&&Explorer", "Open Folder", "add a folder", @@ -19023,7 +19222,10 @@ "You have not yet opened a folder.\n{0}\n{1}", "Connected to remote.\n{0}", "You have not yet opened a folder.\n{0}\nOpening a folder will close all currently open editors. To keep them open, {1} instead.", - "You have not yet opened a folder.\n{0}" + "You have not yet opened a folder.\n{0}", + "Folders", + "Explorer", + "Explorer" ], "vs/workbench/contrib/files/browser/files.contribution": [ "Text File Editor", @@ -19117,7 +19319,7 @@ "Appends the word \"copy\" at the end of the duplicated name potentially followed by a number.", "Adds a number at the end of the duplicated name. If some number is already part of the name, tries to increase that number.", "Disables incremental naming. If two files with the same name exist you will be prompted to overwrite the existing file.", - "Controls what naming strategy to use when a giving a new name to a duplicated Explorer item on paste.", + "Controls which naming strategy to use when giving a new name to a duplicated Explorer item on paste.", "Controls whether the Explorer should render folders in a compact form. In such a form, single child folders will be compressed in a combined tree element. Useful for Java package structures, for example.", "Use slash as path separation character.", "Use backslash as path separation character.", @@ -19153,6 +19355,7 @@ "Another refactoring is being previewed.", "Press 'Continue' to discard the previous refactoring and continue with the current refactoring.", "&&Continue", + "View icon of the refactor preview view.", "Apply Refactoring", "Refactor Preview", "Discard Refactoring", @@ -19165,35 +19368,10 @@ "Refactor Preview", "Group Changes By Type", "Refactor Preview", - "View icon of the refactor preview view.", "Refactor Preview", "Refactor Preview" ], - "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ - "Search Editor", - "Search Editor", - "Search Editor", - "Delete File Results", - "New Search Editor", - "Open Search Editor", - "Open New Search Editor to the Side", - "Open Results in Editor", - "Search Again", - "Focus Search Editor Input", - "Focus Search Editor Files to Include", - "Focus Search Editor Files to Exclude", - "Toggle Match Case", - "Toggle Match Whole Word", - "Toggle Use Regular Expression", - "Toggle Context Lines", - "Increase Context Lines", - "Decrease Context Lines", - "Select All Matches", - "Open New Search Editor" - ], "vs/workbench/contrib/search/browser/search.contribution": [ - "Search", - "Search", "&&Search", "Search files by name (append {0} to go to line or {1} to go to symbol)", "Go to File", @@ -19214,7 +19392,7 @@ "The search cache is kept in the extension host which never shuts down, so this setting is no longer needed.", "When enabled, the searchService process will be kept alive instead of being shut down after an hour of inactivity. This will keep the file search cache in memory.", "Controls whether to use `.gitignore` and `.ignore` files when searching for files.", - "Controls whether to use your global gitignore file (e.g., from `$HOME/.config/git/ignore`) when searching for files. Requires `#search.useIgnoreFiles#` to be enabled.", + "Controls whether to use your global gitignore file (for example, from `$HOME/.config/git/ignore`) when searching for files. Requires `#search.useIgnoreFiles#` to be enabled.", "Controls whether to use `.gitignore` and `.ignore` files in parent directories when searching for files. Requires `#search.useIgnoreFiles#` to be enabled.", "Whether to include results from a global symbol search in the file results for Quick Open.", "Whether to include results from recently opened files in the file results for Quick Open.", @@ -19259,69 +19437,160 @@ "Shows search results as a list.", "Controls the default search result view mode.", "Show notebook editor rich content results for closed notebooks. Please refresh your search results after changing this setting.", - "Controls whether the last typed input to Quick Search should be restored when opening it the next time." - ], - "vs/workbench/contrib/search/browser/searchView": [ - "Search was canceled before any results could be found - ", - "Toggle Search Details", - "files to include", - "e.g. *.ts, src/**/include", - "files to exclude", - "e.g. *.ts, src/**/exclude", - "Replace All", - "&&Replace", - "Replaced {0} occurrence across {1} file with '{2}'.", - "Replaced {0} occurrence across {1} file.", - "Replaced {0} occurrence across {1} files with '{2}'.", - "Replaced {0} occurrence across {1} files.", - "Replaced {0} occurrences across {1} file with '{2}'.", - "Replaced {0} occurrences across {1} file.", - "Replaced {0} occurrences across {1} files with '{2}'.", - "Replaced {0} occurrences across {1} files.", - "Replace {0} occurrence across {1} file with '{2}'?", - "Replace {0} occurrence across {1} file?", - "Replace {0} occurrence across {1} files with '{2}'?", - "Replace {0} occurrence across {1} files?", - "Replace {0} occurrences across {1} file with '{2}'?", - "Replace {0} occurrences across {1} file?", - "Replace {0} occurrences across {1} files with '{2}'?", - "Replace {0} occurrences across {1} files?", - "Empty Search", - "The search results have been cleared", - "Search path not found: {0}", - "No results found in open editors matching '{0}' excluding '{1}' - ", - "No results found in open editors matching '{0}' - ", - "No results found in open editors excluding '{0}' - ", - "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - ", - "No results found in '{0}' excluding '{1}' - ", - "No results found in '{0}' - ", - "No results found excluding '{0}' - ", - "No results found. Review your settings for configured exclusions and check your gitignore files - ", - "Search again", - "Search again in all files", - "Open Settings", - "Learn More", - "Search returned {0} results in {1} files", - "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.", - " - Search: {0}", - "exclude settings and ignore files are disabled", - "enable", - "Use Exclude Settings and Ignore Files", - "searching only in open files", - "disable", - "Search in entire workspace", - "Copy current search results to an editor", - "Open in editor", - "{0} result in {1} file", - "{0} result in {1} files", - "{0} results in {1} file", - "{0} results in {1} files", - "You have not opened or specified a folder. Only open files are currently searched - ", - "Open Folder" + "Controls whether the last typed input to Quick Search should be restored when opening it the next time.", + "Search", + "Search" ], - "vs/workbench/contrib/sash/browser/sash.contribution": [ - "Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if you feel it's hard to resize views using the mouse.", - "Controls the hover feedback delay in milliseconds of the dragging area in between views/editors." + "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ + "Search Editor", + "Search Editor", + "Search Editor", + "Delete File Results", + "New Search Editor", + "Open Search Editor", + "Open New Search Editor to the Side", + "Open Results in Editor", + "Search Again", + "Focus Search Editor Input", + "Focus Search Editor Files to Include", + "Focus Search Editor Files to Exclude", + "Toggle Match Case", + "Toggle Match Whole Word", + "Toggle Use Regular Expression", + "Toggle Context Lines", + "Increase Context Lines", + "Decrease Context Lines", + "Select All Matches", + "Open New Search Editor" + ], + "vs/workbench/contrib/sash/browser/sash.contribution": [ + "Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if you feel it's hard to resize views using the mouse.", + "Controls the hover feedback delay in milliseconds of the dragging area in between views/editors." + ], + "vs/workbench/contrib/scm/browser/scm.contribution": [ + "View icon of the Source Control view.", + "Source Control", + "No source control providers registered.", + "None of the registered source control providers work in Restricted Mode.", + "Manage Workspace Trust", + "Source &&Control", + "Source Control", + "Show the diff decorations in all available locations.", + "Show the diff decorations only in the editor gutter.", + "Show the diff decorations only in the overview ruler.", + "Show the diff decorations only in the minimap.", + "Do not show the diff decorations.", + "Controls diff decorations in the editor.", + "Controls the width(px) of diff decorations in gutter (added & modified).", + "Show the diff decorator in the gutter at all times.", + "Show the diff decorator in the gutter only on hover.", + "Controls the visibility of the Source Control diff decorator in the gutter.", + "Show the inline diff Peek view on click.", + "Do nothing.", + "Controls the behavior of Source Control diff gutter decorations.", + "Controls whether a pattern is used for the diff decorations in gutter.", + "Use pattern for the diff decorations in gutter for added lines.", + "Use pattern for the diff decorations in gutter for modified lines.", + "Ignore leading and trailing whitespace.", + "Do not ignore leading and trailing whitespace.", + "Inherit from `diffEditor.ignoreTrimWhitespace`.", + "Controls whether leading and trailing whitespace is ignored in Source Control diff gutter decorations.", + "Controls whether inline actions are always visible in the Source Control view.", + "Show the sum of all Source Control Provider count badges.", + "Show the count badge of the focused Source Control Provider.", + "Disable the Source Control count badge.", + "Controls the count badge on the Source Control icon on the Activity Bar.", + "Hide Source Control Provider count badges.", + "Only show count badge for Source Control Provider when non-zero.", + "Show Source Control Provider count badges.", + "Controls the count badges on Source Control Provider headers. These headers appear in the \"Source Control\", and \"Source Control Sync\" views when there is more than one provider or when the {0} setting is enabled, as well as in the \"Source Control Repositories\" view.", + "Show the repository changes as a tree.", + "Show the repository changes as a list.", + "Controls the default Source Control repository view mode.", + "Sort the repository changes by file name.", + "Sort the repository changes by path.", + "Sort the repository changes by Source Control status.", + "Controls the default Source Control repository changes sort order when viewed as a list.", + "Controls whether the Source Control view should automatically reveal and select files when opening them.", + "Controls the font for the input message. Use `default` for the workbench user interface font family, `editor` for the `#editor.fontFamily#`'s value, or a custom font family.", + "Controls the font size for the input message in pixels.", + "Controls whether repositories should always be visible in the Source Control view.", + "Repositories in the Source Control Repositories view are sorted by discovery time. Repositories in the Source Control view are sorted in the order that they were selected.", + "Repositories in the Source Control Repositories and Source Control views are sorted by repository name.", + "Repositories in the Source Control Repositories and Source Control views are sorted by repository path.", + "Controls the sort order of the repositories in the source control repositories view.", + "Controls how many repositories are visible in the Source Control Repositories section. Set to 0, to be able to manually resize the view.", + "Controls whether an action button can be shown in the Source Control view.", + "Controls whether the Source Control Sync view is shown.", + "Source Control: Accept Input", + "Source Control: View Next Commit", + "Source Control: View Previous Commit", + "Open in External Terminal", + "Open in Integrated Terminal", + "Source Control", + "Source Control Repositories", + "Source Control Sync" + ], + "vs/workbench/contrib/search/browser/searchView": [ + "Search was canceled before any results could be found - ", + "Toggle Search Details", + "files to include", + "e.g. *.ts, src/**/include", + "files to exclude", + "e.g. *.ts, src/**/exclude", + "Replace All", + "&&Replace", + "Replaced {0} occurrence across {1} file with '{2}'.", + "Replaced {0} occurrence across {1} file.", + "Replaced {0} occurrence across {1} files with '{2}'.", + "Replaced {0} occurrence across {1} files.", + "Replaced {0} occurrences across {1} file with '{2}'.", + "Replaced {0} occurrences across {1} file.", + "Replaced {0} occurrences across {1} files with '{2}'.", + "Replaced {0} occurrences across {1} files.", + "Replace {0} occurrence across {1} file with '{2}'?", + "Replace {0} occurrence across {1} file?", + "Replace {0} occurrence across {1} files with '{2}'?", + "Replace {0} occurrence across {1} files?", + "Replace {0} occurrences across {1} file with '{2}'?", + "Replace {0} occurrences across {1} file?", + "Replace {0} occurrences across {1} files with '{2}'?", + "Replace {0} occurrences across {1} files?", + "Empty Search", + "Search path not found: {0}", + "No results found in open editors matching '{0}' excluding '{1}' - ", + "No results found in open editors matching '{0}' - ", + "No results found in open editors excluding '{0}' - ", + "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - ", + "No results found in '{0}' excluding '{1}' - ", + "No results found in '{0}' - ", + "No results found excluding '{0}' - ", + "No results found. Review your settings for configured exclusions and check your gitignore files - ", + "Search again", + "Search again in all files", + "Open Settings", + "Learn More", + "Search returned {0} results in {1} files", + "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.", + " - Search: {0}", + "exclude settings and ignore files are disabled", + "enable", + "Use Exclude Settings and Ignore Files", + "searching only in open files", + "disable", + "Search in entire workspace", + "Copy current search results to an editor", + "Open in editor", + "{0} result in {1} file", + "{0} result in {1} files", + "{0} results in {1} file", + "{0} results in {1} files", + "You have not opened or specified a folder. Only open files are currently searched - ", + "Open Folder" + ], + "vs/workbench/contrib/debug/browser/debugEditorContribution": [ + "Color for the debug inline value text.", + "Color for the debug inline value background." ], "vs/workbench/contrib/debug/browser/debug.contribution": [ "Debug", @@ -19330,11 +19599,6 @@ "Type the name of a debug console to open.", "Show All Debug Consoles", "Terminate Thread", - "Focus on Debug Console View", - "Jump to Cursor", - "Set Next Statement", - "Inline Breakpoint", - "Terminate Thread", "Restart Frame", "Copy Call Stack", "View Binary Data", @@ -19350,7 +19614,6 @@ "Copy Value", "View Binary Data", "Remove Expression", - "Run or Debug...", "&&Run", "&&Start Debugging", "Run &&Without Debugging", @@ -19364,16 +19627,8 @@ "Inline Breakp&&oint", "&&New Breakpoint", "&&Install Additional Debuggers...", - "Debug Console", - "Debug Console", "De&&bug Console", - "Run and Debug", "&&Run", - "Variables", - "Watch", - "Call Stack", - "Breakpoints", - "Loaded Scripts", "Disassembly", "Debug", "Allow setting breakpoints in any file.", @@ -19421,7 +19676,26 @@ "Always confirm if there are debug sessions.", "Show Source Code in Disassembly View.", "Automatically show values for variables that are lazily resolved by the debugger, such as getters.", - "Color status bar when debugger is active" + "Color of the Status bar when debugger is active.", + "Hide 'Start Debugging' control in title bar of 'Run and Debug' view while debugging is active. Only relevant when `{0}` is not `docked`.", + "Terminate Thread", + "Focus on Debug Console View", + "Jump to Cursor", + "Set Next Statement", + "Inline Breakpoint", + "Run or Debug...", + "Debug Console", + "Debug Console", + "Run and Debug", + "Variables", + "Watch", + "Call Stack", + "Breakpoints", + "Loaded Scripts" + ], + "vs/workbench/contrib/debug/browser/callStackEditorContribution": [ + "Background color for the highlight of line at the top stack frame position.", + "Background color for the highlight of line at focused stack frame position." ], "vs/workbench/contrib/debug/browser/breakpointEditorContribution": [ "Logpoint", @@ -19463,73 +19737,16 @@ "Icon color for the current breakpoint stack frame.", "Icon color for all breakpoint stack frames." ], - "vs/workbench/contrib/scm/browser/scm.contribution": [ - "View icon of the Source Control view.", - "Source Control", - "No source control providers registered.", - "None of the registered source control providers work in Restricted Mode.", - "Manage Workspace Trust", - "Source Control", - "Source &&Control", - "Source Control Repositories", - "Source Control Sync", - "Source Control", - "Show the diff decorations in all available locations.", - "Show the diff decorations only in the editor gutter.", - "Show the diff decorations only in the overview ruler.", - "Show the diff decorations only in the minimap.", - "Do not show the diff decorations.", - "Controls diff decorations in the editor.", - "Controls the width(px) of diff decorations in gutter (added & modified).", - "Show the diff decorator in the gutter at all times.", - "Show the diff decorator in the gutter only on hover.", - "Controls the visibility of the Source Control diff decorator in the gutter.", - "Show the inline diff Peek view on click.", - "Do nothing.", - "Controls the behavior of Source Control diff gutter decorations.", - "Controls whether a pattern is used for the diff decorations in gutter.", - "Use pattern for the diff decorations in gutter for added lines.", - "Use pattern for the diff decorations in gutter for modified lines.", - "Ignore leading and trailing whitespace.", - "Do not ignore leading and trailing whitespace.", - "Inherit from `diffEditor.ignoreTrimWhitespace`.", - "Controls whether leading and trailing whitespace is ignored in Source Control diff gutter decorations.", - "Controls whether inline actions are always visible in the Source Control view.", - "Show the sum of all Source Control Provider count badges.", - "Show the count badge of the focused Source Control Provider.", - "Disable the Source Control count badge.", - "Controls the count badge on the Source Control icon on the Activity Bar.", - "Hide Source Control Provider count badges.", - "Only show count badge for Source Control Provider when non-zero.", - "Show Source Control Provider count badges.", - "Controls the count badges on Source Control Provider headers. These headers only appear when there is more than one provider.", - "Show the repository changes as a tree.", - "Show the repository changes as a list.", - "Controls the default Source Control repository view mode.", - "Sort the repository changes by file name.", - "Sort the repository changes by path.", - "Sort the repository changes by Source Control status.", - "Controls the default Source Control repository changes sort order when viewed as a list.", - "Controls whether the Source Control view should automatically reveal and select files when opening them.", - "Controls the font for the input message. Use `default` for the workbench user interface font family, `editor` for the `#editor.fontFamily#`'s value, or a custom font family.", - "Controls the font size for the input message in pixels.", - "Controls whether repositories should always be visible in the Source Control view.", - "Repositories in the Source Control Repositories view are sorted by discovery time. Repositories in the Source Control view are sorted in the order that they were selected.", - "Repositories in the Source Control Repositories and Source Control views are sorted by repository name.", - "Repositories in the Source Control Repositories and Source Control views are sorted by repository path.", - "Controls the sort order of the repositories in the source control repositories view.", - "Controls how many repositories are visible in the Source Control Repositories section. Set to 0, to be able to manually resize the view.", - "Controls whether an action button can be shown in the Source Control view.", - "Controls whether the Source Control Sync view is shown.", - "Source Control: Accept Input", - "Source Control: View Next Commit", - "Source Control: View Previous Commit", - "Open in External Terminal", - "Open in Integrated Terminal" + "vs/workbench/contrib/debug/browser/debugViewlet": [ + "Open &&Configurations", + "Select a workspace folder to create a launch.json file in or add it to the workspace config file", + "Debug Console", + "Start Additional Session" ], - "vs/workbench/contrib/debug/browser/debugEditorContribution": [ - "Color for the debug inline value text.", - "Color for the debug inline value background." + "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ + "Merge Editor", + "Uses the legacy diffing algorithm.", + "Uses the advanced diffing algorithm." ], "vs/workbench/contrib/debug/browser/repl": [ "Filter (e.g. text, !exclude)", @@ -19541,16 +19758,11 @@ "Debug: Console Copy All", "Select Debug Console", "Clear Console", - "Debug console was cleared", "Collapse All", "Paste", "Copy All", "Copy" ], - "vs/workbench/contrib/debug/browser/callStackEditorContribution": [ - "Background color for the highlight of line at the top stack frame position.", - "Background color for the highlight of line at focused stack frame position." - ], "vs/workbench/contrib/markers/browser/markers.contribution": [ "View icon of the markers view.", "&&Problems", @@ -19591,17 +19803,6 @@ "10K+", "Total {0} Problems" ], - "vs/workbench/contrib/debug/browser/debugViewlet": [ - "Open &&Configurations", - "Select a workspace folder to create a launch.json file in or add it to the workspace config file", - "Debug Console", - "Start Additional Session" - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ - "Merge Editor", - "Uses the legacy diffing algorithm.", - "Uses the advanced diffing algorithm." - ], "vs/workbench/contrib/commands/common/commands.contribution": [ "Run Commands", "Run several commands", @@ -19609,6 +19810,11 @@ "'runCommands' has received an argument with incorrect type. Please, review the argument passed to the command.", "'runCommands' has not received commands to run. Did you forget to pass commands in the 'runCommands' argument?" ], + "vs/workbench/contrib/url/browser/url.contribution": [ + "Open URL", + "URL to open", + "When enabled, trusted domain prompts will appear when opening links in trusted workspaces." + ], "vs/workbench/contrib/comments/browser/comments.contribution": [ "Comments", "Controls when the comments panel should open.", @@ -19640,18 +19846,98 @@ "- Submit Comment ({0})", "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding." ], - "vs/workbench/contrib/url/browser/url.contribution": [ - "Open URL", - "URL to open", - "When enabled, trusted domain prompts will appear when opening links in trusted workspaces." + "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ + "webview editor" ], "vs/workbench/contrib/webview/browser/webview.contribution": [ "Cut", "Copy", "Paste" ], - "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ - "webview editor" + "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ + "Remote", + "Installed", + "Install Local Extensions in '{0}'...", + "Install Remote Extensions Locally...", + "Search Extensions in Marketplace", + "1 extension found in the {0} section.", + "1 extension found.", + "{0} extensions found in the {1} section.", + "{0} extensions found.", + "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", + "Open User Settings", + "{0} requires update", + "{0} require update", + "{0} requires reload", + "{0} require reload", + "We have uninstalled '{0}' which was reported to be problematic.", + "Reload Now", + "Popular", + "Recommended", + "Enabled", + "Disabled", + "Marketplace", + "Installed", + "Recently Updated", + "Enabled", + "Disabled", + "Available Updates", + "Builtin", + "Workspace Unsupported", + "Workspace Recommendations", + "Other Recommendations", + "Features", + "Themes", + "Programming Languages", + "Disabled in Restricted Mode", + "Limited in Restricted Mode", + "Disabled in Virtual Workspaces", + "Limited in Virtual Workspaces", + "Deprecated" + ], + "vs/workbench/contrib/output/browser/outputView": [ + "{0} - Output", + "Output channel for '{0}'", + "Output", + "Output panel" + ], + "vs/workbench/contrib/output/browser/output.contribution": [ + "View icon of the output view.", + "&&Output", + "Switch Output", + "Switch Output", + "Select Output Channel", + "Turn Auto Scrolling Off", + "Turn Auto Scrolling On", + "Extension Logs", + "Select Log", + "The id of the log file to open, for example `\"window\"`. Currently the best way to get this is to get the ID by checking the `workbench.action.output.show.` commands", + "Select Log File", + "Output", + "Enable/disable the ability of smart scrolling in the output view. Smart scrolling allows you to lock scrolling automatically when you click in the output view and unlocks when you click in the last line.", + "Output", + "Output", + "Show Output Channels...", + "Output", + "Clear Output", + "Toggle Auto Scrolling", + "Open Log Output File", + "Show Logs...", + "Open Log File..." + ], + "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ + "Open in Integrated Terminal", + "Open in External Terminal", + "Open in Windows Terminal" + ], + "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ + "A setting has changed that requires a restart to take effect.", + "A setting has changed that requires a reload to take effect.", + "Press the restart button to restart {0} and enable the setting.", + "Press the reload button to reload {0} and enable the setting.", + "&&Restart", + "&&Reload", + "Restarting extension host due to a workspace folder change." ], "vs/workbench/contrib/extensions/browser/extensions.contribution": [ "Press Enter to manage extensions.", @@ -19791,92 +20077,6 @@ "Extensions", "Extensions" ], - "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ - "Remote", - "Installed", - "Install Local Extensions in '{0}'...", - "Install Remote Extensions Locally...", - "Popular", - "Recommended", - "Enabled", - "Disabled", - "Marketplace", - "Installed", - "Recently Updated", - "Enabled", - "Disabled", - "Available Updates", - "Builtin", - "Workspace Unsupported", - "Workspace Recommendations", - "Other Recommendations", - "Features", - "Themes", - "Programming Languages", - "Disabled in Restricted Mode", - "Limited in Restricted Mode", - "Disabled in Virtual Workspaces", - "Limited in Virtual Workspaces", - "Deprecated", - "Search Extensions in Marketplace", - "1 extension found in the {0} section.", - "1 extension found.", - "{0} extensions found in the {1} section.", - "{0} extensions found.", - "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", - "Open User Settings", - "{0} requires update", - "{0} require update", - "{0} requires reload", - "{0} require reload", - "We have uninstalled '{0}' which was reported to be problematic.", - "Reload Now" - ], - "vs/workbench/contrib/output/browser/output.contribution": [ - "View icon of the output view.", - "Output", - "Output", - "&&Output", - "Switch Output", - "Switch Output", - "Show Output Channels...", - "Output", - "Select Output Channel", - "Clear Output", - "Output was cleared", - "Toggle Auto Scrolling", - "Turn Auto Scrolling Off", - "Turn Auto Scrolling On", - "Open Log Output File", - "Show Logs...", - "Extension Logs", - "Select Log", - "Open Log File...", - "The id of the log file to open, for example `\"window\"`. Currently the best way to get this is to get the ID by checking the `workbench.action.output.show.` commands", - "Select Log File", - "Output", - "Enable/disable the ability of smart scrolling in the output view. Smart scrolling allows you to lock scrolling automatically when you click in the output view and unlocks when you click in the last line." - ], - "vs/workbench/contrib/output/browser/outputView": [ - "{0} - Output", - "Output channel for '{0}'", - "Output", - "Output panel" - ], - "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ - "Open in Integrated Terminal", - "Open in External Terminal", - "Open in Windows Terminal" - ], - "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ - "A setting has changed that requires a restart to take effect.", - "A setting has changed that requires a reload to take effect.", - "Press the restart button to restart {0} and enable the setting.", - "Press the reload button to reload {0} and enable the setting.", - "&&Restart", - "&&Reload", - "Restarting extension host due to a workspace folder change." - ], "vs/workbench/contrib/tasks/browser/task.contribution": [ "Building...", "Running Tasks", @@ -19984,14 +20184,14 @@ "User snippet configuration", "A list of language names to which this snippet applies, e.g. 'typescript,javascript'." ], + "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ + "Toggle Keyboard Shortcuts Troubleshooting" + ], "vs/workbench/contrib/folding/browser/folding.contribution": [ "All", "All active folding range providers", "Defines a default folding range provider that takes precedence over all other folding range providers. Must be the identifier of an extension contributing a folding range provider." ], - "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ - "Toggle Keyboard Shortcuts Troubleshooting" - ], "vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution": [ "Configure", "Color Decorator Status", @@ -20021,6 +20221,17 @@ "Apply Update", "&&Update" ], + "vs/workbench/contrib/surveys/browser/nps.contribution": [ + "Do you mind taking a quick feedback survey?", + "Take Survey", + "Remind Me Later", + "Don't Show Again" + ], + "vs/workbench/contrib/surveys/browser/ces.contribution": [ + "Got a moment to help the VS Code team? Please tell us about your experience with VS Code so far.", + "Give Feedback", + "Remind Me Later" + ], "vs/workbench/contrib/themes/browser/themes.contribution": [ "Icon for the 'Manage' action in the theme selection quick pick.", "Type to Search More. Select to Install. Up/Down Keys to Preview", @@ -20064,23 +20275,12 @@ "Cancel", "Visual Studio Code now ships with a new default theme '{0}'. Do you want to give it a try?" ], - "vs/workbench/contrib/surveys/browser/nps.contribution": [ - "Do you mind taking a quick feedback survey?", - "Take Survey", - "Remind Me Later", - "Don't Show Again" - ], "vs/workbench/contrib/surveys/browser/languageSurveys.contribution": [ "Help us improve our support for {0}", "Take Short Survey", "Remind Me Later", "Don't Show Again" ], - "vs/workbench/contrib/surveys/browser/ces.contribution": [ - "Got a moment to help the VS Code team? Please tell us about your experience with VS Code so far.", - "Give Feedback", - "Remind Me Later" - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution": [ "Welcome", "Welcome", @@ -20117,21 +20317,6 @@ "Create New File ({0})", "Text File" ], - "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ - "Document Symbols" - ], - "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ - "Whether a type hierarchy provider is available", - "Whether type hierarchy peek is currently showing", - "whether type hierarchy shows super types or subtypes", - "No results", - "Failed to show type hierarchy", - "Peek Type Hierarchy", - "Show Supertypes", - "Show Subtypes", - "Refocus Type Hierarchy", - "Close" - ], "vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution": [ "Whether a call hierarchy provider is available", "Whether call hierarchy peek is currently showing", @@ -20146,17 +20331,24 @@ "Refocus Call Hierarchy", "Close" ], - "vs/workbench/contrib/languageDetection/browser/languageDetection.contribution": [ - "Accept Detected Language: {0}", - "Language Detection", - "Change to Detected Language: {0}", - "Detect Language from Content", - "Unable to detect editor language" + "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ + "Whether a type hierarchy provider is available", + "Whether type hierarchy peek is currently showing", + "whether type hierarchy shows super types or subtypes", + "No results", + "Failed to show type hierarchy", + "Peek Type Hierarchy", + "Show Supertypes", + "Show Subtypes", + "Refocus Type Hierarchy", + "Close" + ], + "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ + "Document Symbols" ], "vs/workbench/contrib/outline/browser/outline.contribution": [ "View icon of the outline view.", "Outline", - "Outline", "Render Outline elements with icons.", "Controls whether Outline items are collapsed or expanded.", "Collapse all items.", @@ -20189,19 +20381,47 @@ "When enabled, Outline shows `struct`-symbols.", "When enabled, Outline shows `event`-symbols.", "When enabled, Outline shows `operator`-symbols.", - "When enabled, Outline shows `typeParameter`-symbols." + "When enabled, Outline shows `typeParameter`-symbols.", + "Outline" ], - "vs/workbench/contrib/userDataSync/browser/userDataSync.contribution": [ - "Settings sync is suspended temporarily because the current device is making too many requests. Please reload {0} to resume.", - "Settings sync is suspended temporarily because the current device is making too many requests. Please restart {0} to resume.", - "Show Log", - "Reload", - "Restart", + "vs/workbench/contrib/languageDetection/browser/languageDetection.contribution": [ + "Accept Detected Language: {0}", + "Language Detection", + "Change to Detected Language: {0}", + "Detect Language from Content", + "Unable to detect editor language" + ], + "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ + "Editor Language Status", + "Editor Language Status: {0}", + "Add to Status Bar", + "Remove from Status Bar", + "{0}, {1}", + "{0}", + "{0} (Language Status)", + "Reset Language Status Interaction Counter" + ], + "vs/workbench/contrib/userDataSync/browser/userDataSync.contribution": [ + "Settings sync is suspended temporarily because the current device is making too many requests. Please reload {0} to resume.", + "Settings sync is suspended temporarily because the current device is making too many requests. Please restart {0} to resume.", + "Show Log", + "Reload", + "Restart", "Operation Id: {0}", "Settings sync is disabled because the current device is making too many requests. Please wait for 10 minutes and turn on sync.", "Settings Sync. Operation Id: {0}", "Show Log" ], + "vs/workbench/contrib/timeline/browser/timeline.contribution": [ + "View icon of the timeline view.", + "Icon for the open timeline action.", + "Timeline", + "The number of items to show in the Timeline view by default and when loading more items. Setting to `null` (the default) will automatically choose a page size based on the visible area of the Timeline view.", + "Experimental. Controls whether the Timeline view will load the next page of items when you scroll to the end of the list.", + "Open Timeline", + "Icon for the filter timeline action.", + "Filter Timeline" + ], "vs/workbench/contrib/editSessions/browser/editSessions.contribution": [ "Continue Working On...", "Open In Local Folder", @@ -20257,26 +20477,6 @@ "Controls whether to prompt the user to store working changes in the cloud when using Continue Working On.", "Controls whether to surface cloud changes which partially match the current session." ], - "vs/workbench/contrib/timeline/browser/timeline.contribution": [ - "View icon of the timeline view.", - "Icon for the open timeline action.", - "Timeline", - "The number of items to show in the Timeline view by default and when loading more items. Setting to `null` (the default) will automatically choose a page size based on the visible area of the Timeline view.", - "Experimental. Controls whether the Timeline view will load the next page of items when you scroll to the end of the list.", - "Open Timeline", - "Icon for the filter timeline action.", - "Filter Timeline" - ], - "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ - "Editor Language Status", - "Editor Language Status: {0}", - "Add to Status Bar", - "Remove from Status Bar", - "{0}, {1}", - "{0}", - "{0} (Language Status)", - "Reset Language Status Interaction Counter" - ], "vs/workbench/contrib/workspaces/browser/workspaces.contribution": [ "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", "Open Workspace", @@ -20286,12 +20486,6 @@ "Open Workspace", "This workspace is already open." ], - "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ - "The extension 'Bracket pair Colorizer' got disabled because it was deprecated.", - "Uninstall Extension", - "Enable Native Bracket Pair Colorization", - "More Info" - ], "vs/workbench/contrib/workspace/browser/workspace.contribution": [ "You are trying to open untrusted files in a workspace which is trusted.", "You are trying to open untrusted files in a window which is trusted.", @@ -20361,6 +20555,15 @@ "Always open untrusted files in a separate window in restricted mode without prompting.", "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window." ], + "vs/workbench/contrib/share/browser/share.contribution": [ + "Share...", + "Generating link...", + "Copied text to clipboard!", + "Copied link to clipboard!", + "Close", + "Open Link", + "Controls whether to render the Share action next to the command center when {0} is {1}." + ], "vs/workbench/contrib/audioCues/browser/audioCues.contribution": [ "Enable audio cue when a screen reader is attached.", "Enable audio cue.", @@ -20378,34 +20581,35 @@ "Plays a sound when a task fails (non-zero exit code).", "Plays a sound when a terminal command fails (non-zero exit code).", "Plays a sound when terminal Quick Fixes are available.", - "Plays a sound when the focus moves to an inserted line in accessible diff viewer mode or to the next/previous change", - "Plays a sound when the focus moves to a deleted line in accessible diff viewer mode or to the next/previous change", - "Plays a sound when the focus moves to a modified line in accessible diff viewer mode or to the next/previous change", + "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to a deleted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change.", "Plays a sound when a notebook cell execution is successfully completed.", "Plays a sound when a notebook cell execution fails.", "Plays a sound when a chat request is made.", "Plays a sound on loop while the response is pending.", - "Plays a sound on loop while the response has been received." - ], - "vs/workbench/contrib/share/browser/share.contribution": [ - "Share...", - "Generating link...", - "Copied text to clipboard!", - "Copied link to clipboard!", - "Close", - "Open Link", - "Controls whether to render the Share action next to the command center when {0} is {1}." + "Plays a sound on loop while the response has been received.", + "Plays a sound when a feature is cleared (for example, the terminal, debug console, or output channel). When this is disabled, an aria alert will announce 'Cleared'.", + "Plays a sound when a file is saved. Also see {0}", + "Plays the audio cue when a user explicitly saves a file.", + "Plays the audio cue whenever a file is saved, including auto save.", + "Never plays the audio cue.", + "Plays a sound when a file or notebook is formatted. Also see {0}", + "Plays the audio cue when a user explicitly formats a file.", + "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell.", + "Never plays the audio cue." + ], + "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution": [ + "When enabled, available entitlements for the account will be show in the accounts menu." ], "vs/workbench/browser/workbench": [ "Failed to load a required file. Please restart the application to try again. Details: {0}" ], - "vs/workbench/services/configuration/browser/configurationService": [ - "Contribute defaults for configurations", - "Experiments", - "Configure settings to be applied for all profiles." - ], - "vs/platform/workspace/common/workspace": [ - "Code Workspace" + "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ + "The extension 'Bracket pair Colorizer' got disabled because it was deprecated.", + "Uninstall Extension", + "Enable Native Bracket Pair Colorization", + "More Info" ], "vs/workbench/electron-sandbox/window": [ "Restart", @@ -20445,21 +20649,21 @@ "There is a dependency cycle in the AMD modules that needs to be resolved!", "It is not recommended to run {0} as root user.", "Files you store within the installation folder ('{0}') may be OVERWRITTEN or DELETED IRREVERSIBLY without warning at update time.", - "You are running {0} 32-bit, which will soon stop receiving updates on Windows. Consider upgrading to the 64-bit build.", - "Learn More", - "{0}. Use navigation keys to access banner actions.", - "Learn More", "{0} on {1} will soon stop receiving updates. Consider upgrading your macOS version.", "Learn More", - "{0}. Use navigation keys to access banner actions.", - "Learn More", "Resolving shell environment...", "Learn More" ], - "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ - "Open Developer Tools", - "Open in browser", - "Failed to connect to the remote extension host server (Error: {0})" + "vs/platform/workspace/common/workspace": [ + "Code Workspace" + ], + "vs/workbench/services/configuration/browser/configurationService": [ + "Contribute defaults for configurations", + "Experiments", + "Configure settings to be applied for all profiles." + ], + "vs/workbench/services/log/electron-sandbox/logService": [ + "Window" ], "vs/platform/workspace/common/workspaceTrust": [ "Trusted", @@ -20470,8 +20674,16 @@ "Profiles", "Profile" ], - "vs/workbench/services/log/electron-sandbox/logService": [ - "Window" + "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ + "Open Developer Tools", + "Open in browser", + "Failed to connect to the remote extension host server (Error: {0})" + ], + "vs/workbench/electron-sandbox/actions/developerActions": [ + "Toggle Developer Tools", + "Configure Runtime Arguments", + "Reload With Extensions Disabled", + "Open User Data Folder" ], "vs/platform/configuration/common/configurationRegistry": [ "Default Language Configuration Overrides", @@ -20485,12 +20697,6 @@ "Cannot register '{0}'. This property is already registered.", "Cannot register '{0}'. The associated policy {1} is already registered with {2}." ], - "vs/workbench/electron-sandbox/actions/developerActions": [ - "Toggle Developer Tools", - "Configure Runtime Arguments", - "Reload With Extensions Disabled", - "Open User Data Folder" - ], "vs/workbench/electron-sandbox/actions/windowActions": [ "Close Window", "Clos&&e Window", @@ -20506,7 +20712,9 @@ "{0}, window with unsaved changes", "Current Window", "Switch Window...", - "Quick Switch Window..." + "Quick Switch Window...", + "Split Window (Experimental)", + "&&Split Window (Experimental)" ], "vs/platform/contextkey/common/contextkeys": [ "Whether the operating system is macOS", @@ -20519,13 +20727,12 @@ "Quality type of VS Code", "Whether keyboard focus is inside an input box" ], - "vs/workbench/common/configuration": [ - "Application", - "Workbench", - "Security", - "UNC host names must not contain backslashes.", - "A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.", - "If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc." + "vs/workbench/electron-sandbox/actions/installActions": [ + "Shell Command", + "Install '{0}' command in PATH", + "Shell command '{0}' successfully installed in PATH.", + "Uninstall '{0}' command from PATH", + "Shell command '{0}' successfully uninstalled from PATH." ], "vs/workbench/common/contextkeys": [ "The kind of workspace opened in the window, either 'empty' (no workspace), 'folder' (single folder) or 'workspace' (multi-root workspace)", @@ -20561,10 +20768,13 @@ "Whether editors split vertically", "Whether the editor area is visible", "Whether editor tabs are visible", + "Editor group is maximized", "Whether the sidebar is visible", "Whether the sidebar has keyboard focus", "The identifier of the active viewlet", "Whether the status bar has keyboard focus", + "Style of the window title bar", + "Whether the title bar is visible", "Whether the banner has keyboard focus", "Whether a notification has keyboard focus", "Whether the notifications center is visible", @@ -20589,6 +20799,24 @@ "Whether a resource is present or not", "Whether the resource is backed by a file system provider" ], + "vs/workbench/common/configuration": [ + "Application", + "Workbench", + "Security", + "UNC host names must not contain backslashes.", + "A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.", + "If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc." + ], + "vs/workbench/browser/parts/dialogs/dialogHandler": [ + "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + "&&Copy", + "OK" + ], + "vs/workbench/electron-sandbox/parts/dialogs/dialogHandler": [ + "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nElectronBuildId: {4}\nChromium: {5}\nNode.js: {6}\nV8: {7}\nOS: {8}", + "&&Copy", + "OK" + ], "vs/workbench/services/dialogs/browser/abstractFileDialogService": [ "Your changes will be lost if you don't save them.", "Do you want to save the changes you made to {0}?", @@ -20606,13 +20834,6 @@ "All Files", "No Extension" ], - "vs/workbench/electron-sandbox/actions/installActions": [ - "Shell Command", - "Install '{0}' command in PATH", - "Shell command '{0}' successfully installed in PATH.", - "Uninstall '{0}' command from PATH", - "Shell command '{0}' successfully uninstalled from PATH." - ], "vs/workbench/services/textfile/browser/textFileService": [ "File Created", "File Replaced", @@ -20623,17 +20844,10 @@ "File seems to be binary and cannot be opened as text", "'{0}' already exists. Do you want to replace it?", "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", - "&&Replace" - ], - "vs/workbench/browser/parts/dialogs/dialogHandler": [ - "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - "&&Copy", - "OK" - ], - "vs/workbench/electron-sandbox/parts/dialogs/dialogHandler": [ - "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nElectronBuildId: {4}\nChromium: {5}\nNode.js: {6}\nV8: {7}\nOS: {8}", - "&&Copy", - "OK" + "&&Replace", + "'{0}' is marked as read-only. Do you want to save anyway?", + "Paths can be configured as read-only via settings.", + "&&Save Anyway" ], "vs/workbench/common/theme": [ "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", @@ -20665,7 +20879,7 @@ "Border color of an empty editor group that is focused. Editor groups are the containers of editors.", "Background color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.", "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.", - "Background color of the editor group title header when tabs are disabled (`\"workbench.editor.showTabs\": false`). Editor groups are the containers of editors.", + "Background color of the editor group title header when (`\"workbench.editor.showTabs\": \"single\"`). Editor groups are the containers of editors.", "Border color of the editor group title header. Editor groups are the containers of editors.", "Color to separate multiple editor groups from each other. Editor groups are the containers of editors.", "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.", @@ -20787,6 +21001,7 @@ "Foreground color for links in text.", "Foreground color for links in text when clicked on and on mouse hover.", "Foreground color for preformatted text segments.", + "Background color for preformatted text segments.", "Background color for block quotes in text.", "Border color for block quotes in text.", "Background color for code blocks in text.", @@ -21003,11 +21218,6 @@ "Unable to write into workspace configuration file. Please open the file to correct errors/warnings in it and try again.", "Open Workspace Configuration" ], - "vs/platform/keyboardLayout/common/keyboardConfig": [ - "Keyboard", - "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`.", - "Controls if the AltGraph+ modifier should be treated as Ctrl+Alt+." - ], "vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService": [ "Cannot substitute command variable '{0}' because command did not return a result of type string.", "Variable '{0}' must be defined in an '{1}' section of the debug or task configuration.", @@ -21017,6 +21227,11 @@ "Input variable '{0}' can only be of type 'promptString', 'pickString', or 'command'.", "Undefined input variable '{0}' encountered. Remove or define '{0}' to continue." ], + "vs/platform/keyboardLayout/common/keyboardConfig": [ + "Keyboard", + "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`.", + "Controls if the AltGraph+ modifier should be treated as Ctrl+Alt+." + ], "vs/workbench/services/extensionManagement/common/extensionManagementService": [ "Cannot uninstall extension '{0}'. Extension '{1}' depends on this.", "Cannot uninstall extension '{0}'. Extensions '{1}' and '{2}' depend on this.", @@ -21040,15 +21255,59 @@ "Contains extensions which are not supported.", "'{0}' contains extensions which are not supported in {1}." ], + "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService": [ + "Can't install release version of '{0}' extension because it has no release version.", + "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2})." + ], + "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupTracker": [ + "The following editors with unsaved changes could not be saved to the back up location.", + "The following editors with unsaved changes could not be saved or reverted.", + "Try saving or reverting the editors with unsaved changes first and then try again.", + "Backing up editors with unsaved changes is taking a bit longer...", + "Click 'Cancel' to stop waiting and to save or revert editors with unsaved changes.", + "Saving editors with unsaved changes is taking a bit longer...", + "Reverting editors with unsaved changes is taking a bit longer...", + "Discarding backups is taking a bit longer..." + ], "vs/workbench/services/workingCopy/common/workingCopyHistoryService": [ "File Saved", "File Moved", "File Renamed", "Saving local history" ], - "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService": [ - "Can't install release version of '{0}' extension because it has no release version.", - "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2})." + "vs/platform/action/common/actionCommonCategories": [ + "View", + "Help", + "Test", + "File", + "Preferences", + "Developer" + ], + "vs/workbench/services/extensions/common/abstractExtensionService": [ + "The following extensions contain dependency loops and have been disabled: {0}", + "The following extensions contain dependency loops and have been disabled: {0}", + "No extension host found that can launch the test runner at {0}.", + "{0} (Error: {1})", + "The following operation was blocked: {0}", + "The reason for blocking the operation: {0}", + "The reasons for blocking the operation:\n- {0}", + "The remote extension host terminated unexpectedly. Restarting...", + "Remote Extension host terminated unexpectedly 3 times within the last 5 minutes.", + "Restart Remote Extension Host" + ], + "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ + "Extensions have been modified on disk. Please reload the window.", + "Reload Window" + ], + "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService": [ + "Unable to open a new window.", + "The browser interrupted the opening of a new window. Press 'Retry' to try again.", + "To avoid this problem in the future, please ensure to allow popups for this website.", + "&&Retry" + ], + "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ + "Open Logs Folder", + "Open Extension Logs Folder" ], "vs/editor/common/editorContextKeys": [ "Whether the editor text has focus (cursor is blinking)", @@ -21091,15 +21350,11 @@ "Whether the editor has multiple document formatting providers", "Whether the editor has multiple document selection formatting providers" ], - "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupTracker": [ - "The following editors with unsaved changes could not be saved to the back up location.", - "The following editors with unsaved changes could not be saved or reverted.", - "Try saving or reverting the editors with unsaved changes first and then try again.", - "Backing up editors with unsaved changes is taking a bit longer...", - "Click 'Cancel' to stop waiting and to save or revert editors with unsaved changes.", - "Saving editors with unsaved changes is taking a bit longer...", - "Reverting editors with unsaved changes is taking a bit longer...", - "Discarding backups is taking a bit longer..." + "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ + "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.", + "Extension host did not start in 10 seconds, that might be a problem.", + "Reload Window", + "Terminating extension debug session" ], "vs/workbench/common/editor": [ "Text Editor", @@ -21107,52 +21362,31 @@ "Open Anyway", "Configure Limit" ], - "vs/platform/action/common/actionCommonCategories": [ - "View", - "Help", - "Test", - "File", - "Preferences", - "Developer" + "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ + "Search language packs in the Marketplace to change the display language to {0}.", + "Search Marketplace", + "Install language pack to change the display language to {0}.", + "Install and Restart" ], - "vs/workbench/services/extensions/common/abstractExtensionService": [ - "The following extensions contain dependency loops and have been disabled: {0}", - "The following extensions contain dependency loops and have been disabled: {0}", - "No extension host found that can launch the test runner at {0}.", - "{0} (Error: {1})", - "The following operation was blocked: {0}", - "The reason for blocking the operation: {0}", - "The reasons for blocking the operation:\n- {0}", - "The remote extension host terminated unexpectedly. Restarting...", - "Remote Extension host terminated unexpectedly 3 times within the last 5 minutes.", - "Restart Remote Extension Host" - ], - "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ - "Open Logs Folder", - "Open Extension Logs Folder" - ], - "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ - "Extensions have been modified on disk. Please reload the window.", - "Reload Window" - ], - "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ - "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.", - "Extension host did not start in 10 seconds, that might be a problem.", - "Reload Window", - "Terminating extension debug session" + "vs/workbench/contrib/localization/common/localization.contribution": [ + "Contributes localizations to the editor", + "Id of the language into which the display strings are translated.", + "Name of the language in English.", + "Name of the language in contributed language.", + "List of translations associated to the language.", + "Id of VS Code or Extension for which this translation is contributed to. Id of VS Code is always `vscode` and of extension should be in format `publisherId.extensionName`.", + "Id should be `vscode` or in format `publisherId.extensionName` for translating VS code or an extension respectively.", + "A relative path to a file containing translations for the language." ], "vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard": [ "Paste Selection Clipboard" ], - "vs/workbench/browser/editor": [ - "{0}, preview", - "{0}, pinned" - ], "vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate": [ "Start Text Mate Syntax Grammar Logging" ], - "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ - "Running Extensions" + "vs/workbench/contrib/issue/common/issue.contribution": [ + "Report Issue...", + "Report &&Issue" ], "vs/workbench/contrib/extensions/electron-sandbox/runtimeExtensionsEditor": [ "Start Extension Host Profile", @@ -21161,6 +21395,10 @@ "Save Extension Host Profile", "Save" ], + "vs/workbench/browser/editor": [ + "{0}, preview", + "{0}, pinned" + ], "vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction": [ "Start Debugging Extension Host", "Profile Extensions", @@ -21172,6 +21410,9 @@ "Open Extensions Folder", "Cleanup Extensions Folder" ], + "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ + "Running Extensions" + ], "vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService": [ "Extension Profiler", "Profiling Extension Host", @@ -21186,21 +21427,14 @@ "The extension '{0}' took a very long time to complete its last operation and it has prevented other extensions from running.", "Show Extensions" ], - "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ - "Search language packs in the Marketplace to change the display language to {0}.", - "Search Marketplace", - "Install language pack to change the display language to {0}.", - "Install and Restart" - ], - "vs/workbench/contrib/localization/common/localization.contribution": [ - "Contributes localizations to the editor", - "Id of the language into which the display strings are translated.", - "Name of the language in English.", - "Name of the language in contributed language.", - "List of translations associated to the language.", - "Id of VS Code or Extension for which this translation is contributed to. Id of VS Code is always `vscode` and of extension should be in format `publisherId.extensionName`.", - "Id should be `vscode` or in format `publisherId.extensionName` for translating VS code or an extension respectively.", - "A relative path to a file containing translations for the language." + "vs/workbench/contrib/terminal/common/terminal": [ + "Contributes terminal functionality.", + "Defines additional terminal profiles that the user can create.", + "The ID of the terminal profile provider.", + "Title for this terminal profile.", + "A codicon, URI, or light and dark URIs to associate with this terminal type.", + "Icon path when a light theme is used", + "Icon path when a dark theme is used" ], "vs/workbench/services/dialogs/browser/simpleFileDialog": [ "Open Local File...", @@ -21223,19 +21457,6 @@ "Please select a file.", "Please select a folder." ], - "vs/workbench/contrib/issue/common/issue.contribution": [ - "Report Issue...", - "Report &&Issue" - ], - "vs/workbench/contrib/terminal/common/terminal": [ - "Contributes terminal functionality.", - "Defines additional terminal profiles that the user can create.", - "The ID of the terminal profile provider.", - "Title for this terminal profile.", - "A codicon, URI, or light and dark URIs to associate with this terminal type.", - "Icon path when a light theme is used", - "Icon path when a dark theme is used" - ], "vs/editor/common/languages": [ "array", "boolean", @@ -21274,15 +21495,22 @@ "UI State", "Profiles", "Workspace State", - "Settings Sync", "View icon of the Settings Sync view.", - "Download Settings Sync Activity" + "Download Settings Sync Activity", + "Settings Sync" ], "vs/workbench/contrib/tasks/common/tasks": [ "Whether a task is currently running.", "Tasks", "Error: the task identifier '{0}' is missing the required property '{1}'. The task identifier will be ignored." ], + "vs/workbench/contrib/tasks/common/taskService": [ + "Whether CustomExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "Whether ShellExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "Whether the task commands have been registered yet", + "Whether ProcessExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "True when in the web with no remote authority." + ], "vs/workbench/contrib/performance/electron-sandbox/startupProfiler": [ "Successfully created profiles.", "Please create an issue and manually attach the following files:\n{0}", @@ -21292,18 +21520,86 @@ "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", "&&Restart" ], - "vs/workbench/contrib/tasks/common/taskService": [ - "Whether CustomExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "Whether ShellExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "Whether the task commands have been registered yet", - "Whether ProcessExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "True when in the web with no remote authority." - ], "vs/workbench/common/views": [ + "Views", "Default view icon.", "A view with id '{0}' is already registered", "No tree view with id '{0}' registered." ], + "vs/workbench/contrib/terminal/common/terminalContextKey": [ + "Whether the terminal is focused.", + "Whether any terminal is focused, including detached terminals used in other UI.", + "Whether a terminal in the editor area is focused.", + "The current number of terminals.", + "Whether the terminal tabs widget is focused.", + "The shell type of the active terminal, this is set to the last known value when no terminals exist.", + "Whether the terminal's alt buffer is active.", + "Whether the terminal's suggest widget is visible.", + "Whether the terminal view is showing", + "Whether text is selected in the active terminal.", + "Whether text is selected in a focused terminal.", + "Whether terminal processes can be launched in the current workspace.", + "Whether one terminal is selected in the terminal tabs list.", + "Whether the focused tab's terminal is a split terminal.", + "Whether the terminal run command picker is currently open.", + "Whether shell integration is enabled in the active terminal" + ], + "vs/platform/audioCues/browser/audioCueService": [ + "Error on Line", + "Warning on Line", + "Folded Area on Line", + "Breakpoint on Line", + "Inline Suggestion on Line", + "Terminal Quick Fix", + "Debugger Stopped on Breakpoint", + "No Inlay Hints on Line", + "Task Completed", + "Task Failed", + "Terminal Command Failed", + "Terminal Bell", + "Notebook Cell Completed", + "Notebook Cell Failed", + "Diff Line Inserted", + "Diff Line Deleted", + "Diff Line Modified", + "Chat Request Sent", + "Chat Response Received", + "Chat Response Pending", + "Clear", + "Save", + "Format" + ], + "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ + "Open Webview Developer Tools", + "Using standard dev tools to debug iframe based webview" + ], + "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ + "Reveal in File Explorer", + "Reveal in Finder", + "Open Containing Folder" + ], + "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ + "Merge Editor (Dev)", + "Open Merge Editor State from JSON", + "Enter JSON", + "Open Selection In Temporary Merge Editor" + ], + "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ + "A unknown error has occurred while executing a task. See task output log for details.", + "There are issues with task \"{0}\". See the output for more details.", + "There is a dependency cycle. See task \"{0}\".", + "Couldn't resolve dependent task '{0}' in workspace folder '{1}'", + "Task {0} is a background task but uses a problem matcher without a background pattern", + "Executing task in folder {0}: {1}", + "Executing task: {0}", + "Executing task in folder {0}: {1}", + "Executing task: {0}", + "Executing task: {0}", + "Can't execute a shell command on an UNC drive using cmd.exe.", + "Problem matcher {0} can't be resolved. The matcher will be ignored", + "Press any key to close the terminal.", + "Terminal will be reused by tasks, press any key to close it." + ], "vs/workbench/contrib/tasks/browser/abstractTaskService": [ "Configure Task", "Tasks", @@ -21394,77 +21690,6 @@ "Open diff", "Open diffs" ], - "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ - "A unknown error has occurred while executing a task. See task output log for details.", - "There are issues with task \"{0}\". See the output for more details.", - "There is a dependency cycle. See task \"{0}\".", - "Couldn't resolve dependent task '{0}' in workspace folder '{1}'", - "Task {0} is a background task but uses a problem matcher without a background pattern", - "Executing task in folder {0}: {1}", - "Executing task: {0}", - "Executing task in folder {0}: {1}", - "Executing task: {0}", - "Executing task: {0}", - "Can't execute a shell command on an UNC drive using cmd.exe.", - "Problem matcher {0} can't be resolved. The matcher will be ignored", - "Press any key to close the terminal.", - "Terminal will be reused by tasks, press any key to close it." - ], - "vs/platform/audioCues/browser/audioCueService": [ - "Error on Line", - "Warning on Line", - "Folded Area on Line", - "Breakpoint on Line", - "Inline Suggestion on Line", - "Terminal Quick Fix", - "Debugger Stopped on Breakpoint", - "No Inlay Hints on Line", - "Task Completed", - "Task Failed", - "Terminal Command Failed", - "Terminal Bell", - "Notebook Cell Completed", - "Notebook Cell Failed", - "Diff Line Inserted", - "Diff Line Deleted", - "Diff Line Modified", - "Chat Request Sent", - "Chat Response Received", - "Chat Response Pending" - ], - "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ - "Open Webview Developer Tools", - "Using standard dev tools to debug iframe based webview" - ], - "vs/workbench/contrib/terminal/common/terminalContextKey": [ - "Whether the terminal is focused.", - "Whether any terminal is focused, including detached terminals used in other UI.", - "Whether a terminal in the editor area is focused.", - "The current number of terminals.", - "Whether the terminal tabs widget is focused.", - "The shell type of the active terminal, this is set to the last known value when no terminals exist.", - "Whether the terminal's alt buffer is active.", - "Whether the terminal's suggest widget is visible.", - "Whether the terminal view is showing", - "Whether text is selected in the active terminal.", - "Whether text is selected in a focused terminal.", - "Whether terminal processes can be launched in the current workspace.", - "Whether one terminal is selected in the terminal tabs list.", - "Whether the focused tab's terminal is a split terminal.", - "Whether the terminal run command picker is currently open.", - "Whether shell integration is enabled in the active terminal" - ], - "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ - "Reveal in File Explorer", - "Reveal in Finder", - "Open Containing Folder" - ], - "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ - "Merge Editor (Dev)", - "Open Merge Editor State from JSON", - "Enter JSON", - "Open Selection In Temporary Merge Editor" - ], "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions": [ "True when getting ready for receiving voice input from the microphone for voice chat.", "True when voice recording from microphone is in progress for voice chat.", @@ -21472,24 +21697,25 @@ "True when voice recording from microphone is in progress for inline chat.", "True when voice recording from microphone is in progress in the chat view.", "True when voice recording from microphone is in progress in the chat editor.", + "I'm listening", "Voice Chat in Chat View", "Inline Voice Chat", "Quick Voice Chat", - "Start Voice Chat", - "Stop Voice Chat", - "Stop Voice Chat (Chat View)", - "Stop Voice Chat (Chat Editor)", - "Stop Voice Chat (Quick Chat)", - "Stop Voice Chat (Inline Editor)", - "Stop Voice Chat and Submit" + "Use Microphone", + "Stop Listening", + "Stop Listening", + "Stop Listening", + "Stop Listening", + "Stop Listening", + "Stop Listening and Submit" + ], + "vs/workbench/api/common/extHostTelemetry": [ + "Extension Telemetry{0}" ], "vs/workbench/api/common/extHostExtensionService": [ "Cannot load test runner.", "Path {0} does not point to a valid extension test runner." ], - "vs/workbench/api/common/extHostTelemetry": [ - "Extension Telemetry{0}" - ], "vs/workbench/api/common/extHostWorkspace": [ "Extension '{0}' failed to update workspace folders: {1}" ], @@ -21505,18 +21731,77 @@ "Extension Host (Worker)", "Extension Host" ], - "vs/workbench/api/node/extHostDebugService": [ - "Debug Process" - ], "vs/platform/terminal/node/terminalProcess": [ "Starting directory (cwd) \"{0}\" is not a directory", "Starting directory (cwd) \"{0}\" does not exist", "Path to shell executable \"{0}\" does not exist", "Path to shell executable \"{0}\" is not a file or a symlink" ], - "vs/platform/shell/node/shellEnv": [ - "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration and restart.", - "Unable to resolve your shell environment: {0}", + "vs/workbench/api/node/extHostDebugService": [ + "Debug Process" + ], + "vs/platform/extensions/common/extensionValidator": [ + "property publisher must be of type `string`.", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` is mandatory and must be of type `object`", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` can be omitted or must be of type `string[]`", + "property `{0}` can be omitted or must be of type `string[]`", + "property `{0}` should be omitted if the extension doesn't have a `{1}` or `{2}` property.", + "property `{0}` can be defined only if property `main` is also defined.", + "property `{0}` can be omitted or must be of type `string`", + "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", + "property `{0}` can be omitted or must be of type `string`", + "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", + "Extension version is not semver compatible.", + "Could not parse `engines.vscode` value {0}. Please use, for example: ^1.22.0, ^1.22.x, etc.", + "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions before 1.0.0, please define at a minimum the major and minor desired version. E.g. ^0.10.0, 0.10.x, 0.11.0, etc.", + "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions after 1.0.0, please define at a minimum the major desired version. E.g. ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", + "Extension is not compatible with Code {0}. Extension requires: {1}." + ], + "vs/platform/extensionManagement/common/extensionNls": [ + "Couldn't find message for key {0}." + ], + "vs/base/common/jsonErrorMessages": [ + "Invalid symbol", + "Invalid number format", + "Property name expected", + "Value expected", + "Colon expected", + "Comma expected", + "Closing brace expected", + "Closing bracket expected", + "End of file expected" + ], + "vs/base/node/zip": [ + "Error extracting {0}. Invalid file.", + "Incomplete. Found {0} of {1} entries", + "{0} not found inside zip." + ], + "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ + "Marketplace is not enabled", + "Can't install '{0}' extension since it was reported to be problematic.", + "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", + "The '{0}' extension is not available in {1} for {2}.", + "Can't install release version of '{0}' extension because it has no release version.", + "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", + "Cannot uninstall '{0}' extension. '{1}' extension depends on this.", + "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.", + "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.", + "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.", + "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.", + "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this." + ], + "vs/platform/extensionManagement/node/extensionManagementUtil": [ + "VSIX invalid: package.json is not a JSON file." + ], + "vs/platform/files/common/io": [ + "File is too large to open" + ], + "vs/platform/shell/node/shellEnv": [ + "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration and restart.", + "Unable to resolve your shell environment: {0}", "Unexpected exit code from spawned shell (code {0}, signal {1})" ], "vs/platform/dialogs/electron-main/dialogMainService": [ @@ -21526,6 +21811,10 @@ "Open Workspace from File", "&&Open" ], + "vs/platform/files/electron-main/diskFileSystemProviderServer": [ + "Failed to move '{0}' to the recycle bin", + "Failed to move '{0}' to the trash" + ], "vs/platform/externalTerminal/node/externalTerminalService": [ "VS Code Console", "Script '{0}' failed with exit code {1}", @@ -21534,10 +21823,6 @@ "'{0}' failed with exit code {1}", "can't find terminal application '{0}'" ], - "vs/platform/files/electron-main/diskFileSystemProviderServer": [ - "Failed to move '{0}' to the recycle bin", - "Failed to move '{0}' to the trash" - ], "vs/platform/issue/electron-main/issueMainService": [ "Local", "Issue Reporter", @@ -21563,6 +21848,19 @@ "Unable to uninstall the shell command '{0}'.", "Unable to find shell script in '{0}'" ], + "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ + "New Window", + "Opens a new window", + "Recent Folders & Workspaces", + "Recent Folders", + "Untitled (Workspace)", + "{0} (Workspace)" + ], + "vs/platform/workspaces/electron-main/workspacesManagementMainService": [ + "&&OK", + "Unable to save workspace '{0}'", + "The workspace is already opened in another window. Please close that window first and then try again." + ], "vs/platform/windows/electron-main/windowsMainService": [ "&&OK", "Path does not exist", @@ -21576,78 +21874,9 @@ "The path '{0}' uses a host that is not allowed. Unless you trust the host, you should press 'Cancel'", "Permanently allow host '{0}'" ], - "vs/platform/workspaces/electron-main/workspacesManagementMainService": [ - "&&OK", - "Unable to save workspace '{0}'", - "The workspace is already opened in another window. Please close that window first and then try again." - ], - "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ - "New Window", - "Opens a new window", - "Recent Folders & Workspaces", - "Recent Folders", - "Untitled (Workspace)", - "{0} (Workspace)" - ], - "vs/platform/files/common/io": [ - "File is too large to open" - ], "vs/base/browser/ui/button/button": [ "More Actions..." ], - "vs/platform/extensions/common/extensionValidator": [ - "property publisher must be of type `string`.", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` is mandatory and must be of type `object`", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` can be omitted or must be of type `string[]`", - "property `{0}` can be omitted or must be of type `string[]`", - "property `{0}` should be omitted if the extension doesn't have a `{1}` or `{2}` property.", - "property `{0}` can be defined only if property `main` is also defined.", - "property `{0}` can be omitted or must be of type `string`", - "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", - "property `{0}` can be omitted or must be of type `string`", - "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", - "Extension version is not semver compatible.", - "Could not parse `engines.vscode` value {0}. Please use, for example: ^1.22.0, ^1.22.x, etc.", - "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions before 1.0.0, please define at a minimum the major and minor desired version. E.g. ^0.10.0, 0.10.x, 0.11.0, etc.", - "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions after 1.0.0, please define at a minimum the major desired version. E.g. ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", - "Extension is not compatible with Code {0}. Extension requires: {1}." - ], - "vs/base/common/jsonErrorMessages": [ - "Invalid symbol", - "Invalid number format", - "Property name expected", - "Value expected", - "Colon expected", - "Comma expected", - "Closing brace expected", - "Closing bracket expected", - "End of file expected" - ], - "vs/platform/extensionManagement/common/extensionNls": [ - "Couldn't find message for key {0}." - ], - "vs/base/node/zip": [ - "Error extracting {0}. Invalid file.", - "Incomplete. Found {0} of {1} entries", - "{0} not found inside zip." - ], - "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ - "Marketplace is not enabled", - "Can't install '{0}' extension since it was reported to be problematic.", - "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", - "The '{0}' extension is not available in {1} for {2}.", - "Can't install release version of '{0}' extension because it has no release version.", - "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", - "Cannot uninstall '{0}' extension. '{1}' extension depends on this.", - "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.", - "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.", - "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.", - "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.", - "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this." - ], "vs/base/common/date": [ "in {0}", "now", @@ -21704,9 +21933,6 @@ "{0} years", "{0} yrs" ], - "vs/platform/extensionManagement/node/extensionManagementUtil": [ - "VSIX invalid: package.json is not a JSON file." - ], "vs/platform/userDataSync/common/keybindingsSync": [ "Unable to sync keybindings because the content in the file is not valid. Please open the file and correct it.", "Unable to sync keybindings because the content in the file is not valid. Please open the file and correct it." @@ -21812,6 +22038,11 @@ "Border color used to highlight unicode characters.", "Background color used to highlight unicode characters." ], + "vs/editor/browser/coreCommands": [ + "Stick to the end even when going to longer lines", + "Stick to the end even when going to longer lines", + "Removed secondary cursors" + ], "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ "Toggle Collapse Unchanged Regions", "Toggle Show Moved Code Blocks", @@ -21828,6 +22059,10 @@ "Open Accessible Diff Viewer", "Go to Previous Difference" ], + "vs/editor/browser/widget/codeEditorWidget": [ + "The number of cursors has been limited to {0}. Consider using [find and replace](https://code.visualstudio.com/docs/editor/codebasics#_find-and-replace) for larger changes or increase the editor multi cursor limit setting.", + "Increase Multi Cursor Limit" + ], "vs/platform/contextkey/common/scanner": [ "Did you mean {0}?", "Did you mean {0} or {1}?", @@ -21835,11 +22070,6 @@ "Did you forget to open or close the quote?", "Did you forget to escape the '/' (slash) character? Put two backslashes before it to escape, e.g., '\\\\/'." ], - "vs/editor/browser/coreCommands": [ - "Stick to the end even when going to longer lines", - "Stick to the end even when going to longer lines", - "Removed secondary cursors" - ], "vs/editor/contrib/anchorSelect/browser/anchorSelect": [ "Selection Anchor", "Anchor set at {0}:{1}", @@ -21848,10 +22078,6 @@ "Select from Anchor to Cursor", "Cancel Selection Anchor" ], - "vs/editor/contrib/caretOperations/browser/caretOperations": [ - "Move Selected Text Left", - "Move Selected Text Right" - ], "vs/editor/contrib/bracketMatching/browser/bracketMatching": [ "Overview ruler marker color for matching brackets.", "Go to Bracket", @@ -21859,9 +22085,9 @@ "Remove Brackets", "Go to &&Bracket" ], - "vs/editor/browser/widget/codeEditorWidget": [ - "The number of cursors has been limited to {0}. Consider using [find and replace](https://code.visualstudio.com/docs/editor/codebasics#_find-and-replace) for larger changes or increase the editor multi cursor limit setting.", - "Increase Multi Cursor Limit" + "vs/editor/contrib/caretOperations/browser/caretOperations": [ + "Move Selected Text Left", + "Move Selected Text Right" ], "vs/editor/contrib/caretOperations/browser/transpose": [ "Transpose Letters" @@ -21888,7 +22114,7 @@ ], "vs/editor/contrib/codeAction/browser/codeActionContributions": [ "Enable/disable showing group headers in the Code Action menu.", - "Enable/disable showing nearest quickfix within a line when not currently on a diagnostic." + "Enable/disable showing nearest Quick Fix within a line when not currently on a diagnostic." ], "vs/editor/contrib/codelens/browser/codelensController": [ "Show CodeLens Commands For Current Line", @@ -21931,6 +22157,26 @@ "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution": [ "Configures the default drop provider to use for content of a given mime type." ], + "vs/editor/contrib/folding/browser/folding": [ + "Unfold", + "Unfold Recursively", + "Fold", + "Toggle Fold", + "Fold Recursively", + "Fold All Block Comments", + "Fold All Regions", + "Unfold All Regions", + "Fold All Except Selected", + "Unfold All Except Selected", + "Fold All", + "Unfold All", + "Go to Parent Fold", + "Go to Previous Folding Range", + "Go to Next Folding Range", + "Create Folding Range from Selection", + "Remove Manual Folding Ranges", + "Fold Level {0}" + ], "vs/editor/contrib/find/browser/findController": [ "The file is too large to perform a replace all operation.", "Find", @@ -21958,26 +22204,6 @@ "Editor Font Zoom Out", "Editor Font Zoom Reset" ], - "vs/editor/contrib/folding/browser/folding": [ - "Unfold", - "Unfold Recursively", - "Fold", - "Toggle Fold", - "Fold Recursively", - "Fold All Block Comments", - "Fold All Regions", - "Unfold All Regions", - "Fold All Except Selected", - "Unfold All Except Selected", - "Fold All", - "Unfold All", - "Go to Parent Fold", - "Go to Previous Folding Range", - "Go to Next Folding Range", - "Create Folding Range from Selection", - "Remove Manual Folding Ranges", - "Fold Level {0}" - ], "vs/editor/contrib/format/browser/formatActions": [ "Format Document", "Format Selection" @@ -22036,6 +22262,18 @@ "Go to Previous Problem in Files (Error, Warning, Info)", "Previous &&Problem" ], + "vs/editor/contrib/hover/browser/hover": [ + "Show or Focus Hover", + "Show Definition Preview Hover", + "Scroll Up Hover", + "Scroll Down Hover", + "Scroll Left Hover", + "Scroll Right Hover", + "Page Up Hover", + "Page Down Hover", + "Go To Top Hover", + "Go To Bottom Hover" + ], "vs/editor/contrib/indentation/browser/indentation": [ "Convert Indentation to Spaces", "Convert Indentation to Tabs", @@ -22050,40 +22288,12 @@ "Reindent Lines", "Reindent Selected Lines" ], - "vs/editor/contrib/hover/browser/hover": [ - "Show or Focus Hover", - "Show Definition Preview Hover", - "Scroll Up Hover", - "Scroll Down Hover", - "Scroll Left Hover", - "Scroll Right Hover", - "Page Up Hover", - "Page Down Hover", - "Go To Top Hover", - "Go To Bottom Hover" - ], - "vs/editor/contrib/lineSelection/browser/lineSelection": [ - "Expand Line Selection" - ], - "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ - "Start Linked Editing", - "Background color when the editor auto renames on type." - ], "vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace": [ "Replace with Previous Value", "Replace with Next Value" ], - "vs/editor/contrib/links/browser/links": [ - "Failed to open this link because it is not well-formed: {0}", - "Failed to open this link because its target is missing.", - "Execute command", - "Follow link", - "cmd + click", - "ctrl + click", - "option + click", - "alt + click", - "Execute command {0}", - "Open Link" + "vs/editor/contrib/lineSelection/browser/lineSelection": [ + "Expand Line Selection" ], "vs/editor/contrib/linesOperations/browser/linesOperations": [ "Copy Line Up", @@ -22116,6 +22326,22 @@ "Transform to Camel Case", "Transform to Kebab Case" ], + "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ + "Start Linked Editing", + "Background color when the editor auto renames on type." + ], + "vs/editor/contrib/links/browser/links": [ + "Failed to open this link because it is not well-formed: {0}", + "Failed to open this link because its target is missing.", + "Execute command", + "Follow link", + "cmd + click", + "ctrl + click", + "option + click", + "alt + click", + "Execute command {0}", + "Open Link" + ], "vs/editor/contrib/multicursor/browser/multicursor": [ "Cursor added: {0}", "Cursors added: {0}", @@ -22167,6 +22393,9 @@ "Whether there is a previous tab stop when in snippet mode", "Go to next placeholder..." ], + "vs/editor/contrib/tokenization/browser/tokenization": [ + "Developer: Force Retokenize" + ], "vs/editor/contrib/suggest/browser/suggestController": [ "Accepting '{0}' made {1} additional edits", "Trigger Suggest", @@ -22179,21 +22408,11 @@ "show more", "Reset Suggest Widget Size" ], - "vs/editor/contrib/tokenization/browser/tokenization": [ - "Developer: Force Retokenize" - ], "vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode": [ "Toggle Tab Key Moves Focus", "Pressing Tab will now move focus to the next focusable element", "Pressing Tab will now insert the tab character" ], - "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ - "Unusual Line Terminators", - "Detected unusual line terminators", - "The file '{0}' contains one or more unusual line terminator characters, like Line Separator (LS) or Paragraph Separator (PS).\n\nIt is recommended to remove them from the file. This can be configured via `editor.unusualLineTerminators`.", - "&&Remove Unusual Line Terminators", - "Ignore" - ], "vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter": [ "Icon shown with a warning message in the extensions editor.", "This document contains many non-basic ASCII unicode characters", @@ -22220,6 +22439,13 @@ "Allow unicode characters that are more common in the language \"{0}\".", "Configure Unicode Highlight Options" ], + "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ + "Unusual Line Terminators", + "Detected unusual line terminators", + "The file '{0}' contains one or more unusual line terminator characters, like Line Separator (LS) or Paragraph Separator (PS).\n\nIt is recommended to remove them from the file. This can be configured via `editor.unusualLineTerminators`.", + "&&Remove Unusual Line Terminators", + "Ignore" + ], "vs/editor/contrib/wordHighlighter/browser/wordHighlighter": [ "Go to Next Symbol Highlight", "Go to Previous Symbol Highlight", @@ -22252,6 +22478,12 @@ "Pressing Tab in the current editor will insert the tab character. Toggle this behavior {0}.", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.", "Show Accessibility Help", + "`audioCues.save` is disabled, so an alert will occur when a file is saved.", + "`audioCues.save` is enabled, so will play whenever a file is saved.", + "`audioCues.save` is enabled, so will play when a file is saved via user gesture.", + "`audioCues.format` is disabled, so an alert will occur when a file is formatted.", + "`audioCues.format` is enabled, so will play whenever a file is formatted.", + "`audioCues.format` is enabled, so will play when a file is formatted via user gesture.", "Developer: Inspect Tokens", "Go to Line/Column...", "Show all Quick Access Providers", @@ -22264,6 +22496,64 @@ "Toggle High Contrast Theme", "Made {0} edits in {1} files" ], + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ + "Icon to toggle the auxiliary bar off in its right position.", + "Icon to toggle the auxiliary bar on in its right position.", + "Icon to toggle the auxiliary bar in its left position.", + "Icon to toggle the auxiliary bar on in its left position.", + "Toggle Secondary Side Bar Visibility", + "Secondary Side Bar", + "Secondary Si&&de Bar", + "Focus into Secondary Side Bar", + "Toggle Secondary Side Bar", + "Toggle Secondary Side Bar", + "Hide Secondary Side Bar" + ], + "vs/workbench/browser/parts/panel/panelActions": [ + "Icon to maximize a panel.", + "Icon to restore a panel.", + "Icon to close a panel.", + "Icon to toggle the panel off when it is on.", + "Icon to toggle the panel on when it is off.", + "Toggle Panel Visibility", + "Panel", + "&&Panel", + "Focus into Panel", + "Focus into Panel", + "Move Panel Left", + "Left", + "Move Panel Right", + "Right", + "Move Panel To Bottom", + "Bottom", + "Set Panel Alignment to Left", + "Left", + "Set Panel Alignment to Right", + "Right", + "Set Panel Alignment to Center", + "Center", + "Set Panel Alignment to Justify", + "Justify", + "Panel Position", + "Align Panel", + "Previous Panel View", + "Next Panel View", + "Toggle Maximized Panel", + "Maximize Panel Size", + "Restore Panel Size", + "Maximizing the panel is only supported when it is center aligned.", + "Close Panel", + "Close Secondary Side Bar", + "Toggle Panel", + "Hide Panel", + "Move Panel Views To Secondary Side Bar", + "Move Panel Views To Secondary Side Bar", + "Move Secondary Side Bar Views To Panel", + "Move Secondary Side Bar Views To Panel" + ], + "vs/workbench/browser/quickaccess": [ + "Whether keyboard focus is inside the quick open control" + ], "vs/workbench/api/common/jsonValidationExtensionPoint": [ "Contributes json schema configuration.", "The file pattern (or an array of patterns) to match, for example \"package.json\" or \"*.launch\". Exclusion patterns start with '!'", @@ -22275,6 +22565,21 @@ "'configuration.jsonValidation.url' is an invalid relative URL: {0}", "'configuration.jsonValidation.url' must be an absolute URL or start with './' to reference schemas located in the extension." ], + "vs/workbench/services/themes/common/iconExtensionPoint": [ + "Contributes extension defined themable icons", + "The identifier of the themable icon", + "Identifiers can only contain letters, digits and minuses and need to consist of at least two segments in the form `component-iconname`.", + "The description of the themable icon", + "The path of the icon font that defines the icon.", + "The character for the icon in the icon font.", + "The default of the icon. Either a reference to an extisting ThemeIcon or an icon in an icon font.", + "'configuration.icons' must be an object with the icon names as properties.", + "'configuration.icons' keys represent the icon id and can only contain letter, digits and minuses. They need to consist of at least two segments in the form `component-iconname`.", + "'configuration.icons.description' must be defined and can not be empty", + "Expected `contributes.icons.default.fontPath` to have file extension 'woff', woff2' or 'ttf', is '{0}'.", + "Expected `contributes.icons.default.fontPath` ({0}) to be included inside extension's folder ({0}).", + "'configuration.icons.default' must be either a reference to the id of an other theme icon (string) or a icon definition (object) with properties `fontPath` and `fontCharacter`." + ], "vs/workbench/services/themes/common/colorExtensionPoint": [ "Contributes extension defined themable colors", "The identifier of the themable color", @@ -22293,21 +22598,6 @@ "If defined, 'configuration.colors.defaults.highContrast' must be a string.", "If defined, 'configuration.colors.defaults.highContrastLight' must be a string." ], - "vs/workbench/services/themes/common/iconExtensionPoint": [ - "Contributes extension defined themable icons", - "The identifier of the themable icon", - "Identifiers can only contain letters, digits and minuses and need to consist of at least two segments in the form `component-iconname`.", - "The description of the themable icon", - "The path of the icon font that defines the icon.", - "The character for the icon in the icon font.", - "The default of the icon. Either a reference to an extisting ThemeIcon or an icon in an icon font.", - "'configuration.icons' must be an object with the icon names as properties.", - "'configuration.icons' keys represent the icon id and can only contain letter, digits and minuses. They need to consist of at least two segments in the form `component-iconname`.", - "'configuration.icons.description' must be defined and can not be empty", - "Expected `contributes.icons.default.fontPath` to have file extension 'woff', woff2' or 'ttf', is '{0}'.", - "Expected `contributes.icons.default.fontPath` ({0}) to be included inside extension's folder ({0}).", - "'configuration.icons.default' must be either a reference to the id of an other theme icon (string) or a icon definition (object) with properties `fontPath` and `fontCharacter`." - ], "vs/workbench/services/themes/common/tokenClassificationExtensionPoint": [ "Contributes semantic token types.", "The identifier of the semantic token type", @@ -22506,64 +22796,6 @@ "The extension '{0}' wants to sign in using {1}.", "&&Allow" ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ - "Icon to toggle the auxiliary bar off in its right position.", - "Icon to toggle the auxiliary bar on in its right position.", - "Icon to toggle the auxiliary bar in its left position.", - "Icon to toggle the auxiliary bar on in its left position.", - "Toggle Secondary Side Bar Visibility", - "Secondary Side Bar", - "Secondary Si&&de Bar", - "Focus into Secondary Side Bar", - "Toggle Secondary Side Bar", - "Toggle Secondary Side Bar", - "Hide Secondary Side Bar" - ], - "vs/workbench/browser/parts/panel/panelActions": [ - "Icon to maximize a panel.", - "Icon to restore a panel.", - "Icon to close a panel.", - "Icon to toggle the panel off when it is on.", - "Icon to toggle the panel on when it is off.", - "Toggle Panel Visibility", - "Panel", - "&&Panel", - "Focus into Panel", - "Focus into Panel", - "Move Panel Left", - "Left", - "Move Panel Right", - "Right", - "Move Panel To Bottom", - "Bottom", - "Set Panel Alignment to Left", - "Left", - "Set Panel Alignment to Right", - "Right", - "Set Panel Alignment to Center", - "Center", - "Set Panel Alignment to Justify", - "Justify", - "Panel Position", - "Align Panel", - "Previous Panel View", - "Next Panel View", - "Toggle Maximized Panel", - "Maximize Panel Size", - "Restore Panel Size", - "Maximizing the panel is only supported when it is center aligned.", - "Close Panel", - "Close Secondary Side Bar", - "Toggle Panel", - "Hide Panel", - "Move Panel Views To Secondary Side Bar", - "Move Panel Views To Secondary Side Bar", - "Move Secondary Side Bar Views To Panel", - "Move Secondary Side Bar Views To Panel" - ], - "vs/workbench/browser/quickaccess": [ - "Whether keyboard focus is inside the quick open control" - ], "vs/workbench/services/extensions/common/extensionsRegistry": [ "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine.", "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.", @@ -22645,6 +22877,20 @@ "The pricing information for the extension. Can be Free (default) or Trial. For more details visit: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#extension-pricing-label", "API proposals that the respective extensions can freely use." ], + "vs/workbench/browser/parts/titlebar/windowTitle": [ + "[Administrator]", + "[Superuser]", + "[Extension Development Host]" + ], + "vs/workbench/browser/parts/views/treeView": [ + "There is no data provider registered that can provide view data.", + "Whether the the tree view with id {0} enables collapse all.", + "Whether the tree view with id {0} enables refresh.", + "Refresh", + "Collapse All", + "Whether collapse all is toggled for the tree view with id {0}.", + "Error running command {1}: {0}. This is likely caused by the extension that contributes {1}." + ], "vs/workbench/contrib/debug/common/debug": [ "Debug type of the active debug session. For example 'python'.", "Debug type of the selected launch configuration. For example 'python'.", @@ -22700,6 +22946,14 @@ "Configured debug type '{0}' is installed but not supported in this environment.", "Controls when the internal Debug Console should open." ], + "vs/workbench/browser/parts/views/viewPaneContainer": [ + "Views", + "Move View Up", + "Move View Left", + "Move View Down", + "Move View Right", + "Move Views" + ], "vs/workbench/contrib/files/common/files": [ "True when the EXPLORER viewlet is visible.", "True when the FOLDERS view (the file tree within the explorer view container) is visible.", @@ -22716,14 +22970,6 @@ "True when the focus is inside a compact item's last part in the EXPLORER view.", "True when a workspace in the EXPLORER view has some collapsible root child." ], - "vs/workbench/browser/parts/views/viewPaneContainer": [ - "Views", - "Move View Up", - "Move View Left", - "Move View Down", - "Move View Right", - "Move Views" - ], "vs/workbench/contrib/remote/browser/remoteExplorer": [ "No forwarded ports. Forward a port to access your running services locally.\n[Forward a Port]({0})", "No forwarded ports. Forward a port to access your locally running services over the internet.\n[Forward a Port]({0})", @@ -22739,24 +22985,15 @@ "Make Public", "Use Port {0} as Sudo..." ], - "vs/workbench/browser/parts/views/treeView": [ - "There is no data provider registered that can provide view data.", - "Whether the the tree view with id {0} enables collapse all.", - "Whether the tree view with id {0} enables refresh.", - "Refresh", - "Collapse All", - "Whether collapse all is toggled for the tree view with id {0}.", - "Error running command {1}: {0}. This is likely caused by the extension that contributes {1}." - ], "vs/workbench/common/editor/sideBySideEditorInput": [ "{0} - {1}" ], - "vs/workbench/common/editor/diffEditorInput": [ - "{0} ↔ {1}" - ], "vs/workbench/browser/parts/editor/sideBySideEditor": [ "Side by Side Editor" ], + "vs/workbench/common/editor/diffEditorInput": [ + "{0} ↔ {1}" + ], "vs/workbench/browser/parts/editor/textDiffEditor": [ "Text Diff Editor", "At least one file is not displayed in the text compare editor because it is very large ({0}).", @@ -22827,25 +23064,47 @@ "Select File Encoding to Reopen File", "Select File Encoding to Save with" ], - "vs/workbench/browser/parts/editor/editorActions": [ - "Split Editor", - "Split Editor Orthogonal", - "Split Editor Left", - "Split Editor Right", - "Split Editor Up", - "Split Editor Up", - "Split Editor Down", - "Split Editor Down", - "Join Editor Group with Next Group", - "Join All Editor Groups", - "Navigate Between Editor Groups", - "Focus Active Editor Group", - "Focus First Editor Group", - "Focus Last Editor Group", - "Focus Next Editor Group", - "Focus Previous Editor Group", - "Focus Left Editor Group", - "Focus Right Editor Group", + "vs/workbench/browser/parts/editor/editorCommands": [ + "Move the active editor by tabs or groups", + "Active editor move argument", + "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move.", + "Copy the active editor by groups", + "Active editor copy argument", + "Argument Properties:\n\t* 'to': String value providing where to copy.\n\t* 'value': Number value providing how many positions or an absolute position to copy.", + "Go to Next Change", + "Go to Previous Change", + "Toggle Inline View", + "Compare", + "Split Editor in Group", + "Join Editor in Group", + "Toggle Split Editor in Group", + "Toggle Layout of Split Editor in Group", + "Focus First Side in Active Editor", + "Focus Second Side in Active Editor", + "Focus Other Side in Active Editor", + "Toggle Editor Group Lock", + "Lock Editor Group", + "Unlock Editor Group" + ], + "vs/workbench/browser/parts/editor/editorActions": [ + "Split Editor", + "Split Editor Orthogonal", + "Split Editor Left", + "Split Editor Right", + "Split Editor Up", + "Split Editor Up", + "Split Editor Down", + "Split Editor Down", + "Join Editor Group with Next Group", + "Join All Editor Groups", + "Navigate Between Editor Groups", + "Focus Active Editor Group", + "Focus First Editor Group", + "Focus Last Editor Group", + "Focus Next Editor Group", + "Focus Previous Editor Group", + "Focus Left Editor Group", + "Focus Right Editor Group", "Focus Editor Group Above", "Focus Editor Group Below", "Close Editor", @@ -22865,10 +23124,11 @@ "Duplicate Editor Group Right", "Duplicate Editor Group Up", "Duplicate Editor Group Down", - "Maximize Editor Group", + "Expand Editor Group", "Reset Editor Group Sizes", "Toggle Editor Group Sizes", "Maximize Editor Group and Hide Side Bars", + "Toggle Maximize Editor Group", "Open Next Editor", "Open Previous Editor", "Open Next Editor in Group", @@ -22943,7 +23203,9 @@ "New Editor Group Above", "New Editor Group Below", "Toggle Editor Type", - "Reopen Editor With Text Editor" + "Reopen Editor With Text Editor", + "Move Active Editor into a New Window (Experimental)", + "&&Move Active Editor into a New Window (Experimental)" ], "vs/editor/browser/editorExtensions": [ "&&Undo", @@ -22953,28 +23215,6 @@ "&&Select All", "Select All" ], - "vs/workbench/browser/parts/editor/editorCommands": [ - "Move the active editor by tabs or groups", - "Active editor move argument", - "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move.", - "Copy the active editor by groups", - "Active editor copy argument", - "Argument Properties:\n\t* 'to': String value providing where to copy.\n\t* 'value': Number value providing how many positions or an absolute position to copy.", - "Go to Next Change", - "Go to Previous Change", - "Toggle Inline View", - "Compare", - "Split Editor in Group", - "Join Editor in Group", - "Toggle Split Editor in Group", - "Toggle Layout of Split Editor in Group", - "Focus First Side in Active Editor", - "Focus Second Side in Active Editor", - "Focus Other Side in Active Editor", - "Toggle Editor Group Lock", - "Lock Editor Group", - "Unlock Editor Group" - ], "vs/workbench/browser/parts/editor/editorQuickAccess": [ "No matching editors", "{0}, unsaved changes, {1}", @@ -22990,41 +23230,19 @@ "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior.", "Controls the minimum size of a file in MB before asking for confirmation when opening in the editor. Note that this setting may not apply to all editor types and environments." ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ - "Move Secondary Side Bar Left", - "Move Secondary Side Bar Right", - "Hide Secondary Side Bar" - ], - "vs/workbench/browser/parts/activitybar/activitybarPart": [ - "Accounts icon in the view bar.", - "Menu", - "Hide Menu", - "Accounts", - "Hide Activity Bar", - "Reset Location", - "Reset Location", - "Manage", - "Accounts", - "Manage", - "Accounts" - ], "vs/workbench/browser/parts/panel/panelPart": [ - "Reset Location", - "Reset Location", - "Drag a view here to display.", - "More Actions...", "Panel Position", "Align Panel", "Hide Panel" ], - "vs/workbench/browser/parts/editor/editorGroupView": [ - "Empty editor group actions", - "{0} (empty)", - "Group {0}", - "Editor Group {0}" + "vs/workbench/browser/parts/sidebar/sidebarPart": [ + "Manage", + "Accounts" ], - "vs/workbench/browser/parts/editor/editorDropTarget": [ - "Hold __{0}__ to drop into editor" + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ + "Move Secondary Side Bar Left", + "Move Secondary Side Bar Right", + "Hide Secondary Side Bar" ], "vs/workbench/browser/parts/statusbar/statusbarActions": [ "Hide '{0}'", @@ -23048,19 +23266,16 @@ "In Progress", "Close Dialog" ], - "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ - "Keyboard Shortcuts" - ], "vs/workbench/services/preferences/common/preferencesEditorInput": [ "Settings" ], + "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ + "Keyboard Shortcuts" + ], "vs/workbench/services/preferences/common/preferencesModels": [ "Commonly Used", "Override key bindings by placing them into your key bindings file." ], - "vs/workbench/services/editor/common/editorResolverService": [ - "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior." - ], "vs/workbench/services/textfile/common/textFileEditorModel": [ "File Encoding Changed" ], @@ -23086,10 +23301,35 @@ "Alt", "Super" ], + "vs/platform/keybinding/common/abstractKeybindingService": [ + "({0}) was pressed. Waiting for second key of chord...", + "({0}) was pressed. Waiting for next key of chord...", + "The key combination ({0}, {1}) is not a command.", + "The key combination ({0}, {1}) is not a command." + ], + "vs/workbench/services/editor/common/editorResolverService": [ + "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior." + ], + "vs/workbench/services/themes/common/colorThemeData": [ + "Problems parsing JSON theme file: {0}", + "Invalid format for JSON theme file: Object expected.", + "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.", + "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", + "Problem parsing color theme file: {0}. Property 'semanticTokenColors' contains a invalid selector", + "Problem parsing tmTheme file: {0}. 'settings' is not array.", + "Problems parsing tmTheme file: {0}", + "Problems loading tmTheme file {0}: {1}" + ], "vs/workbench/services/themes/common/fileIconThemeSchema": [ "The folder icon for expanded folders. The expanded folder icon is optional. If not set, the icon defined for folder will be shown.", "The folder icon for collapsed folders, and if folderExpanded is not set, also for expanded folders.", "The default file icon, shown for all files that don't match any extension, filename or language id.", + "The folder icon for collapsed root folders, and if rootFolderExpanded is not set, also for expanded root folders.", + "The folder icon for expanded root folders. The expanded root folder icon is optional. If not set, the icon defined for root folder will be shown.", + "Associates root folder names to icons. The object key is the root folder name. No patterns or wildcards are allowed. Root folder name matching is case insensitive.", + "The ID of the icon definition for the association.", + "Associates root folder names to icons for expanded root folders. The object key is the root folder name. No patterns or wildcards are allowed. Root folder name matching is case insensitive.", + "The ID of the icon definition for the association.", "Associates folder names to icons. The object key is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.", "The ID of the icon definition for the association.", "Associates folder names to icons for expanded folders. The object key is the folder name, not including any path segments. No patterns or wildcards are allowed. Folder name matching is case insensitive.", @@ -23125,22 +23365,6 @@ "Problems parsing file icons file: {0}", "Invalid format for file icons theme file: Object expected." ], - "vs/workbench/services/themes/common/colorThemeData": [ - "Problems parsing JSON theme file: {0}", - "Invalid format for JSON theme file: Object expected.", - "Problem parsing color theme file: {0}. Property 'colors' is not of type 'object'.", - "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", - "Problem parsing color theme file: {0}. Property 'semanticTokenColors' contains a invalid selector", - "Problem parsing tmTheme file: {0}. 'settings' is not array.", - "Problems parsing tmTheme file: {0}", - "Problems loading tmTheme file {0}: {1}" - ], - "vs/platform/keybinding/common/abstractKeybindingService": [ - "({0}) was pressed. Waiting for second key of chord...", - "({0}) was pressed. Waiting for next key of chord...", - "The key combination ({0}, {1}) is not a command.", - "The key combination ({0}, {1}) is not a command." - ], "vs/workbench/services/themes/common/colorThemeSchema": [ "Colors and styles for the token.", "Foreground color for the token.", @@ -23189,16 +23413,6 @@ "Skipping icon definition '{0}'. Unknown font.", "Skipping icon definition '{0}'. Unknown fontCharacter." ], - "vs/workbench/services/themes/common/productIconThemeSchema": [ - "The ID of the font.", - "The ID must only contain letters, numbers, underscore and minus.", - "The location of the font.", - "The font path, relative to the current product icon theme file.", - "The format of the font.", - "The weight of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight for valid values.", - "The style of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-style for valid values.", - "Association of icon name to a font character." - ], "vs/workbench/services/themes/common/themeConfiguration": [ "Specifies the color theme used in the workbench.", "Theme is unknown or not installed.", @@ -23220,7 +23434,7 @@ "Default", "Default", "Product icon theme is unknown or not installed.", - "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme. The high contrast theme to use is specified by {0} and {1}", + "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme. The high contrast theme to use is specified by {0} and {1}.", "Sets the colors and styles for comments", "Sets the colors and styles for strings literals.", "Sets the colors and styles for keywords.", @@ -23237,6 +23451,16 @@ "Semantic token styling rules for this theme.", "Overrides editor semantic token color and styles from the currently selected color theme." ], + "vs/workbench/services/themes/common/productIconThemeSchema": [ + "The ID of the font.", + "The ID must only contain letters, numbers, underscore and minus.", + "The location of the font.", + "The font path, relative to the current product icon theme file.", + "The format of the font.", + "The weight of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight for valid values.", + "The style of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-style for valid values.", + "Association of icon name to a font character." + ], "vs/workbench/services/extensionManagement/browser/extensionBisect": [ "I can't reproduce", "I can reproduce", @@ -23272,8 +23496,8 @@ "Snippets", "Select Snippet {0}" ], - "vs/workbench/services/userDataProfile/browser/globalStateResource": [ - "UI State" + "vs/workbench/services/userDataProfile/browser/tasksResource": [ + "User Tasks" ], "vs/workbench/services/userDataProfile/browser/extensionsResource": [ "Extensions", @@ -23281,12 +23505,15 @@ "Select {0} Extension", "Select {0} Extension" ], - "vs/workbench/services/userDataProfile/browser/tasksResource": [ - "User Tasks" + "vs/workbench/services/userDataProfile/browser/globalStateResource": [ + "UI State" ], "vs/workbench/services/userDataProfile/common/userDataProfileIcons": [ "Settings icon in the view bar." ], + "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ + "Saving '{0}'" + ], "vs/workbench/services/remote/common/tunnelModel": [ "Whether the Ports view is enabled.", "User Forwarded", @@ -23294,12 +23521,6 @@ "Local port {0} could not be used for forwarding to remote port {1}.\n\nThis usually happens when there is already another process using local port {0}.\n\nPort number {2} has been used instead.", "Statically Forwarded" ], - "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ - "Saving '{0}'" - ], - "vs/workbench/services/views/common/viewContainerModel": [ - "Views" - ], "vs/workbench/services/hover/browser/hoverWidget": [ "Hold {0} key to mouse over" ], @@ -23316,6 +23537,18 @@ "Invalid value in `contributes.{0}.tokenTypes`. Must be an object map from scope name to token type. Provided value: {1}", "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable." ], + "vs/workbench/contrib/preferences/browser/keybindingWidgets": [ + "Press desired key combination and then press ENTER.", + "1 existing command has this keybinding", + "{0} existing commands have this keybinding", + "chord to" + ], + "vs/workbench/contrib/performance/browser/perfviewEditor": [ + "Startup Performance" + ], + "vs/workbench/contrib/speech/common/speechService": [ + "A speech provider is registered to the speech service." + ], "vs/editor/contrib/suggest/browser/suggest": [ "Whether any suggestion is focused", "Whether suggestion details are visible", @@ -23361,11 +23594,6 @@ "No when context", "use space or enter to change the keybinding." ], - "vs/workbench/contrib/preferences/browser/preferencesActions": [ - "Configure Language Specific Settings...", - "({0})", - "Select Language" - ], "vs/workbench/contrib/preferences/browser/preferencesIcons": [ "Icon for the folder dropdown button in the split JSON Settings editor.", "Icon for the 'more actions' action in the Settings UI.", @@ -23380,6 +23608,18 @@ "Icon for the button that suggests filters for the Settings UI.", "Icon for open settings commands." ], + "vs/workbench/contrib/preferences/browser/preferencesActions": [ + "Configure Language Specific Settings...", + "({0})", + "Select Language" + ], + "vs/workbench/contrib/preferences/common/preferencesContribution": [ + "Split Settings Editor", + "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service.", + "Hide the Table of Contents while searching. The search results will not be grouped by category, and instead will be sorted by similarity to the query, with exact keyword matches coming first.", + "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category. The search results will be grouped by category.", + "Controls the behavior of the Settings editor Table of Contents while searching. If this setting is being changed in the Settings editor, the setting will take effect after the search query is modified." + ], "vs/workbench/contrib/preferences/browser/settingsEditor2": [ "Search settings", "Clear Settings Search Input", @@ -23394,32 +23634,158 @@ "Backup and Sync Settings", "Last synced: {0}" ], - "vs/workbench/contrib/preferences/common/preferencesContribution": [ - "Split Settings Editor", - "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service.", - "Hide the Table of Contents while searching. The search results will not be grouped by category, and instead will be sorted by similarity to the query, with exact keyword matches coming first.", - "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category. The search results will be grouped by category.", - "Controls the behavior of the settings editor Table of Contents while searching." + "vs/workbench/contrib/chat/browser/actions/chatActions": [ + "Chat", + "Quick Chat", + "Accept Chat Input", + "Submit to Secondary Agent", + "Clear Input History", + "Focus Chat List", + "Focus Chat Input", + "Open Editor ({0})", + "Show History", + "Delete", + "Select a chat session to restore" ], - "vs/workbench/contrib/preferences/browser/keybindingWidgets": [ - "Press desired key combination and then press ENTER.", - "1 existing command has this keybinding", - "{0} existing commands have this keybinding", - "chord to" + "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ + "Copy", + "Insert at Cursor", + "Insert Into New File", + "Run in Terminal", + "Next Code Block", + "Previous Code Block" ], - "vs/workbench/contrib/performance/browser/perfviewEditor": [ - "Startup Performance" + "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ + "Copy All", + "Copy" ], - "vs/workbench/contrib/notebook/browser/notebookEditor": [ - "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed and enabled.", - "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed and enabled.", - "Enable extension for '{0}'", - "Install extension for '{0}'", - "Open As Text", - "Open in Text Editor" + "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ + "Submit", + "Cancel" ], - "vs/workbench/contrib/notebook/common/notebookEditorInput": [ - "Notebook '{0}' could not be saved." + "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ + "Open in Chat View", + "Close Quick Chat", + "Quick Chat", + "Toggle the quick chat", + "The query to open the quick chat with", + "Whether the query is partial; it will wait for more user input", + "The query to open the quick chat with", + "Open Quick Chat ({0})" + ], + "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ + "Helpful", + "Unhelpful", + "Insert into Notebook", + "Remove Request and Response" + ], + "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ + "Contributes an Interactive Session provider", + "Unique identifier for this Interactive Session provider.", + "Display name for this Interactive Session provider.", + "An icon for this Interactive Session provider.", + "A condition which must be true to enable this Interactive Session provider.", + "Chat" + ], + "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ + "Chat Session", + "Export Session", + "Import Session" + ], + "vs/workbench/contrib/chat/browser/chatEditorInput": [ + "Chat" + ], + "vs/workbench/contrib/chat/common/chatServiceImpl": [ + "Provider returned null response" + ], + "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ + "Open Session In Editor", + "Open Session In Editor", + "Open Session In Sidebar" + ], + "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ + "Clear", + "Clear", + "Clear" + ], + "vs/workbench/contrib/accessibility/browser/accessibleView": [ + "({0}) {1}", + "({0}) {1}", + "{0} accessibility verbosity is now disabled", + "\n\nOpen a browser window with more information related to accessibility (H).", + "\n\nExit this dialog (Escape).", + "Explore actions such as disabling this hint (Shift+Tab), use Escape to exit this dialog.", + "Explore actions such as disabling this hint (Shift+Tab).", + "Accessibility Help", + "Accessible View", + "Accessible View, {0}", + "Accessibility Help, {0}", + "Accessibility Help", + "Accessible View", + "Navigate to the toolbar (Shift+Tab)).", + "In the accessible view, you can:\n", + "Show the next ({0}) or previous ({1}) item.", + "Show the next or previous item by configuring keybindings for the Show Next & Previous in Accessible View commands.", + "\n\nDisable accessibility verbosity for this feature ({0}).", + "\n\nAdd a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.s", + "Go to a symbol ({0})", + "To go to a symbol, configure a keybinding for the command Go To Symbol in Accessible View", + "Inspect this in the accessible view with {0}", + "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.", + "Type to search symbols", + "Go to Symbol Accessible View" + ], + "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ + "{0} Source: {1}", + "{0}", + "Clear Notification", + "Clear Notification" + ], + "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ + "Show Next in Accessible View", + "Show Previous in Accessible View", + "Go To Symbol in Accessible View", + "Open Accessibility Help", + "Open Accessible View", + "Disable Accessible View Hint", + "Accept Inline Completion" + ], + "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ + "Next File Tree", + "Previous File Tree" + ], + "vs/workbench/contrib/chat/common/chatContextKeys": [ + "True when the provider has assigned an id to this response.", + "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.", + "True when the chat response was filtered out by the server.", + "True when the current request is still in progress.", + "The chat item is a response.", + "The chat item is a request", + "True when the chat input has text.", + "True when focus is in the chat input, false otherwise.", + "True when focus is in the chat widget, false otherwise.", + "True when some chat provider has been registered." + ], + "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ + "Pick a file" + ], + "vs/workbench/contrib/chat/common/chatColors": [ + "The border color of a chat request.", + "The background color of a chat slash command.", + "The foreground color of a chat slash command.", + "The background color of a chat avatar.", + "The foreground color of a chat avatar." + ], + "vs/workbench/contrib/notebook/common/notebookEditorInput": [ + "Notebook '{0}' could not be saved." + ], + "vs/workbench/contrib/notebook/browser/notebookEditor": [ + "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed and enabled.", + "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed and enabled.", + "Enable extension for '{0}'", + "Install extension for '{0}'", + "Open As Text", + "Open in Text Editor" ], "vs/workbench/contrib/notebook/browser/services/notebookServiceImpl": [ "Install extension for '{0}'" @@ -23427,14 +23793,14 @@ "vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor": [ "Notebook Text Diff" ], + "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ + "Executing a notebook cell will run code from this workspace." + ], "vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl": [ "Disable other keymaps ({0}) to avoid conflicts between keybindings?", "Yes", "No" ], - "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ - "Executing a notebook cell will run code from this workspace." - ], "vs/editor/common/languages/modesRegistry": [ "Plain Text" ], @@ -23458,54 +23824,45 @@ "The Quit Edit command will set focus on the cell container and is currently not triggerable by a keybinding.", "The Focus Output command ({0}) will set focus in the cell's output.", "The Quit Edit command will set focus in the cell's output and is currently not triggerable by a keybinding.", - "The up and down arrows will move focus between cells while focused on the outer cell container", + "The Focus Next Cell Editor command ({0}) will set focus in the next cell's editor.", + "The Focus Next Cell Editor command will set focus in the next cell's editor and is currently not triggerable by a keybinding.", + "The Focus Previous Cell Editor command ({0}) will set focus in the previous cell's editor.", + "The Focus Previous Cell Editor command will set focus in the previous cell's editor and is currently not triggerable by a keybinding.", + "The up and down arrows will also move focus between cells while focused on the outer cell container.", "The Execute Cell command ({0}) executes the cell that currently has focus.", "The Execute Cell command executes the cell that currently has focus and is currently not triggerable by a keybinding.", "The Insert Cell Above/Below commands will create new empty code cells", "The Change Cell to Code/Markdown commands are used to switch between cell types." ], - "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ - "Show Next in Accessible View", - "Show Previous in Accessible View", - "Go To Symbol in Accessible View", - "Open Accessibility Help", - "Open Accessible View", - "Disable Accessible View Hint", - "Accept Inline Completion" - ], - "vs/workbench/contrib/accessibility/browser/accessibleView": [ - "({0}) {1}", - "({0}) {1}", - "{0} accessibility verbosity is now disabled", - "\n\nOpen a browser window with more information related to accessibility (H).", - "\n\nExit this dialog (Escape).", - "Explore actions such as disabling this hint (Shift+Tab), use Escape to exit this dialog.", - "Explore actions such as disabling this hint (Shift+Tab).", - "Accessibility Help", - "Accessible View", - "Accessible View, {0}", - "Accessibility Help, {0}", - "Accessibility Help", - "Accessible View", - "Navigate to the toolbar (Shift+Tab)).", - "In the accessible view, you can:\n", - "Show the next ({0}) or previous ({1}) item.", - "Show the next or previous item by configuring keybindings for the Show Next & Previous in Accessible View commands.", - "\n\nDisable accessibility verbosity for this feature ({0}).", - "\n\nAdd a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.s", - "Go to a symbol ({0})", - "To go to a symbol, configure a keybinding for the command Go To Symbol in Accessible View", - "Inspect this in the accessible view with {0}", - "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.", - "Type to search symbols", - "Go to Symbol Accessible View" - ], "vs/workbench/contrib/notebook/browser/controller/coreActions": [ "Notebook", "Insert Cell", "Notebook Cell", "Share" ], + "vs/workbench/contrib/notebook/browser/controller/executeActions": [ + "Render All Markdown Cells", + "Run All", + "Run All", + "Execute Cell", + "Execute Cell", + "Execute Above Cells", + "Execute Cell and Below", + "Execute Cell and Focus Container", + "Execute Cell and Focus Container", + "Stop Cell Execution", + "Stop Cell Execution", + "Execute Notebook Cell and Select Below", + "Execute Notebook Cell and Insert Below", + "Stop Execution", + "Interrupt", + "Go to Running Cell", + "Go to Running Cell", + "Go To", + "Go to Most Recently Failed Cell", + "Go to Most Recently Failed Cell", + "Go To" + ], "vs/workbench/contrib/notebook/browser/controller/insertCellActions": [ "Insert Code Cell Above", "Insert Code Cell Above and Focus Container", @@ -23532,32 +23889,6 @@ "Markdown", "Add Markdown Cell" ], - "vs/workbench/contrib/notebook/browser/controller/executeActions": [ - "Render All Markdown Cells", - "Run All", - "Run All", - "Execute Cell", - "Execute Cell", - "Execute Above Cells", - "Execute Cell and Below", - "Execute Cell and Focus Container", - "Execute Cell and Focus Container", - "Stop Cell Execution", - "Stop Cell Execution", - "Execute Notebook Cell and Select Below", - "Execute Notebook Cell and Insert Below", - "Stop Execution", - "Interrupt", - "Go to Running Cell", - "Go to Running Cell", - "Go To", - "Go to Most Recently Failed Cell", - "Go to Most Recently Failed Cell", - "Go To" - ], - "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ - "Copy Output" - ], "vs/workbench/contrib/notebook/browser/controller/layoutActions": [ "Select between Notebook Layouts", "Customize Notebook Layout", @@ -23592,34 +23923,48 @@ "Accept Detected Language for Cell", "Unable to detect cell language" ], + "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ + "Copy Output" + ], "vs/workbench/contrib/notebook/browser/controller/foldingController": [ "Fold Cell", "Unfold Cell", "Fold Cell" ], + "vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard": [ + "Copy Cell", + "Cut Cell", + "Paste Cell", + "Paste Cell Above", + "Toggle Notebook Clipboard Troubleshooting" + ], + "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ + "Hide Find in Notebook", + "Find in Notebook" + ], "vs/workbench/contrib/notebook/browser/contrib/format/formatting": [ "Format Notebook", "Format Notebook", "Format Cell", "Format Cells" ], + "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants": [ + "Formatting", + "Format Notebook", + "Notebook Trim Trailing Whitespace", + "Trim Final New Lines", + "Insert Final New Line", + "Running 'Notebook' code actions", + "Running 'Cell' code actions", + "Getting code actions from '{0}' ([configure]({1})).", + "Applying code action '{0}'." + ], "vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted": [ "Reset notebook getting started" ], "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions": [ "Toggle Cell Toolbar Position" ], - "vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard": [ - "Copy Cell", - "Cut Cell", - "Paste Cell", - "Paste Cell Above", - "Toggle Notebook Clipboard Troubleshooting" - ], - "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ - "Hide Find in Notebook", - "Find in Notebook" - ], "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow": [ "Focus Next Cell Editor", "Focus Previous Cell Editor", @@ -23634,21 +23979,10 @@ "Cell Cursor Page Down Select", "When enabled cursor can navigate to the next/previous cell when the current cursor in the cell editor is at the first/last line." ], - "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants": [ - "Formatting", - "Format Notebook", - "Notebook Trim Trailing Whitespace", - "Trim Final New Lines", - "Insert Final New Line", - "Running 'Notebook' code actions", - "Running 'Cell' code actions", - "Getting code actions from '{0}' ([configure]({1})).", - "Applying code action '{0}'." - ], "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ "When enabled notebook outline shows code cells.", "When enabled notebook breadcrumbs contain code cells.", - "When enabled goto symbol quickpick will display full code symbols from the notebook, as well as markdown headers." + "When enabled the Go to Symbol Quick Pick will display full code symbols from the notebook, as well as Markdown headers." ], "vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile": [ "Set Profile" @@ -23676,11 +24010,6 @@ "Cell {0} ({1} selected)", "Cell {0} of {1}" ], - "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ - "Toggle Layout Troubleshoot", - "Inspect Notebook Layout", - "Clear Notebook Editor Type Cache" - ], "vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands": [ "Move Cell Up", "Move Cell Down", @@ -23717,120 +24046,10 @@ "Hide Metadata Differences", "Hide Outputs Differences" ], - "vs/workbench/contrib/chat/browser/actions/chatActions": [ - "Chat", - "Quick Chat", - "Accept Chat Input", - "Clear Input History", - "Focus Chat List", - "Focus Chat Input", - "Open Editor ({0})", - "Show History", - "Delete", - "Select a chat session to restore" - ], - "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ - "Copy All", - "Copy" - ], - "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ - "Submit", - "Cancel" - ], - "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ - "Helpful", - "Unhelpful", - "Insert into Notebook", - "Remove Request and Response" - ], - "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ - "Open in Chat View", - "Close Quick Chat", - "Quick Chat", - "Open Quick Chat ({0})" - ], - "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ - "Copy", - "Insert at Cursor", - "Insert Into New File", - "Run in Terminal", - "Next Code Block", - "Previous Code Block" - ], - "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ - "Chat Session", - "Export Session", - "Import Session" - ], - "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ - "Contributes an Interactive Session provider", - "Unique identifier for this Interactive Session provider.", - "Display name for this Interactive Session provider.", - "An icon for this Interactive Session provider.", - "A condition which must be true to enable this Interactive Session provider.", - "Chat" - ], - "vs/workbench/contrib/chat/browser/chatEditorInput": [ - "Chat" - ], - "vs/workbench/contrib/chat/common/chatServiceImpl": [ - "Provider returned null response" - ], - "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ - "Open Session In Editor", - "Open Session In Editor", - "Open Session In Sidebar" - ], - "vs/workbench/contrib/chat/common/chatContextKeys": [ - "True when the provider has assigned an id to this response.", - "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.", - "True when the chat response was filtered out by the server.", - "True when the current request is still in progress.", - "The chat item is a response.", - "The chat item is a request", - "True when the chat input has text.", - "True when focus is in the chat input, false otherwise.", - "True when focus is in the chat widget, false otherwise.", - "True when some chat provider has been registered." - ], - "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ - "Clear", - "Clear", - "Clear" - ], - "vs/workbench/contrib/chat/common/chatViewModel": [ - "Thinking" - ], - "vs/workbench/contrib/chat/common/chatSlashCommands": [ - "The name of the slash command which will be used as prefix.", - "The details of the slash command.", - "Contributes slash commands to chat", - "Invalid {0}: {1}" - ], - "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ - "Next File Tree", - "Previous File Tree" - ], - "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ - "{0} Source: {1}", - "{0}", - "Clear Notification", - "Clear Notification" - ], - "vs/workbench/contrib/chat/common/chatAgents": [ - "The name of the agent which will be used as prefix.", - "The details of the agent.", - "Contributes agents to chat", - "Invalid {0}: {1}" - ], - "vs/workbench/contrib/chat/common/chatColors": [ - "The border color of a chat request.", - "The background color of a chat slash command.", - "The foreground color of a chat slash command." - ], - "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ - "Ask a question or type '@' or '/'", - "Ask a question" + "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ + "Toggle Layout Troubleshoot", + "Inspect Notebook Layout", + "Clear Notebook Editor Type Cache" ], "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ "AI-generated code may be incorrect", @@ -23841,9 +24060,9 @@ "Ask a question", "{0} ({1}, {2} for history)", "Thinking…", + "Review proposed changes in the diff editor.", + "No results, please refine your input and try again", "No results, please refine your input and try again", - "{0}", - "Use tab to navigate to the diff editor and review proposed changes.", "Failed to apply changes.", "Failed to discard changes." ], @@ -23881,11 +24100,12 @@ "Changes are applied directly to the document and are highlighted visually via inline or side-by-side diffs. Ending a session will keep the changes.", "Changes are previewed only and need to be accepted via the apply button. Ending a session will discard the changes.", "Changes are applied directly to the document but can be highlighted via inline diffs. Ending a session will keep the changes.", - "Enable/disable showing the diff when edits are generated. Works only with inlineChat.mode equal to live or livePreview." + "Enable/disable showing the diff when edits are generated. Works only with inlineChat.mode equal to live or livePreview.", + "Show/hide a gutter icon for spawning inline chat on empty lines." ], "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ - "Start Code Chat", - "Resume Last Dismissed Code Chat", + "Start Inline Chat", + "Resume Last Dismissed Inline Chat", "Inline Chat", "Make Request", "Regenerate Response", @@ -23915,6 +24135,10 @@ "Show More", "Show Less" ], + "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations": [ + "Icon which spawns the inline chat from the gutter", + "Toggle Inline Chat Icon" + ], "vs/workbench/contrib/files/browser/fileConstants": [ "Save As...", "Save", @@ -23923,47 +24147,6 @@ "Remove Folder from Workspace", "New Untitled Text File" ], - "vs/workbench/contrib/testing/browser/icons": [ - "View icon of the test view.", - "Icons for test results.", - "Icon of the \"run test\" action.", - "Icon of the \"rerun tests\" action.", - "Icon of the \"run all tests\" action.", - "Icon of the \"debug all tests\" action.", - "Icon of the \"debug test\" action.", - "Icon to cancel ongoing test runs.", - "Icon for the 'Filter' action in the testing view.", - "Icon shown beside hidden tests, when they've been shown.", - "Icon shown when the test explorer is disabled as a tree.", - "Icon shown when the test explorer is disabled as a list.", - "Icon shown to update test profiles.", - "Icon on the button to refresh tests.", - "Icon to turn continuous test runs on.", - "Icon to turn continuous test runs off.", - "Icon when continuous run is on for a test ite,.", - "Icon on the button to cancel refreshing tests.", - "Icon shown for tests that have an error.", - "Icon shown for tests that failed.", - "Icon shown for tests that passed.", - "Icon shown for tests that are queued.", - "Icon shown for tests that are skipped.", - "Icon shown for tests that are in an unset state." - ], - "vs/workbench/contrib/testing/browser/testingDecorations": [ - "Peek Test Output", - "Expected", - "Actual", - "Click for test options", - "Click to debug tests, right click for more options", - "Click to run tests, right click for more options", - "Run Test", - "Debug Test", - "Execute Using Profile...", - "Peek Error", - "Reveal in Test Explorer", - "Run All Tests", - "Debug All Tests" - ], "vs/workbench/contrib/testing/browser/testingProgressUiService": [ "Running tests...", "Running tests, {0}/{1} passed ({2}%)", @@ -23971,20 +24154,32 @@ "{0}/{1} tests passed ({2}%)", "{0}/{1} tests passed ({2}%, {3} skipped)" ], - "vs/workbench/contrib/testing/browser/testingExplorerView": [ - "{0} (Default)", - "Select Default Profile", - "Configure Test Profiles", - "No test results yet.", - "Tests are being watched for changes", - "{0} passed tests", - "{0} skipped tests", - "{0} failed tests", - "No tests were found in this file.", - "Show Workspace Tests", - "{0}, in {1}", - "{0}, outdated result", - "Test Explorer" + "vs/workbench/contrib/testing/browser/testingOutputPeek": [ + "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled.", + "Test Output", + "Expected result", + "Actual result", + "Test output is only available for new test runs.", + "The test run did not record any output.", + "Close", + "Unnamed Task", + "+ {0} more lines", + "+ 1 more line", + "Test Result Messages", + "Show Result Output", + "Show Result Output", + "Rerun Test Run", + "Debug Test Run", + "Show Result Output", + "Reveal in Test Explorer", + "Run Test", + "Debug Test", + "Go to Source", + "Go to Source", + "Go to Next Test Failure", + "Go to Previous Test Failure", + "Open in Editor", + "Toggle Test History in Peek" ], "vs/workbench/contrib/testing/browser/testingViewPaneContainer": [ "Testing" @@ -24017,35 +24212,20 @@ "Controls when the testing view should open.", "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed." ], - "vs/workbench/contrib/testing/browser/testingOutputPeek": [ - "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled.", - "Test Output", - "Expected result", - "Actual result", - "Test output is only available for new test runs.", - "The test run did not record any output.", - "Close", - "Unnamed Task", - "+ {0} more lines", - "+ 1 more line", - "Test Result Messages", - "Show Result Output", - "Show Result Output", - "Rerun Test Run", - "Debug Test Run", - "Show Result Output", - "Reveal in Test Explorer", + "vs/workbench/contrib/testing/browser/testingDecorations": [ + "Peek Test Output", + "Expected", + "Actual", + "Click for test options", + "Click to debug tests, right click for more options", + "Click to run tests, right click for more options", "Run Test", "Debug Test", - "Go to Source", - "Go to Source", - "Go to Next Test Failure", - "Go to Previous Test Failure", - "Open in Editor", - "Toggle Test History in Peek" - ], - "vs/workbench/contrib/testing/common/testingContentProvider": [ - "The test run did not record any output." + "Execute Using Profile...", + "Peek Error", + "Reveal in Test Explorer", + "Run All Tests", + "Debug All Tests" ], "vs/workbench/contrib/testing/common/testServiceImpl": [ "Running tests may execute code in your workspace.", @@ -24053,6 +24233,35 @@ "Running tests may execute code in your workspace.", "An error occurred attempting to run tests: {0}" ], + "vs/workbench/contrib/testing/common/testingContentProvider": [ + "The test run did not record any output." + ], + "vs/workbench/contrib/testing/browser/icons": [ + "View icon of the test view.", + "Icons for test results.", + "Icon of the \"run test\" action.", + "Icon of the \"rerun tests\" action.", + "Icon of the \"run all tests\" action.", + "Icon of the \"debug all tests\" action.", + "Icon of the \"debug test\" action.", + "Icon to cancel ongoing test runs.", + "Icon for the 'Filter' action in the testing view.", + "Icon shown beside hidden tests, when they've been shown.", + "Icon shown when the test explorer is disabled as a tree.", + "Icon shown when the test explorer is disabled as a list.", + "Icon shown to update test profiles.", + "Icon on the button to refresh tests.", + "Icon to turn continuous test runs on.", + "Icon to turn continuous test runs off.", + "Icon when continuous run is on for a test ite,.", + "Icon on the button to cancel refreshing tests.", + "Icon shown for tests that have an error.", + "Icon shown for tests that failed.", + "Icon shown for tests that passed.", + "Icon shown for tests that are queued.", + "Icon shown for tests that are skipped.", + "Icon shown for tests that are in an unset state." + ], "vs/workbench/contrib/testing/common/testingContextKeys": [ "Indicates whether any test controller has an attached refresh handler.", "Indicates whether any test controller is currently refreshing tests.", @@ -24077,27 +24286,6 @@ "Pick a test profile to use", "Update Test Configuration" ], - "vs/workbench/contrib/logs/common/logsActions": [ - "Set Log Level...", - "All", - "Extension Logs", - "Logs", - "Set Log Level", - " {0}: Select log level", - "Select log level", - "Set as Default Log Level", - "Trace", - "Debug", - "Info", - "Warning", - "Error", - "Off", - "Default", - "Open Window Log File (Session)...", - "Current", - "Select Session", - "Select Log file" - ], "vs/workbench/contrib/testing/browser/testExplorerActions": [ "Hide Test", "Unhide Test", @@ -24150,6 +24338,27 @@ "Refresh Tests", "Cancel Test Refresh" ], + "vs/workbench/contrib/logs/common/logsActions": [ + "Set Log Level...", + "All", + "Extension Logs", + "Logs", + "Set Log Level", + " {0}: Select log level", + "Select log level", + "Set as Default Log Level", + "Trace", + "Debug", + "Info", + "Warning", + "Error", + "Off", + "Default", + "Open Window Log File (Session)...", + "Current", + "Select Session", + "Select Log file" + ], "vs/editor/contrib/peekView/browser/peekView": [ "Whether the current code editor is embedded inside peek", "Close", @@ -24169,6 +24378,50 @@ "Match highlight color in the peek view editor.", "Match highlight border in the peek view editor." ], + "vs/workbench/contrib/testing/browser/testingExplorerView": [ + "{0} (Default)", + "Select Default Profile", + "Configure Test Profiles", + "No test results yet.", + "Tests are being watched for changes", + "{0} passed tests", + "{0} skipped tests", + "{0} failed tests", + "No tests were found in this file.", + "Show Workspace Tests", + "{0}, in {1}", + "{0}, outdated result", + "Test Explorer" + ], + "vs/workbench/contrib/interactive/browser/interactiveEditor": [ + "Type '{0}' code here and press {1} to run" + ], + "vs/platform/quickinput/browser/helpQuickAccess": [ + "{0}, {1}" + ], + "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ + "No matching views", + "Side Bar", + "Panel", + "Secondary Side Bar", + "{0}: {1}", + "Terminal", + "Debug Console", + "Output", + "Open View", + "Quick Open View" + ], + "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ + "No matching commands", + "Configure Keybinding", + "Ask {0}: {1}", + "{0}: {1}", + "Show All Commands", + "Clear Command History", + "Do you want to clear the history of recently used commands?", + "This action is irreversible!", + "&&Clear" + ], "vs/workbench/contrib/notebook/browser/notebookIcons": [ "Configure icon to select a kernel in notebook editors.", "Icon to execute in notebook editors.", @@ -24197,66 +24450,6 @@ "Icon for the previous change action in the diff editor.", "Icon for the next change action in the diff editor." ], - "vs/workbench/contrib/interactive/browser/interactiveEditor": [ - "Type '{0}' code here and press {1} to run" - ], - "vs/platform/quickinput/browser/helpQuickAccess": [ - "{0}, {1}" - ], - "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ - "No matching commands", - "Configure Keybinding", - "Ask {0}: {1}", - "{0}: {1}", - "Show All Commands", - "Clear Command History", - "Do you want to clear the history of recently used commands?", - "This action is irreversible!", - "&&Clear" - ], - "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ - "No matching views", - "Side Bar", - "Panel", - "Secondary Side Bar", - "{0}: {1}", - "Terminal", - "Debug Console", - "Output", - "Open View", - "Quick Open View" - ], - "vs/workbench/contrib/files/browser/fileCommands": [ - "{0} (in file) ↔ {1}", - "Failed to save '{0}': {1}", - "Retry", - "Discard", - "Failed to revert '{0}': {1}", - "Create File" - ], - "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ - "Use the actions in the editor tool bar to either undo your changes or overwrite the content of the file with your changes.", - "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents or overwrite the content of the file with your changes.", - "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.", - "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.", - "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.", - "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", - "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", - "Failed to save '{0}': {1}", - "Learn More", - "Don't Show Again", - "Compare", - "{0} (in file) ↔ {1} (in {2}) - Resolve save conflict", - "Overwrite as Admin...", - "Overwrite as Sudo...", - "Retry as Admin...", - "Retry as Sudo...", - "Retry", - "Discard", - "Overwrite", - "Overwrite", - "Configure" - ], "vs/workbench/contrib/files/browser/fileActions": [ "New File...", "New Folder...", @@ -24303,7 +24496,6 @@ "A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", "&&Replace", "Compare Active File With...", - "Toggle Auto Save", "Save All in Group", "Close Group", "Focus on Files Explorer", @@ -24336,10 +24528,40 @@ "Set Active Editor Read-only in Session", "Set Active Editor Writeable in Session", "Toggle Active Editor Read-only in Session", - "Reset Active Editor Read-only in Session" + "Reset Active Editor Read-only in Session", + "Toggle Auto Save", + "Toggle the ability to save files automatically after typing" + ], + "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ + "Use the actions in the editor tool bar to either undo your changes or overwrite the content of the file with your changes.", + "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents or overwrite the content of the file with your changes.", + "Failed to save '{0}': File is read-only. Select 'Overwrite as Admin' to retry as administrator.", + "Failed to save '{0}': File is read-only. Select 'Overwrite as Sudo' to retry as superuser.", + "Failed to save '{0}': File is read-only. Select 'Overwrite' to attempt to make it writeable.", + "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", + "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", + "Failed to save '{0}': {1}", + "Learn More", + "Don't Show Again", + "Compare", + "{0} (in file) ↔ {1} (in {2}) - Resolve save conflict", + "Overwrite as Admin...", + "Overwrite as Sudo...", + "Retry as Admin...", + "Retry as Sudo...", + "Retry", + "Discard", + "Overwrite", + "Overwrite", + "Configure" ], - "vs/workbench/contrib/files/browser/views/emptyView": [ - "No Folder Opened" + "vs/workbench/contrib/files/browser/fileCommands": [ + "{0} (in file) ↔ {1}", + "Failed to save '{0}': {1}", + "Retry", + "Discard", + "Failed to revert '{0}': {1}", + "Create File" ], "vs/workbench/contrib/files/browser/views/explorerView": [ "Explorer Section: {0}", @@ -24349,17 +24571,23 @@ "Collapse Folders in Explorer" ], "vs/workbench/contrib/files/browser/views/openEditorsView": [ - "Open Editors", "{0} unsaved", "Open Editors", "Toggle Vertical/Horizontal Editor Layout", "Flip Layout", "Flip &&Layout", - "New Untitled Text File" + "New Untitled Text File", + "Open Editors" ], "vs/workbench/contrib/files/browser/editors/binaryFileEditor": [ "Binary File Viewer" ], + "vs/workbench/contrib/files/browser/workspaceWatcher": [ + "Unable to watch for file changes in this large workspace folder. Please follow the instructions link to resolve this issue.", + "Instructions", + "File changes watcher stopped unexpectedly. A reload of the window may enable the watcher again unless the workspace cannot be watched for file changes.", + "Reload" + ], "vs/editor/common/config/editorConfigurationSchema": [ "Editor", "The number of spaces a tab is equal to. This setting is overridden based on the file contents when {0} is on.", @@ -24413,12 +24641,6 @@ "1 unsaved file", "{0} unsaved files" ], - "vs/workbench/contrib/files/browser/workspaceWatcher": [ - "Unable to watch for file changes in this large workspace folder. Please follow the instructions link to resolve this issue.", - "Instructions", - "File changes watcher stopped unexpectedly. A reload of the window may enable the watcher again unless the workspace cannot be watched for file changes.", - "Reload" - ], "vs/workbench/contrib/files/browser/editors/textFileEditor": [ "Text File Editor", "Open Folder", @@ -24444,8 +24666,32 @@ "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ "Other" ], - "vs/workbench/contrib/search/browser/searchActionsBase": [ - "Search" + "vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess": [ + "Open a text editor first to go to a line.", + "Go to line {0} and character {1}.", + "Go to line {0}.", + "Current Line: {0}, Character: {1}. Type a line number between 1 and {2} to navigate to.", + "Current Line: {0}, Character: {1}. Type a line number to navigate to." + ], + "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess": [ + "No matching entries", + "Go to Symbol in Editor...", + "Go to &&Symbol in Editor...", + "Type the name of a symbol to go to.", + "Go to Symbol in Editor", + "Go to Symbol in Editor by Category" + ], + "vs/workbench/contrib/search/browser/anythingQuickAccess": [ + "No matching results", + "recently opened", + "file and symbol results", + "file results", + "{0}, {1}", + "Open Quick Chat", + "Open to the Side", + "Open to the Bottom", + "Remove from Recently Opened", + "{0} unsaved changes" ], "vs/workbench/contrib/search/browser/searchIcons": [ "Icon to make search details visible.", @@ -24466,101 +24712,25 @@ "Icon for the action to open a new search editor.", "Icon for the action to go to the file of the current search result." ], - "vs/workbench/contrib/searchEditor/browser/searchEditor": [ - "Toggle Search Details", - "files to include", - "Search Include Patterns", - "files to exclude", - "Search Exclude Patterns", - "Run Search", - "Matched {0} at {1} in file {2}", + "vs/workbench/contrib/search/browser/searchWidget": [ + "Replace All (Submit Search to Enable)", + "Replace All", + "Toggle Replace", + "Search: Type Search Term and press Enter to search", "Search", - "Search editor text input box border." - ], - "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ - "Search: {0}", - "Search: {0}", - "Search" + "Toggle Context Lines", + "Replace: Type replace term and press Enter to preview", + "Replace" ], - "vs/workbench/contrib/search/browser/patternInputWidget": [ - "input", - "Search only in Open Editors", - "Use Exclude Settings and Ignore Files" + "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ + "No matching workspace symbols", + "Open to the Side", + "Open to the Bottom" ], - "vs/workbench/contrib/search/browser/searchMessage": [ - "Unable to open command link from untrusted source: {0}", - "Unable to open unknown link: {0}" - ], - "vs/workbench/browser/parts/views/viewPane": [ - "Icon for an expanded view pane container.", - "Icon for a collapsed view pane container.", - "{0} actions" - ], - "vs/workbench/contrib/search/browser/searchResultsView": [ - "Other files", - "Other files", - "{0} files found", - "{0} file found", - "{0} matches found", - "{0} match found", - "From line {0}", - "{0} more lines", - "Search", - "{0} matches in folder root {1}, Search result", - "{0} matches outside of the workspace, Search result", - "{0} matches in file {1} of folder {2}, Search result", - "'{0}' at column {1} replace {2} with {3}", - "'{0}' at column {1} found {2}" - ], - "vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess": [ - "Open a text editor first to go to a line.", - "Go to line {0} and character {1}.", - "Go to line {0}.", - "Current Line: {0}, Character: {1}. Type a line number between 1 and {2} to navigate to.", - "Current Line: {0}, Character: {1}. Type a line number to navigate to." - ], - "vs/workbench/contrib/search/browser/searchWidget": [ - "Replace All (Submit Search to Enable)", - "Replace All", - "Toggle Replace", - "Search: Type Search Term and press Enter to search", - "Search", - "Toggle Context Lines", - "Replace: Type replace term and press Enter to preview", - "Replace" - ], - "vs/workbench/services/search/common/queryBuilder": [ - "Workspace folder does not exist: {0}" - ], - "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess": [ - "No matching entries", - "Go to Symbol in Editor...", - "Go to &&Symbol in Editor...", - "Type the name of a symbol to go to.", - "Go to Symbol in Editor", - "Go to Symbol in Editor by Category" - ], - "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ - "No matching workspace symbols", - "Open to the Side", - "Open to the Bottom" - ], - "vs/workbench/contrib/search/browser/anythingQuickAccess": [ - "No matching results", - "recently opened", - "file and symbol results", - "file results", - "{0}, {1}", - "Open Quick Chat", - "Open to the Side", - "Open to the Bottom", - "Remove from Recently Opened", - "{0} unsaved changes" - ], - "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess": [ - "See More Files", - "Open File", - "More" + "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess": [ + "See More Files", + "Open File", + "More" ], "vs/workbench/contrib/search/browser/searchActionsCopy": [ "Copy", @@ -24603,11 +24773,6 @@ "Replace All", "Replace All" ], - "vs/workbench/contrib/search/browser/searchActionsSymbol": [ - "Go to Symbol in Workspace...", - "Go to Symbol in Workspace...", - "Go to Symbol in &&Workspace..." - ], "vs/workbench/contrib/search/browser/searchActionsTopBar": [ "Clear Search History", "Cancel Search", @@ -24618,77 +24783,147 @@ "View as Tree", "View as List" ], + "vs/workbench/contrib/search/browser/searchActionsSymbol": [ + "Go to Symbol in Workspace...", + "Go to Symbol in Workspace...", + "Go to Symbol in &&Workspace..." + ], "vs/workbench/contrib/search/browser/searchActionsTextQuickAccess": [ "Quick Text Search (Experimental)" ], - "vs/workbench/contrib/debug/browser/debugColors": [ - "Debug toolbar background color.", - "Debug toolbar border color.", - "Debug toolbar icon for start debugging.", - "Debug toolbar icon for pause.", - "Debug toolbar icon for stop.", - "Debug toolbar icon for disconnect.", - "Debug toolbar icon for restart.", - "Debug toolbar icon for step over.", - "Debug toolbar icon for step into.", - "Debug toolbar icon for step over.", - "Debug toolbar icon for continue.", - "Debug toolbar icon for step back." + "vs/workbench/contrib/search/browser/searchActionsBase": [ + "Search" ], - "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ - "Start a New Debug Session" + "vs/workbench/contrib/searchEditor/browser/searchEditor": [ + "Toggle Search Details", + "files to include", + "Search Include Patterns", + "files to exclude", + "Search Exclude Patterns", + "Run Search", + "Matched {0} at {1} in file {2}", + "Search", + "Search editor text input box border." ], - "vs/workbench/contrib/debug/browser/callStackView": [ - "Running", - "Show More Stack Frames", - "Session", - "Running", - "Restart Frame", - "Load More Stack Frames", - "Show {0} More: {1}", - "Show {0} More Stack Frames", + "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ + "Search: {0}", + "Search: {0}", + "Search" + ], + "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ + "{0} - {1} of {2} changes", + "{0} - {1} of {2} change", + "{0} of {1} changes", + "{0} of {1} change", + "Close", + "Show Previous Change", + "Show Next Change", + "Next &&Change", + "Previous &&Change", + "Go to Previous Change", + "Go to Next Change", + "Editor gutter background color for lines that are modified.", + "Editor gutter background color for lines that are added.", + "Editor gutter background color for lines that are deleted.", + "Minimap gutter background color for lines that are modified.", + "Minimap gutter background color for lines that are added.", + "Minimap gutter background color for lines that are deleted.", + "Overview ruler marker color for modified content.", + "Overview ruler marker color for added content.", + "Overview ruler marker color for deleted content." + ], + "vs/workbench/contrib/scm/browser/activity": [ + "Source Control", + "{0} pending changes" + ], + "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ + "Source Control" + ], + "vs/workbench/contrib/scm/browser/scmViewPane": [ + "Source Control Management", + "Source Control Input", + "View & Sort", + "Repositories", + "View as List", + "View as Tree", + "Sort by Discovery Time", + "Sort by Name", + "Sort by Path", + "Sort Changes by Name", + "Sort Changes by Path", + "Sort Changes by Status", + "Collapse All Repositories", + "Expand All Repositories", + "Close" + ], + "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ + "Source Control Repositories" + ], + "vs/workbench/contrib/workspace/common/workspace": [ + "Whether the workspace trust feature is enabled.", + "Whether the current workspace has been trusted by the user." + ], + "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ + "Source Control Sync", + "Incoming Changes", + "Outgoing Changes", + "Refresh", + "View as List", + "View as Tree" + ], + "vs/workbench/browser/parts/views/viewPane": [ + "Icon for an expanded view pane container.", + "Icon for a collapsed view pane container.", + "{0} actions" + ], + "vs/workbench/contrib/search/browser/patternInputWidget": [ + "input", + "Search only in Open Editors", + "Use Exclude Settings and Ignore Files" + ], + "vs/workbench/contrib/search/browser/searchMessage": [ + "Unable to open command link from untrusted source: {0}", + "Unable to open unknown link: {0}" + ], + "vs/workbench/contrib/search/browser/searchResultsView": [ + "Other files", + "Other files", + "{0} files found", + "{0} file found", + "{0} matches found", + "{0} match found", + "From line {0}", + "{0} more lines", + "Search", + "{0} matches in folder root {1}, Search result", + "{0} matches outside of the workspace, Search result", + "{0} matches in file {1} of folder {2}, Search result", + "'{0}' at column {1} replace {2} with {3}", + "'{0}' at column {1} found {2}" + ], + "vs/workbench/services/search/common/queryBuilder": [ + "Workspace folder does not exist: {0}" + ], + "vs/workbench/contrib/debug/browser/debugHover": [ + "Hold {0} key to switch to editor language hover", + "Debug Hover", + "{0}, value {1}, variables, debug" + ], + "vs/workbench/contrib/debug/browser/exceptionWidget": [ + "Exception widget border color.", + "Exception widget background color.", + "Exception has occurred: {0}", + "Exception has occurred.", + "Close" + ], + "vs/workbench/contrib/debug/common/debugModel": [ + "Invalid variable attributes", + "Please start a debug session to evaluate expressions", + "not available", "Paused on {0}", "Paused", - "Debug Call Stack", - "Thread {0} {1}", - "Stack Frame {0}, line {1}, {2}", "Running", - "Session {0} {1}", - "Show {0} More Stack Frames", - "Collapse All" - ], - "vs/workbench/contrib/debug/browser/debugCommands": [ - "Debug", - "Restart", - "Step Over", - "Step Into", - "Step Into Target", - "Step Out", - "Pause", - "Disconnect", - "Disconnect and Suspend", - "Stop", - "Continue", - "Focus Session", - "Select and Start Debugging", - "Open '{0}'", - "Start Debugging", - "Start Without Debugging", - "Focus Next Debug Console", - "Focus Previous Debug Console", - "Open Loaded Script...", - "Navigate to Top of Call Stack", - "Navigate to Bottom of Call Stack", - "Navigate Up Call Stack", - "Navigate Down Call Stack", - "Select Debug Console", - "Select Debug Session", - "Choose the specific location", - "No executable code is associated at the current cursor position.", - "Jump to Cursor", - "No step targets available", - "Add Configuration...", - "Add Inline Breakpoint" + "Unverified breakpoint. File is modified, please restart debug session." ], "vs/workbench/contrib/debug/browser/breakpointsView": [ "Unverified Exception Breakpoint", @@ -24744,6 +24979,98 @@ "Edit Function Breakpoint...", "Edit Hit Count..." ], + "vs/workbench/contrib/debug/browser/callStackView": [ + "Running", + "Show More Stack Frames", + "Session", + "Running", + "Restart Frame", + "Load More Stack Frames", + "Show {0} More: {1}", + "Show {0} More Stack Frames", + "Paused on {0}", + "Paused", + "Debug Call Stack", + "Thread {0} {1}", + "Stack Frame {0}, line {1}, {2}", + "Running", + "Session {0} {1}", + "Show {0} More Stack Frames", + "Collapse All" + ], + "vs/workbench/contrib/debug/browser/debugColors": [ + "Debug toolbar background color.", + "Debug toolbar border color.", + "Debug toolbar icon for start debugging.", + "Debug toolbar icon for pause.", + "Debug toolbar icon for stop.", + "Debug toolbar icon for disconnect.", + "Debug toolbar icon for restart.", + "Debug toolbar icon for step over.", + "Debug toolbar icon for step into.", + "Debug toolbar icon for step over.", + "Debug toolbar icon for continue.", + "Debug toolbar icon for step back." + ], + "vs/workbench/contrib/debug/browser/debugCommands": [ + "Debug", + "Restart", + "Step Over", + "Step Into", + "Step Into Target", + "Step Out", + "Pause", + "Disconnect", + "Disconnect and Suspend", + "Stop", + "Continue", + "Focus Session", + "Select and Start Debugging", + "Open '{0}'", + "Start Debugging", + "Start Without Debugging", + "Focus Next Debug Console", + "Focus Previous Debug Console", + "Open Loaded Script...", + "Navigate to Top of Call Stack", + "Navigate to Bottom of Call Stack", + "Navigate Up Call Stack", + "Navigate Down Call Stack", + "Select Debug Console", + "Select Debug Session", + "Choose the specific location", + "No executable code is associated at the current cursor position.", + "Jump to Cursor", + "No step targets available", + "Add Configuration...", + "Add Inline Breakpoint" + ], + "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ + "Start a New Debug Session" + ], + "vs/workbench/contrib/debug/browser/debugEditorActions": [ + "Debug: Toggle Breakpoint", + "Toggle &&Breakpoint", + "Debug: Add Conditional Breakpoint...", + "&&Conditional Breakpoint...", + "Debug: Add Logpoint...", + "&&Logpoint...", + "Debug: Edit Breakpoint", + "&&Edit Breakpoint", + "Open Disassembly View", + "&&DisassemblyView", + "Toggle Source Code in Disassembly View", + "&&ToggleSource", + "Debug: Show Hover", + "Step targets are not available here", + "Step Into Target", + "Debug: Go to Next Breakpoint", + "Debug: Go to Previous Breakpoint", + "Close Exception Widget", + "Run to Cursor", + "Evaluate in Debug Console", + "Add to Watch" + ], "vs/workbench/contrib/debug/browser/debugIcons": [ "View icon of the debug console view.", "View icon of the run view.", @@ -24801,28 +25128,15 @@ "Icon for the debug evaluation prompt.", "Icon for the inspect memory action." ], - "vs/workbench/contrib/debug/browser/debugEditorActions": [ - "Debug: Toggle Breakpoint", - "Toggle &&Breakpoint", - "Debug: Add Conditional Breakpoint...", - "&&Conditional Breakpoint...", - "Debug: Add Logpoint...", - "&&Logpoint...", - "Debug: Edit Breakpoint", - "&&Edit Breakpoint", - "Open Disassembly View", - "&&DisassemblyView", - "Toggle Source Code in Disassembly View", - "&&ToggleSource", - "Run to Cursor", - "Evaluate in Debug Console", - "Add to Watch", - "Debug: Show Hover", - "Step targets are not available here", - "Step Into Target", - "Debug: Go to Next Breakpoint", - "Debug: Go to Previous Breakpoint", - "Close Exception Widget" + "vs/workbench/contrib/debug/browser/debugQuickAccess": [ + "No matching launch configurations", + "Configure Launch Configuration", + "contributed", + "Remove Launch Configuration", + "{0} contributed configurations", + "configure", + "Add Config ({0})...", + "Add Configuration..." ], "vs/workbench/contrib/debug/browser/debugService": [ "1 active session", @@ -24847,26 +25161,16 @@ "Added breakpoint, line {0}, file {1}", "Removed breakpoint, line {0}, file {1}" ], - "vs/workbench/contrib/debug/browser/debugQuickAccess": [ - "No matching launch configurations", - "Configure Launch Configuration", - "contributed", - "Remove Launch Configuration", - "{0} contributed configurations", - "configure", - "Add Config ({0})...", - "Add Configuration..." + "vs/workbench/contrib/debug/browser/debugStatus": [ + "Debug", + "Debug: {0}", + "Select and start debug configuration" ], "vs/workbench/contrib/debug/browser/debugToolBar": [ "More...", "Step Back", "Reverse" ], - "vs/workbench/contrib/debug/browser/debugStatus": [ - "Debug", - "Debug: {0}", - "Select and start debug configuration" - ], "vs/workbench/contrib/debug/browser/disassemblyView": [ "Disassembly not available.", "instructions", @@ -24912,28 +25216,28 @@ "Add Expression", "Remove All Expressions" ], - "vs/workbench/contrib/debug/browser/welcomeView": [ - "Run", - "[Open a file](command:{0}) which can be debugged or run.", - "Run and Debug", - "Show all automatic debug configurations", - "To customize Run and Debug [create a launch.json file](command:{0}).", - "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", - "All debug extensions are disabled. Enable a debug extension or install a new one from the Marketplace." - ], "vs/workbench/contrib/debug/common/debugContentProvider": [ "Unable to resolve the resource without a debug session", "Could not load source '{0}': {1}.", "Could not load source '{0}'." ], - "vs/workbench/contrib/debug/common/disassemblyViewInput": [ - "Disassembly" - ], "vs/workbench/contrib/debug/common/debugLifecycle": [ "There is an active debug session, are you sure you want to stop it?", "There are active debug sessions, are you sure you want to stop them?", "&&Stop Debugging" ], + "vs/workbench/contrib/debug/common/disassemblyViewInput": [ + "Disassembly" + ], + "vs/workbench/contrib/debug/browser/welcomeView": [ + "[Open a file](command:{0}) which can be debugged or run.", + "Run and Debug", + "Show all automatic debug configurations", + "To customize Run and Debug [create a launch.json file](command:{0}).", + "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", + "All debug extensions are disabled. Enable a debug extension or install a new one from the Marketplace.", + "Run" + ], "vs/workbench/contrib/debug/browser/breakpointWidget": [ "Message to log when breakpoint is hit. Expressions within {} are interpolated. '{0}' to accept, '{1}' to cancel.", "Break when hit count condition is met. '{0}' to accept, '{1}' to cancel.", @@ -24943,104 +25247,72 @@ "Log Message", "Breakpoint Type" ], - "vs/workbench/contrib/scm/browser/activity": [ - "Source Control", - "{0} pending changes" + "vs/platform/actions/browser/menuEntryActionViewItem": [ + "{0} ({1})", + "{0} ({1})", + "{0}\n[{1}] {2}" ], - "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ - "Source Control" + "vs/workbench/contrib/debug/browser/debugActionViewItems": [ + "Debug Launch Configurations", + "No Configurations", + "Add Config ({0})...", + "Add Configuration...", + "Debug Session" ], - "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ - "{0} - {1} of {2} changes", - "{0} - {1} of {2} change", - "{0} of {1} changes", - "{0} of {1} change", - "Close", - "Show Previous Change", - "Show Next Change", - "Next &&Change", - "Previous &&Change", - "Go to Previous Change", - "Go to Next Change", - "Editor gutter background color for lines that are modified.", - "Editor gutter background color for lines that are added.", - "Editor gutter background color for lines that are deleted.", - "Minimap gutter background color for lines that are modified.", - "Minimap gutter background color for lines that are added.", - "Minimap gutter background color for lines that are deleted.", - "Overview ruler marker color for modified content.", - "Overview ruler marker color for added content.", - "Overview ruler marker color for deleted content." + "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ + "Merge Editor (Dev)", + "Copy Merge Editor State as JSON", + "Merge Editor", + "No active merge editor", + "Merge Editor", + "Successfully copied merge editor state", + "Save Merge Editor State to Folder", + "Merge Editor", + "No active merge editor", + "Select folder to save to", + "Merge Editor", + "Successfully saved merge editor state to folder", + "Load Merge Editor State from Folder", + "Select folder to save to" ], - "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ - "Source Control Repositories" + "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ + "Open Merge Editor", + "Mixed Layout", + "Column Layout", + "Show Non-Conflicting Changes", + "Show Base", + "Show Base Top", + "Show Base Center", + "Merge Editor", + "Open File", + "Go to Next Unhandled Conflict", + "Go to Previous Unhandled Conflict", + "Toggle Current Conflict from Left", + "Toggle Current Conflict from Right", + "Compare Input 1 With Base", + "Compare With Base", + "Compare Input 2 With Base", + "Compare With Base", + "Open Base File", + "Accept All Changes from Left", + "Accept All Changes from Right", + "Reset Result", + "Reset", + "Reset Choice for 'Close with Conflicts'", + "Complete Merge", + "Do you want to complete the merge of {0}?", + "The file contains unhandled conflicts.", + "&&Complete with Conflicts" ], - "vs/workbench/contrib/workspace/common/workspace": [ - "Whether the workspace trust feature is enabled.", - "Whether the current workspace has been trusted by the user." - ], - "vs/workbench/contrib/scm/browser/scmViewPane": [ - "Source Control Management", - "Source Control Input", - "View & Sort", - "Repositories", - "View as List", - "View as Tree", - "Sort by Discovery Time", - "Sort by Name", - "Sort by Path", - "Sort Changes by Name", - "Sort Changes by Path", - "Sort Changes by Status", - "Collapse All Repositories", - "Expand All Repositories", - "Close", - "SCM Provider separator border." - ], - "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ - "Source Control Sync", - "Incoming Changes", - "Outgoing Changes", - "Refresh", - "View as List", - "View as Tree" - ], - "vs/workbench/contrib/debug/browser/exceptionWidget": [ - "Exception widget border color.", - "Exception widget background color.", - "Exception has occurred: {0}", - "Exception has occurred.", - "Close" - ], - "vs/workbench/contrib/debug/browser/debugHover": [ - "Hold {0} key to switch to editor language hover", - "Debug Hover", - "{0}, value {1}, variables, debug" + "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ + "Merging: {0}" ], - "vs/workbench/contrib/debug/common/debugModel": [ - "Invalid variable attributes", - "Please start a debug session to evaluate expressions", - "not available", - "Paused on {0}", - "Paused", - "Running", - "Unverified breakpoint. File is modified, please restart debug session." + "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ + "Text Merge Editor" ], "vs/platform/history/browser/contextScopedHistoryWidget": [ "Whether suggestion are visible" ], - "vs/workbench/contrib/debug/browser/debugActionViewItems": [ - "Debug Launch Configurations", - "No Configurations", - "Add Config ({0})...", - "Add Configuration...", - "Debug Session" - ], - "vs/platform/actions/browser/menuEntryActionViewItem": [ - "{0} ({1})", - "{0} ({1})", - "{0}\n[{1}] {2}" - ], "vs/workbench/contrib/debug/browser/linkDetector": [ "follow link using forwarded port", "follow link", @@ -25049,9 +25321,6 @@ "Cmd + click to {0}", "Ctrl + click to {0}" ], - "vs/workbench/contrib/debug/common/replModel": [ - "Console was cleared" - ], "vs/workbench/contrib/debug/browser/replViewer": [ "Debug Console", "Variable {0}, value {1}", @@ -25059,6 +25328,9 @@ "Debug console variable {0}, value {1}", "Debug console group {0}" ], + "vs/workbench/contrib/debug/common/replModel": [ + "Console was cleared" + ], "vs/workbench/contrib/markers/browser/markersView": [ "Showing {0} of {1}", "Showing {0} problems", @@ -25075,7 +25347,6 @@ "Controls the order in which problems are navigated.", "Navigate problems ordered by severity", "Navigate problems ordered by position", - "Problems", "No problems have been detected in the workspace.", "No problems have been detected in the current file.", "No results found with provided filter criteria.", @@ -25112,67 +25383,31 @@ "Problem: {0} at line {1} and character {2}.{3} generated by {4}", "Problem: {0} at line {1} and character {2}.{3}", "{0} at line {1} and character {2} in {3}", - "Show Errors and Warnings" + "Show Errors and Warnings", + "Problems" ], "vs/workbench/browser/parts/views/viewFilter": [ "More Filters..." ], - "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ - "Open Merge Editor", - "Mixed Layout", - "Column Layout", - "Show Non-Conflicting Changes", - "Show Base", - "Show Base Top", - "Show Base Center", - "Merge Editor", - "Open File", - "Go to Next Unhandled Conflict", - "Go to Previous Unhandled Conflict", - "Toggle Current Conflict from Left", - "Toggle Current Conflict from Right", - "Compare Input 1 With Base", - "Compare With Base", - "Compare Input 2 With Base", - "Compare With Base", - "Open Base File", - "Accept All Changes from Left", - "Accept All Changes from Right", - "Reset Result", - "Reset", - "Reset Choice for 'Close with Conflicts'", - "Complete Merge", - "Do you want to complete the merge of {0}?", - "The file contains unhandled conflicts.", - "&&Complete with Conflicts" - ], "vs/workbench/contrib/markers/browser/markersFileDecorations": [ "Problems", "1 problem in this file", "{0} problems in this file", "Show Errors & Warnings on files and folder." ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ - "Merging: {0}" - ], - "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ - "Merge Editor (Dev)", - "Copy Merge Editor State as JSON", - "Merge Editor", - "No active merge editor", - "Merge Editor", - "Successfully copied merge editor state", - "Save Merge Editor State to Folder", - "Merge Editor", - "No active merge editor", - "Select folder to save to", - "Merge Editor", - "Successfully saved merge editor state to folder", - "Load Merge Editor State from Folder", - "Select folder to save to" + "vs/workbench/contrib/url/browser/trustedDomains": [ + "Manage Trusted Domains", + "Trust {0}", + "Trust {0} on all ports", + "Trust {0} and all its subdomains", + "Trust all domains (disables link protection)", + "Manage Trusted Domains" ], - "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ - "Text Merge Editor" + "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ + "Do you want {0} to open the external website?", + "&&Open", + "&&Copy", + "Configure &&Trusted Domains" ], "vs/workbench/contrib/comments/common/commentContextKeys": [ "Whether the position at the active cursor has a commenting range", @@ -25185,14 +25420,6 @@ "The comment controller id associated with a comment thread", "Set when the comment is focused" ], - "vs/workbench/contrib/url/browser/trustedDomains": [ - "Manage Trusted Domains", - "Trust {0}", - "Trust {0} on all ports", - "Trust {0} and all its subdomains", - "Trust all domains (disables link protection)", - "Manage Trusted Domains" - ], "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ "Go to Next Commenting Range", "Go to Previous Commenting Range", @@ -25203,12 +25430,6 @@ "Expand All Comments", "Expand Unresolved Comments" ], - "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ - "Do you want {0} to open the external website?", - "&&Open", - "&&Copy", - "Configure &&Trusted Domains" - ], "vs/workbench/contrib/webviewPanel/browser/webviewCommands": [ "Show find", "Stop find", @@ -25219,11 +25440,8 @@ "vs/workbench/contrib/webviewPanel/browser/webviewEditor": [ "The viewType of the currently active webview panel." ], - "vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService": [ - "Open in new browser window", - "Open in default browser", - "Configure default opener...", - "How would you like to open: {0}" + "vs/workbench/contrib/customEditor/common/customEditor": [ + "The viewType of the currently active custom editor." ], "vs/workbench/contrib/externalUriOpener/common/configuration": [ "Configure the opener to use for external URIs (http, https).", @@ -25231,148 +25449,56 @@ "Map URI pattern to an opener id.\nExample patterns: \n{0}", "Open using VS Code's standard opener." ], - "vs/workbench/contrib/customEditor/common/customEditor": [ - "The viewType of the currently active custom editor." + "vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService": [ + "Open in new browser window", + "Open in default browser", + "Configure default opener...", + "How would you like to open: {0}" ], "vs/workbench/contrib/extensions/common/extensionsInput": [ "Extension: {0}" ], - "vs/workbench/contrib/extensions/common/extensionsUtils": [ - "Disable other keymaps ({0}) to avoid conflicts between keybindings?", - "Yes", - "No" + "vs/workbench/contrib/extensions/browser/extensionsViews": [ + "Extensions", + "Unable to search the Marketplace when offline, please check your network connection.", + "Error while fetching extensions. {0}", + "No extensions found.", + "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", + "Open User Settings", + "There are no extensions to install.", + "Verified Publisher {0}", + "Publisher {0}", + "Deprecated", + "Rated {0} out of 5 stars by {1} users" ], - "vs/workbench/contrib/extensions/browser/extensionEditor": [ - "Extension Version", - "Pre-Release", - "Extension name", - "Preview", - "Preview", - "Built-in", - "Publisher", - "Install count", - "Rating", - "Details", - "Extension details, rendered from the extension's 'README.md' file", - "Feature Contributions", - "Lists contributions to VS Code by this extension", - "Changelog", - "Extension update history, rendered from the extension's 'CHANGELOG.md' file", - "Dependencies", - "Lists extensions this extension depends on", - "Extension Pack", - "Lists extensions those will be installed together with this extension", - "Runtime Status", - "Extension runtime status", - "No README available.", - "Readme", - "Extension Pack ({0})", - "No README available.", - "Readme", - "Categories", - "Marketplace", - "Repository", - "License", - "Extension Resources", - "More Info", - "Published", - "Last released", - "Last updated", - "Identifier", - "No Changelog available.", - "Changelog", - "No Contributions", - "No Contributions", - "No Dependencies", - "Activation Event:", - "Startup", - "Activation Time:", - "Activated By:", - "Not yet activated.", - "Uncaught Errors ({0})", - "Messages ({0})", - "No status available.", - "Settings ({0})", - "ID", - "Description", - "Default", - "Debuggers ({0})", - "Name", - "Type", - "View Containers ({0})", - "ID", - "Title", - "Where", - "Views ({0})", - "ID", - "Name", - "Where", - "Localizations ({0})", - "Language ID", - "Language Name", - "Language Name (Localized)", - "Custom Editors ({0})", - "View Type", - "Priority", - "Filename Pattern", - "Code Actions ({0})", - "Title", - "Kind", - "Description", - "Languages", - "Authentication ({0})", - "Label", - "ID", - "Color Themes ({0})", - "File Icon Themes ({0})", - "Product Icon Themes ({0})", - "Colors ({0})", - "ID", - "Description", - "Dark Default", - "Light Default", - "High Contrast Default", - "JSON Validation ({0})", - "File Match", - "Schema", - "Commands ({0})", - "ID", - "Title", - "Keyboard Shortcuts", - "Menu Contexts", - "Languages ({0})", - "ID", - "Name", - "File Extensions", - "Grammar", - "Snippets", - "Activation Events ({0})", - "Notebooks ({0})", - "ID", - "Name", - "Notebook Renderers ({0})", - "Name", - "Mimetypes", - "Find", - "Find Next", - "Find Previous" - ], - "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ - "Extensions", - "List of extensions which should be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", - "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.", - "List of extensions recommended by VS Code that should not be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", - "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." - ], - "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ - "Activating Extensions..." + "vs/workbench/contrib/extensions/browser/extensionsIcons": [ + "View icon of the extensions view.", + "Icon for the 'Manage' action in the extensions view.", + "Icon for the 'Clear Search Result' action in the extensions view.", + "Icon for the 'Refresh' action in the extensions view.", + "Icon for the 'Filter' action in the extensions view.", + "Icon for the 'Install Local Extension in Remote' action in the extensions view.", + "Icon for the 'Install Workspace Recommended Extensions' action in the extensions view.", + "Icon for the 'Configure Recommended Extensions' action in the extensions view.", + "Icon to indicate that an extension is synced.", + "Icon to indicate that an extension is ignored when syncing.", + "Icon to indicate that an extension is remote in the extensions view and editor.", + "Icon shown along with the install count in the extensions view and editor.", + "Icon shown along with the rating in the extensions view and editor.", + "Icon used for the verified extension publisher in the extensions view and editor.", + "Icon shown for extensions having pre-release versions in extensions view and editor.", + "Icon used for sponsoring extensions in the extensions view and editor.", + "Full star icon used for the rating in the extensions editor.", + "Half star icon used for the rating in the extensions editor.", + "Empty star icon used for the rating in the extensions editor.", + "Icon shown with a error message in the extensions editor.", + "Icon shown with a warning message in the extensions editor.", + "Icon shown with an info message in the extensions editor.", + "Icon shown with a workspace trust message in the extension editor.", + "Icon shown with a activation time message in the extension editor." ], - "vs/workbench/contrib/extensions/browser/extensionsDependencyChecker": [ - "Extensions", - "Install Missing Dependencies", - "Finished installing missing dependencies. Please reload the window now.", - "Reload Window", - "There are no missing dependencies to install." + "vs/platform/dnd/browser/dnd": [ + "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again." ], "vs/workbench/contrib/extensions/browser/extensionsActions": [ "{0} for the Web", @@ -25534,132 +25660,12 @@ "Button foreground color for extension actions that stand out (e.g. install button).", "Button background hover color for extension actions that stand out (e.g. install button)." ], - "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ - "Type an extension name to install or search.", - "Press Enter to search for extension '{0}'.", - "Press Enter to install extension '{0}'.", - "Press Enter to manage your extensions." - ], - "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService": [ - "Don't Show Again", - "Do you want to ignore all extension recommendations?", - "Yes, Ignore All", - "No", - "this repository", - "'{0}' extension from {1}", - "extensions from {0}, {1} and others", - "extensions from {0} and {1}", - "extensions from {0}", - "Do you want to install the recommended {0} for {1}?", - "You have {0} installed on your system. Do you want to install the recommended {1} for it?", - "Install", - "Install (Do not sync)", - "Show Recommendations" - ], - "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ - "Restarting extension host due to workspace trust change." - ], - "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ - "Manifest is not found", - "Please reload Visual Studio Code to complete the uninstallation of this extension.", - "Please reload Visual Studio Code to enable the updated extension.", - "Please reload Visual Studio Code to enable this extension locally.", - "Please reload Visual Studio Code to enable this extension in {0}.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to disable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "This extension is reported to be problematic.", - "Can't install '{0}' extension because it is not compatible.", - "Uninstalling extension....", - "Unable to install extension '{0}' because the requested version '{1}' is not found.", - "Installing extension....", - "Installing '{0}' extension....", - "Disable All", - "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", - "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", - "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?" - ], - "vs/workbench/contrib/extensions/browser/extensionsIcons": [ - "View icon of the extensions view.", - "Icon for the 'Manage' action in the extensions view.", - "Icon for the 'Clear Search Result' action in the extensions view.", - "Icon for the 'Refresh' action in the extensions view.", - "Icon for the 'Filter' action in the extensions view.", - "Icon for the 'Install Local Extension in Remote' action in the extensions view.", - "Icon for the 'Install Workspace Recommended Extensions' action in the extensions view.", - "Icon for the 'Configure Recommended Extensions' action in the extensions view.", - "Icon to indicate that an extension is synced.", - "Icon to indicate that an extension is ignored when syncing.", - "Icon to indicate that an extension is remote in the extensions view and editor.", - "Icon shown along with the install count in the extensions view and editor.", - "Icon shown along with the rating in the extensions view and editor.", - "Icon used for the verified extension publisher in the extensions view and editor.", - "Icon shown for extensions having pre-release versions in extensions view and editor.", - "Icon used for sponsoring extensions in the extensions view and editor.", - "Full star icon used for the rating in the extensions editor.", - "Half star icon used for the rating in the extensions editor.", - "Empty star icon used for the rating in the extensions editor.", - "Icon shown with a error message in the extensions editor.", - "Icon shown with a warning message in the extensions editor.", - "Icon shown with an info message in the extensions editor.", - "Icon shown with a workspace trust message in the extension editor.", - "Icon shown with a activation time message in the extension editor." - ], - "vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor": [ - "Activated by {0} on start-up", - "Activated by {1} because a file matching {0} exists in your workspace", - "Activated by {1} because file {0} exists in your workspace", - "Activated by {1} because searching for {0} took too long", - "Activated by {0} after start-up finished", - "Activated by {1} because you opened a {0} file", - "Activated by {1} on {0}", - "Extension is activating...", - "Extension has caused the extension host to freeze.", - "{0} uncaught errors", - "Runtime Extensions", - "Copy id ({0})", - "Disable (Workspace)", - "Disable", - "Show Running Extensions" - ], - "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ - "Example" - ], - "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ - "You have deprecated extensions installed. We recommend to review them and migrate to alternatives.", - "Show Deprecated Extensions", - "Don't Show Again" - ], - "vs/workbench/contrib/extensions/browser/extensionsViews": [ - "Extensions", - "Unable to search the Marketplace when offline, please check your network connection.", - "Error while fetching extensions. {0}", - "No extensions found.", - "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", - "Open User Settings", - "There are no extensions to install.", - "Verified Publisher {0}", - "Publisher {0}", - "Deprecated", - "Rated {0} out of 5 stars by {1} users" - ], - "vs/platform/dnd/browser/dnd": [ - "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again." - ], "vs/workbench/contrib/terminal/browser/terminal.contribution": [ "Type the name of a terminal to open.", "Show All Opened Terminals", + "&&Terminal", "Terminal", - "Terminal", - "&&Terminal" - ], - "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ - "Show Terminal Texture Atlas", - "Write Data to Terminal", - "Enter data to write directly to the terminal, bypassing the pty", - "Restart Pty Host" + "Terminal" ], "vs/workbench/contrib/terminal/browser/terminalView": [ "Use 'monospace'", @@ -25672,34 +25678,241 @@ "Accessible Buffer Go to Next Command", "Accessible Buffer Go to Previous Command" ], + "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ + "Show Terminal Texture Atlas", + "Write Data to Terminal", + "Enter data to write directly to the terminal, bypassing the pty", + "Restart Pty Host" + ], "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ "Show Environment Contributions", "Terminal Environment Changes", "Extension: {0}", "workspace" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ - "Open Detected Link...", - "Open Last URL Link", - "Open Last Local File Link" + "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ + "Focus Find", + "Hide Find", + "Toggle Find Using Regex", + "Toggle Find Using Whole Word", + "Toggle Find Using Case Sensitive", + "Find Next", + "Find Previous", + "Search Workspace" + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ + "Open Detected Link...", + "Open Last URL Link", + "Open Last Local File Link" + ], + "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ + "Show Terminal Quick Fixes" + ], + "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ + "Extensions", + "List of extensions which should be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", + "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.", + "List of extensions recommended by VS Code that should not be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", + "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + ], + "vs/workbench/contrib/extensions/browser/extensionEditor": [ + "Extension Version", + "Pre-Release", + "Extension name", + "Preview", + "Preview", + "Built-in", + "Publisher", + "Install count", + "Rating", + "Details", + "Extension details, rendered from the extension's 'README.md' file", + "Feature Contributions", + "Lists contributions to VS Code by this extension", + "Changelog", + "Extension update history, rendered from the extension's 'CHANGELOG.md' file", + "Dependencies", + "Lists extensions this extension depends on", + "Extension Pack", + "Lists extensions those will be installed together with this extension", + "Runtime Status", + "Extension runtime status", + "No README available.", + "Readme", + "Extension Pack ({0})", + "No README available.", + "Readme", + "Categories", + "Marketplace", + "Repository", + "License", + "Extension Resources", + "More Info", + "Published", + "Last released", + "Last updated", + "Identifier", + "No Changelog available.", + "Changelog", + "No Contributions", + "No Contributions", + "No Dependencies", + "Activation Event:", + "Startup", + "Activation Time:", + "Activated By:", + "Not yet activated.", + "Uncaught Errors ({0})", + "Messages ({0})", + "No status available.", + "Settings ({0})", + "ID", + "Description", + "Default", + "Debuggers ({0})", + "Name", + "Type", + "View Containers ({0})", + "ID", + "Title", + "Where", + "Views ({0})", + "ID", + "Name", + "Where", + "Localizations ({0})", + "Language ID", + "Language Name", + "Language Name (Localized)", + "Custom Editors ({0})", + "View Type", + "Priority", + "Filename Pattern", + "Code Actions ({0})", + "Title", + "Kind", + "Description", + "Languages", + "Authentication ({0})", + "Label", + "ID", + "Color Themes ({0})", + "File Icon Themes ({0})", + "Product Icon Themes ({0})", + "Colors ({0})", + "ID", + "Description", + "Dark Default", + "Light Default", + "High Contrast Default", + "JSON Validation ({0})", + "File Match", + "Schema", + "Commands ({0})", + "ID", + "Title", + "Keyboard Shortcuts", + "Menu Contexts", + "Languages ({0})", + "ID", + "Name", + "File Extensions", + "Grammar", + "Snippets", + "Activation Events ({0})", + "Notebooks ({0})", + "ID", + "Name", + "Notebook Renderers ({0})", + "Name", + "Mimetypes", + "Find", + "Find Next", + "Find Previous" + ], + "vs/workbench/contrib/extensions/common/extensionsUtils": [ + "Disable other keymaps ({0}) to avoid conflicts between keybindings?", + "Yes", + "No" + ], + "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ + "Activating Extensions..." + ], + "vs/workbench/contrib/extensions/browser/extensionsDependencyChecker": [ + "Extensions", + "Install Missing Dependencies", + "Finished installing missing dependencies. Please reload the window now.", + "Reload Window", + "There are no missing dependencies to install." + ], + "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ + "Type an extension name to install or search.", + "Press Enter to search for extension '{0}'.", + "Press Enter to install extension '{0}'.", + "Press Enter to manage your extensions." + ], + "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService": [ + "Don't Show Again", + "Do you want to ignore all extension recommendations?", + "Yes, Ignore All", + "No", + "this repository", + "'{0}' extension from {1}", + "extensions from {0}, {1} and others", + "extensions from {0} and {1}", + "extensions from {0}", + "Do you want to install the recommended {0} for {1}?", + "You have {0} installed on your system. Do you want to install the recommended {1} for it?", + "Install", + "Install (Do not sync)", + "Show Recommendations" + ], + "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ + "Manifest is not found", + "Please reload Visual Studio Code to complete the uninstallation of this extension.", + "Please reload Visual Studio Code to enable the updated extension.", + "Please reload Visual Studio Code to enable this extension locally.", + "Please reload Visual Studio Code to enable this extension in {0}.", + "Please reload Visual Studio Code to enable this extension.", + "Please reload Visual Studio Code to enable this extension.", + "Please reload Visual Studio Code to disable this extension.", + "Please reload Visual Studio Code to enable this extension.", + "Please reload Visual Studio Code to enable this extension.", + "This extension is reported to be problematic.", + "Can't install '{0}' extension because it is not compatible.", + "Uninstalling extension....", + "Unable to install extension '{0}' because the requested version '{1}' is not found.", + "Installing extension....", + "Installing '{0}' extension....", + "Disable All", + "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", + "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", + "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?" ], - "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ - "Focus Find", - "Hide Find", - "Toggle Find Using Regex", - "Toggle Find Using Whole Word", - "Toggle Find Using Case Sensitive", - "Find Next", - "Find Previous", - "Search Workspace" + "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ + "Example" ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ - "Show Terminal Quick Fixes" + "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ + "You have deprecated extensions installed. We recommend to review them and migrate to alternatives.", + "Show Deprecated Extensions", + "Don't Show Again" ], - "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ - "Manage Automatic Tasks", - "Allow Automatic Tasks", - "Disallow Automatic Tasks" + "vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor": [ + "Activated by {0} on start-up", + "Activated by {1} because a file matching {0} exists in your workspace", + "Activated by {1} because file {0} exists in your workspace", + "Activated by {1} because searching for {0} took too long", + "Activated by {0} after start-up finished", + "Activated by {1} because you opened a {0} file", + "Activated by {1} on {0}", + "Extension is activating...", + "Extension has caused the extension host to freeze.", + "{0} uncaught errors", + "Runtime Extensions", + "Copy id ({0})", + "Disable (Workspace)", + "Disable", + "Show Running Extensions" ], "vs/workbench/contrib/tasks/common/problemMatcher": [ "The problem pattern is missing a regular expression.", @@ -25771,6 +25984,11 @@ "ESLint stylish problems", "Go problems" ], + "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ + "Manage Automatic Tasks", + "Allow Automatic Tasks", + "Disallow Automatic Tasks" + ], "vs/workbench/contrib/tasks/common/jsonSchema_v2": [ "Specifies whether the command is a shell command or an external program. Defaults to false if omitted.", "The property isShellCommand is deprecated. Use the type property of the task and the shell property in the options instead. See also the 1.14 release notes.", @@ -25855,16 +26073,8 @@ "Mac specific command configuration", "Linux specific command configuration" ], - "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ - "No matching tasks", - "Select the task to run" - ], - "vs/workbench/contrib/tasks/common/taskDefinitionRegistry": [ - "The actual task type. Please note that types starting with a '$' are reserved for internal usage.", - "Additional properties of the task type", - "Condition which must be true to enable this type of task. Consider using `shellExecutionSupported`, `processExecutionSupported`, and `customExecutionSupported` as appropriate for this task definition. See the [API documentation](https://code.visualstudio.com/api/extension-guides/task-provider#when-clause) for more information.", - "The task type configuration is missing the required 'taskType' property", - "Contributes task kinds" + "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ + "Restarting extension host due to workspace trust change." ], "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ "Task version 0.1.0 is deprecated. Please use 2.0.0", @@ -25876,25 +26086,27 @@ "Linux specific command configuration", "Specifies whether the command is a shell command or an external program. Defaults to false if omitted." ], + "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ + "No matching tasks", + "Select the task to run" + ], + "vs/workbench/contrib/tasks/common/taskDefinitionRegistry": [ + "The actual task type. Please note that types starting with a '$' are reserved for internal usage.", + "Additional properties of the task type", + "Condition which must be true to enable this type of task. Consider using `shellExecutionSupported`, `processExecutionSupported`, and `customExecutionSupported` as appropriate for this task definition. See the [API documentation](https://code.visualstudio.com/api/extension-guides/task-provider#when-clause) for more information.", + "The task type configuration is missing the required 'taskType' property", + "Contributes task kinds" + ], "vs/workbench/contrib/remote/browser/tunnelFactory": [ "Private", "Public" ], "vs/workbench/contrib/remote/browser/remote": [ - "The ID of a Get Started walkthrough to open.", - "Contributes help information for Remote", - "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension", - "The url, or a command that returns the url, to your project's documentation page", - "The url, or a command that returns the url, to your project's feedback reporter", - "Use {0} instead", - "The url, or a command that returns the url, to your project's issue reporter", - "The url, or a command that returns the url, to your project's issues list", "Get Started", "Read Documentation", "Review Issues", "Report Issue", "Select url to open", - "Help and feedback", "Remote Help", "Remote Explorer", "Remote Explorer", @@ -25905,11 +26117,8 @@ "Connection Lost", "Disconnected. Attempting to reconnect...", "Cannot reconnect. Please reload the window.", - "&&Reload Window" - ], - "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ - "Emmet: Expand Abbreviation", - "Emmet: E&&xpand Abbreviation" + "&&Reload Window", + "Help and feedback" ], "vs/workbench/contrib/remote/browser/remoteIndicator": [ "Remote", @@ -25936,81 +26145,9 @@ "Select an option to open a Remote Window", "Installing extension... " ], - "vs/workbench/contrib/format/browser/formatActionsNone": [ - "Format Document", - "This file cannot be formatted because it is too large", - "There is no formatter for '{0}' files installed.", - "&&Install Formatter..." - ], - "vs/workbench/contrib/format/browser/formatModified": [ - "Format Modified Lines" - ], - "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ - "(global)", - "({0})", - "({0}) {1}", - "Type snippet file name", - "Invalid file name", - "'{0}' is not a valid file name", - "'{0}' already exists", - "Configure User Snippets", - "User Snippets", - "User &&Snippets", - "global", - "New Global Snippets file...", - "{0} workspace", - "New Snippets file for '{0}'...", - "Existing Snippets", - "New Snippets", - "New Snippets", - "Select Snippets File or Create Snippets" - ], - "vs/workbench/contrib/format/browser/formatActionsMultiple": [ - "None", - "None", - "Extension '{0}' is configured as formatter but it cannot format '{1}'-files", - "There are multiple formatters for '{0}' files. One of them should be configured as default formatter.", - "Extension '{0}' is configured as formatter but not available. Select a different default formatter to continue.", - "Configure Default Formatter", - "&&Configure...", - "Configure...", - "Select a default formatter for '{0}' files", - "Configure...", - "Formatter Conflicts", - "Formatting", - "Defines a default formatter which takes precedence over all other formatter settings. Must be the identifier of an extension contributing a formatter.", - "(default)", - "Configure Default Formatter...", - "Select a formatter", - "Select a default formatter for '{0}' files", - "Format Document With...", - "Format Selection With..." - ], - "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ - "Fill File with Snippet", - "Select a snippet" - ], - "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ - "Surround With Snippet..." - ], - "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ - "Insert Snippet" - ], - "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ - "Surround With: {0}", - "Start with Snippet", - "Start with: {0}" - ], - "vs/workbench/contrib/snippets/browser/snippetsService": [ - "Expected string in `contributes.{0}.path`. Provided value: {1}", - "When omitting the language, the value of `contributes.{0}.path` must be a `.code-snippets`-file. Provided value: {1}", - "Unknown language in `contributes.{0}.language`. Provided value: {1}", - "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", - "Contributes snippets.", - "Language identifier for which this snippet is contributed to.", - "Path of the snippets file. The path is relative to the extension folder and typically starts with './snippets/'.", - "One or more snippets from the extension '{0}' very likely confuse snippet-variables and snippet-placeholders (see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax for more details)", - "The snippet file \"{0}\" could not be read." + "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ + "Emmet: Expand Abbreviation", + "Emmet: E&&xpand Abbreviation" ], "vs/workbench/contrib/codeEditor/browser/accessibility/accessibility": [ "Toggle Screen Reader Accessibility Mode" @@ -26036,11 +26173,6 @@ "Developer: Inspect Editor Tokens and Scopes", "Loading..." ], - "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ - "Go to Line/Column...", - "Type the line number and optional column to go to (e.g. 42:5 for line 42 and column 5).", - "Go to Line/Column" - ], "vs/workbench/contrib/codeEditor/browser/saveParticipants": [ "Running '{0}' Formatter ([configure]({1})).", "Quick Fixes", @@ -26055,9 +26187,10 @@ "Toggle Minimap", "&&Minimap" ], - "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ - "Toggle Render Whitespace", - "&&Render Whitespace" + "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ + "Go to Line/Column...", + "Type the line number and optional column to go to (e.g. 42:5 for line 42 and column 5).", + "Go to Line/Column" ], "vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier": [ "Toggle Multi-Cursor Modifier", @@ -26069,6 +26202,10 @@ "Toggle Control Characters", "Render &&Control Characters" ], + "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ + "Toggle Render Whitespace", + "&&Render Whitespace" + ], "vs/workbench/contrib/codeEditor/browser/toggleWordWrap": [ "Whether the editor is currently using word wrapping.", "View: Toggle Word Wrap", @@ -26076,13 +26213,89 @@ "Enable wrapping for this file", "&&Word Wrap" ], - "vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint": [ - "Press {0} to ask {1} to do something. ", - "Start typing to dismiss.", - "[[Ask {0} to do something]] or start typing to dismiss.", - "[[Select a language]], or [[fill with template]], or [[open a different editor]] to get started.\nStart typing to dismiss or [[don't show]] this again.", - "Execute {0} to select a language, execute {1} to fill with template, or execute {2} to open a different editor and get started. Start typing to dismiss.", - " Toggle {0} in settings to disable this hint." + "vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint": [ + "Press {0} to ask {1} to do something. ", + "Start typing to dismiss.", + "[[Ask {0} to do something]] or start typing to dismiss.", + "[[Select a language]], or [[fill with template]], or [[open a different editor]] to get started.\nStart typing to dismiss or [[don't show]] this again.", + "Execute {0} to select a language, execute {1} to fill with template, or execute {2} to open a different editor and get started. Start typing to dismiss.", + " Toggle {0} in settings to disable this hint." + ], + "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ + "(global)", + "({0})", + "({0}) {1}", + "Type snippet file name", + "Invalid file name", + "'{0}' is not a valid file name", + "'{0}' already exists", + "Configure User Snippets", + "User Snippets", + "User &&Snippets", + "global", + "New Global Snippets file...", + "{0} workspace", + "New Snippets file for '{0}'...", + "Existing Snippets", + "New Snippets", + "New Snippets", + "Select Snippets File or Create Snippets" + ], + "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ + "Fill File with Snippet", + "Select a snippet" + ], + "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ + "Insert Snippet" + ], + "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ + "Surround With Snippet..." + ], + "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ + "Surround With: {0}", + "Start with Snippet", + "Start with: {0}" + ], + "vs/workbench/contrib/snippets/browser/snippetsService": [ + "Expected string in `contributes.{0}.path`. Provided value: {1}", + "When omitting the language, the value of `contributes.{0}.path` must be a `.code-snippets`-file. Provided value: {1}", + "Unknown language in `contributes.{0}.language`. Provided value: {1}", + "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", + "Contributes snippets.", + "Language identifier for which this snippet is contributed to.", + "Path of the snippets file. The path is relative to the extension folder and typically starts with './snippets/'.", + "One or more snippets from the extension '{0}' very likely confuse snippet-variables and snippet-placeholders (see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax for more details)", + "The snippet file \"{0}\" could not be read." + ], + "vs/workbench/contrib/format/browser/formatActionsNone": [ + "Format Document", + "This file cannot be formatted because it is too large", + "There is no formatter for '{0}' files installed.", + "&&Install Formatter..." + ], + "vs/workbench/contrib/format/browser/formatActionsMultiple": [ + "None", + "None", + "Extension '{0}' is configured as formatter but it cannot format '{1}'-files", + "There are multiple formatters for '{0}' files. One of them should be configured as default formatter.", + "Extension '{0}' is configured as formatter but not available. Select a different default formatter to continue.", + "Configure Default Formatter", + "&&Configure...", + "Configure...", + "Select a default formatter for '{0}' files", + "Configure...", + "Formatter Conflicts", + "Formatting", + "Defines a default formatter which takes precedence over all other formatter settings. Must be the identifier of an extension contributing a formatter.", + "(default)", + "Configure Default Formatter...", + "Select a formatter", + "Select a default formatter for '{0}' files", + "Format Document With...", + "Format Selection With..." + ], + "vs/workbench/contrib/format/browser/formatModified": [ + "Format Modified Lines" ], "vs/workbench/contrib/update/browser/update": [ "This version of {0} does not have release notes online", @@ -26134,19 +26347,6 @@ "Welcome Page", "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled." ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ - "Used to represent walkthrough steps which have not been completed", - "Used to represent walkthrough steps which have been completed" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ - "Built-In", - "Developer", - "Reset Welcome Page Walkthrough Progress" - ], - "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ - "unbound", - "It looks like Git is not installed on your system." - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted": [ "Overview of how to get up to speed with your editor.", "Open Walkthrough...", @@ -26180,6 +26380,19 @@ "opt out", "{0} collects usage data. Read our {1} and learn how to {2}." ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ + "Built-In", + "Developer", + "Reset Welcome Page Walkthrough Progress" + ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ + "Used to represent walkthrough steps which have not been completed", + "Used to represent walkthrough steps which have been completed" + ], + "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ + "unbound", + "It looks like Git is not installed on your system." + ], "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ "Editor Playground", "Interactive Editor Playground" @@ -26197,11 +26410,12 @@ "Group to which this welcome content belongs. Proposed API.", "Condition when the welcome content buttons and command links should be enabled." ], - "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree": [ - "{0} ({1})", - "1 problem in this element", - "{0} problems in this element", - "Contains elements with problems" + "vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek": [ + "Calls from '{0}'", + "Callers of '{0}'", + "Loading...", + "No calls from '{0}'", + "No callers of '{0}'" ], "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek": [ "Supertypes of '{0}'", @@ -26210,12 +26424,11 @@ "No supertypes of '{0}'", "No subtypes of '{0}'" ], - "vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek": [ - "Calls from '{0}'", - "Callers of '{0}'", - "Loading...", - "No calls from '{0}'", - "No callers of '{0}'" + "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree": [ + "{0} ({1})", + "1 problem in this element", + "{0} problems in this element", + "Contains elements with problems" ], "vs/workbench/contrib/outline/browser/outlinePane": [ "The active editor cannot provide outline information.", @@ -26231,42 +26444,6 @@ "Sort By: Name", "Sort By: Category" ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ - "Create a Temporary Profile", - "Rename...", - "Rename {0}", - "Profile with name {0} already exists.", - "Current", - "Rename Profile...", - "Select Profile to Rename", - "Manage...", - "Cleanup Profiles", - "Reset Workspace Profiles Associations" - ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ - "Profiles ({0})", - "Switch Profile...", - "Select Profile", - "Edit Profile...", - "Show Profile Contents", - "Export Profile...", - "Export Profile ({0})...", - "Import Profile...", - "Import from URL", - "Select File...", - "Profile Templates", - "Import from Profile Template...", - "Provide Profile Template URL", - "Error while creating profile: {0}", - "Select Profile Template File", - "Import Profile...", - "Save Current Profile As...", - "Create Profile...", - "Delete Profile...", - "Current", - "Delete Profile...", - "Select Profiles to Delete" - ], "vs/workbench/contrib/userDataSync/browser/userDataSync": [ "Turn Off", "Configure...", @@ -26346,41 +26523,41 @@ "Successfully downloaded Settings Sync activity.", "Clear Data in Cloud..." ], - "vs/workbench/contrib/editSessions/common/editSessions": [ - "Cloud Changes", - "Cloud Changes", - "View icon of the cloud changes view." - ], - "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ - "Cloud Changes" - ], - "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ - "Select an account to restore your working changes from the cloud", - "Select an account to store your working changes in the cloud", - "Signed In", - "Others", - "Sign in with {0}", - "Turn on Cloud Changes...", - "Turn on Cloud Changes... (1)", - "Turn off Cloud Changes...", - "Do you want to disable storing working changes in the cloud?", - "Delete all stored data from the cloud." + "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ + "Profiles ({0})", + "Switch Profile...", + "Select Profile", + "Edit Profile...", + "Show Profile Contents", + "Export Profile...", + "Export Profile ({0})...", + "Import Profile...", + "Import from URL", + "Select File...", + "Profile Templates", + "Import from Profile Template...", + "Provide Profile Template URL", + "Error while creating profile: {0}", + "Select Profile Template File", + "Import Profile...", + "Save Current Profile As...", + "Create Profile...", + "Delete Profile...", + "Current", + "Delete Profile...", + "Select Profiles to Delete" ], - "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ - "You have no stored changes in the cloud to display.\n{0}", - "Store Working Changes", - "Resume Working Changes", - "Store Working Changes", - "Delete Working Changes", - "Are you sure you want to permanently delete your working changes with ref {0}?", - " You cannot undo this action.", - "Delete All Working Changes from Cloud", - "Are you sure you want to permanently delete all stored changes from the cloud?", - " You cannot undo this action.", - "Compare Changes", - "Local Copy", - "Cloud Changes", - "Open File" + "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ + "Create a Temporary Profile", + "Rename...", + "Rename {0}", + "Profile with name {0} already exists.", + "Current", + "Rename Profile...", + "Select Profile to Rename", + "Manage...", + "Cleanup Profiles", + "Reset Workspace Profiles Associations" ], "vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint": [ "Configure which editor to use for a resource.", @@ -26404,13 +26581,12 @@ "Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of \"explicit\".", "Never triggers Code Actions on save. This value will be deprecated in favor of \"never\".", "Controls whether auto fix action should be run on file save.", - "Run CodeActions for the editor on save. CodeActions must be specified and the editor must not be shutting down. Example: `\"source.organizeImports\": \"explicit\" `", + "Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. Example: `\"source.organizeImports\": \"explicit\" `", "Controls whether '{0}' actions should be run on file save." ], "vs/workbench/contrib/timeline/browser/timelinePane": [ "Loading...", "Load more", - "Timeline", "The active editor cannot provide timeline information.", "All timeline sources have been filtered out.", "Local History will track recent changes as you save them unless the file has been excluded or is too large.", @@ -26429,6 +26605,7 @@ "Pin the Current Timeline", "Timeline", "Unpin the Current Timeline", + "Timeline", "Timeline" ], "vs/workbench/contrib/localHistory/browser/localHistoryTimeline": [ @@ -26469,14 +26646,41 @@ "{0} ({1} • {2}) ↔ {3}", "{0} ({1} • {2}) ↔ {3} ({4} • {5})" ], - "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ - "Workspace Trust" + "vs/workbench/contrib/editSessions/common/editSessions": [ + "Cloud Changes", + "View icon of the cloud changes view.", + "Cloud Changes" ], - "vs/workbench/contrib/audioCues/browser/commands": [ - "Help: List Audio Cues", - "Disabled", - "Enable/Disable Audio Cue", - "Select an audio cue to play" + "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ + "Cloud Changes" + ], + "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ + "Select an account to restore your working changes from the cloud", + "Select an account to store your working changes in the cloud", + "Signed In", + "Others", + "Sign in with {0}", + "Turn on Cloud Changes...", + "Turn on Cloud Changes... (1)", + "Turn off Cloud Changes...", + "Do you want to disable storing working changes in the cloud?", + "Delete all stored data from the cloud." + ], + "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ + "You have no stored changes in the cloud to display.\n{0}", + "Store Working Changes", + "Resume Working Changes", + "Store Working Changes", + "Delete Working Changes", + "Are you sure you want to permanently delete your working changes with ref {0}?", + " You cannot undo this action.", + "Delete All Working Changes from Cloud", + "Are you sure you want to permanently delete all stored changes from the cloud?", + " You cannot undo this action.", + "Compare Changes", + "Local Copy", + "Cloud Changes", + "Open File" ], "vs/workbench/contrib/workspace/browser/workspaceTrustEditor": [ "Icon for workspace trust ion the banner.", @@ -26551,21 +26755,38 @@ "This folder is trusted via the bolded entries in the the trusted folders below.", "This window is trusted by nature of the workspace that is opened." ], + "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ + "Workspace Trust" + ], "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ "Accessibility", - "Provide information about how to access the terminal accessibility help menu when the terminal is focused", - "Provide information about how to navigate changes in the diff editor when it is focused", - "Provide information about how to access the chat help menu when the chat input is focused", - "Provide information about how to access the inline editor chat accessibility help menu and alert with hints which describe how to use the feature when the input is focused", - "Provide information about how to access the inline completions hover and accessible view", - "Provide information about how to change a keybinding in the keybindings editor when a row is focused", + "Provide information about how to access the terminal accessibility help menu when the terminal is focused.", + "Provide information about how to navigate changes in the diff editor when it is focused.", + "Provide information about how to access the chat help menu when the chat input is focused.", + "Provide information about how to access the inline editor chat accessibility help menu and alert with hints that describe how to use the feature when the input is focused.", + "Provide information about how to access the inline completions hover and accessible view.", + "Provide information about how to change a keybinding in the keybindings editor when a row is focused.", "Provide information about how to focus the cell container or inner editor when a notebook cell is focused.", "Provide information about how to open the hover in an accessible view.", "Provide information about how to open the notification in an accessible view.", "Provide information about relevant actions in an empty text editor.", "Provide information about actions that can be taken in the comment widget or in a file which contains comments.", + "When in screen reader mode, alerts when a file is saved. Note that this will be ignored when {0} is enabled.", + "Alerts when a file is saved via user gesture.", + "Alerts whenever is a file is saved, including auto save.", + "Never alerts.", + "When in screen reader mode, alerts when a file or notebook cell is formatted. Note that this will be ignored when {0} is enabled.", + "Alerts when a file is formatted via user gesture.", + "Alerts whenever is a file is formatted, including auto save, on cell execution, and more.", + "Never alerts.", "Whether to dim unfocused editors and terminals, which makes it more clear where typed input will go to. This works with the majority of editors with the notable exceptions of those that utilize iframes like notebooks and extension webview editors.", - "The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled." + "The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.", + "Controls whether the accessible view is hidden." + ], + "vs/workbench/contrib/accessibility/browser/accessibleNotificationService": [ + "Cleared", + "Saved", + "Formatted" ], "vs/workbench/contrib/accessibility/browser/accessibilityStatus": [ "Are you using a screen reader to operate VS Code?", @@ -26574,6 +26795,12 @@ "Screen Reader Optimized", "Screen Reader Mode" ], + "vs/workbench/contrib/audioCues/browser/commands": [ + "Help: List Audio Cues", + "Disabled", + "Enable/Disable Audio Cue", + "Select an audio cue to play" + ], "vs/workbench/contrib/share/browser/shareService": [ "The number of available share providers", "Choose how to share {0}" @@ -26584,6 +26811,11 @@ "Notification Center Actions", "Notifications Center" ], + "vs/workbench/browser/parts/notifications/notificationsAlerts": [ + "Error: {0}", + "Warning: {0}", + "Info: {0}" + ], "vs/workbench/browser/parts/notifications/notificationsStatus": [ "Notifications", "Notifications", @@ -26599,23 +26831,18 @@ "{0} New Notifications ({1} in progress)", "Status Message" ], - "vs/workbench/browser/parts/notifications/notificationsCommands": [ - "Notifications", - "Show Notifications", - "Hide Notifications", - "Clear All Notifications", - "Accept Notification Primary Action", - "Toggle Do Not Disturb Mode", - "Focus Notification Toast" - ], "vs/workbench/browser/parts/notifications/notificationsToasts": [ "{0}, notification", "{0}, source: {1}, notification" ], - "vs/workbench/browser/parts/notifications/notificationsAlerts": [ - "Error: {0}", - "Warning: {0}", - "Info: {0}" + "vs/workbench/browser/parts/notifications/notificationsCommands": [ + "Notifications", + "Show Notifications", + "Hide Notifications", + "Clear All Notifications", + "Accept Notification Primary Action", + "Toggle Do Not Disturb Mode", + "Focus Notification Toast" ], "vs/workbench/services/configuration/common/configurationEditing": [ "Error while writing to {0}. {1}", @@ -26661,12 +26888,12 @@ "Workspace Settings", "Folder Settings" ], - "vs/workbench/common/editor/textEditorModel": [ - "Language {0} was automatically detected and set as the language mode." - ], "vs/workbench/services/textfile/common/textFileEditorModelManager": [ "Failed to save '{0}': {1}" ], + "vs/workbench/common/editor/textEditorModel": [ + "Language {0} was automatically detected and set as the language mode." + ], "vs/workbench/browser/parts/titlebar/titlebarPart": [ "Focus Title Bar", "Command Center", @@ -26692,24 +26919,25 @@ "vs/workbench/services/workingCopy/common/workingCopyHistoryTracker": [ "Undo / Redo" ], + "vs/workbench/services/extensions/common/extensionHostManager": [ + "Measure Extension Host Latency" + ], "vs/workbench/services/extensions/common/extensionsUtil": [ "Overwriting extension {0} with {1}.", "Overwriting extension {0} with {1}.", "Loading development extension at {0}" ], - "vs/workbench/services/extensions/common/extensionHostManager": [ - "Measure Extension Host Latency" - ], - "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ - "Report Issue" - ], "vs/workbench/contrib/localization/common/localizationsActions": [ - "Configure Display Language", "Select Display Language", "Installed", "Available", "More Info", - "Clear Display Language Preference" + "Clear Display Language Preference", + "Configure Display Language", + "Changes the locale of VS Code based on installed language packs. Common languages include French, Chinese, Spanish, Japanese, German, Korean, and more." + ], + "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ + "Report Issue" ], "vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions": [ "Performance Issue", @@ -26723,6 +26951,46 @@ "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote": [ "Create New Integrated Terminal (Local)" ], + "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ + "Pty Host Status", + "Pty Host", + "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host.", + "Pty Host is unresponsive" + ], + "vs/workbench/contrib/localHistory/browser/localHistory": [ + "Icon for a local history entry in the timeline view.", + "Icon for restoring contents of a local history entry." + ], + "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ + "Task is running", + "Task succeeded", + "Task succeeded and waiting...", + "Task has errors", + "Task has errors and is waiting...", + "Task has warnings", + "Task has warnings and is waiting...", + "Task has infos", + "Task has infos and is waiting...", + "Beginning of detected errors for this run" + ], + "vs/workbench/contrib/tasks/common/taskConfiguration": [ + "Warning: options.cwd must be of type string. Ignoring value {0}\n", + "Error: command argument must either be a string or a quoted string. Provided value is:\n{0}", + "Warning: shell configuration is only supported when executing tasks in the terminal.", + "Error: Problem Matcher in declare scope must have a name:\n{0}\n", + "Warning: the defined problem matcher is unknown. Supported types are string | ProblemMatcher | Array.\n{0}\n", + "Error: Invalid problemMatcher reference: {0}\n", + "Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n", + "Error: there is no registered task type '{0}'. Did you miss installing an extension that provides a corresponding task provider?", + "Error: the task configuration '{0}' is missing the required property 'type'. The task configuration will be ignored.", + "Error: the task configuration '{0}' is using an unknown type. The task configuration will be ignored.", + "Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n", + "Error: a task must provide a label property. The task will be ignored.\n{0}\n", + "Warning: {0} tasks are unavailable in the current environment.\n", + "Error: the task '{0}' neither specifies a command nor a dependsOn property. The task will be ignored. Its definition is:\n{1}", + "Error: the task '{0}' doesn't define a command. The task will be ignored. Its definition is:\n{1}", + "Task version 2.0.0 doesn't support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}" + ], "vs/workbench/contrib/tasks/common/taskTemplates": [ "Executes .NET Core build command", "Executes the build target", @@ -26748,46 +27016,6 @@ "No {0} tasks found. Go back ↩", "There is no task provider registered for tasks of type \"{0}\"." ], - "vs/workbench/contrib/tasks/common/taskConfiguration": [ - "Warning: options.cwd must be of type string. Ignoring value {0}\n", - "Error: command argument must either be a string or a quoted string. Provided value is:\n{0}", - "Warning: shell configuration is only supported when executing tasks in the terminal.", - "Error: Problem Matcher in declare scope must have a name:\n{0}\n", - "Warning: the defined problem matcher is unknown. Supported types are string | ProblemMatcher | Array.\n{0}\n", - "Error: Invalid problemMatcher reference: {0}\n", - "Error: tasks configuration must have a type property. The configuration will be ignored.\n{0}\n", - "Error: there is no registered task type '{0}'. Did you miss installing an extension that provides a corresponding task provider?", - "Error: the task configuration '{0}' is missing the required property 'type'. The task configuration will be ignored.", - "Error: the task configuration '{0}' is using an unknown type. The task configuration will be ignored.", - "Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n", - "Error: a task must provide a label property. The task will be ignored.\n{0}\n", - "Warning: {0} tasks are unavailable in the current environment.\n", - "Error: the task '{0}' neither specifies a command nor a dependsOn property. The task will be ignored. Its definition is:\n{1}", - "Error: the task '{0}' doesn't define a command. The task will be ignored. Its definition is:\n{1}", - "Task version 2.0.0 doesn't support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}" - ], - "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ - "Task is running", - "Task succeeded", - "Task succeeded and waiting...", - "Task has errors", - "Task has errors and is waiting...", - "Task has warnings", - "Task has warnings and is waiting...", - "Task has infos", - "Task has infos and is waiting...", - "Beginning of detected errors for this run" - ], - "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ - "Pty Host Status", - "Pty Host", - "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host.", - "Pty Host is unresponsive" - ], - "vs/workbench/contrib/localHistory/browser/localHistory": [ - "Icon for a local history entry in the timeline view.", - "Icon for restoring contents of a local history entry." - ], "vs/workbench/contrib/debug/common/abstractDebugAdapter": [ "Timeout after {0} ms for '{1}'" ], @@ -26885,6 +27113,9 @@ "The default terminal profile on macOS.", "The default terminal profile on Windows." ], + "vs/base/browser/ui/findinput/findInput": [ + "input" + ], "vs/base/browser/ui/inputbox/inputBox": [ "Error: {0}", "Warning: {0}", @@ -26892,14 +27123,6 @@ "for history", "Cleared Input" ], - "vs/base/browser/ui/findinput/findInput": [ - "input" - ], - "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ - "Show Code Actions. Preferred Quick Fix Available ({0})", - "Show Code Actions ({0})", - "Show Code Actions" - ], "vs/editor/contrib/codeAction/browser/codeActionCommands": [ "Kind of the code action to run.", "Controls when the returned actions are applied.", @@ -26935,6 +27158,11 @@ "Hide Disabled", "Show Disabled" ], + "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ + "Show Code Actions. Preferred Quick Fix Available ({0})", + "Show Code Actions ({0})", + "Show Code Actions" + ], "vs/base/browser/ui/actionbar/actionViewItems": [ "{0} ({1})" ], @@ -26960,6 +27188,14 @@ "Show drop options...", "Running drop handlers. Click to cancel" ], + "vs/editor/contrib/folding/browser/foldingDecorations": [ + "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations.", + "Color of the folding control in the editor gutter.", + "Icon for expanded ranges in the editor glyph margin.", + "Icon for collapsed ranges in the editor glyph margin.", + "Icon for manually collapsed ranges in the editor glyph margin.", + "Icon for manually expanded ranges in the editor glyph margin." + ], "vs/editor/contrib/find/browser/findWidget": [ "Icon for 'Find in Selection' in the editor find widget.", "Icon to indicate that the editor find widget is collapsed.", @@ -26989,23 +27225,6 @@ "{0} found for '{1}'", "Ctrl+Enter now inserts line break instead of replacing all. You can modify the keybinding for editor.action.replaceAll to override this behavior." ], - "vs/editor/contrib/folding/browser/foldingDecorations": [ - "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations.", - "Color of the folding control in the editor gutter.", - "Icon for expanded ranges in the editor glyph margin.", - "Icon for collapsed ranges in the editor glyph margin.", - "Icon for manually collapsed ranges in the editor glyph margin.", - "Icon for manually expanded ranges in the editor glyph margin." - ], - "vs/editor/contrib/format/browser/format": [ - "Made 1 formatting edit on line {0}", - "Made {0} formatting edits on line {1}", - "Made 1 formatting edit between lines {0} and {1}", - "Made {0} formatting edits between lines {1} and {2}" - ], - "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ - "Suggestion:" - ], "vs/editor/contrib/inlineCompletions/browser/commands": [ "Show Next Inline Suggestion", "Show Previous Inline Suggestion", @@ -27019,6 +27238,9 @@ "Hide Inline Suggestion", "Always Show Toolbar" ], + "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ + "Suggestion:" + ], "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController": [ "Inspect this in the accessible view ({0})" ], @@ -27027,11 +27249,6 @@ "Loading...", "{0} ({1})" ], - "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ - "Whether there are symbol locations that can be navigated via keyboard-only.", - "Symbol {0} of {1}, {2} for next", - "Symbol {0} of {1}" - ], "vs/editor/contrib/gotoSymbol/browser/referencesModel": [ "in {0} on line {1} at column {2}", "{0} in {1} on line {2} at column {3}", @@ -27042,6 +27259,11 @@ "Found {0} symbols in {1}", "Found {0} symbols in {1} files" ], + "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ + "Whether there are symbol locations that can be navigated via keyboard-only.", + "Symbol {0} of {1}, {2} for next", + "Symbol {0} of {1}" + ], "vs/editor/contrib/message/browser/messageController": [ "Whether the editor is currently showing an inline message" ], @@ -27061,16 +27283,6 @@ "Editor marker navigation widget info heading background.", "Editor marker navigation widget background." ], - "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ - "Double-click to insert", - "cmd + click", - "ctrl + click", - "option + click", - "alt + click", - "Go to Definition ({0}), right click for more", - "Go to Definition ({0})", - "Execute Command" - ], "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ "Loading...", "Rendering paused for long line for performance reasons. This can be configured via `editor.stopRenderingLineAfter`.", @@ -27090,6 +27302,16 @@ "Previous", "Next" ], + "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ + "Double-click to insert", + "cmd + click", + "ctrl + click", + "option + click", + "alt + click", + "Go to Definition ({0}), right click for more", + "Go to Definition ({0})", + "Execute Command" + ], "vs/editor/contrib/wordHighlighter/browser/highlightDecorations": [ "Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations.", "Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations.", @@ -27215,30 +27437,35 @@ "Expand All" ], "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ - "Comments", "{0} comments", "1 comment", "Image: {0}", "Image", "[Ln {0}]", "[Ln {0}-{1}]", - "Last reply from {0}" + "Last reply from {0}", + "Comments" ], "vs/workbench/contrib/testing/common/testResult": [ "Test run at {0}" ], - "vs/workbench/browser/parts/compositeBarActions": [ - "{0} ({1})", - "{0} - {1}", - "Additional Views", - "{0} ({1})", - "Manage Extension", - "Hide '{0}'", - "Keep '{0}'", - "Hide Badge", - "Show Badge", - "Toggle View Pinned", - "Toggle View Badge" + "vs/workbench/browser/parts/editor/editorDropTarget": [ + "Hold __{0}__ to drop into editor" + ], + "vs/workbench/browser/parts/editor/editorGroupView": [ + "Empty editor group actions", + "{0} (empty)", + "{0}: Group {1}", + "Group {0}", + "{0}: Editor Group {1}", + "Editor Group {0}" + ], + "vs/base/browser/ui/tree/treeDefaults": [ + "Collapse All" + ], + "vs/workbench/browser/parts/views/checkbox": [ + "Checked", + "Unchecked" ], "vs/base/browser/ui/splitview/paneview": [ "{0} Section" @@ -27270,7 +27497,6 @@ "Unknown", "Private", "Whether the Ports view has focus.", - "Ports", "Tunnel View", "Set Port Label", "Port label", @@ -27303,7 +27529,8 @@ "HTTPS", "Port Visibility", "Change Port Protocol", - "The color of the icon for a port that has an associated running process." + "The color of the icon for a port that has an associated running process.", + "Ports" ], "vs/workbench/contrib/remote/browser/remoteIcons": [ "Getting started icon in the remote explorer view.", @@ -27320,88 +27547,45 @@ "Icon for the open browser action.", "Icon for the open preview action.", "Icon for the copy local address action.", - "Icon for the label port action.", - "Icon for forwarded ports that don't have a running process.", - "Icon for forwarded ports that do have a running process." - ], - "vs/base/browser/ui/tree/treeDefaults": [ - "Collapse All" - ], - "vs/workbench/browser/parts/views/checkbox": [ - "Checked", - "Unchecked" - ], - "vs/workbench/browser/parts/editor/textCodeEditor": [ - "Text Editor" - ], - "vs/workbench/browser/parts/editor/binaryEditor": [ - "Binary Viewer", - "The file is not displayed in the text editor because it is either binary or uses an unsupported text encoding.", - "Open Anyway" - ], - "vs/workbench/browser/parts/activitybar/activitybarActions": [ - "Loading...", - "{0} is currently unavailable", - "Manage Trusted Extensions", - "Sign Out", - "You are not signed in to any accounts", - "Hide Accounts", - "Manage {0} (Profile)", - "Previous Primary Side Bar View", - "Next Primary Side Bar View", - "Focus Activity Bar" - ], - "vs/workbench/browser/parts/compositeBar": [ - "Active View Switcher" - ], - "vs/workbench/browser/parts/titlebar/menubarControl": [ - "&&File", - "&&Edit", - "&&Selection", - "&&View", - "&&Go", - "&&Terminal", - "&&Help", - "Preferences", - "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style.", - "Open Settings", - "Focus Application Menu", - "Check for &&Updates...", - "Checking for Updates...", - "D&&ownload Update", - "Downloading Update...", - "Install &&Update...", - "Installing Update...", - "Restart to &&Update" + "Icon for the label port action.", + "Icon for forwarded ports that don't have a running process.", + "Icon for forwarded ports that do have a running process." ], - "vs/workbench/browser/parts/compositePart": [ - "{0} actions", - "Views and More Actions...", - "{0} ({1})" + "vs/workbench/browser/parts/editor/textCodeEditor": [ + "Text Editor" ], - "vs/workbench/browser/parts/sidebar/sidebarActions": [ - "Focus into Primary Side Bar" + "vs/workbench/browser/parts/editor/binaryEditor": [ + "Binary Viewer", + "The file is not displayed in the text editor because it is either binary or uses an unsupported text encoding.", + "Open Anyway" ], - "vs/base/browser/ui/toolbar/toolbar": [ - "More Actions..." + "vs/workbench/browser/parts/paneCompositePart": [ + "Drag a view here to display.", + "More Actions...", + "Views" ], - "vs/workbench/browser/parts/editor/editorPanes": [ - "Unable to open '{0}'", - "&&OK" + "vs/workbench/browser/parts/activitybar/activitybarPart": [ + "Menu", + "Hide Menu", + "Activity Bar Position", + "Move Activity Bar to Side", + "&&Side", + "Side", + "Move Activity Bar to Top", + "&&Top", + "Top", + "Hide Activity Bar", + "&&Hidden", + "Hidden", + "Activity Bar Position", + "Activity Bar Position", + "Activity Bar Position", + "Previous Primary Side Bar View", + "Next Primary Side Bar View", + "Focus Activity Bar" ], - "vs/workbench/browser/parts/editor/editorGroupWatermark": [ - "Show All Commands", - "Go to File", - "Open File", - "Open Folder", - "Open File or Folder", - "Open Recent", - "New Untitled Text File", - "Find in Files", - "Toggle Terminal", - "Start Debugging", - "Toggle Full Screen", - "Show Settings" + "vs/workbench/browser/parts/sidebar/sidebarActions": [ + "Focus into Primary Side Bar" ], "vs/base/browser/ui/iconLabel/iconLabelHover": [ "Loading..." @@ -27475,6 +27659,18 @@ "Defines which scope names contain balanced brackets.", "Defines which scope names do not contain balanced brackets." ], + "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ + "User", + "Remote", + "Workspace", + "Folder", + "Settings Switcher", + "User", + "Remote", + "Workspace", + "User", + "Workspace" + ], "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ "Unbound" ], @@ -27519,17 +27715,28 @@ "Manage Workspace Trust", "Unsupported Property" ], - "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ - "User", - "Remote", - "Workspace", - "Folder", - "Settings Switcher", - "User", - "Remote", - "Workspace", - "User", - "Workspace" + "vs/base/browser/ui/toolbar/toolbar": [ + "More Actions..." + ], + "vs/workbench/contrib/preferences/browser/settingsTree": [ + "Extensions", + "The setting has been configured in the current scope.", + "More Actions... ", + "Show matching extensions", + "Edit in settings.json", + "Edit settings for {0}", + "default", + "The setting has been configured in the current scope.", + "Show Extension", + "Reset Setting", + "Validation Error.", + "Validation Error.", + "Modified.", + "Settings", + "Copy Setting ID", + "Copy Setting as JSON", + "Sync This Setting", + "Apply Setting to all Profiles" ], "vs/workbench/contrib/preferences/browser/settingsLayout": [ "Commonly Used", @@ -27581,26 +27788,6 @@ "Security", "Workspace" ], - "vs/workbench/contrib/preferences/browser/settingsTree": [ - "Extensions", - "The setting has been configured in the current scope.", - "More Actions... ", - "Show matching extensions", - "Edit in settings.json", - "Edit settings for {0}", - "default", - "The setting has been configured in the current scope.", - "Show Extension", - "Reset Setting", - "Validation Error.", - "Validation Error.", - "Modified.", - "Settings", - "Copy Setting ID", - "Copy Setting as JSON", - "Sync This Setting", - "Apply Setting to all Profiles" - ], "vs/workbench/contrib/preferences/browser/tocTree": [ "Settings Table of Contents", "{0}, group" @@ -27621,6 +27808,74 @@ "Policy services", "Show settings for policy services" ], + "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ + "The chat view is comprised of an input box and a request/response list. The input box is used to make requests and the list is used to display responses.", + "In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the submit button to run a new request.", + "In the input box, inspect the last response in the accessible view via {0}", + "With the input box focused, inspect the last response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", + "Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.", + "To focus the chat request/response list, which can be navigated with up and down arrows, invoke the Focus Chat command ({0}).", + "To focus the chat request/response list, which can be navigated with up and down arrows, invoke The Focus Chat List command, which is currently not triggerable by a keybinding.", + "To focus the input box for chat requests, invoke the Focus Chat Input command ({0})", + "To focus the input box for chat requests, invoke the Focus Chat Input command, which is currently not triggerable by a keybinding.", + "To focus the next code block within a response, invoke the Chat: Next Code Block command ({0}).", + "To focus the next code block within a response, invoke the Chat: Next Code Block command, which is currently not triggerable by a keybinding.", + "To focus the next file tree within a response, invoke the Chat: Next File Tree command ({0}).", + "To focus the next file tree within a response, invoke the Chat: Next File Tree command, which is currently not triggerable by a keybinding.", + "To clear the request/response list, invoke the Chat Clear command ({0}).", + "To clear the request/response list, invoke the Chat Clear command, which is currently not triggerable by a keybinding.", + "Inline chat occurs within a code editor and takes into account the current selection. It is useful for making changes to the current editor. For example, fixing diagnostics, documenting or refactoring code. Keep in mind that AI generated code may be incorrect.", + "It can be activated via code actions or directly using the command: Inline Chat: Start Inline Chat ({0}).", + "In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.", + "In the input box, inspect the response in the accessible view via {0}", + "With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", + "Context menu actions may run a request prefixed with a /. Type / to discover such ready-made commands.", + "If a fix action is invoked, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.", + "Once in the diff editor, enter review mode with ({0}). Use up and down arrows to navigate lines with the proposed changes.", + "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes.", + "Use tab to reach conditional parts like commands, status, message responses and more.", + "Audio cues can be changed via settings with a prefix of audioCues.chat. By default, if a request takes more than 4 seconds, you will hear an audio cue indicating that progress is still occurring." + ], + "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ + "Cannot join cells of different kinds", + "Join Notebook Cells" + ], + "vs/workbench/contrib/chat/browser/chatInputPart": [ + "Chat Input, Type to ask questions or type / for topics, press enter to send out the request. Use {0} for Chat Accessibility Help.", + "Chat Input, Type code here and press Enter to run. Use the Chat Accessibility Help command for more information.", + "Chat Input" + ], + "vs/workbench/contrib/chat/browser/chatListRenderer": [ + "used {0}", + "using {0}", + "Thinking", + "Used {0} references", + "Used {0} reference", + "{0}, expanded", + "{0}, collapsed", + "Chat", + "Command: {0}", + "Commands: {0}", + "1 file tree", + "{0} file trees", + "{0} {1} {2}", + "{0} {1}", + "{0} 1 code block: {1} {2}", + "{0} 1 code block: {1}", + "{0} {1} code blocks: {2}", + "{0} {1} code blocks", + "File Tree" + ], + "vs/platform/actions/browser/toolbar": [ + "Hide", + "Reset Menu" + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ + "Whether an inline suggestion is visible", + "Whether the inline suggestion starts with whitespace", + "Whether the inline suggestion starts with whitespace that is less than what would be inserted by tab", + "Whether suggestions should be suppressed for the current suggestion" + ], "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView": [ "Select Notebook Kernel", "Notebook Kernel Args" @@ -27698,11 +27953,10 @@ "Deleted", "'{0}' already exists. Do you want to replace it?", "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", - "&&Replace" - ], - "vs/platform/actions/browser/toolbar": [ - "Hide", - "Reset Menu" + "&&Replace", + "'{0}' is marked as read-only. Do you want to save anyway?", + "Paths can be configured as read-only via settings.", + "&&Save Anyway" ], "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy": [ "Currently Selected", @@ -27722,10 +27976,6 @@ "Detecting Kernels", "Select Kernel" ], - "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ - "Cannot join cells of different kinds", - "Join Notebook Cells" - ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget": [ "{0} found", "{0} found for '{1}'", @@ -27734,76 +27984,27 @@ "vs/editor/contrib/codeAction/browser/codeAction": [ "An unknown error occurred while applying the code action" ], - "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ - "The chat view is comprised of an input box and a request/response list. The input box is used to make requests and the list is used to display responses.", - "In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the submit button to run a new request.", - "In the input box, inspect the last response in the accessible view via {0}", - "With the input box focused, inspect the last response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", - "Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.", - "To focus the chat request/response list, which can be navigated with up and down arrows, invoke the Focus Chat command ({0}).", - "To focus the chat request/response list, which can be navigated with up and down arrows, invoke The Focus Chat List command, which is currently not triggerable by a keybinding.", - "To focus the input box for chat requests, invoke the Focus Chat Input command ({0})", - "To focus the input box for chat requests, invoke the Focus Chat Input command, which is currently not triggerable by a keybinding.", - "To focus the next code block within a response, invoke the Chat: Next Code Block command ({0}).", - "To focus the next code block within a response, invoke the Chat: Next Code Block command, which is currently not triggerable by a keybinding.", - "To focus the next file tree within a response, invoke the Chat: Next File Tree command ({0}).", - "To focus the next file tree within a response, invoke the Chat: Next File Tree command, which is currently not triggerable by a keybinding.", - "To clear the request/response list, invoke the Chat Clear command ({0}).", - "To clear the request/response list, invoke the Chat Clear command, which is currently not triggerable by a keybinding.", - "Inline chat occurs within a code editor and takes into account the current selection. It is useful for making changes to the current editor. For example, fixing diagnostics, documenting or refactoring code. Keep in mind that AI generated code may be incorrect.", - "It can be activated via code actions or directly using the command: Inline Chat: Start Code Chat ({0}).", - "In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.", - "In the input box, inspect the response in the accessible view via {0}", - "With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", - "Context menu actions may run a request prefixed with a /. Type / to discover such ready-made commands.", - "If a fix action is invoked, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.", - "Once in the diff editor, enter review mode with ({0}). Use up and down arrows to navigate lines with the proposed changes.", - "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes.", - "Use tab to reach conditional parts like commands, status, message responses and more.", - "Audio cues can be changed via settings with a prefix of audioCues.chat. By default, if a request takes more than 4 seconds, you will hear an audio cue indicating that progress is still occurring." - ], - "vs/workbench/contrib/chat/browser/chatInputPart": [ - "Chat Input, Type to ask questions or type / for topics, press enter to send out the request. Use {0} for Chat Accessibility Help.", - "Chat Input, Type code here and press Enter to run. Use the Chat Accessibility Help command for more information.", - "Chat Input" - ], - "vs/workbench/contrib/chat/browser/chatListRenderer": [ - "Chat", - "Command: {0}", - "Commands: {0}", - "1 file tree", - "{0} file trees", - "{0} {1} {2}", - "{0} {1}", - "{0} 1 code block: {1} {2}", - "{0} 1 code block: {1}", - "{0} {1} code blocks: {2}", - "{0} {1} code blocks", - "Code block", - "Toolbar for code block which can be reached via tab", - "Code block toolbar", - "Code block {0}", - "File Tree" - ], "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ "Nothing changed", "Changed 1 line", "Changed {0} lines" ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ - "Whether an inline suggestion is visible", - "Whether the inline suggestion starts with whitespace", - "Whether the inline suggestion starts with whitespace that is less than what would be inserted by tab", - "Whether suggestions should be suppressed for the current suggestion" - ], "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ "Inline Chat Input", "Original", "Modified", "Inline Chat Input, Use {0} for Inline Chat Accessibility Help.", "Inline Chat Input, Run the Inline Chat Accessibility Help command for more information.", + "Using {0} to generate response...", "Closed inline chat widget" ], + "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ + "The terminal has no selection to copy", + "Yes", + "No", + "Don't Show Again", + "Terminal GPU acceleration appears to be slow on your computer. Would you like to switch to disable it which may improve performance? [Read more about terminal settings](https://code.visualstudio.com/docs/editor/integrated-terminal#_changing-how-the-terminal-is-rendered)." + ], "vs/workbench/contrib/testing/browser/theme": [ "Color for the 'failed' icon in the test explorer.", "Color for the 'Errored' icon in the test explorer.", @@ -27819,6 +28020,29 @@ "Text color of test info messages shown inline in the editor.", "Margin color beside info messages shown inline in the editor." ], + "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ + "The background color of the terminal, this allows coloring the terminal differently to the panel.", + "The foreground color of the terminal.", + "The foreground color of the terminal cursor.", + "The background color of the terminal cursor. Allows customizing the color of a character overlapped by a block cursor.", + "The selection background color of the terminal.", + "The selection background color of the terminal when it does not have focus.", + "The selection foreground color of the terminal. When this is null the selection foreground will be retained and have the minimum contrast ratio feature applied.", + "The default terminal command decoration background color.", + "The terminal command decoration background color for successful commands.", + "The terminal command decoration background color for error commands.", + "The overview ruler cursor color.", + "The color of the border that separates split panes within the terminal. This defaults to panel.border.", + "Color of the current search match in the terminal. The color must not be opaque so as not to hide underlying terminal content.", + "Border color of the other search matches in the terminal.", + "Border color of the current search match in the terminal.", + "Color of the other search matches in the terminal. The color must not be opaque so as not to hide underlying terminal content.", + "Border color of the other search matches in the terminal.", + "Overview ruler marker color for find matches in the terminal.", + "Background color when dragging on top of terminals. The color should have transparency so that the terminal contents can still shine through.", + "Border on the side of the terminal tab in the panel. This defaults to tab.activeBorder.", + "'{0}' ANSI color in the terminal." + ], "vs/workbench/contrib/testing/common/constants": [ "Errored", "Failed", @@ -27844,36 +28068,6 @@ "Show Hidden Tests", "Unhide All Tests" ], - "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ - "The terminal has no selection to copy", - "Yes", - "No", - "Don't Show Again", - "Terminal GPU acceleration appears to be slow on your computer. Would you like to switch to disable it which may improve performance? [Read more about terminal settings](https://code.visualstudio.com/docs/editor/integrated-terminal#_changing-how-the-terminal-is-rendered)." - ], - "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ - "The background color of the terminal, this allows coloring the terminal differently to the panel.", - "The foreground color of the terminal.", - "The foreground color of the terminal cursor.", - "The background color of the terminal cursor. Allows customizing the color of a character overlapped by a block cursor.", - "The selection background color of the terminal.", - "The selection background color of the terminal when it does not have focus.", - "The selection foreground color of the terminal. When this is null the selection foreground will be retained and have the minimum contrast ratio feature applied.", - "The default terminal command decoration background color.", - "The terminal command decoration background color for successful commands.", - "The terminal command decoration background color for error commands.", - "The overview ruler cursor color.", - "The color of the border that separates split panes within the terminal. This defaults to panel.border.", - "Color of the current search match in the terminal. The color must not be opaque so as not to hide underlying terminal content.", - "Border color of the other search matches in the terminal.", - "Border color of the current search match in the terminal.", - "Color of the other search matches in the terminal. The color must not be opaque so as not to hide underlying terminal content.", - "Border color of the other search matches in the terminal.", - "Overview ruler marker color for find matches in the terminal.", - "Background color when dragging on top of terminals. The color should have transparency so that the terminal contents can still shine through.", - "Border on the side of the terminal tab in the panel. This defaults to tab.activeBorder.", - "'{0}' ANSI color in the terminal." - ], "vs/platform/quickinput/browser/commandsQuickAccess": [ "recently used", "similar commands", @@ -27918,6 +28112,12 @@ "This action is irreversible!", "&&Replace" ], + "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ + "Unable to resolve workspace folder ({0})", + "Symbolic Link", + "Unknown File Type", + "Explorer" + ], "vs/workbench/contrib/files/browser/views/explorerViewer": [ "Files Explorer", "Type file name. Press Enter to confirm or Escape to cancel.", @@ -27934,21 +28134,6 @@ "{0} folders", "{0} files" ], - "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ - "Unable to resolve workspace folder ({0})", - "Symbolic Link", - "Unknown File Type", - "Explorer" - ], - "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ - "All backslashes in Query string must be escaped (\\\\)", - "{0} files", - "1 file", - "{0} results", - "1 result", - "No Results", - "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results." - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree": [ "Bulk Edit", "Renaming {0} to {1}, also making text edits", @@ -27967,9 +28152,6 @@ "(deleting)", "{0} - {1}" ], - "vs/workbench/contrib/search/browser/searchFindInput": [ - "Notebook Find Filters" - ], "vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess": [ "To go to a symbol, first open a text editor with symbol information.", "The active text editor does not provide symbol information.", @@ -28009,9 +28191,31 @@ "Search and Replace", "{0} ↔ {1} (Replace Preview)" ], + "vs/workbench/contrib/search/browser/searchFindInput": [ + "Notebook Find Filters" + ], + "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ + "All backslashes in Query string must be escaped (\\\\)", + "{0} files", + "1 file", + "{0} results", + "1 result", + "No Results", + "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results." + ], + "vs/workbench/contrib/scm/browser/dirtyDiffSwitcher": [ + "Switch quick diff base", + "Switch Quick Diff Base" + ], + "vs/workbench/contrib/scm/browser/menus": [ + "Share" + ], "vs/workbench/contrib/debug/browser/baseDebugView": [ "Click to expand" ], + "vs/workbench/contrib/debug/common/debugSource": [ + "Unknown Source" + ], "vs/workbench/contrib/debug/browser/debugSessionPicker": [ "Search debug sessions by name", "Start a New Debug Session", @@ -28041,23 +28245,6 @@ "workspace", "user settings" ], - "vs/workbench/contrib/debug/browser/debugTaskRunner": [ - "Errors exist after running preLaunchTask '{0}'.", - "Error exists after running preLaunchTask '{0}'.", - "The preLaunchTask '{0}' terminated with exit code {1}.", - "The preLaunchTask '{0}' terminated.", - "&&Debug Anyway", - "&&Show Errors", - "Abort", - "Remember my choice in user settings", - "&&Debug Anyway", - "Remember my choice for this task", - "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", - "Could not find the task '{0}'.", - "Could not find the specified task.", - "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined.", - "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined." - ], "vs/workbench/contrib/debug/browser/debugSession": [ "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", @@ -28096,32 +28283,30 @@ "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", + "Started running without debugging.", "Debugging started.", "Debugging stopped." ], - "vs/workbench/contrib/debug/common/debugSource": [ - "Unknown Source" - ], - "vs/workbench/contrib/scm/browser/menus": [ - "Share" - ], - "vs/workbench/contrib/scm/browser/dirtyDiffSwitcher": [ - "Switch quick diff base", - "Switch Quick Diff Base" - ], - "vs/base/browser/ui/findinput/replaceInput": [ - "input", - "Preserve Case" + "vs/workbench/contrib/debug/browser/debugTaskRunner": [ + "Errors exist after running preLaunchTask '{0}'.", + "Error exists after running preLaunchTask '{0}'.", + "The preLaunchTask '{0}' terminated with exit code {1}.", + "The preLaunchTask '{0}' terminated.", + "&&Debug Anyway", + "&&Show Errors", + "Abort", + "Remember my choice in user settings", + "&&Debug Anyway", + "Remember my choice for this task", + "Task '{0}' can not be referenced from a launch configuration that is in a different workspace folder.", + "Could not find the task '{0}'.", + "Could not find the specified task.", + "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined.", + "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined." ], "vs/base/browser/ui/dropdown/dropdownActionViewItem": [ "More Actions..." ], - "vs/workbench/contrib/markers/browser/markersTable": [ - "Code", - "Message", - "File", - "Source" - ], "vs/workbench/contrib/mergeEditor/common/mergeEditor": [ "The editor is a merge editor", "The editor is a the result editor of a merge editor.", @@ -28132,13 +28317,6 @@ "The uri of the baser of a merge editor", "The uri of the result of a merge editor" ], - "vs/workbench/contrib/markers/browser/markersTreeViewer": [ - "Problems View", - "Icon indicating that multiple lines are shown in the markers view.", - "Icon indicating that multiple lines are collapsed in the markers view.", - "Show message in single line", - "Show message in multiple lines" - ], "vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel": [ "Do you want keep the merge result of {0} files?", "Do you want keep the merge result of {0}?", @@ -28170,14 +28348,16 @@ "&&Close", "Don't ask again" ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ - "Base", - "Comparing with {0}", - "Differences are highlighted with a background color." - ], "vs/workbench/contrib/mergeEditor/browser/view/viewModel": [ "There is currently no conflict focused that can be toggled." ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ + "Result", + "{0} Conflict Remaining", + "{0} Conflicts Remaining ", + "Go to next conflict", + "All conflicts handled, the merge can be completed now." + ], "vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView": [ "Input 1", "Input 2", @@ -28206,12 +28386,22 @@ "The background color of decorations in input 1.", "The background color of decorations in input 2." ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ - "Result", - "{0} Conflict Remaining", - "{0} Conflicts Remaining ", - "Go to next conflict", - "All conflicts handled, the merge can be completed now." + "vs/base/browser/ui/findinput/replaceInput": [ + "input", + "Preserve Case" + ], + "vs/workbench/contrib/markers/browser/markersTreeViewer": [ + "Problems View", + "Icon indicating that multiple lines are shown in the markers view.", + "Icon indicating that multiple lines are collapsed in the markers view.", + "Show message in single line", + "Show message in multiple lines" + ], + "vs/workbench/contrib/markers/browser/markersTable": [ + "Code", + "Message", + "File", + "Source" ], "vs/workbench/contrib/comments/browser/commentsController": [ "Line {0}", @@ -28221,67 +28411,18 @@ "Editor has commenting ranges.", "Select Comment Provider" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ + "Base", + "Comparing with {0}", + "Differences are highlighted with a background color." + ], "vs/workbench/contrib/customEditor/common/contributedCustomEditors": [ "Built-in" ], - "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ - "Average rating: {0} out of 5", - "Sponsor", - "Extension in {0}", - "This extension is ignored during sync.", - "Activation time", - "Startup", - "Pre-Release", - "Sponsor", - "This publisher has verified ownership of {0}", - "Latest version:", - "Activation time", - "Startup", - "1 uncaught error", - "{0} uncaught errors", - "1 message", - "{0} messages", - "Show Dependencies", - "Pre-Release version", - "This extension has a {0} available", - "You have chosen not to receive recommendations for this extension.", - "The icon color for extension ratings.", - "The icon color for extension verified publisher.", - "The icon color for pre-release extension.", - "The icon color for extension sponsor." - ], - "vs/workbench/contrib/extensions/browser/extensionsViewer": [ - "Error", - "Unknown Extension:", - "Extensions" - ], - "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ - "This extension is recommended by users of the current workspace." - ], - "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ - "This extension is recommended based on the files you recently opened.", - "the {0} language" - ], - "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ - "This extension is recommended because you have {0} installed." - ], - "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ - "This extension is recommended because of the current workspace configuration" - ], - "vs/workbench/contrib/extensions/browser/webRecommendations": [ - "This extension is recommended for {0} for the Web" - ], "vs/platform/files/browser/htmlFileSystemProvider": [ "Rename is only supported for files.", "Insufficient permissions. Please retry and allow the operation." ], - "vs/workbench/contrib/terminal/browser/terminalService": [ - "Do you want to terminate the active terminal session?", - "Do you want to terminate the {0} active terminal sessions?", - "&&Terminate", - "This shell is open to a {0}local{1} folder, NOT to the virtual folder", - "This shell is running on your {0}local{1} machine, NOT on the connected remote machine" - ], "vs/workbench/contrib/terminal/browser/terminalActions": [ "Show Tabs", "Select current working directory for new terminal", @@ -28368,6 +28509,13 @@ "Create New Terminal With Profile", "Rename Terminal" ], + "vs/workbench/contrib/terminal/browser/terminalService": [ + "Do you want to terminate the active terminal session?", + "Do you want to terminate the {0} active terminal sessions?", + "&&Terminate", + "This shell is open to a {0}local{1} folder, NOT to the virtual folder", + "This shell is running on your {0}local{1} machine, NOT on the connected remote machine" + ], "vs/workbench/contrib/terminal/common/terminalConfiguration": [ "the terminal's current working directory", "the terminal's current working directory, displayed for multi-root workspaces or in a single root workspace when the value differs from the initial working directory. On Windows, this will only be displayed when shell integration is enabled.", @@ -28519,7 +28667,8 @@ "Controls whether the terminal, accessible buffer, or neither will be focused after `Terminal: Run Selected Text In Active Terminal` has been run.", "Always focus the terminal.", "Always focus the accessible buffer.", - "Do nothing." + "Do nothing.", + "Preserve the cursor position on reopen of the terminal's accessible view rather than setting it to the bottom of the buffer." ], "vs/workbench/contrib/terminal/browser/terminalMenus": [ "&&New Terminal", @@ -28575,6 +28724,8 @@ "Do Not Show Again", "current session", "previous session", + "Task", + "Local", "Terminal", "Focus Terminal", "Focus Terminal and Hide Accessible Buffer", @@ -28612,6 +28763,7 @@ "vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp": [ "The Focus Accessible Buffer ({0}) command enables screen readers to read terminal contents.", "The Focus Accessible Buffer command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.", + "Customize the behavior of the cursor when toggling between the terminal and accessible view with `terminal.integrated.accessibleViewPreserveCursorPosition.`", "Consider using powershell instead of command prompt for an improved experience", "The terminal has a feature called shell integration that offers an enhanced experience and provides useful commands for screen readers such as:", "Go to Next Command ({0}) in the accessible view", @@ -28630,8 +28782,16 @@ "The Open Detected Link command enables screen readers to easily open links found in the terminal and is currently not triggerable by a keybinding.", "The Create New Terminal (With Profile) ({0}) command allows for easy terminal creation using a specific profile.", "The Create New Terminal (With Profile) command allows for easy terminal creation using a specific profile and is currently not triggerable by a keybinding.", - "Configure what gets focused after running selected text in the terminal with `{0}`.", - "Access accessibility settings such as `terminal.integrated.tabFocusMode` via the Preferences: Open Accessibility Settings command." + "Configure what gets focused after running selected text in the terminal with `{0}`." + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ + "option + click", + "alt + click", + "cmd + click", + "ctrl + click", + "Follow link", + "Follow link using forwarded port", + "Link" ], "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick": [ "Url", @@ -28644,15 +28804,6 @@ "Folder", "Workspace Search" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ - "option + click", - "alt + click", - "cmd + click", - "ctrl + click", - "Follow link", - "Follow link using forwarded port", - "Link" - ], "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ "Run: {0}", "Open: {0}", @@ -28670,6 +28821,53 @@ "The command exit result to match on", "The kind of the resulting quick fix. This changes how the quick fix is presented. Defaults to {0}." ], + "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ + "Average rating: {0} out of 5", + "Sponsor", + "Extension in {0}", + "This extension is ignored during sync.", + "Activation time", + "Startup", + "Pre-Release", + "Sponsor", + "This publisher has verified ownership of {0}", + "Latest version:", + "Activation time", + "Startup", + "1 uncaught error", + "{0} uncaught errors", + "1 message", + "{0} messages", + "Show Dependencies", + "Pre-Release version", + "This extension has a {0} available", + "You have chosen not to receive recommendations for this extension.", + "The icon color for extension ratings.", + "The icon color for extension verified publisher.", + "The icon color for pre-release extension.", + "The icon color for extension sponsor." + ], + "vs/workbench/contrib/extensions/browser/extensionsViewer": [ + "Error", + "Unknown Extension:", + "Extensions" + ], + "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ + "This extension is recommended because you have {0} installed." + ], + "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ + "This extension is recommended by users of the current workspace." + ], + "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ + "This extension is recommended based on the files you recently opened.", + "the {0} language" + ], + "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ + "This extension is recommended because of the current workspace configuration" + ], + "vs/workbench/contrib/extensions/browser/webRecommendations": [ + "This extension is recommended for {0} for the Web" + ], "vs/workbench/contrib/tasks/common/jsonSchemaCommon": [ "Additional command options", "The current working directory of the executed program or script. If omitted Code's current workspace root is used.", @@ -28735,7 +28933,6 @@ "Optional arguments passed to the command." ], "vs/workbench/contrib/remote/browser/explorerViewItems": [ - "Switch Remote", "Switch Remote" ], "vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions": [ @@ -28760,6 +28957,11 @@ "{0}, {1}", "{0}, {1}" ], + "vs/workbench/contrib/update/browser/releaseNotesEditor": [ + "Release Notes: {0}", + "unassigned", + "Show release notes after an update" + ], "vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent": [ "Icon used for the setup category of welcome page", "Icon used for the beginner category of welcome page", @@ -28786,12 +28988,12 @@ "Connect to a remote machine through a Tunnel", "Get Started with VS Code", "Discover the best customizations to make VS Code yours.", + "Personalize your VS Code", + "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", + "Backup and Sync Settings", "Choose the look you want", "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", "Browse Color Themes", - "Sync to and from other devices", - "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", - "Enable Settings Sync", "One shortcut to access everything", "Commands are the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequent ones to save time.\n{0}\n__Try searching for 'view toggle'.__", "Open Command Palette", @@ -28812,12 +29014,12 @@ "Quick Open a File", "Get Started with VS Code for the Web", "Discover the best customizations to make VS Code for the Web yours.", + "Personalize your VS Code", + "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", + "Backup and Sync Settings", "Choose the look you want", "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", "Browse Color Themes", - "Sync to and from other devices", - "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", - "Enable Settings Sync", "One shortcut to access everything", "Commands are the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequent ones to save time.\n{0}\n__Try searching for 'view toggle'.__", "Open Command Palette", @@ -28891,10 +29093,17 @@ "Select the layout for your notebooks", "Get notebooks to feel just the way you prefer" ], - "vs/workbench/contrib/update/browser/releaseNotesEditor": [ - "Release Notes: {0}", - "unassigned", - "Show release notes after an update" + "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ + "Recommended" + ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ + "Background color for the Welcome page.", + "Background color for the tiles on the Welcome page.", + "Hover background color for the tiles on the Welcome.", + "Border color for the tiles on the Welcome page.", + "Foreground color for the Welcome page progress bars.", + "Background color for the Welcome page progress bars.", + "Foreground color of the heading of each walkthrough step" ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint": [ "Title", @@ -28938,17 +29147,15 @@ "vs/workbench/contrib/welcomeWalkthrough/common/walkThroughUtils": [ "Background color for the embedded editors on the Interactive Playground." ], - "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ - "Recommended" + "vs/workbench/contrib/callHierarchy/browser/callHierarchyTree": [ + "Call Hierarchy", + "calls from {0}", + "callers of {0}" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ - "Background color for the Welcome page.", - "Background color for the tiles on the Welcome page.", - "Hover background color for the tiles on the Welcome.", - "Border color for the tiles on the Welcome page.", - "Foreground color for the Welcome page progress bars.", - "Background color for the Welcome page progress bars.", - "Foreground color of the heading of each walkthrough step" + "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree": [ + "Type Hierarchy", + "supertypes of {0}", + "subtypes of {0}" ], "vs/editor/contrib/symbolIcons/browser/symbolIcons": [ "The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.", @@ -28985,24 +29192,9 @@ "The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.", "The foreground color for variable symbols. These symbols appear in the outline, breadcrumb, and suggest widget." ], - "vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree": [ - "Type Hierarchy", - "supertypes of {0}", - "subtypes of {0}" - ], - "vs/workbench/contrib/callHierarchy/browser/callHierarchyTree": [ - "Call Hierarchy", - "calls from {0}", - "callers of {0}" - ], "vs/workbench/contrib/userDataSync/browser/userDataSyncViews": [ - "Conflicts", - "Synced Machines", "Edit Name", "Turn off Settings Sync", - "Sync Activity (Remote)", - "Sync Activity (Local)", - "Sync Activity (Developer)", "Load Sync Activity", "Select Sync Activity File or Folder", "Show raw JSON sync data", @@ -29012,7 +29204,6 @@ "{0} (Local)", "Restore", "Would you like to replace your current {0} with selected?", - "Troubleshoot", "Reset Synced Data", "{0} ↔ {1}", "Current", @@ -29028,7 +29219,13 @@ "Machine name should be unique and not empty", "Logs", "Last Synced Remotes", - "Current" + "Current", + "Conflicts", + "Synced Machines", + "Sync Activity (Remote)", + "Sync Activity (Local)", + "Sync Activity (Developer)", + "Troubleshoot" ], "vs/workbench/browser/parts/notifications/notificationsList": [ "Inspect the response in the accessible view with {0}", @@ -29059,6 +29256,26 @@ "vs/workbench/services/textfile/common/textFileSaveParticipant": [ "Saving '{0}'" ], + "vs/workbench/browser/parts/titlebar/menubarControl": [ + "&&File", + "&&Edit", + "&&Selection", + "&&View", + "&&Go", + "&&Terminal", + "&&Help", + "Preferences", + "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style.", + "Open Settings", + "Focus Application Menu", + "Check for &&Updates...", + "Checking for Updates...", + "D&&ownload Update", + "Downloading Update...", + "Install &&Update...", + "Installing Update...", + "Restart to &&Update" + ], "vs/workbench/browser/parts/titlebar/commandCenterControl": [ "Search", "{0} {1}", @@ -29067,10 +29284,19 @@ "Search {0} — {1}", "Command Center" ], - "vs/workbench/browser/parts/titlebar/windowTitle": [ - "[Administrator]", - "[Superuser]", - "[Extension Development Host]" + "vs/workbench/browser/parts/globalCompositeBar": [ + "Accounts icon in the view bar.", + "Hide Accounts", + "Manage", + "Accounts", + "Accounts", + "Loading...", + "{0} is currently unavailable", + "Manage Trusted Extensions", + "Sign Out", + "You are not signed in to any accounts", + "Manage", + "Manage {0} (Profile)" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopy": [ "Failed to save '{0}': The content of the file is newer. Do you want to overwrite the file with your changes?", @@ -29094,14 +29320,14 @@ "vs/platform/terminal/common/terminalProfiles": [ "Automatically detect the default" ], + "vs/workbench/contrib/webview/browser/webviewElement": [ + "Error loading webview: {0}" + ], "vs/platform/quickinput/browser/quickPickPin": [ "pinned", "Pin command", "Pinned command" ], - "vs/workbench/contrib/webview/browser/webviewElement": [ - "Error loading webview: {0}" - ], "vs/workbench/api/common/extHostDiagnostics": [ "Not showing {0} further errors and warnings." ], @@ -29127,6 +29353,9 @@ "Provider returned null response", "Error from provider: {0}" ], + "vs/workbench/api/common/extHostChatAgents2": [ + "Error from provider: {0}" + ], "vs/base/browser/ui/findinput/findInputToggles": [ "Match Case", "Match Whole Word", @@ -29148,12 +29377,6 @@ "+ {0} modified line {1}", "- {0} original line {1}" ], - "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ - "Code moved with changes to line {0}-{1}", - "Code moved with changes from line {0}-{1}", - "Code moved to line {0}-{1}", - "Code moved from line {0}-{1}" - ], "vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature": [ "Fold Unchanged Region", "Click or drag to show more above", @@ -29162,6 +29385,12 @@ "{0} hidden lines", "Double click to unfold" ], + "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ + "Code moved with changes to line {0}-{1}", + "Code moved with changes from line {0}-{1}", + "Code moved to line {0}-{1}", + "Code moved from line {0}-{1}" + ], "vs/editor/browser/widget/diffEditor/diffEditorEditors": [ " use {0} to open the accessibility help." ], @@ -29176,15 +29405,6 @@ "{0} To enable screen reader optimized mode, open the quick pick with {1} and run the command Toggle Screen Reader Accessibility Mode, which is currently not triggerable via keyboard.", "{0} Please assign a keybinding for the command Toggle Screen Reader Accessibility Mode by accessing the keybindings editor with {1} and run it." ], - "vs/platform/actionWidget/browser/actionWidget": [ - "Background color for toggled action items in action bar.", - "Whether the action widget list is visible", - "Hide action widget", - "Select previous action", - "Select next action", - "Accept selected action", - "Preview selected action" - ], "vs/editor/contrib/codeAction/browser/codeActionMenu": [ "More Actions...", "Quick Fix", @@ -29195,6 +29415,15 @@ "Surround With", "Source Action" ], + "vs/platform/actionWidget/browser/actionWidget": [ + "Background color for toggled action items in action bar.", + "Whether the action widget list is visible", + "Hide action widget", + "Select previous action", + "Select next action", + "Accept selected action", + "Preview selected action" + ], "vs/editor/contrib/colorPicker/browser/colorPickerWidget": [ "Click to toggle color options (rgb/hsl/hex)", "Icon to close the color picker" @@ -29272,6 +29501,34 @@ "Comments", "Show Resolved" ], + "vs/workbench/contrib/comments/browser/commentColors": [ + "Icon color for resolved comments.", + "Icon color for unresolved comments.", + "Color of borders and arrow for resolved comments.", + "Color of borders and arrow for unresolved comments.", + "Color of background for comment ranges.", + "Color of background for currently selected or hovered comment range." + ], + "vs/workbench/browser/parts/editor/editorPanes": [ + "This type of editor cannot be opened in floating windows yet.", + "Close Editor", + "Unable to open '{0}'", + "&&OK" + ], + "vs/workbench/browser/parts/editor/editorGroupWatermark": [ + "Show All Commands", + "Go to File", + "Open File", + "Open Folder", + "Open File or Folder", + "Open Recent", + "New Untitled Text File", + "Find in Files", + "Toggle Terminal", + "Start Debugging", + "Toggle Full Screen", + "Show Settings" + ], "vs/workbench/browser/parts/editor/editorPlaceholder": [ "Workspace Trust Required", "The file is not displayed in the editor because trust has not been granted to the folder.", @@ -29283,33 +29540,14 @@ "The editor could not be opened due to an unexpected error.", "Try Again" ], - "vs/workbench/contrib/comments/browser/commentColors": [ - "Icon color for resolved comments.", - "Icon color for unresolved comments.", - "Color of borders and arrow for resolved comments.", - "Color of borders and arrow for unresolved comments.", - "Color of background for comment ranges.", - "Color of background for currently selected or hovered comment range." - ], - "vs/base/browser/ui/menu/menubar": [ - "Application Menu", - "More" - ], - "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ - "Tab actions" + "vs/workbench/browser/parts/compositePart": [ + "{0} actions", + "Views and More Actions...", + "{0} ({1})" ], - "vs/workbench/browser/parts/editor/breadcrumbsControl": [ - "Icon for the separator in the breadcrumbs.", - "Whether the editor can show breadcrumbs", - "Whether breadcrumbs are currently visible", - "Whether breadcrumbs have focus", - "no elements", - "Toggle Breadcrumbs", - "Toggle &&Breadcrumbs", - "Breadcrumbs", - "&&Breadcrumbs", - "Focus and Select Breadcrumbs", - "Focus Breadcrumbs" + "vs/workbench/browser/parts/paneCompositeBar": [ + "Reset Location", + "Reset Location" ], "vs/platform/quickinput/browser/quickInput": [ "Back", @@ -29404,6 +29642,12 @@ "Item", "Value" ], + "vs/workbench/contrib/chat/browser/codeBlockPart": [ + "Code block", + "Toolbar for code block which can be reached via tab", + "Code block toolbar", + "Code block {0}" + ], "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer": [ "Execution Order" ], @@ -29437,11 +29681,24 @@ "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ "empty cell" ], + "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ + "Exited {0} mode" + ], "vs/platform/actions/browser/buttonbar": [ "{0} ({1})" ], - "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ - "Exited {0} mode" + "vs/workbench/contrib/debug/common/debugger": [ + "Cannot find debug adapter for type '{0}'.", + "Use IntelliSense to learn about possible attributes.", + "Hover to view descriptions of existing attributes.", + "For more information, visit: {0}", + "Type of configuration.", + "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled.", + "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".", + "Request type of configuration. Can be \"launch\" or \"attach\".", + "Windows specific launch configuration attributes.", + "OS X specific launch configuration attributes.", + "Linux specific launch configuration attributes." ], "vs/workbench/contrib/terminal/browser/xterm/decorationAddon": [ "Rerun Command", @@ -29461,19 +29718,6 @@ "Gutter command decorations", "Overview ruler command decorations" ], - "vs/workbench/contrib/debug/common/debugger": [ - "Cannot find debug adapter for type '{0}'.", - "Use IntelliSense to learn about possible attributes.", - "Hover to view descriptions of existing attributes.", - "For more information, visit: {0}", - "Type of configuration.", - "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled.", - "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".", - "Request type of configuration. Can be \"launch\" or \"attach\".", - "Windows specific launch configuration attributes.", - "OS X specific launch configuration attributes.", - "Linux specific launch configuration attributes." - ], "vs/workbench/contrib/debug/common/debugSchemas": [ "Contributes debug adapters.", "Unique identifier for this debug adapter.", @@ -29517,10 +29761,6 @@ "Controls whether manually terminating one session will stop all of the compound sessions.", "Task to run before any of the compound configurations start." ], - "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ - "1 Conflicting Line", - "{0} Conflicting Lines" - ], "vs/workbench/contrib/debug/browser/rawDebugSession": [ "No debug adapter, can not start debug session.", "The debugger needs to open a new tab or window for the debuggee but the browser prevented this. You must give permission to continue.", @@ -29532,6 +29772,10 @@ "Set Input Handled", "Undo Mark As Handled" ], + "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ + "1 Conflicting Line", + "{0} Conflicting Lines" + ], "vs/workbench/contrib/mergeEditor/browser/view/conflictActions": [ "Accept {0}", "Accept {0} in the result document.", @@ -29562,10 +29806,6 @@ "Editor gutter decoration color for commenting glyphs.", "Editor gutter decoration color for commenting glyphs for unresolved comment threads." ], - "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ - "The '{0}' extension is recommended for opening a terminal in WSL.", - "Install" - ], "vs/workbench/contrib/customEditor/common/extensionPoint": [ "Contributed custom editors.", "Identifier for the custom editor. This must be unique across all custom editors, so we recommend including your extension id as part of `viewType`. The `viewType` is used when registering custom editors with `vscode.registerCustomEditorProvider` and in the `onCustomEditor:${id}` [activation event](https://code.visualstudio.com/api/references/activation-events).", @@ -29576,9 +29816,24 @@ "The editor is automatically used when the user opens a resource, provided that no other default custom editors are registered for that resource.", "The editor is not automatically used when the user opens a resource, but a user can switch to the editor using the `Reopen With` command." ], + "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ + "The '{0}' extension is recommended for opening a terminal in WSL.", + "Install" + ], + "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ + "Select the terminal profile to create", + "Select your default terminal profile", + "Enter terminal profile name", + "A terminal profile already exists with that name", + "profiles", + "contributed", + "detected", + "This terminal profile uses a potentially unsafe path that can be modified by another user: {0}. Are you sure you want to use it?", + "Yes", + "Cancel", + "Configure Terminal Profile" + ], "vs/workbench/contrib/terminal/browser/terminalInstance": [ - "Task", - "Local", "Terminal input", "Use the accessible buffer {0} to manually review output", "Use the Terminal: Focus Accessible Buffer command to manually review output", @@ -29611,19 +29866,6 @@ "The terminal process terminated with exit code: {0}.", "The terminal process failed to launch: {0}." ], - "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ - "Select the terminal profile to create", - "Select your default terminal profile", - "Enter terminal profile name", - "A terminal profile already exists with that name", - "profiles", - "contributed", - "detected", - "This terminal profile uses a potentially unsafe path that can be modified by another user: {0}. Are you sure you want to use it?", - "Yes", - "Cancel", - "Configure Terminal Profile" - ], "vs/workbench/contrib/terminal/browser/terminalTabsList": [ "Type terminal name. Press Enter to confirm or Escape to cancel.", "Terminal tabs", @@ -29631,13 +29873,6 @@ "Terminal {0} {1}", "Terminal" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ - "Search workspace", - "Open file in editor", - "Focus folder in explorer", - "Open folder in new window", - "Follow link" - ], "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget": [ "Find", "Find (⇅ for history)", @@ -29650,6 +29885,13 @@ "{0} found for '{1}'", "Border color of the sash border." ], + "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ + "Search workspace", + "Open file in editor", + "Focus folder in explorer", + "Open folder in new window", + "Follow link" + ], "vs/workbench/contrib/terminal/browser/xterm/decorationStyles": [ "Show Command Actions", "Command executed {0} and failed", @@ -29663,6 +29905,11 @@ "Light High Contrast", "See More Themes..." ], + "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ + "Default", + "Jupyter", + "Colab" + ], "vs/workbench/contrib/userDataSync/browser/userDataSyncConflictsView": [ "Please go through each entry and merge to resolve conflicts.", "Show Conflicts", @@ -29673,11 +29920,6 @@ "Theirs", "Yours" ], - "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ - "Default", - "Jupyter", - "Colab" - ], "vs/workbench/browser/parts/notifications/notificationsViewer": [ "Click to execute command '{0}'", "Notification Actions", @@ -29688,6 +29930,28 @@ "close", "find" ], + "vs/base/browser/ui/menu/menubar": [ + "Application Menu", + "More" + ], + "vs/workbench/browser/parts/compositeBarActions": [ + "{0} ({1})", + "{0} - {1}", + "Additional Views", + "{0} ({1})", + "Manage Extension", + "Hide '{0}'", + "Keep '{0}'", + "Hide Badge", + "Show Badge", + "Toggle View Pinned", + "Toggle View Badge" + ], + "vs/editor/browser/widget/diffEditor/decorations": [ + "Line decoration for inserts in the diff editor.", + "Line decoration for removals in the diff editor.", + "Click to revert change" + ], "vs/editor/common/viewLayout/viewLineRenderer": [ "Show more ({0})", "{0} chars" @@ -29701,11 +29965,6 @@ "Copy changed line ({0})", "Revert this change" ], - "vs/editor/browser/widget/diffEditor/decorations": [ - "Line decoration for inserts in the diff editor.", - "Line decoration for removals in the diff editor.", - "Click to revert change" - ], "vs/platform/actionWidget/browser/actionList": [ "{0} to apply, {1} to preview", "{0} to apply", @@ -29717,55 +29976,24 @@ "{0} reference", "References" ], - "vs/workbench/browser/parts/editor/editorTabsControl": [ - "Editor actions", - "{0} (+{1})" + "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ + "Tab actions" ], - "vs/workbench/browser/parts/editor/breadcrumbs": [ - "Breadcrumb Navigation", - "Enable/disable navigation breadcrumbs.", - "Controls whether and how file paths are shown in the breadcrumbs view.", - "Show the file path in the breadcrumbs view.", - "Do not show the file path in the breadcrumbs view.", - "Only show the last element of the file path in the breadcrumbs view.", - "Controls whether and how symbols are shown in the breadcrumbs view.", - "Show all symbols in the breadcrumbs view.", - "Do not show symbols in the breadcrumbs view.", - "Only show the current symbol in the breadcrumbs view.", - "Controls how symbols are sorted in the breadcrumbs outline view.", - "Show symbol outline in file position order.", - "Show symbol outline in alphabetical order.", - "Show symbol outline in symbol type order.", - "Render breadcrumb items with icons.", - "When enabled breadcrumbs show `file`-symbols.", - "When enabled breadcrumbs show `module`-symbols.", - "When enabled breadcrumbs show `namespace`-symbols.", - "When enabled breadcrumbs show `package`-symbols.", - "When enabled breadcrumbs show `class`-symbols.", - "When enabled breadcrumbs show `method`-symbols.", - "When enabled breadcrumbs show `property`-symbols.", - "When enabled breadcrumbs show `field`-symbols.", - "When enabled breadcrumbs show `constructor`-symbols.", - "When enabled breadcrumbs show `enum`-symbols.", - "When enabled breadcrumbs show `interface`-symbols.", - "When enabled breadcrumbs show `function`-symbols.", - "When enabled breadcrumbs show `variable`-symbols.", - "When enabled breadcrumbs show `constant`-symbols.", - "When enabled breadcrumbs show `string`-symbols.", - "When enabled breadcrumbs show `number`-symbols.", - "When enabled breadcrumbs show `boolean`-symbols.", - "When enabled breadcrumbs show `array`-symbols.", - "When enabled breadcrumbs show `object`-symbols.", - "When enabled breadcrumbs show `key`-symbols.", - "When enabled breadcrumbs show `null`-symbols.", - "When enabled breadcrumbs show `enumMember`-symbols.", - "When enabled breadcrumbs show `struct`-symbols.", - "When enabled breadcrumbs show `event`-symbols.", - "When enabled breadcrumbs show `operator`-symbols.", - "When enabled breadcrumbs show `typeParameter`-symbols." + "vs/workbench/browser/parts/editor/breadcrumbsControl": [ + "Icon for the separator in the breadcrumbs.", + "Whether the editor can show breadcrumbs", + "Whether breadcrumbs are currently visible", + "Whether breadcrumbs have focus", + "no elements", + "Toggle Breadcrumbs", + "Toggle &&Breadcrumbs", + "Breadcrumbs", + "&&Breadcrumbs", + "Focus and Select Breadcrumbs", + "Focus Breadcrumbs" ], - "vs/workbench/browser/parts/editor/breadcrumbsPicker": [ - "Breadcrumbs" + "vs/workbench/browser/parts/compositeBar": [ + "Active View Switcher" ], "vs/platform/quickinput/browser/quickInputUtils": [ "Click to execute command '{0}'" @@ -29792,6 +30020,11 @@ "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar": [ "More..." ], + "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ + "Outputs are collapsed", + "Double-click to expand cell output ({0})", + "Expand Cell Output (${0})" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ "1 cell hidden", "{0} cells hidden" @@ -29800,11 +30033,6 @@ "Double-click to expand cell input ({0})", "Expand Cell Input ({0})" ], - "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ - "Outputs are collapsed", - "Double-click to expand cell output ({0})", - "Expand Cell Output (${0})" - ], "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ "Suggest", "{0}{1}, {2}", @@ -29817,6 +30045,10 @@ "{0}, use ({1}) for accessibility help", "{0}, run the command Open Accessibility Help which is currently not triggerable via keybinding." ], + "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ + "Could not kill process listening on port {0}, command exited with error {1}", + "Restarting the terminal because the connection to the shell process was lost..." + ], "vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick": [ "Remove from Command History", "View Command Output", @@ -29826,9 +30058,55 @@ "Select a directory to go to (hold Option-key to edit the command)", "Select a directory to go to (hold Alt-key to edit the command)" ], - "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ - "Could not kill process listening on port {0}, command exited with error {1}", - "Restarting the terminal because the connection to the shell process was lost..." + "vs/workbench/browser/parts/editor/editorTabsControl": [ + "Editor actions", + "{0} (+{1})" + ], + "vs/workbench/browser/parts/editor/breadcrumbs": [ + "Breadcrumb Navigation", + "Enable/disable navigation breadcrumbs.", + "Controls whether and how file paths are shown in the breadcrumbs view.", + "Show the file path in the breadcrumbs view.", + "Do not show the file path in the breadcrumbs view.", + "Only show the last element of the file path in the breadcrumbs view.", + "Controls whether and how symbols are shown in the breadcrumbs view.", + "Show all symbols in the breadcrumbs view.", + "Do not show symbols in the breadcrumbs view.", + "Only show the current symbol in the breadcrumbs view.", + "Controls how symbols are sorted in the breadcrumbs outline view.", + "Show symbol outline in file position order.", + "Show symbol outline in alphabetical order.", + "Show symbol outline in symbol type order.", + "Render breadcrumb items with icons.", + "When enabled breadcrumbs show `file`-symbols.", + "When enabled breadcrumbs show `module`-symbols.", + "When enabled breadcrumbs show `namespace`-symbols.", + "When enabled breadcrumbs show `package`-symbols.", + "When enabled breadcrumbs show `class`-symbols.", + "When enabled breadcrumbs show `method`-symbols.", + "When enabled breadcrumbs show `property`-symbols.", + "When enabled breadcrumbs show `field`-symbols.", + "When enabled breadcrumbs show `constructor`-symbols.", + "When enabled breadcrumbs show `enum`-symbols.", + "When enabled breadcrumbs show `interface`-symbols.", + "When enabled breadcrumbs show `function`-symbols.", + "When enabled breadcrumbs show `variable`-symbols.", + "When enabled breadcrumbs show `constant`-symbols.", + "When enabled breadcrumbs show `string`-symbols.", + "When enabled breadcrumbs show `number`-symbols.", + "When enabled breadcrumbs show `boolean`-symbols.", + "When enabled breadcrumbs show `array`-symbols.", + "When enabled breadcrumbs show `object`-symbols.", + "When enabled breadcrumbs show `key`-symbols.", + "When enabled breadcrumbs show `null`-symbols.", + "When enabled breadcrumbs show `enumMember`-symbols.", + "When enabled breadcrumbs show `struct`-symbols.", + "When enabled breadcrumbs show `event`-symbols.", + "When enabled breadcrumbs show `operator`-symbols.", + "When enabled breadcrumbs show `typeParameter`-symbols." + ], + "vs/workbench/browser/parts/editor/breadcrumbsPicker": [ + "Breadcrumbs" ], "vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput": [ "Cell has no output", @@ -29955,7 +30233,6 @@ "vs/editor/contrib/folding/browser/folding", "vs/editor/contrib/folding/browser/foldingDecorations", "vs/editor/contrib/fontZoom/browser/fontZoom", - "vs/editor/contrib/format/browser/format", "vs/editor/contrib/format/browser/formatActions", "vs/editor/contrib/gotoError/browser/gotoError", "vs/editor/contrib/gotoError/browser/gotoErrorWidget", @@ -30097,7 +30374,6 @@ "vs/workbench/browser/actions/workspaceActions", "vs/workbench/browser/actions/workspaceCommands", "vs/workbench/browser/editor", - "vs/workbench/browser/parts/activitybar/activitybarActions", "vs/workbench/browser/parts/activitybar/activitybarPart", "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions", "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart", @@ -30119,6 +30395,7 @@ "vs/workbench/browser/parts/editor/editorGroupView", "vs/workbench/browser/parts/editor/editorGroupWatermark", "vs/workbench/browser/parts/editor/editorPanes", + "vs/workbench/browser/parts/editor/editorParts", "vs/workbench/browser/parts/editor/editorPlaceholder", "vs/workbench/browser/parts/editor/editorQuickAccess", "vs/workbench/browser/parts/editor/editorStatus", @@ -30128,6 +30405,7 @@ "vs/workbench/browser/parts/editor/textCodeEditor", "vs/workbench/browser/parts/editor/textDiffEditor", "vs/workbench/browser/parts/editor/textEditor", + "vs/workbench/browser/parts/globalCompositeBar", "vs/workbench/browser/parts/notifications/notificationsActions", "vs/workbench/browser/parts/notifications/notificationsAlerts", "vs/workbench/browser/parts/notifications/notificationsCenter", @@ -30136,9 +30414,12 @@ "vs/workbench/browser/parts/notifications/notificationsStatus", "vs/workbench/browser/parts/notifications/notificationsToasts", "vs/workbench/browser/parts/notifications/notificationsViewer", + "vs/workbench/browser/parts/paneCompositeBar", + "vs/workbench/browser/parts/paneCompositePart", "vs/workbench/browser/parts/panel/panelActions", "vs/workbench/browser/parts/panel/panelPart", "vs/workbench/browser/parts/sidebar/sidebarActions", + "vs/workbench/browser/parts/sidebar/sidebarPart", "vs/workbench/browser/parts/statusbar/statusbarActions", "vs/workbench/browser/parts/statusbar/statusbarPart", "vs/workbench/browser/parts/titlebar/commandCenterControl", @@ -30165,8 +30446,10 @@ "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration", "vs/workbench/contrib/accessibility/browser/accessibilityContributions", "vs/workbench/contrib/accessibility/browser/accessibilityStatus", + "vs/workbench/contrib/accessibility/browser/accessibleNotificationService", "vs/workbench/contrib/accessibility/browser/accessibleView", "vs/workbench/contrib/accessibility/browser/accessibleViewActions", + "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution", "vs/workbench/contrib/audioCues/browser/audioCues.contribution", "vs/workbench/contrib/audioCues/browser/commands", "vs/workbench/contrib/bulkEdit/browser/bulkEditService", @@ -30194,13 +30477,11 @@ "vs/workbench/contrib/chat/browser/chatInputPart", "vs/workbench/contrib/chat/browser/chatListRenderer", "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget", + "vs/workbench/contrib/chat/browser/codeBlockPart", "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib", - "vs/workbench/contrib/chat/common/chatAgents", "vs/workbench/contrib/chat/common/chatColors", "vs/workbench/contrib/chat/common/chatContextKeys", "vs/workbench/contrib/chat/common/chatServiceImpl", - "vs/workbench/contrib/chat/common/chatSlashCommands", - "vs/workbench/contrib/chat/common/chatViewModel", "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions", "vs/workbench/contrib/codeActions/browser/codeActionsContribution", "vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint", @@ -30348,7 +30629,6 @@ "vs/workbench/contrib/files/browser/fileConstants", "vs/workbench/contrib/files/browser/fileImportExport", "vs/workbench/contrib/files/browser/files.contribution", - "vs/workbench/contrib/files/browser/views/emptyView", "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider", "vs/workbench/contrib/files/browser/views/explorerView", "vs/workbench/contrib/files/browser/views/explorerViewer", @@ -30364,6 +30644,7 @@ "vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty", "vs/workbench/contrib/inlineChat/browser/inlineChatActions", "vs/workbench/contrib/inlineChat/browser/inlineChatController", + "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations", "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies", "vs/workbench/contrib/inlineChat/browser/inlineChatWidget", "vs/workbench/contrib/inlineChat/common/inlineChat", @@ -30548,6 +30829,7 @@ "vs/workbench/contrib/snippets/browser/snippets.contribution", "vs/workbench/contrib/snippets/browser/snippetsFile", "vs/workbench/contrib/snippets/browser/snippetsService", + "vs/workbench/contrib/speech/common/speechService", "vs/workbench/contrib/surveys/browser/ces.contribution", "vs/workbench/contrib/surveys/browser/languageSurveys.contribution", "vs/workbench/contrib/surveys/browser/nps.contribution", @@ -30683,6 +30965,7 @@ "vs/workbench/services/actions/common/menusExtensionPoint", "vs/workbench/services/assignment/common/assignmentService", "vs/workbench/services/authentication/browser/authenticationService", + "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService", "vs/workbench/services/configuration/browser/configurationService", "vs/workbench/services/configuration/common/configurationEditing", "vs/workbench/services/configuration/common/jsonEditingService", @@ -30728,6 +31011,7 @@ "vs/workbench/services/preferences/common/preferencesModels", "vs/workbench/services/preferences/common/preferencesValidation", "vs/workbench/services/progress/browser/progressService", + "vs/workbench/services/remote/common/remoteExplorerService", "vs/workbench/services/remote/common/tunnelModel", "vs/workbench/services/remote/electron-sandbox/remoteAgentService", "vs/workbench/services/search/common/queryBuilder", @@ -30765,8 +31049,6 @@ "vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService", "vs/workbench/services/userDataSync/common/userDataSync", "vs/workbench/services/views/browser/viewDescriptorService", - "vs/workbench/services/views/common/viewContainerModel", - "vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService", "vs/workbench/services/workingCopy/common/fileWorkingCopyManager", "vs/workbench/services/workingCopy/common/storedFileWorkingCopy", "vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager", @@ -30801,6 +31083,7 @@ "vs/platform/theme/common/iconRegistry", "vs/platform/workspace/common/workspace", "vs/workbench/api/common/extHostChat", + "vs/workbench/api/common/extHostChatAgents2", "vs/workbench/api/common/extHostDiagnostics", "vs/workbench/api/common/extHostExtensionService", "vs/workbench/api/common/extHostLanguageFeatures", @@ -30869,6 +31152,7 @@ "vs/platform/userDataProfile/common/userDataProfile", "vs/platform/workspace/common/workspace", "vs/workbench/api/common/extHostChat", + "vs/workbench/api/common/extHostChatAgents2", "vs/workbench/api/common/extHostDiagnostics", "vs/workbench/api/common/extHostExtensionService", "vs/workbench/api/common/extHostLanguageFeatures", @@ -30972,7 +31256,8 @@ "vs/base/common/actions", "vs/base/common/platform", "vs/code/electron-sandbox/issue/issueReporterPage", - "vs/code/electron-sandbox/issue/issueReporterService" + "vs/code/electron-sandbox/issue/issueReporterService", + "vs/platform/theme/common/iconRegistry" ], "vs/code/node/sharedProcess/sharedProcessMain": [ "vs/base/common/date", diff --git a/packages/editor/src/browser/editor-generated-preference-schema.ts b/packages/editor/src/browser/editor-generated-preference-schema.ts index 7b164b19d5fcf..37d5749bee459 100644 --- a/packages/editor/src/browser/editor-generated-preference-schema.ts +++ b/packages/editor/src/browser/editor-generated-preference-schema.ts @@ -272,9 +272,9 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "off" ], "enumDescriptions": [ - nls.localizeByDefault('Use platform APIs to detect when a Screen Reader is attached'), - nls.localizeByDefault("Optimize for usage with a Screen Reader"), - nls.localizeByDefault("Assume a screen reader is not attached") + nls.localizeByDefault('Use platform APIs to detect when a Screen Reader is attached.'), + nls.localizeByDefault("Optimize for usage with a Screen Reader."), + nls.localizeByDefault("Assume a screen reader is not attached.") ], "default": "auto", "description": nls.localizeByDefault("Controls if the UI should run in a mode where it is optimized for screen readers."), diff --git a/packages/navigator/src/browser/navigator-widget.tsx b/packages/navigator/src/browser/navigator-widget.tsx index bf193fd0572de..c4a2ae3857cb3 100644 --- a/packages/navigator/src/browser/navigator-widget.tsx +++ b/packages/navigator/src/browser/navigator-widget.tsx @@ -30,7 +30,7 @@ import { nls } from '@theia/core/lib/common/nls'; import { AbstractNavigatorTreeWidget } from './abstract-navigator-tree-widget'; export const FILE_NAVIGATOR_ID = 'files'; -export const LABEL = nls.localizeByDefault('No Folder Opened'); +export const LABEL = nls.localize('theia/navigator/noFolderOpened', 'No Folder Opened'); export const CLASS = 'theia-Files'; @injectable() From f55710d25992c292ccc5782267f150a393ae2c89 Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Wed, 20 Dec 2023 23:08:21 -0800 Subject: [PATCH 020/441] Deploy plugins asynchronously (#13134) - Make `initialize` method in `PluginDeployerContribution` sync to continue with backend loading while plugins are deployed - Fix wrong performance measurements of `resolvePlugins` & `deployPlugins` & in `PluginDeployerImpl` - Improve performance logging in `HostedPluginSupport` by only logging relevent measurements of `sync/`load` and `start` plugins (i.e. if the plugin count is 0 just stop the measurement but don`t log) - Update `I18nPreloadContribution` to ensure that we only load the localizations from the backend if the current locale is not equal to the default locale --- .../preload/i18n-preload-contribution.ts | 2 +- .../src/hosted/browser/hosted-plugin.ts | 22 ++++++++++++++----- .../main/node/plugin-deployer-contribution.ts | 4 ++-- .../src/main/node/plugin-deployer-impl.ts | 3 ++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/core/src/browser/preload/i18n-preload-contribution.ts b/packages/core/src/browser/preload/i18n-preload-contribution.ts index 309c8df5f9436..0cddfa112e723 100644 --- a/packages/core/src/browser/preload/i18n-preload-contribution.ts +++ b/packages/core/src/browser/preload/i18n-preload-contribution.ts @@ -33,7 +33,7 @@ export class I18nPreloadContribution implements PreloadContribution { locale: defaultLocale }); } - if (nls.locale) { + if (nls.locale && nls.locale !== nls.defaultLocale) { const localization = await this.localizationServer.loadLocalization(nls.locale); if (localization.languagePack) { nls.localization = localization; diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 5772914b3ac19..fc454e5a58966 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -401,8 +401,12 @@ export class HostedPluginSupport { waitPluginsMeasurement.error('Backend deployment failed.'); } } - - syncPluginsMeasurement?.log(`Sync of ${this.getPluginCount(initialized)}`); + if (initialized > 0) { + // Only log sync measurement if there are were plugins to sync. + syncPluginsMeasurement?.log(`Sync of ${this.getPluginCount(initialized)}`); + } else { + syncPluginsMeasurement.stop(); + } } /** @@ -440,8 +444,12 @@ export class HostedPluginSupport { })); } } - - loadPluginsMeasurement.log(`Load contributions of ${this.getPluginCount(loaded)}`); + if (loaded > 0) { + // Only log load measurement if there are were plugins to load. + loadPluginsMeasurement?.log(`Load contributions of ${this.getPluginCount(loaded)}`); + } else { + loadPluginsMeasurement.stop(); + } return hostContributions; } @@ -512,7 +520,11 @@ export class HostedPluginSupport { return; } - startPluginsMeasurement.log(`Start of ${this.getPluginCount(started)}`); + if (started > 0) { + startPluginsMeasurement.log(`Start of ${this.getPluginCount(started)}`); + } else { + startPluginsMeasurement.stop(); + } } protected async obtainManager(host: string, hostContributions: PluginContributions[], toDisconnect: DisposableCollection): Promise { diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts index 053ce001bb9df..d5c9f38d7aab9 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts @@ -28,7 +28,7 @@ export class PluginDeployerContribution implements BackendApplicationContributio @inject(PluginDeployer) protected pluginDeployer: PluginDeployer; - initialize(): Promise { - return this.pluginDeployer.start(); + initialize(): void { + this.pluginDeployer.start().catch(error => this.logger.error('Initializing plugin deployer failed.', error)); } } diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts index 0fe63e7b5f24f..1eb032c397741 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts @@ -130,8 +130,9 @@ export class PluginDeployerImpl implements PluginDeployer { id, type: PluginType.System })); + const resolvePlugins = this.measure('resolvePlugins'); const plugins = await this.resolvePlugins([...unresolvedUserEntries, ...unresolvedSystemEntries]); - deployPlugins.log('Resolve plugins list'); + resolvePlugins.log('Resolve plugins list'); await this.deployPlugins(plugins); deployPlugins.log('Deploy plugins list'); } From a20db9252392b32e7318feec5113680762daf2a6 Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Thu, 21 Dec 2023 11:05:45 +0100 Subject: [PATCH 021/441] fix: revise vscode extension directories (#13178) This patch eliminates the use of the temporary directories vscode-unpacked and vscode-copied for installing and deploying vscode extensions. The file-handler and directory handler for vscode extensions have been adjusted accordingly: * A temporary directory is now only used for downloading extensions from the registry. This temporary directory is now user-specific and resides within the configdir (i.e., per default in the user's home: /.theia/tmp/) to avoid clashes and permission issues on multi-user operating systems that share temporary directories, such as Linux or BSDs. Having this temporary directory in a location that is configurable by the user also seems the more sensible approach when extensions are considered confidential data. * $configDir/deployedPlugins replaces our volatile /tmp/vscode-copied deployment directory. Having a more permanent way of handling installed extensions should improve startup time and reduce issues with multiple instances running in parallel. * The local file resolver unpacks the vsix file from the temp dir into $configDir/deployedPlugins/. * The simplified directory handler loads unpacked extensions directly from $configDir/deployedPlugins/. * We use $configDir/extensions as a location for the user to drop vsix files that will be installed to the deployment location automatically on startup. We do not manage or remove files within $configDir/extensions. Overall, this should improve the stability on systems with shared temp dir locations and reduce the startup of the first application start after a reboot. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- CHANGELOG.md | 4 + packages/plugin-ext-vscode/package.json | 1 + .../src/common/plugin-vscode-environment.ts | 33 +++++- ...ocal-vsix-file-plugin-deployer-resolver.ts | 32 ++---- .../plugin-vscode-deployer-participant.ts | 22 +++- .../node/plugin-vscode-directory-handler.ts | 49 ++------- .../src/node/plugin-vscode-file-handler.ts | 61 ++++------- .../src/node/plugin-vscode-utils.ts | 101 ++++++++++++++++++ .../main/node/plugin-deployer-contribution.ts | 3 +- .../src/node/vsx-extension-resolver.ts | 25 +++-- 10 files changed, 208 insertions(+), 123 deletions(-) create mode 100644 packages/plugin-ext-vscode/src/node/plugin-vscode-utils.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dbc881f452a9..933afb251a78a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - [terminal] Use application shell methods for expanding/collapsing bottom panel for "Terminal: Toggle Terminal" command [#13131](https://github.com/eclipse-theia/theia/pull/13131) - [workspace] Create an empty workspace if no workspace is active on updateWorkspaceFolders [#13181](https://github.com/eclipse-theia/theia/pull/13181) - contributed on behalf of STMicroelectronics +[Breaking Changes:](#breaking_changes_1.45.0) + +- [plugin] handling of vscode extension locations has changed: deployment dir switched to `$CONFDIR/deployedPlugin`, `.vsix` files from `$CONFDIR/extensions` are deployed automatically [#13178](https://github.com/eclipse-theia/theia/pull/13178) - Contributed on behalf of STMicroelectronics + ## v1.44.0 - 11/30/2023 - [application-manager] added option to copy `trash` dependency to the bundle [#13112](https://github.com/eclipse-theia/theia/pull/13112) diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 7451191db6c63..5723f802ce95d 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -16,6 +16,7 @@ "@theia/typehierarchy": "1.44.0", "@theia/userstorage": "1.44.0", "@theia/workspace": "1.44.0", + "decompress": "^4.2.1", "filenamify": "^4.1.0" }, "publishConfig": { diff --git a/packages/plugin-ext-vscode/src/common/plugin-vscode-environment.ts b/packages/plugin-ext-vscode/src/common/plugin-vscode-environment.ts index 16601eac78174..6e534f81c245b 100644 --- a/packages/plugin-ext-vscode/src/common/plugin-vscode-environment.ts +++ b/packages/plugin-ext-vscode/src/common/plugin-vscode-environment.ts @@ -24,13 +24,36 @@ export class PluginVSCodeEnvironment { @inject(EnvVariablesServer) protected readonly environments: EnvVariablesServer; - protected _extensionsDirUri: URI | undefined; - async getExtensionsDirUri(): Promise { - if (!this._extensionsDirUri) { + protected _userExtensionsDirUri: URI | undefined; + protected _deployedPluginsUri: URI | undefined; + protected _tmpDirUri: URI | undefined; + + async getUserExtensionsDirUri(): Promise { + if (!this._userExtensionsDirUri) { + const configDir = new URI(await this.environments.getConfigDirUri()); + this._userExtensionsDirUri = configDir.resolve('extensions'); + } + return this._userExtensionsDirUri; + } + + async getDeploymentDirUri(): Promise { + if (!this._deployedPluginsUri) { const configDir = new URI(await this.environments.getConfigDirUri()); - this._extensionsDirUri = configDir.resolve('extensions'); + this._deployedPluginsUri = configDir.resolve('deployedPlugins'); } - return this._extensionsDirUri; + return this._deployedPluginsUri; } + async getTempDirUri(prefix?: string): Promise { + if (!this._tmpDirUri) { + const configDir: URI = new URI(await this.environments.getConfigDirUri()); + this._tmpDirUri = configDir.resolve('tmp'); + } + + if (prefix) { + return this._tmpDirUri.resolve(prefix); + } + + return this._tmpDirUri; + } } diff --git a/packages/plugin-ext-vscode/src/node/local-vsix-file-plugin-deployer-resolver.ts b/packages/plugin-ext-vscode/src/node/local-vsix-file-plugin-deployer-resolver.ts index a151ea26395bf..5de67f2206fbb 100644 --- a/packages/plugin-ext-vscode/src/node/local-vsix-file-plugin-deployer-resolver.ts +++ b/packages/plugin-ext-vscode/src/node/local-vsix-file-plugin-deployer-resolver.ts @@ -14,18 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import * as fs from '@theia/core/shared/fs-extra'; import * as path from 'path'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { FileUri } from '@theia/core/lib/node'; import { PluginDeployerResolverContext } from '@theia/plugin-ext'; import { LocalPluginDeployerResolver } from '@theia/plugin-ext/lib/main/node/resolvers/local-plugin-deployer-resolver'; import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment'; import { isVSCodePluginFile } from './plugin-vscode-file-handler'; +import { existsInDeploymentDir, unpackToDeploymentDir } from './plugin-vscode-utils'; @injectable() export class LocalVSIXFilePluginDeployerResolver extends LocalPluginDeployerResolver { static LOCAL_FILE = 'local-file'; + static FILE_EXTENSION = '.vsix'; @inject(PluginVSCodeEnvironment) protected readonly environment: PluginVSCodeEnvironment; @@ -38,28 +38,14 @@ export class LocalVSIXFilePluginDeployerResolver extends LocalPluginDeployerReso } async resolveFromLocalPath(pluginResolverContext: PluginDeployerResolverContext, localPath: string): Promise { - const fileName = path.basename(localPath); - const pathInUserExtensionsDirectory = await this.ensureDiscoverability(localPath); - pluginResolverContext.addPlugin(fileName, pathInUserExtensionsDirectory); - } + const extensionId = path.basename(localPath, LocalVSIXFilePluginDeployerResolver.FILE_EXTENSION); - /** - * Ensures that a user-installed plugin file is transferred to the user extension folder. - */ - protected async ensureDiscoverability(localPath: string): Promise { - const userExtensionsDir = await this.environment.getExtensionsDirUri(); - if (!userExtensionsDir.isEqualOrParent(FileUri.create(localPath))) { - try { - const newPath = FileUri.fsPath(userExtensionsDir.resolve(path.basename(localPath))); - await fs.mkdirp(FileUri.fsPath(userExtensionsDir)); - await new Promise((resolve, reject) => { - fs.copyFile(localPath, newPath, error => error ? reject(error) : resolve()); - }); - return newPath; - } catch (e) { - console.warn(`Problem copying plugin at ${localPath}:`, e); - } + if (await existsInDeploymentDir(this.environment, extensionId)) { + console.log(`[${pluginResolverContext.getOriginId()}]: Target dir already exists in plugin deployment dir`); + return; } - return localPath; + + const extensionDeploymentDir = await unpackToDeploymentDir(this.environment, localPath, extensionId); + pluginResolverContext.addPlugin(extensionId, extensionDeploymentDir); } } diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-deployer-participant.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-deployer-participant.ts index 5508feda6d79f..aefa68b3f7b5b 100644 --- a/packages/plugin-ext-vscode/src/node/plugin-vscode-deployer-participant.ts +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-deployer-participant.ts @@ -15,8 +15,11 @@ // ***************************************************************************** import { injectable, inject } from '@theia/core/shared/inversify'; +import * as fs from '@theia/core/shared/fs-extra'; +import { FileUri } from '@theia/core/lib/node'; import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment'; import { PluginDeployerParticipant, PluginDeployerStartContext } from '@theia/plugin-ext/lib/common/plugin-protocol'; +import { LocalVSIXFilePluginDeployerResolver } from './local-vsix-file-plugin-deployer-resolver'; @injectable() export class PluginVSCodeDeployerParticipant implements PluginDeployerParticipant { @@ -25,8 +28,21 @@ export class PluginVSCodeDeployerParticipant implements PluginDeployerParticipan protected readonly environments: PluginVSCodeEnvironment; async onWillStart(context: PluginDeployerStartContext): Promise { - const extensionsDirUri = await this.environments.getExtensionsDirUri(); - context.userEntries.push(extensionsDirUri.withScheme('local-dir').toString()); - } + const extensionDeploymentDirUri = await this.environments.getDeploymentDirUri(); + context.userEntries.push(extensionDeploymentDirUri.withScheme('local-dir').toString()); + + const userExtensionDirUri = await this.environments.getUserExtensionsDirUri(); + const userExtensionDirPath = FileUri.fsPath(userExtensionDirUri); + if (await fs.pathExists(userExtensionDirPath)) { + const files = await fs.readdir(userExtensionDirPath); + for (const file of files) { + if (file.endsWith(LocalVSIXFilePluginDeployerResolver.FILE_EXTENSION)) { + const extensionUri = userExtensionDirUri.resolve(file).withScheme('local-file').toString(); + console.log(`found drop-in extension "${extensionUri}"`); + context.userEntries.push(extensionUri); + } + } + } + } } diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-directory-handler.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-directory-handler.ts index f135aead0413e..bcbce5d3fc0cb 100644 --- a/packages/plugin-ext-vscode/src/node/plugin-vscode-directory-handler.ts +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-directory-handler.ts @@ -15,18 +15,16 @@ // ***************************************************************************** import * as path from 'path'; -import * as filenamify from 'filenamify'; import * as fs from '@theia/core/shared/fs-extra'; import { inject, injectable } from '@theia/core/shared/inversify'; import type { RecursivePartial, URI } from '@theia/core'; import { Deferred, firstTrue } from '@theia/core/lib/common/promise-util'; -import { getTempDirPathAsync } from '@theia/plugin-ext/lib/main/node/temp-dir-util'; import { PluginDeployerDirectoryHandler, PluginDeployerEntry, PluginDeployerDirectoryHandlerContext, - PluginDeployerEntryType, PluginPackage, PluginType, PluginIdentifiers + PluginDeployerEntryType, PluginPackage, PluginIdentifiers } from '@theia/plugin-ext'; -import { FileUri } from '@theia/core/lib/node'; import { PluginCliContribution } from '@theia/plugin-ext/lib/main/node/plugin-cli-contribution'; +import { TMP_DIR_PREFIX } from './plugin-vscode-utils'; @injectable() export class PluginVsCodeDirectoryHandler implements PluginDeployerDirectoryHandler { @@ -35,14 +33,12 @@ export class PluginVsCodeDirectoryHandler implements PluginDeployerDirectoryHand @inject(PluginCliContribution) protected readonly pluginCli: PluginCliContribution; - constructor() { - this.deploymentDirectory = new Deferred(); - getTempDirPathAsync('vscode-copied') - .then(deploymentDirectoryPath => this.deploymentDirectory.resolve(FileUri.create(deploymentDirectoryPath))); - } - async accept(plugin: PluginDeployerEntry): Promise { console.debug(`Resolving "${plugin.id()}" as a VS Code extension...`); + if (plugin.path().startsWith(TMP_DIR_PREFIX)) { + // avoid adding corrupted plugins from temporary directories + return false; + } return this.attemptResolution(plugin); } @@ -62,7 +58,6 @@ export class PluginVsCodeDirectoryHandler implements PluginDeployerDirectoryHand } async handle(context: PluginDeployerDirectoryHandlerContext): Promise { - await this.copyDirectory(context); const types: PluginDeployerEntryType[] = []; const packageJson: PluginPackage = context.pluginEntry().getValue('package.json'); if (packageJson.browser) { @@ -74,33 +69,6 @@ export class PluginVsCodeDirectoryHandler implements PluginDeployerDirectoryHand context.pluginEntry().accept(...types); } - protected async copyDirectory(context: PluginDeployerDirectoryHandlerContext): Promise { - if (this.pluginCli.copyUncompressedPlugins() && context.pluginEntry().type === PluginType.User) { - const entry = context.pluginEntry(); - const id = entry.id(); - const pathToRestore = entry.path(); - const origin = entry.originalPath(); - const targetDir = await this.getExtensionDir(context); - try { - if (await fs.pathExists(targetDir) || !entry.path().startsWith(origin)) { - console.log(`[${id}]: already copied.`); - } else { - console.log(`[${id}]: copying to "${targetDir}"`); - const deploymentDirectory = await this.deploymentDirectory.promise; - await fs.mkdirp(FileUri.fsPath(deploymentDirectory)); - await context.copy(origin, targetDir); - entry.updatePath(targetDir); - if (!this.deriveMetadata(entry)) { - throw new Error('Unable to resolve plugin metadata after copying'); - } - } - } catch (e) { - console.warn(`[${id}]: Error when copying.`, e); - entry.updatePath(pathToRestore); - } - } - } - protected async resolveFromSources(plugin: PluginDeployerEntry): Promise { const pluginPath = plugin.path(); const pck = await this.requirePackage(pluginPath); @@ -152,9 +120,4 @@ export class PluginVsCodeDirectoryHandler implements PluginDeployerDirectoryHand return undefined; } } - - protected async getExtensionDir(context: PluginDeployerDirectoryHandlerContext): Promise { - const deploymentDirectory = await this.deploymentDirectory.promise; - return FileUri.fsPath(deploymentDirectory.resolve(filenamify(context.pluginEntry().id(), { replacement: '_' }))); - } } diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts index 23e3e3e9292d3..f3f6459aa95cc 100644 --- a/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts @@ -14,33 +14,21 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { PluginDeployerFileHandler, PluginDeployerEntry, PluginDeployerFileHandlerContext, PluginType } from '@theia/plugin-ext'; -import * as fs from '@theia/core/shared/fs-extra'; -import * as path from 'path'; +import { PluginDeployerFileHandler, PluginDeployerEntry, PluginDeployerFileHandlerContext } from '@theia/plugin-ext'; import * as filenamify from 'filenamify'; -import type { URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { Deferred } from '@theia/core/lib/common/promise-util'; -import { getTempDirPathAsync } from '@theia/plugin-ext/lib/main/node/temp-dir-util'; +import * as fs from '@theia/core/shared/fs-extra'; +import { FileUri } from '@theia/core/lib/node'; import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { unpackToDeploymentDir } from './plugin-vscode-utils'; export const isVSCodePluginFile = (pluginPath?: string) => Boolean(pluginPath && (pluginPath.endsWith('.vsix') || pluginPath.endsWith('.tgz'))); @injectable() export class PluginVsCodeFileHandler implements PluginDeployerFileHandler { - @inject(PluginVSCodeEnvironment) protected readonly environment: PluginVSCodeEnvironment; - private readonly systemExtensionsDirUri: Deferred; - - constructor() { - this.systemExtensionsDirUri = new Deferred(); - getTempDirPathAsync('vscode-unpacked') - .then(systemExtensionsDirPath => this.systemExtensionsDirUri.resolve(FileUri.create(systemExtensionsDirPath))); - } - async accept(resolvedPlugin: PluginDeployerEntry): Promise { return resolvedPlugin.isFile().then(file => { if (!file) { @@ -51,33 +39,24 @@ export class PluginVsCodeFileHandler implements PluginDeployerFileHandler { } async handle(context: PluginDeployerFileHandlerContext): Promise { - const id = context.pluginEntry().id(); - const extensionDir = await this.getExtensionDir(context); - console.log(`[${id}]: trying to decompress into "${extensionDir}"...`); - if (context.pluginEntry().type === PluginType.User && await fs.pathExists(extensionDir)) { - console.log(`[${id}]: already found`); - context.pluginEntry().updatePath(extensionDir); - return; - } - await this.decompress(extensionDir, context); - console.log(`[${id}]: decompressed`); - context.pluginEntry().updatePath(extensionDir); - } - - protected async getExtensionDir(context: PluginDeployerFileHandlerContext): Promise { - const systemExtensionsDirUri = await this.systemExtensionsDirUri.promise; - return FileUri.fsPath(systemExtensionsDirUri.resolve(filenamify(context.pluginEntry().id(), { replacement: '_' }))); - } - - protected async decompress(extensionDir: string, context: PluginDeployerFileHandlerContext): Promise { - await context.unzip(context.pluginEntry().path(), extensionDir); - if (context.pluginEntry().path().endsWith('.tgz')) { - const extensionPath = path.join(extensionDir, 'package'); - const vscodeNodeModulesPath = path.join(extensionPath, 'vscode_node_modules.zip'); - if (await fs.pathExists(vscodeNodeModulesPath)) { - await context.unzip(vscodeNodeModulesPath, path.join(extensionPath, 'node_modules')); + const id = this.getNormalizedExtensionId(context.pluginEntry().id()); + const extensionDeploymentDir = await unpackToDeploymentDir(this.environment, context.pluginEntry().path(), id); + context.pluginEntry().updatePath(extensionDeploymentDir); + console.log(`root path: ${context.pluginEntry().rootPath}`); + const originalPath = context.pluginEntry().originalPath(); + if (originalPath && originalPath !== extensionDeploymentDir) { + const tempDirUri = await this.environment.getTempDirUri(); + if (originalPath.startsWith(FileUri.fsPath(tempDirUri))) { + try { + await fs.remove(FileUri.fsPath(originalPath)); + } catch (e) { + console.error(`[${id}]: failed to remove temporary files: "${originalPath}"`, e); + } } } } + protected getNormalizedExtensionId(pluginId: string): string { + return filenamify(pluginId, { replacement: '_' }).replace(/\.vsix$/, ''); + } } diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-utils.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-utils.ts new file mode 100644 index 0000000000000..6f6b7d6c6d60c --- /dev/null +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-utils.ts @@ -0,0 +1,101 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as decompress from 'decompress'; +import * as path from 'path'; +import * as filenamify from 'filenamify'; +import { FileUri } from '@theia/core/lib/node'; +import * as fs from '@theia/core/shared/fs-extra'; +import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment'; + +export async function decompressExtension(sourcePath: string, destPath: string): Promise { + try { + await decompress(sourcePath, destPath); + if (sourcePath.endsWith('.tgz')) { + // unzip node_modules from built-in extensions, see https://github.com/eclipse-theia/theia/issues/5756 + const extensionPath = path.join(destPath, 'package'); + const vscodeNodeModulesPath = path.join(extensionPath, 'vscode_node_modules.zip'); + if (await fs.pathExists(vscodeNodeModulesPath)) { + await decompress(vscodeNodeModulesPath, path.join(extensionPath, 'node_modules')); + } + } + return true; + } catch (error) { + console.error(`Failed to decompress ${sourcePath} to ${destPath}: ${error}`); + throw error; + } +} + +export async function existsInDeploymentDir(env: PluginVSCodeEnvironment, extensionId: string): Promise { + return fs.pathExists(await getExtensionDeploymentDir(env, extensionId)); +} + +export const TMP_DIR_PREFIX = 'tmp-vscode-unpacked-'; +export async function unpackToDeploymentDir(env: PluginVSCodeEnvironment, sourcePath: string, extensionId: string): Promise { + const extensionDeploymentDir = await getExtensionDeploymentDir(env, extensionId); + if (await fs.pathExists(extensionDeploymentDir)) { + console.log(`[${extensionId}]: deployment dir "${extensionDeploymentDir}" already exists`); + return extensionDeploymentDir; + } + + const tempDir = await getTempDir(env, TMP_DIR_PREFIX); + try { + console.log(`[${extensionId}]: trying to decompress "${sourcePath}" into "${tempDir}"...`); + if (!await decompressExtension(sourcePath, tempDir)) { + await fs.remove(tempDir); + const msg = `[${extensionId}]: decompressing "${sourcePath}" to "${tempDir}" failed`; + console.error(msg); + throw new Error(msg); + } + } catch (e) { + await fs.remove(tempDir); + const msg = `[${extensionId}]: error while decompressing "${sourcePath}" to "${tempDir}"`; + console.error(msg, e); + throw e; + } + console.log(`[${extensionId}]: decompressed to temp dir "${tempDir}"`); + + try { + console.log(`[${extensionId}]: renaming to extension dir "${extensionDeploymentDir}"...`); + await fs.rename(tempDir, extensionDeploymentDir); + return extensionDeploymentDir; + } catch (e) { + await fs.remove(tempDir); + console.error(`[${extensionId}]: error while renaming "${tempDir}" to "${extensionDeploymentDir}"`, e); + throw e; + } +} + +export async function getExtensionDeploymentDir(env: PluginVSCodeEnvironment, extensionId: string): Promise { + const deployedPluginsDirUri = await env.getDeploymentDirUri(); + const normalizedExtensionId = filenamify(extensionId, { replacement: '_' }); + const extensionDeploymentDirPath = FileUri.fsPath(deployedPluginsDirUri.resolve(normalizedExtensionId)); + return extensionDeploymentDirPath; +} + +export async function getTempDir(env: PluginVSCodeEnvironment, prefix: string): Promise { + const deploymentDirPath = FileUri.fsPath(await env.getDeploymentDirUri()); + try { + if (!await fs.pathExists(deploymentDirPath)) { + console.log(`Creating deployment dir ${deploymentDirPath}`); + await fs.mkdirs(deploymentDirPath); + } + return await fs.mkdtemp(path.join(deploymentDirPath, prefix)); + } catch (error) { + console.error(`Failed to create deployment dir ${deploymentDirPath}: ${error}`); + throw error; + } +} diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts index d5c9f38d7aab9..ffc6dafc83ca0 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts @@ -28,7 +28,8 @@ export class PluginDeployerContribution implements BackendApplicationContributio @inject(PluginDeployer) protected pluginDeployer: PluginDeployer; - initialize(): void { + initialize(): Promise { this.pluginDeployer.start().catch(error => this.logger.error('Initializing plugin deployer failed.', error)); + return Promise.resolve(); } } diff --git a/packages/vsx-registry/src/node/vsx-extension-resolver.ts b/packages/vsx-registry/src/node/vsx-extension-resolver.ts index cfe6ab3d09369..cef7c940918a7 100644 --- a/packages/vsx-registry/src/node/vsx-extension-resolver.ts +++ b/packages/vsx-registry/src/node/vsx-extension-resolver.ts @@ -20,6 +20,7 @@ import * as fs from '@theia/core/shared/fs-extra'; import { injectable, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { PluginDeployerHandler, PluginDeployerResolver, PluginDeployerResolverContext, PluginDeployOptions, PluginIdentifiers } from '@theia/plugin-ext/lib/common/plugin-protocol'; +import { FileUri } from '@theia/core/lib/node'; import { VSCodeExtensionUri } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-uri'; import { OVSXClientProvider } from '../common/ovsx-client-provider'; import { OVSXApiFilter, VSXExtensionRaw } from '@theia/ovsx-client'; @@ -41,6 +42,8 @@ export class VSXExtensionResolver implements PluginDeployerResolver { return !!VSCodeExtensionUri.toId(new URI(pluginId)); } + static readonly TEMP_DIR_PREFIX = 'vscode-download'; + async resolve(context: PluginDeployerResolverContext, options?: PluginDeployOptions): Promise { const id = VSCodeExtensionUri.toId(new URI(context.getOriginId())); if (!id) { @@ -74,16 +77,24 @@ export class VSXExtensionResolver implements PluginDeployerResolver { return; } } - const downloadPath = (await this.environment.getExtensionsDirUri()).path.fsPath(); - await fs.ensureDir(downloadPath); - const extensionPath = path.resolve(downloadPath, path.basename(downloadUrl)); - console.log(`[${resolvedId}]: trying to download from "${downloadUrl}"...`, 'to path', downloadPath); - if (!await this.download(downloadUrl, extensionPath)) { + const downloadDir = await this.getTempDir(); + await fs.ensureDir(downloadDir); + const downloadedExtensionPath = path.resolve(downloadDir, path.basename(downloadUrl)); + console.log(`[${resolvedId}]: trying to download from "${downloadUrl}"...`, 'to path', downloadDir); + if (!await this.download(downloadUrl, downloadedExtensionPath)) { console.log(`[${resolvedId}]: not found`); return; } - console.log(`[${resolvedId}]: downloaded to ${extensionPath}"`); - context.addPlugin(resolvedId, extensionPath); + console.log(`[${resolvedId}]: downloaded to ${downloadedExtensionPath}"`); + context.addPlugin(resolvedId, downloadedExtensionPath); + } + + protected async getTempDir(): Promise { + const tempDir = FileUri.fsPath(await this.environment.getTempDirUri(VSXExtensionResolver.TEMP_DIR_PREFIX)); + if (!await fs.pathExists(tempDir)) { + await fs.mkdirs(tempDir); + } + return tempDir; } protected hasSameOrNewerVersion(id: string, extension: VSXExtensionRaw): string | undefined { From 901c80654437593db7c1686e15a51cf8df53de41 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 21 Dec 2023 15:14:24 +0100 Subject: [PATCH 022/441] Allow to rebind messaging services in preload (#13199) --- .../application-manager/src/generator/frontend-generator.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index 7b121dd635b64..d4218f3b174b6 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -92,9 +92,7 @@ function load(container, jsModule) { .then(containerModule => container.load(containerModule.default)); } -async function preload(parent) { - const container = new Container(); - container.parent = parent; +async function preload(container) { try { ${Array.from(frontendPreloadModules.values(), jsModulePath => `\ await load(container, ${this.importOrRequire()}('${jsModulePath}'));`).join(EOL)} From 118e514a8073166cd3d6293a47c0a4ce14831e3b Mon Sep 17 00:00:00 2001 From: Arek Zaluski Date: Thu, 21 Dec 2023 14:29:04 +0000 Subject: [PATCH 023/441] fix: debug configuration provider replaces different provider (#13196) Signed-off-by: arekzaluski --- .../src/main/browser/debug/plugin-debug-service.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/plugin-ext/src/main/browser/debug/plugin-debug-service.ts b/packages/plugin-ext/src/main/browser/debug/plugin-debug-service.ts index 6d64adad17f0f..fb612bb3ea6ab 100644 --- a/packages/plugin-ext/src/main/browser/debug/plugin-debug-service.ts +++ b/packages/plugin-ext/src/main/browser/debug/plugin-debug-service.ts @@ -97,6 +97,13 @@ export class PluginDebugService implements DebugService { }, 100); registerDebugConfigurationProvider(provider: PluginDebugConfigurationProvider): Disposable { + if (this.configurationProviders.has(provider.handle)) { + const configuration = this.configurationProviders.get(provider.handle); + if (configuration && configuration.type !== provider.type) { + console.warn(`Different debug configuration provider with type '${configuration.type}' already registered.`); + provider.handle = this.configurationProviders.size; + } + } const handle = provider.handle; this.configurationProviders.set(handle, provider); this.fireOnDidConfigurationProvidersChanged(); From 0055c8cb129e2b5ff5747d2afe072a388307e0b8 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 21 Dec 2023 16:50:31 +0100 Subject: [PATCH 024/441] Fix `onView` activation event generation (#13091) --- .../hosted/node/plugin-activation-events.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/plugin-ext/src/hosted/node/plugin-activation-events.ts b/packages/plugin-ext/src/hosted/node/plugin-activation-events.ts index a95b5138dd2d5..ddcec451da5a8 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-activation-events.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-activation-events.ts @@ -20,6 +20,7 @@ import { PluginPackage, PluginPackageAuthenticationProvider, PluginPackageCommand, + PluginPackageContribution, PluginPackageCustomEditor, PluginPackageLanguageContribution, PluginPackageNotebook, @@ -31,7 +32,7 @@ import { * This function will update the manifest based on the plugin contributions. */ export function updateActivationEvents(manifest: PluginPackage): void { - if (!isObject(manifest) || !isObject(manifest.contributes) || !manifest.contributes) { + if (!isObject(manifest) || !isObject(manifest.contributes) || !manifest.contributes) { return; } @@ -42,8 +43,8 @@ export function updateActivationEvents(manifest: PluginPackage): void { const commands = Array.isArray(value) ? value : [value]; updateCommandsContributions(commands, activationEvents); } - if (Array.isArray(manifest.contributes.views)) { - const views = flatten(Object.values(manifest.contributes.views)) as PluginPackageView[]; + if (isObject(manifest.contributes.views)) { + const views = flatten(Object.values(manifest.contributes.views)); updateViewsContribution(views, activationEvents); } if (Array.isArray(manifest.contributes.customEditors)) { @@ -64,7 +65,7 @@ export function updateActivationEvents(manifest: PluginPackage): void { function updateViewsContribution(views: PluginPackageView[], activationEvents: Set): void { for (const view of views) { - if (isObject(view) && typeof view.id === 'string') { + if (isObject(view) && typeof view.id === 'string') { activationEvents.add(`onView:${view.id}`); } } @@ -72,7 +73,7 @@ function updateViewsContribution(views: PluginPackageView[], activationEvents: S function updateCustomEditorsContribution(customEditors: PluginPackageCustomEditor[], activationEvents: Set): void { for (const customEditor of customEditors) { - if (isObject(customEditor) && typeof customEditor.viewType === 'string') { + if (isObject(customEditor) && typeof customEditor.viewType === 'string') { activationEvents.add(`onCustomEditor:${customEditor.viewType}`); } } @@ -80,7 +81,7 @@ function updateCustomEditorsContribution(customEditors: PluginPackageCustomEdito function updateCommandsContributions(commands: PluginPackageCommand[], activationEvents: Set): void { for (const command of commands) { - if (isObject(command) && typeof command.command === 'string') { + if (isObject(command) && typeof command.command === 'string') { activationEvents.add(`onCommand:${command.command}`); } } @@ -88,7 +89,7 @@ function updateCommandsContributions(commands: PluginPackageCommand[], activatio function updateAuthenticationProviderContributions(authProviders: PluginPackageAuthenticationProvider[], activationEvents: Set): void { for (const authProvider of authProviders) { - if (isObject(authProvider) && typeof authProvider.id === 'string') { + if (isObject(authProvider) && typeof authProvider.id === 'string') { activationEvents.add(`onAuthenticationRequest:${authProvider.id}`); } } @@ -96,7 +97,7 @@ function updateAuthenticationProviderContributions(authProviders: PluginPackageA function updateLanguageContributions(languages: PluginPackageLanguageContribution[], activationEvents: Set): void { for (const language of languages) { - if (isObject(language) && typeof language.id === 'string') { + if (isObject(language) && typeof language.id === 'string') { activationEvents.add(`onLanguage:${language.id}`); } } @@ -104,7 +105,7 @@ function updateLanguageContributions(languages: PluginPackageLanguageContributio function updateNotebookContributions(notebooks: PluginPackageNotebook[], activationEvents: Set): void { for (const notebook of notebooks) { - if (isObject(notebook) && typeof notebook.type === 'string') { + if (isObject(notebook) && typeof notebook.type === 'string') { activationEvents.add(`onNotebookSerializer:${notebook.type}`); } } From 94103a29c640246f1f45b7fc52c10c93a023a0ed Mon Sep 17 00:00:00 2001 From: Vlad Arama <86936229+vladarama@users.noreply.github.com> Date: Thu, 21 Dec 2023 11:28:49 -0500 Subject: [PATCH 025/441] Set initial size and position of secondary windows (#13201) This implements the initial size and positions of secondary windows. It also adds a preference, called `Secondary Window Placement` allowing the user to choose the placement of the secondary windows between: - originalSize: same size as the widget. - halfWidth: half the size of the Theia application, positioned to the side. - fullScreen: the secondary window will take up the full screen. Also allows to display secondary windows on top. Signed-off-by: Marc Dumais Signed-off-by: Vlad Arama Co-authored-by: Marc Dumais Co-authored-by: Vlad Arama --- packages/core/src/browser/core-preferences.ts | 16 ++++++ .../default-secondary-window-service.ts | 55 ++++++++++++++++++- .../electron-main-application.ts | 6 +- 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 45ae47154a852..74fc83ffa8105 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -119,6 +119,22 @@ export const corePreferenceSchema: PreferenceSchema = { scope: 'application', markdownDescription: nls.localizeByDefault('Separator used by {0}.', '`#window.title#`') }, + 'window.secondaryWindowPlacement': { + type: 'string', + enum: ['originalSize', 'halfWidth', 'fullSize'], + enumDescriptions: [ + nls.localize('theia/core/secondaryWindow/originalSize', 'The position and size of the extracted widget will be the same as the original widget.'), + nls.localize('theia/core/secondaryWindow/halfWidth', 'The position and size of the extracted widget will be half the width of the running Theia application.'), + nls.localize('theia/core/secondaryWindow/fullSize', 'The position and size of the extracted widget will be the same as the running Theia application.'), + ], + default: 'originalSize', + description: nls.localize('theia/core/secondaryWindow/description', 'Sets the initial position and size of the extracted secondary window.'), + }, + 'window.secondaryWindowAlwaysOnTop': { + type: 'boolean', + default: false, + description: nls.localize('theia/core/secondaryWindow/alwaysOnTop', 'When enabled, the secondary window stays above all other windows, including those of different applications.'), + }, 'http.proxy': { type: 'string', pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$', diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index 7dc9745f52b34..c175acfddb78f 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -19,6 +19,8 @@ import { WindowService } from './window-service'; import { ExtractableWidget } from '../widgets'; import { ApplicationShell } from '../shell'; import { Saveable } from '../saveable'; +import { PreferenceService } from '../preferences'; +import { environment } from '../../common'; @injectable() export class DefaultSecondaryWindowService implements SecondaryWindowService { @@ -38,6 +40,9 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { @inject(WindowService) protected readonly windowService: WindowService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + @postConstruct() init(): void { // Set up messaging with secondary windows @@ -100,7 +105,13 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), 'popup') ?? undefined; + let options; + const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget); + options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; + if (this.preferenceService.get('window.secondaryWindowAlwaysOnTop')) { + options += ',alwaysOnTop=true'; + } + const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined; if (newWindow) { newWindow.addEventListener('DOMContentLoaded', () => { newWindow.addEventListener('beforeunload', evt => { @@ -124,6 +135,48 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { return newWindow; } + protected findSecondaryWindowCoordinates(widget: ExtractableWidget): (number | undefined)[] { + const clientBounds = widget.node.getBoundingClientRect(); + const preference = this.preferenceService.get('window.secondaryWindowPlacement'); + + let height; let width; let left; let top; + const offsetY = 20; // Offset to avoid the window title bar + + switch (preference) { + case 'originalSize': { + height = widget.node.clientHeight; + width = widget.node.clientWidth; + left = window.screenLeft + clientBounds.x; + top = window.screenTop + (window.outerHeight - window.innerHeight) + offsetY; + if (environment.electron.is()) { + top = window.screenTop + clientBounds.y; + } + break; + } + case 'halfWidth': { + height = window.innerHeight - (window.outerHeight - window.innerHeight); + width = window.innerWidth / 2; + left = window.screenLeft; + top = window.screenTop; + if (!environment.electron.is()) { + height = window.innerHeight + clientBounds.y - offsetY; + } + break; + } + case 'fullSize': { + height = window.innerHeight - (window.outerHeight - window.innerHeight); + width = window.innerWidth; + left = window.screenLeft; + top = window.screenTop; + if (!environment.electron.is()) { + height = window.innerHeight + clientBounds.y - offsetY; + } + break; + } + } + return [height, width, left, top]; + } + focus(win: Window): void { win.focus(); } diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 645048d78012c..cc9b03039b15c 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -397,7 +397,7 @@ export class ElectronMainApplication { electronWindow.webContents.setWindowOpenHandler(() => { const { minWidth, minHeight } = this.getDefaultOptions(); const options: BrowserWindowConstructorOptions = { - ...this.getDefaultTheiaWindowBounds(), + ...this.getDefaultTheiaSecondaryWindowBounds(), // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. @@ -463,6 +463,10 @@ export class ElectronMainApplication { }; } + protected getDefaultTheiaSecondaryWindowBounds(): TheiaBrowserWindowOptions { + return {}; + } + protected getDefaultTheiaWindowBounds(): TheiaBrowserWindowOptions { // The `screen` API must be required when the application is ready. // See: https://electronjs.org/docs/api/screen#screen From 1a0aff0884961e8f22df1ce8d485ef4946d7a755 Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Thu, 21 Dec 2023 12:51:25 -0500 Subject: [PATCH 026/441] repo: fix misc typos 1.45.0 Signed-off-by: vince-fugnitto --- packages/core/i18n/nls.cs.json | 2 +- packages/core/i18n/nls.de.json | 2 +- packages/core/i18n/nls.es.json | 2 +- packages/core/i18n/nls.fr.json | 2 +- packages/core/i18n/nls.hu.json | 2 +- packages/core/i18n/nls.it.json | 2 +- packages/core/i18n/nls.ja.json | 2 +- packages/core/i18n/nls.json | 2 +- packages/core/i18n/nls.pl.json | 2 +- packages/core/i18n/nls.pt-br.json | 2 +- packages/core/i18n/nls.pt-pt.json | 2 +- packages/core/i18n/nls.ru.json | 2 +- packages/core/i18n/nls.zh-cn.json | 2 +- packages/core/src/browser/messaging/frontend-id-provider.ts | 2 +- packages/core/src/browser/messaging/ws-connection-provider.ts | 2 +- packages/git/src/browser/git-quick-open-service.ts | 2 +- packages/terminal/src/browser/terminal-widget-impl.ts | 4 ++-- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 6f1c0b02a30a9..7b5f1234707cc 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "před několika sekundami", "addSignedOff": "Přidat Signed-off-by", - "amendReuseMessag": "Chcete-li znovu použít poslední zprávu o revizi, stiskněte klávesu \"Enter\" nebo klávesu \"Escape\" pro zrušení.", + "amendReuseMessage": "Chcete-li znovu použít poslední zprávu o revizi, stiskněte klávesu \"Enter\" nebo klávesu \"Escape\" pro zrušení.", "amendRewrite": "Přepsání předchozí zprávy o revizi. Stisknutím klávesy 'Enter' potvrdíte nebo klávesou 'Escape' zrušíte.", "checkoutCreateLocalBranchWithName": "Vytvořte novou místní větev s názvem: {0}. Stiskněte klávesu \"Enter\" pro potvrzení nebo \"Escape\" pro zrušení.", "checkoutProvideBranchName": "Uveďte prosím název pobočky.", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index ea9d6662ef5bf..bd011d108bb84 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "vor ein paar Sekunden", "addSignedOff": "Abgezeichnet von hinzufügen", - "amendReuseMessag": "Um die letzte Meldung wieder zu verwenden, drücken Sie \"Enter\" oder \"Escape\", um den Vorgang abzubrechen.", + "amendReuseMessage": "Um die letzte Meldung wieder zu verwenden, drücken Sie \"Enter\" oder \"Escape\", um den Vorgang abzubrechen.", "amendRewrite": "Vorherige Übermittlungsnachricht neu schreiben. Bestätigen Sie mit \"Enter\" oder brechen Sie mit \"Escape\" ab.", "checkoutCreateLocalBranchWithName": "Erstellen Sie einen neuen lokalen Zweig mit dem Namen: {0}. Drücken Sie \"Enter\" zur Bestätigung oder \"Escape\" zum Abbrechen.", "checkoutProvideBranchName": "Bitte geben Sie den Namen einer Zweigstelle an.", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 9b0018fc22319..74a4d0de43a97 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "hace unos segundos", "addSignedOff": "Agregar a la lista de firmas", - "amendReuseMessag": "Para reutilizar el último mensaje de confirmación, pulse \"Enter\" o \"Escape\" para cancelar.", + "amendReuseMessage": "Para reutilizar el último mensaje de confirmación, pulse \"Enter\" o \"Escape\" para cancelar.", "amendRewrite": "Reescribir el mensaje de confirmación anterior. Pulse 'Enter' para confirmar o 'Escape' para cancelar.", "checkoutCreateLocalBranchWithName": "Cree una nueva sucursal local con el nombre: {0}. Pulse 'Enter' para confirmar o 'Escape' para cancelar.", "checkoutProvideBranchName": "Por favor, indique el nombre de la sucursal.", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index c5127822a80ab..2bf852b0843e8 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "il y a quelques secondes", "addSignedOff": "Ajouter Signé-par", - "amendReuseMessag": "Pour réutiliser le dernier message de validation, appuyez sur 'Enter' ou 'Escape' pour annuler.", + "amendReuseMessage": "Pour réutiliser le dernier message de validation, appuyez sur 'Enter' ou 'Escape' pour annuler.", "amendRewrite": "Réécrire le message de livraison précédent. Appuyez sur 'Enter' pour confirmer ou 'Escape' pour annuler.", "checkoutCreateLocalBranchWithName": "Créez une nouvelle branche locale avec le nom : {0}. Appuyez sur 'Enter' pour confirmer ou 'Escape' pour annuler.", "checkoutProvideBranchName": "Veuillez indiquer le nom de la succursale.", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index 2dd14c9ac756b..bc292a90dd33a 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "néhány másodperccel ezelőtt", "addSignedOff": "Signed-off-by hozzáadása", - "amendReuseMessag": "Az utolsó átadási üzenet újbóli használatához nyomja meg az 'Enter' billentyűt, vagy az 'Escape' billentyűt a törléshez.", + "amendReuseMessage": "Az utolsó átadási üzenet újbóli használatához nyomja meg az 'Enter' billentyűt, vagy az 'Escape' billentyűt a törléshez.", "amendRewrite": "Írja át az előző commit üzenetet. Nyomja meg az 'Enter' billentyűt a megerősítéshez vagy az 'Escape' billentyűt a törléshez.", "checkoutCreateLocalBranchWithName": "Hozzon létre egy új helyi ágat {0} névvel. Nyomja meg az 'Enter' billentyűt a megerősítéshez vagy az 'Escape' billentyűt a törléshez.", "checkoutProvideBranchName": "Kérjük, adja meg a fióktelep nevét.", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 6d8ecfe6a5372..1159ae488ccfa 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "pochi secondi fa", "addSignedOff": "Aggiungi Firmato-da", - "amendReuseMessag": "Per riutilizzare l'ultimo messaggio di commit, premi 'Enter' o 'Escape' per annullare.", + "amendReuseMessage": "Per riutilizzare l'ultimo messaggio di commit, premi 'Enter' o 'Escape' per annullare.", "amendRewrite": "Riscrivere il messaggio di commit precedente. Premi 'Enter' per confermare o 'Escape' per annullare.", "checkoutCreateLocalBranchWithName": "Crea un nuovo ramo locale con nome: {0}. Premi 'Enter' per confermare o 'Escape' per annullare.", "checkoutProvideBranchName": "Si prega di fornire il nome di una filiale.", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 4d5f59f5dc7f8..6c0cbf5355b6a 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "さきほど", "addSignedOff": "サイン・オフ・バイの追加", - "amendReuseMessag": "最後のコミットメッセージを再利用するには、「Enter」または「Escape」を押してキャンセルしてください。", + "amendReuseMessage": "最後のコミットメッセージを再利用するには、「Enter」または「Escape」を押してキャンセルしてください。", "amendRewrite": "前回のコミットメッセージを書き換えます。確定するには「Enter」を、キャンセルするには「Escape」を押してください。", "checkoutCreateLocalBranchWithName": "名前:{0}で新しいローカルブランチを作成します。確定するには「Enter」を、キャンセルするには「Escape」を押してください。", "checkoutProvideBranchName": "支店名をご記入ください。", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index 0405d836b3aad..ac16032a85ae3 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "a few seconds ago", "addSignedOff": "Add Signed-off-by", - "amendReuseMessag": "To reuse the last commit message, press 'Enter' or 'Escape' to cancel.", + "amendReuseMessage": "To reuse the last commit message, press 'Enter' or 'Escape' to cancel.", "amendRewrite": "Rewrite previous commit message. Press 'Enter' to confirm or 'Escape' to cancel.", "checkoutCreateLocalBranchWithName": "Create a new local branch with name: {0}. Press 'Enter' to confirm or 'Escape' to cancel.", "checkoutProvideBranchName": "Please provide a branch name. ", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 3c71f0f27a011..bf7adc2a110da 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "kilka sekund temu", "addSignedOff": "Dodaj podpisane przez", - "amendReuseMessag": "Aby ponownie użyć ostatniego komunikatu commit, należy nacisnąć 'Enter' lub 'Escape', aby anulować.", + "amendReuseMessage": "Aby ponownie użyć ostatniego komunikatu commit, należy nacisnąć 'Enter' lub 'Escape', aby anulować.", "amendRewrite": "Ponownie napisać poprzednią wiadomość. Wcisnąć 'Enter', aby potwierdzić lub 'Escape', aby anulować.", "checkoutCreateLocalBranchWithName": "Utworzyć nowy oddział lokalny o nazwie: {0}. Wcisnąć 'Enter', aby potwierdzić lub 'Escape', aby anulować.", "checkoutProvideBranchName": "Proszę podać nazwę oddziału.", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index 989f735cea0c5..c2cf9f71ad0ef 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "alguns segundos atrás", "addSignedOff": "Adicionar Signed-off-by", - "amendReuseMessag": "Para reutilizar a última mensagem de compromisso, pressione 'Enter' ou 'Escape' para cancelar.", + "amendReuseMessage": "Para reutilizar a última mensagem de compromisso, pressione 'Enter' ou 'Escape' para cancelar.", "amendRewrite": "Reescrever mensagem de compromisso anterior. Pressione 'Enter' para confirmar ou 'Escape' para cancelar.", "checkoutCreateLocalBranchWithName": "Criar uma nova filial local com o nome: {0}. Pressione 'Enter' para confirmar ou 'Escape' para cancelar.", "checkoutProvideBranchName": "Por favor, forneça um nome de filial.", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 05b4bd72fd2e8..9c3346b8ab06f 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "há alguns segundos", "addSignedOff": "Adicionar Signed-off-by", - "amendReuseMessag": "Para reutilizar a última mensagem de compromisso, prima 'Enter' ou 'Escape' para cancelar.", + "amendReuseMessage": "Para reutilizar a última mensagem de compromisso, prima 'Enter' ou 'Escape' para cancelar.", "amendRewrite": "Reescrever mensagem de compromisso anterior. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", "checkoutCreateLocalBranchWithName": "Criar uma nova filial local com o nome: {0}. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", "checkoutProvideBranchName": "Por favor, forneça um nome de filial.", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index 074a5c34cb027..dfe6dbde9b629 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "несколько секунд назад", "addSignedOff": "Добавить подписанный", - "amendReuseMessag": "Чтобы повторно использовать последнее сообщение фиксации, нажмите 'Enter' или 'Escape' для отмены.", + "amendReuseMessage": "Чтобы повторно использовать последнее сообщение фиксации, нажмите 'Enter' или 'Escape' для отмены.", "amendRewrite": "Переписать предыдущее сообщение о фиксации. Нажмите 'Enter' для подтверждения или 'Escape' для отмены.", "checkoutCreateLocalBranchWithName": "Создайте новый локальный филиал с именем: {0}. Нажмите 'Enter' для подтверждения или 'Escape' для отмены.", "checkoutProvideBranchName": "Пожалуйста, укажите название филиала.", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 873735f09c054..3a20bad16a2df 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -164,7 +164,7 @@ "git": { "aFewSecondsAgo": "几秒钟前", "addSignedOff": "添加 \"已签署\"。", - "amendReuseMessag": "要重新使用最后一条提交信息,请按'Enter'或'Escape'来取消。", + "amendReuseMessage": "要重新使用最后一条提交信息,请按'Enter'或'Escape'来取消。", "amendRewrite": "重写之前的提交信息。按'Enter'键确认或按'Escape'键取消。", "checkoutCreateLocalBranchWithName": "创建一个新的本地分支,名称为:{0}。按'Enter'键确认或按'Escape'键取消。", "checkoutProvideBranchName": "请提供分支机构名称。", diff --git a/packages/core/src/browser/messaging/frontend-id-provider.ts b/packages/core/src/browser/messaging/frontend-id-provider.ts index 24fd5aa67e398..9ec93c1b4200c 100644 --- a/packages/core/src/browser/messaging/frontend-id-provider.ts +++ b/packages/core/src/browser/messaging/frontend-id-provider.ts @@ -20,7 +20,7 @@ import { generateUuid } from '../../common/uuid'; export const FrontendIdProvider = Symbol('FrontendIdProvider'); /** - * A FronendIdProvider computes an id for an instance of the front end that may be reconnected to a back end + * A FrontendIdProvider computes an id for an instance of the front end that may be reconnected to a back end * connection context. */ export interface FrontendIdProvider { diff --git a/packages/core/src/browser/messaging/ws-connection-provider.ts b/packages/core/src/browser/messaging/ws-connection-provider.ts index 676990461b061..3f9a3b45c7e73 100644 --- a/packages/core/src/browser/messaging/ws-connection-provider.ts +++ b/packages/core/src/browser/messaging/ws-connection-provider.ts @@ -22,7 +22,7 @@ decorate(injectable(), RpcProxyFactory); decorate(unmanaged(), RpcProxyFactory, 0); /** - * @deprecated This class serves to keep API compatiliblity for a while. Use {@linkcode ServiceConnectionProvider} instead. + * @deprecated This class serves to keep API compatibility for a while. Use {@linkcode ServiceConnectionProvider} instead. */ @injectable() export class WebSocketConnectionProvider { diff --git a/packages/git/src/browser/git-quick-open-service.ts b/packages/git/src/browser/git-quick-open-service.ts index 965037e1dbed6..cc83fe0711079 100644 --- a/packages/git/src/browser/git-quick-open-service.ts +++ b/packages/git/src/browser/git-quick-open-service.ts @@ -364,7 +364,7 @@ export class GitQuickOpenService { const getItems = (lookFor?: string) => { const items = []; if (!lookFor) { - const label = nls.localize('theia/git/amendReuseMessag', "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."); + const label = nls.localize('theia/git/amendReuseMessage', "To reuse the last commit message, press 'Enter' or 'Escape' to cancel."); items.push(new GitQuickPickItem(label, () => resolve(lastMessage), label)); } else { items.push(new GitQuickPickItem( diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 93bf528ca0cc4..7c233c7fe6a6a 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -89,7 +89,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget override lastCwd = new URI(); @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - @inject(RemoteConnectionProvider) protected readonly conectionProvider: ServiceConnectionProvider; + @inject(RemoteConnectionProvider) protected readonly connectionProvider: ServiceConnectionProvider; @inject(TerminalWidgetOptions) options: TerminalWidgetOptions; @inject(ShellTerminalServerProxy) protected readonly shellTerminalServer: ShellTerminalServerProxy; @inject(TerminalWatcher) protected readonly terminalWatcher: TerminalWatcher; @@ -630,7 +630,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.toDisposeOnConnect.dispose(); this.toDispose.push(this.toDisposeOnConnect); const waitForConnection = this.waitForConnection = new Deferred(); - this.conectionProvider.listen( + this.connectionProvider.listen( `${terminalsPath}/${this.terminalId}`, (path, connection) => { connection.onMessage(e => { From 64d0ecc000e236aeccf57986332cdd3ff3cb2a89 Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Thu, 21 Dec 2023 13:47:31 -0500 Subject: [PATCH 027/441] docs: updated changelog for 1.45.0 Signed-off-by: vince-fugnitto --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 933afb251a78a..9f9dc0fef1f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,36 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## Unreleased - -- [plugin] Support TestMessage.contextValue from vscode API [#13176](https://github.com/eclipse-theia/theia/pull/13176) - contributed on behalf of STMicroelectronics -- [terminal] Use application shell methods for expanding/collapsing bottom panel for "Terminal: Toggle Terminal" command [#13131](https://github.com/eclipse-theia/theia/pull/13131) -- [workspace] Create an empty workspace if no workspace is active on updateWorkspaceFolders [#13181](https://github.com/eclipse-theia/theia/pull/13181) - contributed on behalf of STMicroelectronics - -[Breaking Changes:](#breaking_changes_1.45.0) - -- [plugin] handling of vscode extension locations has changed: deployment dir switched to `$CONFDIR/deployedPlugin`, `.vsix` files from `$CONFDIR/extensions` are deployed automatically [#13178](https://github.com/eclipse-theia/theia/pull/13178) - Contributed on behalf of STMicroelectronics +## v1.45.0 - 12/21/2023 + +- [application-manager] updated logic to allow rebinding messaging services in preload [#13199](https://github.com/eclipse-theia/theia/pull/13199) +- [application-package] bumped the default supported API from `1.83.1` to `1.84.2` [#13198](https://github.com/eclipse-theia/theia/pull/13198) +- [core] added cli parameter `--electronUserData` to control `userDataPath` [#13155](https://github.com/eclipse-theia/theia/pull/13155) +- [core] added logic to control the size and position of secondary windows [#13201](https://github.com/eclipse-theia/theia/pull/13201) +- [core] added logic to save untitled files to the last active folder [#13184](https://github.com/eclipse-theia/theia/pull/13184) +- [core] fixed regression preventing closing the application when a dirty editor is present [#13173](https://github.com/eclipse-theia/theia/pull/13173) +- [core] fixed styling for compressed navigator indents [#13162](https://github.com/eclipse-theia/theia/pull/13162) +- [core] introduced timeout logic for keeping connection contexts alive [#13082](https://github.com/eclipse-theia/theia/pull/13082) +- [core] updated `nls.metadata.json` for `1.84.2` [#13200](https://github.com/eclipse-theia/theia/pull/13200) +- [debug] fixed issue where debug configuration providers would replace other providers [#13196](https://github.com/eclipse-theia/theia/pull/13196) +- [documentation] improved documentation regarding the addition of the plugin API in the plugin host [#13153](https://github.com/eclipse-theia/theia/pull/13153) +- [notebook] fixed notebook kernel selection [#13171](https://github.com/eclipse-theia/theia/pull/13171) +- [notebook] implemented general API improvements [#13012](https://github.com/eclipse-theia/theia/pull/13012) +- [notebook] optimized output logic [#13137](https://github.com/eclipse-theia/theia/pull/13137) +- [plugin] added documentation about adding custom activation events [#13190](https://github.com/eclipse-theia/theia/pull/13190) +- [plugin] added logic to deploy plugins asynchronously [#13134](https://github.com/eclipse-theia/theia/pull/13134) +- [plugin] added logic to not reject unknown schemas in `WindowStateExt.asExternalUri` [#13057](https://github.com/eclipse-theia/theia/pull/13057) +- [plugin] added support for the `TestMessage.contextValue` VS Code API [#13176](https://github.com/eclipse-theia/theia/pull/13176) - contributed on behalf of STMicroelectronics +- [plugin] added support for the `webview/context` menu contribution point [#13166](https://github.com/eclipse-theia/theia/pull/13166) +- [plugin] fixed incorrect `unsupported activation error` in stdout [#13095](https://github.com/eclipse-theia/theia/pull/13095) +- [plugin] fixed issue where the `onView` activation event was incorrectly generated [#13091](https://github.com/eclipse-theia/theia/pull/13091) +- [plugin] fixed plugin icon styling [#13101](https://github.com/eclipse-theia/theia/pull/13101) +- [terminal] updated logic to use `ApplicationShell` when expanding/collapsing the bottom panel [#13131](https://github.com/eclipse-theia/theia/pull/13131) +- [workspace] added logic to create an empty workspace if no workspace is active on `updateWorkspaceFolders` event [#13181](https://github.com/eclipse-theia/theia/pull/13181) - contributed on behalf of STMicroelectronics + +[Breaking Changes:](#breaking_changes_1.45.0) + +- [plugin] updated VS Code extension locations: deployment dir switched to `$CONFDIR/deployedPlugin`, `.vsix` files from `$CONFDIR/extensions` are deployed automatically [#13178](https://github.com/eclipse-theia/theia/pull/13178) - Contributed on behalf of STMicroelectronics ## v1.44.0 - 11/30/2023 From 2b20a60a0f9b54b19838a0f71760989a19622495 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 14:22:51 -0500 Subject: [PATCH 028/441] Translation update for version 1.45.0 (#13205) Co-authored-by: vince-fugnitto --- packages/core/i18n/nls.cs.json | 11 +++++++++-- packages/core/i18n/nls.de.json | 11 +++++++++-- packages/core/i18n/nls.es.json | 11 +++++++++-- packages/core/i18n/nls.fr.json | 11 +++++++++-- packages/core/i18n/nls.hu.json | 11 +++++++++-- packages/core/i18n/nls.it.json | 11 +++++++++-- packages/core/i18n/nls.ja.json | 11 +++++++++-- packages/core/i18n/nls.json | 11 +++++++++-- packages/core/i18n/nls.pl.json | 11 +++++++++-- packages/core/i18n/nls.pt-br.json | 11 +++++++++-- packages/core/i18n/nls.pt-pt.json | 11 +++++++++-- packages/core/i18n/nls.ru.json | 11 +++++++++-- packages/core/i18n/nls.zh-cn.json | 11 +++++++++-- 13 files changed, 117 insertions(+), 26 deletions(-) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 7b5f1234707cc..49be2b4124272 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -65,6 +65,13 @@ "next": "Další (dolů)", "previous": "Předchozí (Nahoru)" }, + "secondaryWindow": { + "alwaysOnTop": "Je-li tato funkce povolena, zůstane sekundární okno nad všemi ostatními okny, včetně oken různých aplikací.", + "description": "Nastaví počáteční pozici a velikost extrahovaného sekundárního okna.", + "fullSize": "Pozice a velikost extrahovaného widgetu bude stejná jako u spuštěné aplikace Theia.", + "halfWidth": "Pozice a velikost extrahovaného widgetu bude odpovídat polovině šířky spuštěné aplikace Theia.", + "originalSize": "Pozice a velikost extrahovaného widgetu bude stejná jako u původního widgetu." + }, "silentNotifications": "Řídí, zda se mají potlačit vyskakovací okna s oznámeními.", "tabDefaultSize": "Určuje výchozí velikost karet s ouškem.", "tabMaximize": "Ovládá, zda se mají karty maximalizovat při dvojím kliknutí.", @@ -313,6 +320,7 @@ "autoReveal": "Automatické odhalení", "clipboardWarn": "Přístup do schránky je odepřen. Zkontrolujte oprávnění prohlížeče.", "clipboardWarnFirefox": "Rozhraní API schránky není k dispozici. Lze ji povolit pomocí předvolby '{0}' na stránce '{1}'. Poté znovu načtěte aplikaci Theia. Všimněte si, že to umožní FireFoxu získat plný přístup k systémové schránce.", + "noFolderOpened": "Žádná otevřená složka", "refresh": "Obnovení v Průzkumníku", "reveal": "Odhalení v Průzkumníkovi", "toggleHiddenFiles": "Přepínání skrytých souborů" @@ -401,8 +409,7 @@ "extract-widget": "Přesunutí zobrazení do sekundárního okna" }, "shell-area": { - "secondary": "Sekundární okno", - "top": "Nahoru" + "secondary": "Sekundární okno" }, "task": { "attachTask": "Připojte úkol...", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index bd011d108bb84..638c34d1298c4 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -65,6 +65,13 @@ "next": "Nächste (Unten)", "previous": "Zurück (Oben)" }, + "secondaryWindow": { + "alwaysOnTop": "Wenn diese Funktion aktiviert ist, bleibt das sekundäre Fenster über allen anderen Fenstern, auch über denen anderer Anwendungen.", + "description": "Legt die Anfangsposition und -größe des extrahierten Sekundärfensters fest.", + "fullSize": "Die Position und Größe des extrahierten Widgets entspricht der der laufenden Theia-Anwendung.", + "halfWidth": "Die Position und Größe des extrahierten Widgets entspricht der halben Breite der laufenden Theia-Anwendung.", + "originalSize": "Position und Größe des extrahierten Widgets entsprechen denen des ursprünglichen Widgets." + }, "silentNotifications": "Legt fest, ob Benachrichtigungs-Popups unterdrückt werden sollen.", "tabDefaultSize": "Gibt die Standardgröße für Registerkarten an.", "tabMaximize": "Steuert, ob die Registerkarten bei einem Doppelklick maximiert werden sollen.", @@ -313,6 +320,7 @@ "autoReveal": "Auto-Enthüllung", "clipboardWarn": "Der Zugriff auf die Zwischenablage wird verweigert. Überprüfen Sie die Berechtigung Ihres Browsers.", "clipboardWarnFirefox": "Die Zwischenablage-API ist nicht verfügbar. Sie kann durch die Einstellung '{0}' auf der Seite '{1}' aktiviert werden. Dann laden Sie Theia neu. Beachten Sie, dass FireFox dadurch vollen Zugriff auf die Systemzwischenablage erhält.", + "noFolderOpened": "Kein Ordner geöffnet", "refresh": "Aktualisieren im Explorer", "reveal": "Enthüllen im Explorer", "toggleHiddenFiles": "Versteckte Dateien umschalten" @@ -401,8 +409,7 @@ "extract-widget": "Ansicht in sekundäres Fenster verschieben" }, "shell-area": { - "secondary": "Sekundäres Fenster", - "top": "Top" + "secondary": "Sekundäres Fenster" }, "task": { "attachTask": "Aufgabe anhängen...", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 74a4d0de43a97..0ec7fd0e738c8 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -65,6 +65,13 @@ "next": "Siguiente (abajo)", "previous": "Anterior (Arriba)" }, + "secondaryWindow": { + "alwaysOnTop": "Cuando está activada, la ventana secundaria se mantiene por encima de todas las demás ventanas, incluidas las de distintas aplicaciones.", + "description": "Establece la posición inicial y el tamaño de la ventana secundaria extraída.", + "fullSize": "La posición y el tamaño del widget extraído serán los mismos que los de la aplicación Theia en ejecución.", + "halfWidth": "La posición y el tamaño del widget extraído serán la mitad de la anchura de la aplicación Theia en ejecución.", + "originalSize": "La posición y el tamaño del widget extraído serán los mismos que los del widget original." + }, "silentNotifications": "Controla si se suprimen las ventanas emergentes de notificación.", "tabDefaultSize": "Especifica el tamaño por defecto de las pestañas.", "tabMaximize": "Controla si se maximizan las pestañas al hacer doble clic.", @@ -313,6 +320,7 @@ "autoReveal": "Auto Reveal", "clipboardWarn": "El acceso al portapapeles está denegado. Comprueba los permisos de tu navegador.", "clipboardWarnFirefox": "La API del portapapeles no está disponible. Se puede activar mediante la preferencia '{0}' en la página '{1}'. A continuación, vuelva a cargar Theia. Tenga en cuenta que esto permitirá a FireFox tener acceso completo al portapapeles del sistema.", + "noFolderOpened": "Ninguna carpeta abierta", "refresh": "Actualizar en el Explorador", "reveal": "Revelar en el Explorador", "toggleHiddenFiles": "Activar los archivos ocultos" @@ -401,8 +409,7 @@ "extract-widget": "Mover la vista a la ventana secundaria" }, "shell-area": { - "secondary": "Ventana secundaria", - "top": "Top" + "secondary": "Ventana secundaria" }, "task": { "attachTask": "Adjuntar tarea...", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index 2bf852b0843e8..f69223826e88c 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -65,6 +65,13 @@ "next": "Suivant (en bas)", "previous": "Précédent (en haut)" }, + "secondaryWindow": { + "alwaysOnTop": "Lorsqu'elle est activée, la fenêtre secondaire reste au-dessus de toutes les autres fenêtres, y compris celles des différentes applications.", + "description": "Définit la position et la taille initiales de la fenêtre secondaire extraite.", + "fullSize": "La position et la taille du widget extrait seront identiques à celles de l'application Theia en cours d'exécution.", + "halfWidth": "La position et la taille du widget extrait correspondront à la moitié de la largeur de l'application Theia en cours d'exécution.", + "originalSize": "La position et la taille du widget extrait seront identiques à celles du widget original." + }, "silentNotifications": "Contrôle la suppression des popups de notification.", "tabDefaultSize": "Spécifie la taille par défaut des onglets.", "tabMaximize": "Contrôle si les onglets doivent être maximisés lors d'un double-clic.", @@ -313,6 +320,7 @@ "autoReveal": "Révélation automobile", "clipboardWarn": "L'accès au presse-papiers est refusé. Vérifiez les autorisations de votre navigateur.", "clipboardWarnFirefox": "L'API Presse-papiers n'est pas disponible. Elle peut être activée par la préférence '{0}' sur la page '{1}'. Rechargez ensuite Theia. Notez que cela permettra à FireFox d'avoir un accès complet au presse-papiers du système.", + "noFolderOpened": "Aucun dossier ouvert", "refresh": "Rafraîchir dans l'Explorateur", "reveal": "Révéler dans Explorer", "toggleHiddenFiles": "Basculer les fichiers cachés" @@ -401,8 +409,7 @@ "extract-widget": "Déplacer la vue vers une fenêtre secondaire" }, "shell-area": { - "secondary": "Fenêtre secondaire", - "top": "Haut" + "secondary": "Fenêtre secondaire" }, "task": { "attachTask": "Attacher la tâche...", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index bc292a90dd33a..7d15f2570f673 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -65,6 +65,13 @@ "next": "Következő (lefelé)", "previous": "Előző (fel)" }, + "secondaryWindow": { + "alwaysOnTop": "Ha engedélyezve van, a másodlagos ablak minden más ablak fölött marad, beleértve a különböző alkalmazások ablakait is.", + "description": "A kivont másodlagos ablak kezdeti pozíciójának és méretének beállítása.", + "fullSize": "A kivont widget pozíciója és mérete megegyezik a futó Theia alkalmazáséval.", + "halfWidth": "A kivont widget pozíciója és mérete a futó Theia alkalmazás szélességének fele lesz.", + "originalSize": "A kivont widget pozíciója és mérete megegyezik az eredeti widgetével." + }, "silentNotifications": "Beállítja, hogy az értesítések felugró ablakai el legyenek-e nyomva.", "tabDefaultSize": "Megadja a lapok alapértelmezett méretét.", "tabMaximize": "Szabályozza, hogy a lapok dupla kattintásra maximalizálódjanak-e.", @@ -313,6 +320,7 @@ "autoReveal": "Automatikus felfedés", "clipboardWarn": "A vágólaphoz való hozzáférés megtagadva. Ellenőrizze a böngésző engedélyeit.", "clipboardWarnFirefox": "A vágólap API nem érhető el. A '{0}' beállítással engedélyezhető a '{1}' oldalon. Ezután töltse be újra a Theia-t. Figyelem, ez lehetővé teszi, hogy a FireFox teljes hozzáférést kapjon a rendszer vágólapjához.", + "noFolderOpened": "Nincs megnyitott mappa", "refresh": "Frissítés az Explorerben", "reveal": "Feltárása az Explorerben", "toggleHiddenFiles": "Rejtett fájlok kapcsolása" @@ -401,8 +409,7 @@ "extract-widget": "Nézet áthelyezése másodlagos ablakba" }, "shell-area": { - "secondary": "Másodlagos ablak", - "top": "Tetejére" + "secondary": "Másodlagos ablak" }, "task": { "attachTask": "Feladat csatolása...", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 1159ae488ccfa..60c0ced3cb709 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -65,6 +65,13 @@ "next": "Avanti (Giù)", "previous": "Precedente (Up)" }, + "secondaryWindow": { + "alwaysOnTop": "Quando è attivata, la finestra secondaria rimane al di sopra di tutte le altre finestre, comprese quelle di applicazioni diverse.", + "description": "Imposta la posizione e le dimensioni iniziali della finestra secondaria estratta.", + "fullSize": "La posizione e le dimensioni del widget estratto saranno le stesse dell'applicazione Theia in esecuzione.", + "halfWidth": "La posizione e le dimensioni del widget estratto saranno pari alla metà della larghezza dell'applicazione Theia in esecuzione.", + "originalSize": "La posizione e le dimensioni del widget estratto saranno uguali a quelle del widget originale." + }, "silentNotifications": "Controlla se sopprimere i popup di notifica.", "tabDefaultSize": "Specifica la dimensione predefinita delle schede.", "tabMaximize": "Controlla se massimizzare le schede al doppio clic.", @@ -313,6 +320,7 @@ "autoReveal": "Rivelazione auto", "clipboardWarn": "L'accesso agli appunti è negato. Controllare i permessi del browser.", "clipboardWarnFirefox": "L'API Appunti non è disponibile. È possibile abilitarla tramite la preferenza '{0}' nella pagina '{1}'. Quindi ricaricare Theia. Ciò consentirà a FireFox di avere pieno accesso agli appunti di sistema.", + "noFolderOpened": "Nessuna cartella aperta", "refresh": "Aggiorna in Explorer", "reveal": "Rivelare in Explorer", "toggleHiddenFiles": "Toggle Hidden Files" @@ -401,8 +409,7 @@ "extract-widget": "Sposta la vista nella finestra secondaria" }, "shell-area": { - "secondary": "Finestra secondaria", - "top": "Alto" + "secondary": "Finestra secondaria" }, "task": { "attachTask": "Allegare il compito...", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 6c0cbf5355b6a..bc31ae4a46c77 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -65,6 +65,13 @@ "next": "次へ(下)", "previous": "前へ (上)" }, + "secondaryWindow": { + "alwaysOnTop": "有効にすると、セカンダリウィンドウは、異なるアプリケーションを含む他のすべてのウィンドウの上に表示されます。", + "description": "抽出されたセカンダリウィンドウの初期位置とサイズを設定する。", + "fullSize": "抽出されたウィジェットの位置とサイズは、実行中のTheiaアプリケーションと同じになります。", + "halfWidth": "抽出されたウィジェットの位置とサイズは、実行中のTheiaアプリケーションの幅の半分になります。", + "originalSize": "抽出されたウィジェットの位置とサイズは、元のウィジェットと同じになる。" + }, "silentNotifications": "通知のポップアップを抑制するかどうかを制御します。", "tabDefaultSize": "タブのデフォルトサイズを指定します。", "tabMaximize": "ダブルクリック時にタブを最大化するかどうかを制御します。", @@ -313,6 +320,7 @@ "autoReveal": "オートリヴェール", "clipboardWarn": "クリップボードへのアクセスが拒否されました。ブラウザのアクセス許可を確認してください。", "clipboardWarnFirefox": "クリップボードAPIは使用できません。これは、'{1}' ページの '{0}' 環境設定によって有効にすることができます。その後Theiaをリロードしてください。これにより、FireFoxがシステムクリップボードにフルアクセスできるようになります。", + "noFolderOpened": "フォルダが開かない", "refresh": "エクスプローラーでの更新", "reveal": "エクスプローラーでの表示", "toggleHiddenFiles": "隠しファイルのトグル" @@ -401,8 +409,7 @@ "extract-widget": "セカンダリーウィンドウへの表示移動" }, "shell-area": { - "secondary": "セカンダリーウィンドウ", - "top": "トップ" + "secondary": "セカンダリーウィンドウ" }, "task": { "attachTask": "タスクの添付...", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index ac16032a85ae3..f094b4eeec4d2 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -65,6 +65,13 @@ "next": "Next (Down)", "previous": "Previous (Up)" }, + "secondaryWindow": { + "alwaysOnTop": "When enabled, the secondary window stays above all other windows, including those of different applications.", + "description": "Sets the initial position and size of the extracted secondary window.", + "fullSize": "The position and size of the extracted widget will be the same as the running Theia application.", + "halfWidth": "The position and size of the extracted widget will be half the width of the running Theia application.", + "originalSize": "The position and size of the extracted widget will be the same as the original widget." + }, "silentNotifications": "Controls whether to suppress notification popups.", "tabDefaultSize": "Specifies the default size for tabs.", "tabMaximize": "Controls whether to maximize tabs on double click.", @@ -313,6 +320,7 @@ "autoReveal": "Auto Reveal", "clipboardWarn": "Access to the clipboard is denied. Check your browser's permission.", "clipboardWarnFirefox": "Clipboard API is not available. It can be enabled by '{0}' preference on '{1}' page. Then reload Theia. Note, it will allow FireFox getting full access to the system clipboard.", + "noFolderOpened": "No Folder Opened", "refresh": "Refresh in Explorer", "reveal": "Reveal in Explorer", "toggleHiddenFiles": "Toggle Hidden Files" @@ -401,8 +409,7 @@ "extract-widget": "Move View to Secondary Window" }, "shell-area": { - "secondary": "Secondary Window", - "top": "Top" + "secondary": "Secondary Window" }, "task": { "attachTask": "Attach Task...", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index bf7adc2a110da..11d774d51e38c 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -65,6 +65,13 @@ "next": "Następny (w dół)", "previous": "Poprzedni (Up)" }, + "secondaryWindow": { + "alwaysOnTop": "Po włączeniu tej opcji okno dodatkowe pozostaje nad wszystkimi innymi oknami, w tym nad oknami różnych aplikacji.", + "description": "Ustawia początkową pozycję i rozmiar wyodrębnionego okna pomocniczego.", + "fullSize": "Pozycja i rozmiar wyodrębnionego widżetu będą takie same jak uruchomionej aplikacji Theia.", + "halfWidth": "Pozycja i rozmiar wyodrębnionego widżetu będą równe połowie szerokości uruchomionej aplikacji Theia.", + "originalSize": "Pozycja i rozmiar wyodrębnionego widżetu będą takie same jak oryginalnego widżetu." + }, "silentNotifications": "Określa, czy wyłączyć wyskakujące okienka powiadomień.", "tabDefaultSize": "Określa domyślny rozmiar dla zakładek.", "tabMaximize": "Określa, czy karty mają być maksymalizowane po dwukrotnym kliknięciu.", @@ -313,6 +320,7 @@ "autoReveal": "Auto ujawnienie", "clipboardWarn": "Dostęp do schowka jest zablokowany. Sprawdź uprawnienia przeglądarki.", "clipboardWarnFirefox": "Interfejs API schowka nie jest dostępny. Można go włączyć za pomocą opcji '{0}' na stronie '{1}'. Następnie należy ponownie załadować Theia. Uwaga, pozwoli to FireFox uzyskać pełny dostęp do schowka systemowego.", + "noFolderOpened": "Folder nie został otwarty", "refresh": "Odśwież w Eksploratorze", "reveal": "Ujawnij w Eksploratorze", "toggleHiddenFiles": "Przełącz ukryte pliki" @@ -401,8 +409,7 @@ "extract-widget": "Przenieś widok do okna podrzędnego" }, "shell-area": { - "secondary": "Drugie okno", - "top": "Top" + "secondary": "Drugie okno" }, "task": { "attachTask": "Dołącz zadanie...", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index c2cf9f71ad0ef..b37bd60a787b3 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -65,6 +65,13 @@ "next": "Próximo (Abaixo)", "previous": "Anterior (Para cima)" }, + "secondaryWindow": { + "alwaysOnTop": "Quando ativada, a janela secundária fica acima de todas as outras janelas, inclusive as de aplicativos diferentes.", + "description": "Define a posição inicial e o tamanho da janela secundária extraída.", + "fullSize": "A posição e o tamanho do widget extraído serão os mesmos do aplicativo Theia em execução.", + "halfWidth": "A posição e o tamanho do widget extraído terão a metade da largura do aplicativo Theia em execução.", + "originalSize": "A posição e o tamanho do widget extraído serão os mesmos do widget original." + }, "silentNotifications": "Controla se devem ser suprimidas as popups de notificação.", "tabDefaultSize": "Especifica o tamanho padrão das guias.", "tabMaximize": "Controla se é necessário maximizar as abas com duplo clique.", @@ -313,6 +320,7 @@ "autoReveal": "Auto Revelação", "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão de seu navegador.", "clipboardWarnFirefox": "A API da área de transferência não está disponível. Ela pode ser ativada pela preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Observe que isso permitirá que o FireFox obtenha acesso total à área de transferência do sistema.", + "noFolderOpened": "Nenhuma pasta foi aberta", "refresh": "Refrescar no Explorer", "reveal": "Revelar no Explorer", "toggleHiddenFiles": "Alternar arquivos ocultos" @@ -401,8 +409,7 @@ "extract-widget": "Mover vista para a janela secundária" }, "shell-area": { - "secondary": "Janela Secundária", - "top": "Topo" + "secondary": "Janela Secundária" }, "task": { "attachTask": "Anexar Tarefa...", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 9c3346b8ab06f..2f52b0b10b0a3 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -65,6 +65,13 @@ "next": "Próximo (Para baixo)", "previous": "Anterior (Para cima)" }, + "secondaryWindow": { + "alwaysOnTop": "Quando activada, a janela secundária permanece acima de todas as outras janelas, incluindo as de diferentes aplicações.", + "description": "Define a posição inicial e o tamanho da janela secundária extraída.", + "fullSize": "A posição e o tamanho do widget extraído serão os mesmos da aplicação Theia em execução.", + "halfWidth": "A posição e o tamanho do widget extraído serão metade da largura da aplicação Theia em execução.", + "originalSize": "A posição e o tamanho do widget extraído serão os mesmos que os do widget original." + }, "silentNotifications": "Controla se deve suprimir popups de notificação.", "tabDefaultSize": "Especifica o tamanho predefinido dos separadores.", "tabMaximize": "Controla se pretende maximizar os separadores com um duplo clique.", @@ -313,6 +320,7 @@ "autoReveal": "Auto Revelação", "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão do seu browser.", "clipboardWarnFirefox": "A API da área de transferência não está disponível. Pode ser activada através da preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Note que isso permitirá ao FireFox obter acesso total à área de transferência do sistema.", + "noFolderOpened": "Nenhuma pasta aberta", "refresh": "Actualizar no Explorer", "reveal": "Revelar no Explorer", "toggleHiddenFiles": "Alternar ficheiros escondidos" @@ -401,8 +409,7 @@ "extract-widget": "Mover vista para a janela secundária" }, "shell-area": { - "secondary": "Janela Secundária", - "top": "Início" + "secondary": "Janela Secundária" }, "task": { "attachTask": "Anexar Tarefa...", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index dfe6dbde9b629..e27fa1d750c96 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -65,6 +65,13 @@ "next": "Далее (вниз)", "previous": "Предыдущий (Вверх)" }, + "secondaryWindow": { + "alwaysOnTop": "Если эта функция включена, вторичное окно остается выше всех остальных окон, в том числе окон различных приложений.", + "description": "Устанавливает начальное положение и размер извлеченного вторичного окна.", + "fullSize": "Положение и размер извлеченного виджета будут такими же, как и в запущенном приложении Theia.", + "halfWidth": "Положение и размер извлеченного виджета будут равны половине ширины запущенного приложения Theia.", + "originalSize": "Положение и размер извлеченного виджета будут такими же, как у исходного." + }, "silentNotifications": "Управляет тем, следует ли подавлять всплывающие окна уведомлений.", "tabDefaultSize": "Определяет размер по умолчанию для вкладок.", "tabMaximize": "Управляет тем, следует ли максимизировать вкладки при двойном щелчке.", @@ -313,6 +320,7 @@ "autoReveal": "Автоматическое раскрытие", "clipboardWarn": "Доступ к буферу обмена запрещен. Проверьте разрешение вашего браузера.", "clipboardWarnFirefox": "API буфера обмена недоступен. Его можно включить с помощью '{0}' предпочтения на странице '{1}'. Затем перезагрузите Theia. Обратите внимание, это позволит FireFox получить полный доступ к системному буферу обмена.", + "noFolderOpened": "Папка не открыта", "refresh": "Обновить в Проводнике", "reveal": "Раскрытие в Проводнике", "toggleHiddenFiles": "Переключение скрытых файлов" @@ -401,8 +409,7 @@ "extract-widget": "Переместить вид в дополнительное окно" }, "shell-area": { - "secondary": "Вторичное окно", - "top": "Топ" + "secondary": "Вторичное окно" }, "task": { "attachTask": "Прикрепите задание...", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 3a20bad16a2df..1db0707465509 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -65,6 +65,13 @@ "next": "下一页 (向下)", "previous": "上一页 (向上)" }, + "secondaryWindow": { + "alwaysOnTop": "启用后,辅助窗口将保持在所有其他窗口(包括不同应用程序的窗口)之上。", + "description": "设置提取的辅助窗口的初始位置和大小。", + "fullSize": "提取部件的位置和大小将与运行中的 Theia 应用程序相同。", + "halfWidth": "提取部件的位置和大小将是运行中的 Theia 应用程序宽度的一半。", + "originalSize": "提取部件的位置和大小将与原始部件相同。" + }, "silentNotifications": "控制是否抑制弹出通知。", "tabDefaultSize": "指定标签的默认尺寸。", "tabMaximize": "控制是否在双击时最大化标签。", @@ -313,6 +320,7 @@ "autoReveal": "自动显示", "clipboardWarn": "对剪贴板的访问被拒绝了。检查你的浏览器的权限。", "clipboardWarnFirefox": "剪贴板API是不可用的。它可以通过'{0}'页面上的'{1}'偏好启用。然后重新加载Theia。注意,这将允许FireFox获得对系统剪贴板的完全访问。", + "noFolderOpened": "未打开文件夹", "refresh": "在资源管理器中刷新", "reveal": "在资源管理器中显示", "toggleHiddenFiles": "切换隐藏文件" @@ -401,8 +409,7 @@ "extract-widget": "将视图移至第二窗口" }, "shell-area": { - "secondary": "二级窗口", - "top": "返回顶部" + "secondary": "二级窗口" }, "task": { "attachTask": "附加任务...", From bdc57d93b17c1422cbd7c42caa915095705cb550 Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Thu, 21 Dec 2023 14:34:14 -0500 Subject: [PATCH 029/441] v1.45.0 Signed-off-by: vince-fugnitto --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser/package.json | 97 +++++++++---------- examples/electron/package.json | 94 +++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 28 +++--- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 12 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-vscode/package.json | 28 +++--- packages/plugin-ext/package.json | 56 +++++------ packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 10 +- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 16 +-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 18 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- 67 files changed, 442 insertions(+), 443 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 1a468a1c92396..1d7cfbdf2478e 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.44.0", - "@theia/ffmpeg": "1.44.0", - "@theia/native-webpack-plugin": "1.44.0", + "@theia/application-package": "1.45.0", + "@theia/ffmpeg": "1.45.0", + "@theia/native-webpack-plugin": "1.45.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -73,7 +73,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 28d4a3f4ae04b..c5fbae2d10987 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.44.0", + "@theia/request": "1.45.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -42,7 +42,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index b84bb17fb0576..ee50fc60bfde2 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -30,12 +30,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.44.0", - "@theia/application-package": "1.44.0", - "@theia/ffmpeg": "1.44.0", - "@theia/localization-manager": "1.44.0", - "@theia/ovsx-client": "1.44.0", - "@theia/request": "1.44.0", + "@theia/application-manager": "1.45.0", + "@theia/application-package": "1.45.0", + "@theia/ffmpeg": "1.45.0", + "@theia/localization-manager": "1.45.0", + "@theia/ovsx-client": "1.45.0", + "@theia/request": "1.45.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index 34081b3c658de..b0397338f4d1c 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index d71a049f7781b..c470dde42c975 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "typescript": "~4.5.5" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index 1a287aa69a4ee..b0b088767f6e5 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.44.0", + "version": "1.45.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index a6b4e9b38ef9a..e6670246ba8b3 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.44.0", + "@theia/request": "1.45.0", "semver": "^7.5.4" } } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index b1daa89267ea3..61b33c6ce3c0b 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.44.0", + "version": "1.45.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.44.0", - "@theia/ext-scripts": "1.44.0", - "@theia/re-exports": "1.44.0", + "@theia/core": "1.45.0", + "@theia/ext-scripts": "1.45.0", + "@theia/re-exports": "1.45.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index 4e2787d56772e..bd748c367e710 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.44.0", + "version": "1.45.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index 83769f80bae9e..5f27e40dbc82d 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index 3c3ae2d1d41e9..408239cb86252 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 4f6bfc0da7da3..d300bb90c332b 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.44.0", - "@theia/file-search": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/output": "1.44.0", - "@theia/ovsx-client": "1.44.0", - "@theia/search-in-workspace": "1.44.0", - "@theia/test": "1.44.0", - "@theia/toolbar": "1.44.0", - "@theia/vsx-registry": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/output": "1.45.0", + "@theia/ovsx-client": "1.45.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/test": "1.45.0", + "@theia/toolbar": "1.45.0", + "@theia/vsx-registry": "1.45.0", + "@theia/workspace": "1.45.0" }, "theiaExtensions": [ { @@ -54,6 +54,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 4f2ca4d13bd70..393dbf6eab7cf 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.44.0" + "@theia/core": "1.45.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser/package.json b/examples/browser/package.json index b68dbb7208c41..f26ff0b0ff168 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.44.0", + "version": "1.45.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -17,56 +17,55 @@ "config": { "frontendConnectionTimeout": 3000 } - } }, "dependencies": { - "@theia/api-samples": "1.44.0", - "@theia/bulk-edit": "1.44.0", - "@theia/callhierarchy": "1.44.0", - "@theia/console": "1.44.0", - "@theia/core": "1.44.0", - "@theia/debug": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/editor-preview": "1.44.0", - "@theia/file-search": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/getting-started": "1.44.0", - "@theia/git": "1.44.0", - "@theia/keymaps": "1.44.0", - "@theia/markers": "1.44.0", - "@theia/memory-inspector": "1.44.0", - "@theia/messages": "1.44.0", - "@theia/metrics": "1.44.0", - "@theia/mini-browser": "1.44.0", - "@theia/monaco": "1.44.0", - "@theia/navigator": "1.44.0", - "@theia/notebook": "1.44.0", - "@theia/outline-view": "1.44.0", - "@theia/output": "1.44.0", - "@theia/plugin-dev": "1.44.0", - "@theia/plugin-ext": "1.44.0", - "@theia/plugin-ext-vscode": "1.44.0", - "@theia/plugin-metrics": "1.44.0", - "@theia/preferences": "1.44.0", - "@theia/preview": "1.44.0", - "@theia/process": "1.44.0", - "@theia/property-view": "1.44.0", - "@theia/remote": "1.44.0", - "@theia/scm": "1.44.0", - "@theia/scm-extra": "1.44.0", - "@theia/search-in-workspace": "1.44.0", - "@theia/secondary-window": "1.44.0", - "@theia/task": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/test": "1.44.0", - "@theia/timeline": "1.44.0", - "@theia/toolbar": "1.44.0", - "@theia/typehierarchy": "1.44.0", - "@theia/userstorage": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/vsx-registry": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/api-samples": "1.45.0", + "@theia/bulk-edit": "1.45.0", + "@theia/callhierarchy": "1.45.0", + "@theia/console": "1.45.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/editor-preview": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/getting-started": "1.45.0", + "@theia/git": "1.45.0", + "@theia/keymaps": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/memory-inspector": "1.45.0", + "@theia/messages": "1.45.0", + "@theia/metrics": "1.45.0", + "@theia/mini-browser": "1.45.0", + "@theia/monaco": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/notebook": "1.45.0", + "@theia/outline-view": "1.45.0", + "@theia/output": "1.45.0", + "@theia/plugin-dev": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/plugin-ext-vscode": "1.45.0", + "@theia/plugin-metrics": "1.45.0", + "@theia/preferences": "1.45.0", + "@theia/preview": "1.45.0", + "@theia/process": "1.45.0", + "@theia/property-view": "1.45.0", + "@theia/remote": "1.45.0", + "@theia/scm": "1.45.0", + "@theia/scm-extra": "1.45.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/secondary-window": "1.45.0", + "@theia/task": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/test": "1.45.0", + "@theia/timeline": "1.45.0", + "@theia/toolbar": "1.45.0", + "@theia/typehierarchy": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/vsx-registry": "1.45.0", + "@theia/workspace": "1.45.0" }, "scripts": { "clean": "theia clean", @@ -89,6 +88,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.44.0" + "@theia/cli": "1.45.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 11646f24e893a..0f2fe49b360e7 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.44.0", + "version": "1.45.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -20,51 +20,51 @@ } }, "dependencies": { - "@theia/api-samples": "1.44.0", - "@theia/bulk-edit": "1.44.0", - "@theia/callhierarchy": "1.44.0", - "@theia/console": "1.44.0", - "@theia/core": "1.44.0", - "@theia/debug": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/editor-preview": "1.44.0", - "@theia/electron": "1.44.0", - "@theia/external-terminal": "1.44.0", - "@theia/file-search": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/getting-started": "1.44.0", - "@theia/git": "1.44.0", - "@theia/keymaps": "1.44.0", - "@theia/markers": "1.44.0", - "@theia/memory-inspector": "1.44.0", - "@theia/messages": "1.44.0", - "@theia/metrics": "1.44.0", - "@theia/mini-browser": "1.44.0", - "@theia/monaco": "1.44.0", - "@theia/navigator": "1.44.0", - "@theia/outline-view": "1.44.0", - "@theia/output": "1.44.0", - "@theia/plugin-dev": "1.44.0", - "@theia/plugin-ext": "1.44.0", - "@theia/plugin-ext-vscode": "1.44.0", - "@theia/preferences": "1.44.0", - "@theia/preview": "1.44.0", - "@theia/process": "1.44.0", - "@theia/property-view": "1.44.0", - "@theia/remote": "1.44.0", - "@theia/scm": "1.44.0", - "@theia/scm-extra": "1.44.0", - "@theia/search-in-workspace": "1.44.0", - "@theia/secondary-window": "1.44.0", - "@theia/task": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/timeline": "1.44.0", - "@theia/toolbar": "1.44.0", - "@theia/typehierarchy": "1.44.0", - "@theia/userstorage": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/vsx-registry": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/api-samples": "1.45.0", + "@theia/bulk-edit": "1.45.0", + "@theia/callhierarchy": "1.45.0", + "@theia/console": "1.45.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/editor-preview": "1.45.0", + "@theia/electron": "1.45.0", + "@theia/external-terminal": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/getting-started": "1.45.0", + "@theia/git": "1.45.0", + "@theia/keymaps": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/memory-inspector": "1.45.0", + "@theia/messages": "1.45.0", + "@theia/metrics": "1.45.0", + "@theia/mini-browser": "1.45.0", + "@theia/monaco": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/outline-view": "1.45.0", + "@theia/output": "1.45.0", + "@theia/plugin-dev": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/plugin-ext-vscode": "1.45.0", + "@theia/preferences": "1.45.0", + "@theia/preview": "1.45.0", + "@theia/process": "1.45.0", + "@theia/property-view": "1.45.0", + "@theia/remote": "1.45.0", + "@theia/scm": "1.45.0", + "@theia/scm-extra": "1.45.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/secondary-window": "1.45.0", + "@theia/task": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/timeline": "1.45.0", + "@theia/toolbar": "1.45.0", + "@theia/typehierarchy": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/vsx-registry": "1.45.0", + "@theia/workspace": "1.45.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -82,7 +82,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.44.0", + "@theia/cli": "1.45.0", "electron": "^23.2.4" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index d618c6a0c6e38..bfb70db2bfecb 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.44.0", + "version": "1.45.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index 4e84189a3d20a..8121b8e9bc681 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.44.0", + "version": "1.45.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index f231132ffe941..34f24159ef41a 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/workspace": "1.44.0" + "@theia/workspace": "1.45.0" }, "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index 73eb81a3e448f..bff3ebd1683b0 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", "ts-md5": "^1.2.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index ac238ffa8396a..2ac8242ca0e9a 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", "anser": "^2.0.1" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index ee08a56e24377..85c21e0f86dce 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.44.0", - "@theia/request": "1.44.0", + "@theia/application-package": "1.45.0", + "@theia/request": "1.45.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -200,8 +200,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", - "@theia/re-exports": "1.44.0", + "@theia/ext-scripts": "1.45.0", + "@theia/re-exports": "1.45.0", "minimist": "^1.2.0" }, "nyc": { diff --git a/packages/debug/package.json b/packages/debug/package.json index 364fc079a0917..488b7fa24f32a 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,21 +1,21 @@ { "name": "@theia/debug", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.44.0", - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/markers": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/console": "1.45.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/output": "1.44.0", - "@theia/process": "1.44.0", - "@theia/task": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/output": "1.45.0", + "@theia/process": "1.45.0", + "@theia/task": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/workspace": "1.45.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -56,7 +56,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index 973cd587db484..108c7975208e0 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/navigator": "1.44.0" + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/navigator": "1.45.0" }, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index f1028d74c7f00..205287d5bc19c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/variable-resolver": "1.44.0" + "@theia/core": "1.45.0", + "@theia/variable-resolver": "1.45.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index 152857d7f1838..e7ed637bf66b2 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", - "@theia/re-exports": "1.44.0" + "@theia/ext-scripts": "1.45.0", + "@theia/re-exports": "1.45.0" }, "peerDependencies": { "electron": "^23.2.4" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index 8e0fcd1597c64..d2e08d16bc33d 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/workspace": "1.45.0" }, "publishConfig": { "access": "public" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 113f6d0444c76..f6f0fa5614cd4 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/process": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/process": "1.45.0", + "@theia/workspace": "1.45.0", "@vscode/ripgrep": "^1.14.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 0fcf3791a7b0e..a1517d8fe786f 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.44.0", + "@theia/core": "1.45.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -70,7 +70,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index d7ca8fdcc93a9..2dbdafdef4f1a 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/keymaps": "1.44.0", - "@theia/preview": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/keymaps": "1.45.0", + "@theia/preview": "1.45.0", + "@theia/workspace": "1.45.0" }, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 0671f6f1df151..340c9d6b568f8 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.44.0", - "@theia/scm": "1.44.0", - "@theia/scm-extra": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/navigator": "1.45.0", + "@theia/scm": "1.45.0", + "@theia/scm-extra": "1.45.0", + "@theia/workspace": "1.45.0", "@types/diff": "^3.2.2", "@types/p-queue": "^2.3.1", "diff": "^3.4.0", @@ -66,7 +66,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index 16728e9341d9c..9f4672a743e99 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,17 +1,17 @@ { "name": "@theia/keymaps", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/preferences": "1.44.0", - "@theia/userstorage": "1.44.0", + "@theia/preferences": "1.45.0", + "@theia/userstorage": "1.45.0", "jsonc-parser": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index 11ad5167e0b0b..4822f6b584fa2 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/workspace": "1.44.0" + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/workspace": "1.45.0" }, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index d78b4049cbd5d..f297627427049 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.44.0", - "@theia/debug": "1.44.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0" }, diff --git a/packages/messages/package.json b/packages/messages/package.json index a66892583fcbd..182c0d62e8fa4 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.44.0", + "@theia/core": "1.45.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2" }, @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index f50c84618c93b..eb37f0e96cd41 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.44.0", + "@theia/core": "1.45.0", "prom-client": "^10.2.0" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 2e1a9cd5a5da9..3dae7d419dca5 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 6c4d20c85cff5..39474e39a2da1 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/markers": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/markers": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/outline-view": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/outline-view": "1.45.0", + "@theia/workspace": "1.45.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index ebff02fed78f7..da5e4e31b047a 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/workspace": "1.45.0", "minimatch": "^5.1.0" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 56914c2e284fe..5578169830a78 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,12 +1,12 @@ { "name": "@theia/notebook", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "uuid": "^8.3.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/vscode-notebook-renderer": "^1.72.0" }, "nyc": { diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index e48c7a3ad9a80..ae0473e38cb62 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.44.0" + "@theia/core": "1.45.0" }, "publishConfig": { "access": "public" @@ -38,7 +38,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index cf70145d85283..1f78b4733b9f1 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index 8644d6274e2c9..2677963d91308 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.44.0", - "@theia/debug": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/output": "1.44.0", - "@theia/plugin-ext": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/output": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/workspace": "1.45.0", "ps-tree": "^1.2.0" }, "publishConfig": { @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 5723f802ce95d..aa26a3341769a 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,21 +1,21 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.44.0", - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/callhierarchy": "1.45.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.44.0", - "@theia/plugin": "1.44.0", - "@theia/plugin-ext": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/typehierarchy": "1.44.0", - "@theia/userstorage": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/navigator": "1.45.0", + "@theia/plugin": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/typehierarchy": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/workspace": "1.45.0", "decompress": "^4.2.1", "filenamify": "^4.1.0" }, @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 8ed3e78710ff1..2a069d8489e48 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.44.0", - "@theia/callhierarchy": "1.44.0", - "@theia/console": "1.44.0", - "@theia/core": "1.44.0", - "@theia/debug": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/editor-preview": "1.44.0", - "@theia/file-search": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/markers": "1.44.0", - "@theia/messages": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/bulk-edit": "1.45.0", + "@theia/callhierarchy": "1.45.0", + "@theia/console": "1.45.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/editor-preview": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/messages": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.44.0", - "@theia/notebook": "1.44.0", - "@theia/output": "1.44.0", - "@theia/plugin": "1.44.0", - "@theia/preferences": "1.44.0", - "@theia/scm": "1.44.0", - "@theia/search-in-workspace": "1.44.0", - "@theia/task": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/timeline": "1.44.0", - "@theia/typehierarchy": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/workspace": "1.44.0", - "@theia/test": "1.44.0", + "@theia/navigator": "1.45.0", + "@theia/notebook": "1.45.0", + "@theia/output": "1.45.0", + "@theia/plugin": "1.45.0", + "@theia/preferences": "1.45.0", + "@theia/scm": "1.45.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/task": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/timeline": "1.45.0", + "@theia/typehierarchy": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/workspace": "1.45.0", + "@theia/test": "1.45.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index fb03ce94327fb..d3c8dfdd48beb 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.44.0", - "@theia/metrics": "1.44.0", + "@theia/core": "1.45.0", + "@theia/metrics": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/plugin": "1.44.0", - "@theia/plugin-ext": "1.44.0" + "@theia/plugin": "1.45.0", + "@theia/plugin-ext": "1.45.0" }, "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 1413060ae147a..260f69211e174 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index fb541228a2ddd..48061689a0d53 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/userstorage": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/userstorage": "1.45.0", + "@theia/workspace": "1.45.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index cb73d607f55ba..5704c932e77fa 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/mini-browser": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/mini-browser": "1.45.0", + "@theia/monaco": "1.45.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index ee4fe3cb628d1..799f0d70490b1 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.44.0", + "@theia/core": "1.45.0", "node-pty": "0.11.0-beta17", "string-argv": "^0.1.1" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index 96b2afefb22f0..192ad48314610 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0" + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 11193dbdeceef..5262012b76e1d 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index b2725c136adfe..f62cadb415786 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/navigator": "1.44.0", - "@theia/scm": "1.44.0" + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/scm": "1.45.0" }, "publishConfig": { "access": "public" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index e398d924d66e4..23dfd44e7cbd2 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,11 +1,11 @@ { "name": "@theia/scm", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", "@types/diff": "^3.2.2", "diff": "^3.4.0", "p-debounce": "^2.1.0", @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 52a9a642a69b8..53556d2ba3308 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/navigator": "1.44.0", - "@theia/process": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/process": "1.45.0", + "@theia/workspace": "1.45.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0" @@ -47,6 +47,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index 821cd7f63fc8f..acdaa23befb8a 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.44.0" + "@theia/core": "1.45.0" }, "publishConfig": { "access": "public" @@ -38,6 +38,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index eae0a3f2c4558..64b2817055bad 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/markers": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/process": "1.44.0", - "@theia/terminal": "1.44.0", - "@theia/userstorage": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/process": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/workspace": "1.45.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0" @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 418df815a8bd3..ba1069c93d9a5 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,14 +1,14 @@ { "name": "@theia/terminal", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/process": "1.44.0", - "@theia/variable-resolver": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/process": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/workspace": "1.45.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0", "xterm-addon-search": "^0.8.2" @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index dc622b789f01f..5be5bb957e437 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/navigator": "1.44.0", - "@theia/terminal": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/terminal": "1.45.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index b71766dda3e67..a30fea1a14403 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/navigator": "1.44.0" + "@theia/core": "1.45.0", + "@theia/navigator": "1.45.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index b042d2b09939d..67c585779a1d9 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", - "@theia/file-search": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/monaco": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/monaco": "1.45.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/search-in-workspace": "1.44.0", - "@theia/userstorage": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/workspace": "1.45.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0" diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 2b15241846163..6c3d008f01805 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/editor": "1.44.0", + "@theia/core": "1.45.0", + "@theia/editor": "1.45.0", "@types/uuid": "^7.0.3", "uuid": "^8.0.0" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 128faf347d47e..32fe52e0014c2 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0" + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index 212107c8affcf..e92a79e18db93 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.44.0" + "@theia/core": "1.45.0" }, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 19fd8269872af..056e3447357b4 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,15 +1,15 @@ { "name": "@theia/vsx-registry", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/ovsx-client": "1.44.0", - "@theia/plugin-ext": "1.44.0", - "@theia/plugin-ext-vscode": "1.44.0", - "@theia/preferences": "1.44.0", - "@theia/workspace": "1.44.0", + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/ovsx-client": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/plugin-ext-vscode": "1.45.0", + "@theia/preferences": "1.45.0", + "@theia/workspace": "1.45.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0", + "@theia/ext-scripts": "1.45.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 6eee65185071b..797c43fedbdb2 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.44.0", + "version": "1.45.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.44.0", - "@theia/filesystem": "1.44.0", - "@theia/variable-resolver": "1.44.0", + "@theia/core": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/variable-resolver": "1.45.0", "jsonc-parser": "^2.2.0", "valid-filename": "^2.0.1" }, @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.44.0" + "@theia/ext-scripts": "1.45.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 550d6df713ff4..64c9d35ed75d7 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.44.0", + "version": "1.45.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 28db7507856b8..d47151177379a 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.44.0", + "version": "1.45.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { From b18be89f36382d7f84f405c9a8a801f87ec9922e Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Thu, 21 Dec 2023 14:36:28 -0500 Subject: [PATCH 030/441] core: update re-exports for `1.45.0` Signed-off-by: vince-fugnitto --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index f7998ed224233..4d0a824e94fea 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.44.0`](https://www.npmjs.com/package/@theia/application-package/v/1.44.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.44.0`](https://www.npmjs.com/package/@theia/application-package/v/1.44.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.44.0`](https://www.npmjs.com/package/@theia/application-package/v/1.44.0)) - - `@theia/request` (from [`@theia/request@1.44.0`](https://www.npmjs.com/package/@theia/request/v/1.44.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.44.0`](https://www.npmjs.com/package/@theia/request/v/1.44.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.44.0`](https://www.npmjs.com/package/@theia/request/v/1.44.0)) + - `@theia/application-package` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) + - `@theia/request` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From 72421be24d0461f811a39324579913e91056d7c4 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Wed, 3 Jan 2024 16:46:45 +0100 Subject: [PATCH 031/441] Use more stable hostname for webviews #13092 (#13225) * add optional viewId to WebviewWidgetIdentifier, which may be used to create stable hostnames for webviews * use this viewId to store a uuid which will be used as part of the stable hostname --- .../main/browser/view/plugin-view-registry.ts | 9 ++++++--- .../browser/webview/webview-widget-factory.ts | 16 +++++++++++++++- .../src/main/browser/webview/webview.ts | 1 + .../plugin-ext/src/main/browser/webviews-main.ts | 4 ++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 27ec5950870a1..03312edd5803f 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -18,7 +18,7 @@ import { injectable, inject, postConstruct, optional } from '@theia/core/shared/ import { ApplicationShell, ViewContainer as ViewContainerWidget, WidgetManager, QuickViewService, ViewContainerIdentifier, ViewContainerTitleOptions, Widget, FrontendApplicationContribution, - StatefulWidget, CommonMenus, TreeViewWelcomeWidget, ViewContainerPart, BaseWidget + StatefulWidget, CommonMenus, TreeViewWelcomeWidget, ViewContainerPart, BaseWidget, } from '@theia/core/lib/browser'; import { ViewContainer, View, ViewWelcome, PluginViewType } from '../../../common'; import { PluginSharedStyle } from '../plugin-shared-style'; @@ -424,7 +424,10 @@ export class PluginViewRegistry implements FrontendApplicationContribution { protected async createNewWebviewView(viewId: string): Promise { const webview = await this.widgetManager.getOrCreateWidget( - WebviewWidget.FACTORY_ID, { id: v4() }); + WebviewWidget.FACTORY_ID, { + id: v4(), + viewId, + }); webview.setContentOptions({ allowScripts: true }); let _description: string | undefined; @@ -893,7 +896,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { const webviewView = await this.createNewWebviewView(viewId); webviewId = webviewView.webview.identifier.id; } - const webviewWidget = this.widgetManager.getWidget(WebviewWidget.FACTORY_ID, { id: webviewId }); + const webviewWidget = this.widgetManager.getWidget(WebviewWidget.FACTORY_ID, { id: webviewId, viewId }); return webviewWidget; } diff --git a/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts b/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts index 47f4e89c0abfe..aec77f37c448e 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts @@ -17,6 +17,8 @@ import { interfaces } from '@theia/core/shared/inversify'; import { WebviewWidget, WebviewWidgetIdentifier, WebviewWidgetExternalEndpoint } from './webview'; import { WebviewEnvironment } from './webview-environment'; +import { StorageService } from '@theia/core/lib/browser'; +import { v4 } from 'uuid'; export class WebviewWidgetFactory { @@ -30,7 +32,7 @@ export class WebviewWidgetFactory { async createWidget(identifier: WebviewWidgetIdentifier): Promise { const externalEndpoint = await this.container.get(WebviewEnvironment).externalEndpoint(); - let endpoint = externalEndpoint.replace('{{uuid}}', identifier.id); + let endpoint = externalEndpoint.replace('{{uuid}}', identifier.viewId ? await this.getOrigin(this.container.get(StorageService), identifier.viewId) : identifier.id); if (endpoint[endpoint.length - 1] === '/') { endpoint = endpoint.slice(0, endpoint.length - 1); } @@ -40,4 +42,16 @@ export class WebviewWidgetFactory { return child.get(WebviewWidget); } + protected async getOrigin(storageService: StorageService, viewId: string): Promise { + const key = 'plugin-view-registry.origin.' + viewId; + const origin = await storageService.getData(key); + if (!origin) { + const newOrigin = v4(); + storageService.setData(key, newOrigin); + return newOrigin; + } else { + return origin; + } + } + } diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts index 2d35997b5cd97..fec02264a12c3 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview.ts @@ -97,6 +97,7 @@ export interface WebviewConsoleLog { @injectable() export class WebviewWidgetIdentifier { id: string; + viewId?: string; } export const WebviewWidgetExternalEndpoint = Symbol('WebviewWidgetExternalEndpoint'); diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index b6b8e29760ab2..aa534de603d0e 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -63,7 +63,7 @@ export class WebviewsMainImpl implements WebviewsMain, Disposable { showOptions: WebviewPanelShowOptions, options: WebviewPanelOptions & WebviewOptions ): Promise { - const view = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: panelId }); + const view = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: panelId, viewId: viewType }); this.hookWebview(view); view.viewType = viewType; view.title.label = title; @@ -269,7 +269,7 @@ export class WebviewsMainImpl implements WebviewsMain, Disposable { } private async tryGetWebview(id: string): Promise { - const webview = await this.widgetManager.getWidget(WebviewWidget.FACTORY_ID, { id }) + const webview = this.widgetManager.getWidgets(WebviewWidget.FACTORY_ID).find(widget => widget instanceof WebviewWidget && widget.identifier.id === id) as WebviewWidget || await this.widgetManager.getWidget(CustomEditorWidget.FACTORY_ID, { id }); return webview; } From aeee293abca62856cacdfdc67a03ed76ebaa81ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 4 Jan 2024 16:02:27 +0100 Subject: [PATCH 032/441] Fix using copy/paste from a menu in electron. Fixes #12487 (#13220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/browser/browser.ts | 7 ++++++- packages/core/src/browser/common-frontend-contribution.ts | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/core/src/browser/browser.ts b/packages/core/src/browser/browser.ts index eace2b5f2670d..c39a76057af7b 100644 --- a/packages/core/src/browser/browser.ts +++ b/packages/core/src/browser/browser.ts @@ -18,6 +18,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { environment } from '../common'; + const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : ''; export const isIE = (userAgent.indexOf('Trident') >= 0); @@ -31,7 +33,10 @@ export const isChrome = (userAgent.indexOf('Chrome') >= 0); export const isSafari = (userAgent.indexOf('Chrome') === -1) && (userAgent.indexOf('Safari') >= 0); export const isIPad = (userAgent.indexOf('iPad') >= 0); // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const isNative = typeof (window as any).process !== 'undefined'; +/** + * @deprecated us Environment.electron.is + */ +export const isNative = environment.electron.is(); // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isBasicWasmSupported = typeof (window as any).WebAssembly !== 'undefined'; diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index dc1532e3315a4..99376fa1467a5 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -351,12 +351,12 @@ export namespace CommonCommands { }); } -export const supportCut = browser.isNative || document.queryCommandSupported('cut'); -export const supportCopy = browser.isNative || document.queryCommandSupported('copy'); +export const supportCut = environment.electron.is() || document.queryCommandSupported('cut'); +export const supportCopy = environment.electron.is() || document.queryCommandSupported('copy'); // Chrome incorrectly returns true for document.queryCommandSupported('paste') // when the paste feature is available but the calling script has insufficient // privileges to actually perform the action -export const supportPaste = browser.isNative || (!browser.isChrome && document.queryCommandSupported('paste')); +export const supportPaste = environment.electron.is() || (!browser.isChrome && document.queryCommandSupported('paste')); export const RECENT_COMMANDS_STORAGE_KEY = 'commands'; From bfa524deb1cb5d8b2b273f7b6b8267f81b676a40 Mon Sep 17 00:00:00 2001 From: Torbjorn-Svensson Date: Wed, 10 Jan 2024 09:17:36 +0100 Subject: [PATCH 033/441] Fixed minor typo in error message (#13245) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed by STMicroelectronics Signed-off-by: Torbjörn SVENSSON --- packages/terminal/src/browser/terminal-frontend-contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index 0602a6ca5f078..723db33c3e477 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -990,7 +990,7 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu if (!terminalProfile) { profile = this.profileService.defaultProfile; if (!profile) { - throw new Error('There are not profiles registered'); + throw new Error('There are no profiles registered'); } } From 6a7bd87126e435a84deeda154838f8698ce9f723 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Wed, 10 Jan 2024 09:18:07 +0100 Subject: [PATCH 034/441] fix: update message for missing Electron main entries (#13242) The warning message for missing Electron main entries still refered to 'src-gen/frontend'. However since Theia 1.39.0 it should be either 'src-gen/backend' or as now indicated 'lib/backend'. --- .../application-manager/src/application-package-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/application-manager/src/application-package-manager.ts b/dev-packages/application-manager/src/application-package-manager.ts index beec9f99ec70c..86b5126c8eb6b 100644 --- a/dev-packages/application-manager/src/application-package-manager.ts +++ b/dev-packages/application-manager/src/application-package-manager.ts @@ -139,7 +139,7 @@ export class ApplicationPackageManager { console.warn( `WARNING: ${this.pck.packagePath} does not have a "main" entry.\n` + 'Please add the following line:\n' + - ' "main": "src-gen/frontend/electron-main.js"' + ' "main": "lib/backend/electron-main.js"' ); } From c48705453377f4cbfc3bb22a8973417979ecdb01 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Wed, 10 Jan 2024 18:19:06 +0100 Subject: [PATCH 035/441] Fix localization for the removeSession method (#13257) --- packages/plugin-ext/src/main/browser/authentication-main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/authentication-main.ts b/packages/plugin-ext/src/main/browser/authentication-main.ts index 046e5a7c81751..65765e457ab98 100644 --- a/packages/plugin-ext/src/main/browser/authentication-main.ts +++ b/packages/plugin-ext/src/main/browser/authentication-main.ts @@ -361,7 +361,9 @@ export class AuthenticationProviderImpl implements AuthenticationProvider { removeSession(sessionId: string): Thenable { return this.proxy.$removeSession(this.id, sessionId) - .then(() => { this.messageService.info('Successfully signed out.'); }); + .then(() => { + this.messageService.info(nls.localizeByDefault('Successfully signed out.')); + }); } } From 160dce9a8f2ebbebf4b080669ad658be37569d04 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Thu, 11 Jan 2024 14:23:59 +0100 Subject: [PATCH 036/441] Fix SelectComponent rendering in dialog (#13261) --- .../browser/menu/sample-menu-contribution.ts | 29 ++++++++++++++++++- .../src/browser/style/select-component.css | 25 ++++++++-------- .../src/browser/widgets/select-component.tsx | 1 + 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/examples/api-samples/src/browser/menu/sample-menu-contribution.ts b/examples/api-samples/src/browser/menu/sample-menu-contribution.ts index a8a2590623497..e1972603d9068 100644 --- a/examples/api-samples/src/browser/menu/sample-menu-contribution.ts +++ b/examples/api-samples/src/browser/menu/sample-menu-contribution.ts @@ -14,12 +14,16 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { ConfirmDialog, QuickInputService } from '@theia/core/lib/browser'; +import { ConfirmDialog, Dialog, QuickInputService } from '@theia/core/lib/browser'; +import { ReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog'; +import { SelectComponent } from '@theia/core/lib/browser/widgets/select-component'; import { Command, CommandContribution, CommandRegistry, MAIN_MENU_BAR, MenuContribution, MenuModelRegistry, MenuNode, MessageService, SubMenuOptions } from '@theia/core/lib/common'; import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { ReactNode } from '@theia/core/shared/react'; const SampleCommand: Command = { id: 'sample-command', @@ -50,6 +54,10 @@ const SampleQuickInputCommand: Command = { category: 'Quick Input', label: 'Test Positive Integer' }; +const SampleSelectDialog: Command = { + id: 'sample-command-select-dialog', + label: 'Sample Select Component Dialog' +}; @injectable() export class SampleCommandContribution implements CommandContribution { @@ -114,6 +122,25 @@ export class SampleCommandContribution implements CommandContribution { this.messageService.info(`Sample confirm dialog returned with: \`${JSON.stringify(choice)}\``); } }); + commands.registerCommand(SampleSelectDialog, { + execute: async () => { + await new class extends ReactDialog { + constructor() { + super({ title: 'Sample Select Component Dialog' }); + this.appendAcceptButton(Dialog.OK); + } + protected override render(): ReactNode { + return React.createElement(SelectComponent, { + options: Array.from(Array(10).keys()).map(i => ({ label: 'Option ' + ++i })), + defaultValue: 0 + }); + } + override get value(): boolean { + return true; + } + }().open(); + } + }); commands.registerCommand(SampleQuickInputCommand, { execute: async () => { const result = await this.quickInputService.input({ diff --git a/packages/core/src/browser/style/select-component.css b/packages/core/src/browser/style/select-component.css index fdcc1e175498f..887d9b7bb1a05 100644 --- a/packages/core/src/browser/style/select-component.css +++ b/packages/core/src/browser/style/select-component.css @@ -14,6 +14,13 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 ********************************************************************************/ +.theia-select-component-container { + /* required to set z-index */ + position: fixed; + /* dialog overlay has a z-index of 5000 */ + z-index: 6000; +} + .theia-select-component { background-color: var(--theia-dropdown-background); cursor: pointer; @@ -64,33 +71,25 @@ padding: 6px 5px; } -.theia-select-component-dropdown - .theia-select-component-description:first-child { +.theia-select-component-dropdown .theia-select-component-description:first-child { border-bottom: 1px solid var(--theia-editorWidget-border); margin-bottom: 2px; } -.theia-select-component-dropdown - .theia-select-component-description:last-child { +.theia-select-component-dropdown .theia-select-component-description:last-child { border-top: 1px solid var(--theia-editorWidget-border); margin-top: 2px; } -.theia-select-component-dropdown - .theia-select-component-option - .theia-select-component-option-value { +.theia-select-component-dropdown .theia-select-component-option .theia-select-component-option-value { width: 100%; } -.theia-select-component-dropdown - .theia-select-component-option - .theia-select-component-option-detail { +.theia-select-component-dropdown .theia-select-component-option .theia-select-component-option-detail { padding-left: 4px; } -.theia-select-component-dropdown - .theia-select-component-option:not(.selected) - .theia-select-component-option-detail { +.theia-select-component-dropdown .theia-select-component-option:not(.selected) .theia-select-component-option-detail { color: var(--theia-textLink-foreground); } diff --git a/packages/core/src/browser/widgets/select-component.tsx b/packages/core/src/browser/widgets/select-component.tsx index 68bcdd22d325a..0e038e5a8043e 100644 --- a/packages/core/src/browser/widgets/select-component.tsx +++ b/packages/core/src/browser/widgets/select-component.tsx @@ -77,6 +77,7 @@ export class SelectComponent extends React.Component Date: Thu, 11 Jan 2024 16:42:59 +0100 Subject: [PATCH 037/441] Pass argument to webview context menu action (#13228) --- .../src/main/browser/menus/plugin-menu-command-adapter.ts | 2 +- packages/plugin-ext/src/main/browser/webview/webview.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index 0a3cccc5e4593..c01e01cee1c37 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -108,7 +108,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { ['timeline/item/context', (...args) => this.toTimelineArgs(...args)], ['view/item/context', (...args) => this.toTreeArgs(...args)], ['view/title', noArgs], - ['webview/context', noArgs] + ['webview/context', firstArgOnly] ]).forEach(([contributionPoint, adapter]) => { if (adapter) { const paths = codeToTheiaMappings.get(contributionPoint); diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts index fec02264a12c3..f841c5be90a15 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview.ts @@ -405,6 +405,7 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract () => { this.contextMenuRenderer.render({ menuPath: WEBVIEW_CONTEXT_MENU, + args: [event.context], anchor: { x: domRect.x + event.clientX, y: domRect.y + event.clientY } From 78c90dd536150894b7bf0af65938830c4a61f2cc Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Fri, 12 Jan 2024 10:36:45 +0100 Subject: [PATCH 038/441] Fix inputbox onTriggerButton() event (#13207) Ensure that the clicked button is correctly forwarded to event handlers when using `InputBox.onDidTriggerButton` (Plugin API). The interface definition of `QuickInputButtonHandle` expects an `index` property but monaco actually calls the property `handle` Fixes #13077 Contributed on behalf of ST Microelectronics --- packages/core/src/common/quick-pick-service.ts | 2 +- packages/plugin-ext/src/plugin/quick-open.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/common/quick-pick-service.ts b/packages/core/src/common/quick-pick-service.ts index 96a1aba08bd57..6762f47cb554a 100644 --- a/packages/core/src/common/quick-pick-service.ts +++ b/packages/core/src/common/quick-pick-service.ts @@ -131,7 +131,7 @@ export namespace QuickInputButton { } export interface QuickInputButtonHandle extends QuickInputButton { - index: number; // index of where they are in buttons array if QuickInputButton or -1 if QuickInputButtons.Back + handle: number; // index of where the button is in buttons array if QuickInputButton or -1 if QuickInputButtons.Back } export enum QuickInputHideReason { diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index 82a564107a404..56523f0932507 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -246,11 +246,11 @@ export class QuickOpenExtImpl implements QuickOpenExt { async $acceptOnDidTriggerButton(sessionId: number, btn: QuickInputButtonHandle): Promise { const session = this._sessions.get(sessionId); if (session) { - if (btn.index === -1) { + if (btn.handle === -1) { session._fireButtonTrigger(QuickInputButtons.Back); } else if (session && (session instanceof InputBoxExt || session instanceof QuickPickExt)) { - const btnFromIndex = session.buttons[btn.index]; - session._fireButtonTrigger(btnFromIndex as theia.QuickInputButton); + const btnFromHandle = session.buttons[btn.handle]; + session._fireButtonTrigger(btnFromHandle as theia.QuickInputButton); } } } From f7e2ef91b0d1436f313b385cc3a961eae1dcddc4 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Fri, 12 Jan 2024 10:44:12 +0100 Subject: [PATCH 039/441] Only read unbuffered when we read the whole file (#13197) Fixes https://github.com/eclipse-theia/theia/issues/13161 --- .../filesystem/src/browser/file-service.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/filesystem/src/browser/file-service.ts b/packages/filesystem/src/browser/file-service.ts index 5cefe4c1007b1..31eb03f9084f1 100644 --- a/packages/filesystem/src/browser/file-service.ts +++ b/packages/filesystem/src/browser/file-service.ts @@ -694,12 +694,7 @@ export class FileService { async read(resource: URI, options?: ReadTextFileOptions): Promise { const [bufferStream, decoder] = await this.doRead(resource, { ...options, - // optimization: since we know that the caller does not - // care about buffering, we indicate this to the reader. - // this reduces all the overhead the buffered reading - // has (open, read, close) if the provider supports - // unbuffered reading. - preferUnbuffered: true + preferUnbuffered: this.shouldReadUnbuffered(options) }); return { @@ -888,17 +883,25 @@ export class FileService { options.mtime < stat.mtime && options.etag !== etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size }); } + protected shouldReadUnbuffered(options?: ReadFileOptions): boolean { + // optimization: since we know that the caller does not + // care about buffering, we indicate this to the reader. + // this reduces all the overhead the buffered reading + // has (open, read, close) if the provider supports + // unbuffered reading. + // + // However, if we read only part of the file we still + // want buffered reading as otherwise we need to read + // the whole file and cut out the specified part later. + return options?.position === undefined && options?.length === undefined; + } + async readFile(resource: URI, options?: ReadFileOptions): Promise { const provider = await this.withReadProvider(resource); const stream = await this.doReadAsFileStream(provider, resource, { ...options, - // optimization: since we know that the caller does not - // care about buffering, we indicate this to the reader. - // this reduces all the overhead the buffered reading - // has (open, read, close) if the provider supports - // unbuffered reading. - preferUnbuffered: true + preferUnbuffered: this.shouldReadUnbuffered(options) }); return { From 38756d62681f8f5b01bdfde6cf7ae3a946446756 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Mon, 15 Jan 2024 11:24:42 +0100 Subject: [PATCH 040/441] Improve terminalQuickFixProvider stubbed API with vscode 1.85 (#13240) Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 4 ++++ packages/plugin-ext/src/plugin/plugin-context.ts | 4 ++-- packages/plugin-ext/src/plugin/types-impl.ts | 8 ++++++-- .../src/theia.proposed.terminalQuickFixProvider.d.ts | 12 ++++++++---- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f9dc0fef1f86..b822118051b05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +## not yet released + +- [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics + ## v1.45.0 - 12/21/2023 - [application-manager] updated logic to allow rebinding messaging services in preload [#13199](https://github.com/eclipse-theia/theia/pull/13199) diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 76a6a87da7c89..a896cdfdacdb7 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -200,7 +200,7 @@ import { ExternalUriOpenerPriority, EditSessionIdentityMatch, TerminalOutputAnchor, - TerminalQuickFixExecuteTerminalCommand, + TerminalQuickFixTerminalCommand, TerminalQuickFixOpener, TestResultState } from './types-impl'; @@ -1375,7 +1375,7 @@ export function createAPIFactory( TerminalExitReason, DocumentPasteEdit, ExternalUriOpenerPriority, - TerminalQuickFixExecuteTerminalCommand, + TerminalQuickFixTerminalCommand, TerminalQuickFixOpener, EditSessionIdentityMatch, TestResultState diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 69e74af05355f..6fda0080d14f8 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3673,15 +3673,19 @@ export enum EditSessionIdentityMatch { // #endregion // #region terminalQuickFixProvider -export class TerminalQuickFixExecuteTerminalCommand { +export class TerminalQuickFixTerminalCommand { /** * The terminal command to run */ terminalCommand: string; + /** + * Whether the command should be executed or just inserted (default) + */ + shouldExecute?: boolean; /** * @stubbed */ - constructor(terminalCommand: string) { } + constructor(terminalCommand: string, shouldExecute?: boolean) { } } export class TerminalQuickFixOpener { /** diff --git a/packages/plugin/src/theia.proposed.terminalQuickFixProvider.d.ts b/packages/plugin/src/theia.proposed.terminalQuickFixProvider.d.ts index 60d86230880c4..7dcebe7a97459 100644 --- a/packages/plugin/src/theia.proposed.terminalQuickFixProvider.d.ts +++ b/packages/plugin/src/theia.proposed.terminalQuickFixProvider.d.ts @@ -40,7 +40,7 @@ export module '@theia/plugin' { * @return Terminal quick fix(es) if any */ provideTerminalQuickFixes(commandMatchResult: TerminalCommandMatchResult, token: CancellationToken): - ProviderResult>; + ProviderResult>; } export interface TerminalCommandMatchResult { @@ -52,12 +52,16 @@ export module '@theia/plugin' { }; } - export class TerminalQuickFixExecuteTerminalCommand { + export class TerminalQuickFixTerminalCommand { /** - * The terminal command to run + * The terminal command to insert or run */ terminalCommand: string; - constructor(terminalCommand: string); + /** + * Whether the command should be executed or just inserted (default) + */ + shouldExecute?: boolean; + constructor(terminalCommand: string, shouldExecute?: boolean); } export class TerminalQuickFixOpener { /** From 59ea81903b380e4ca536968bc8e6e29d6cbb6158 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Mon, 15 Jan 2024 12:30:34 +0100 Subject: [PATCH 041/441] Stub implementation of proposed API for multiDocumentHighlightProvider. (#13248) fix #13232 Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + .../plugin-ext/src/plugin/plugin-context.ts | 9 ++ packages/plugin-ext/src/plugin/types-impl.ts | 24 ++++++ packages/plugin/src/theia.d.ts | 1 + ...proposed.multiDocumentHighlightProvider.ts | 82 +++++++++++++++++++ 5 files changed, 117 insertions(+) create mode 100644 packages/plugin/src/theia.proposed.multiDocumentHighlightProvider.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b822118051b05..fcf2f5a2f8c03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## not yet released +- [plugin] stub multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics ## v1.45.0 - 12/21/2023 diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index a896cdfdacdb7..c90338531dd4e 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -87,6 +87,7 @@ import { InlineValueContext, DocumentHighlightKind, DocumentHighlight, + MultiDocumentHighlight, DocumentLink, DocumentDropEdit, CodeLens, @@ -916,6 +917,13 @@ export function createAPIFactory( registerDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.DocumentHighlightProvider): theia.Disposable { return languagesExt.registerDocumentHighlightProvider(selector, provider, pluginToPluginInfo(plugin)); }, + /** + * @stubbed + * @monaco-uplift: wait until API is available in Monaco (1.85.0+) + */ + registerMultiDocumentHighlightProvider(selector: theia.DocumentSelector, provider: theia.MultiDocumentHighlightProvider): theia.Disposable { + return Disposable.NULL; + }, registerWorkspaceSymbolProvider(provider: theia.WorkspaceSymbolProvider): theia.Disposable { return languagesExt.registerWorkspaceSymbolProvider(provider, pluginToPluginInfo(plugin)); }, @@ -1261,6 +1269,7 @@ export function createAPIFactory( InlineValueContext, DocumentHighlightKind, DocumentHighlight, + MultiDocumentHighlight, DocumentLink, DocumentDropEdit, CodeLens, diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 6fda0080d14f8..2115d74cabf74 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1593,6 +1593,30 @@ export class DocumentHighlight { } } +@es5ClassCompat +export class MultiDocumentHighlight { + + /** + * The URI of the document containing the highlights. + */ + uri: URI; + + /** + * The highlights for the document. + */ + highlights: DocumentHighlight[]; + + /** + * Creates a new instance of MultiDocumentHighlight. + * @param uri The URI of the document containing the highlights. + * @param highlights The highlights for the document. + */ + constructor(uri: URI, highlights: DocumentHighlight[]) { + this.uri = uri; + this.highlights = highlights; + } +} + export type Definition = Location | Location[]; @es5ClassCompat diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 7451907b61cb5..c27a73a6b6ddc 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -35,6 +35,7 @@ import './theia.proposed.notebookKernelSource'; import './theia.proposed.notebookMessaging'; import './theia.proposed.findTextInFiles'; import './theia.proposed.fsChunks'; +import './theia.proposed.multiDocumentHighlightProvider'; import './theia.proposed.profileContentHandlers'; import './theia.proposed.resolvers'; import './theia.proposed.scmValidation'; diff --git a/packages/plugin/src/theia.proposed.multiDocumentHighlightProvider.ts b/packages/plugin/src/theia.proposed.multiDocumentHighlightProvider.ts new file mode 100644 index 0000000000000..6f3206d522a2b --- /dev/null +++ b/packages/plugin/src/theia.proposed.multiDocumentHighlightProvider.ts @@ -0,0 +1,82 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// code copied and modified from https://github.com/microsoft/vscode/blob/1.85.1/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts + +declare module '@theia/plugin' { + + /** + * Represents a collection of document highlights from multiple documents. + */ + export class MultiDocumentHighlight { + + /** + * The URI of the document containing the highlights. + */ + uri: Uri; + + /** + * The highlights for the document. + */ + highlights: DocumentHighlight[]; + + /** + * Creates a new instance of MultiDocumentHighlight. + * @param uri The URI of the document containing the highlights. + * @param highlights The highlights for the document. + */ + constructor(uri: Uri, highlights: DocumentHighlight[]); + } + + export interface MultiDocumentHighlightProvider { + + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param otherDocuments An array of additional valid documents for which highlights should be provided. + * @param token A cancellation token. + * @returns A Map containing a mapping of the Uri of a document to the document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty map. + */ + provideMultiDocumentHighlights(document: TextDocument, position: Position, otherDocuments: TextDocument[], token: CancellationToken): + ProviderResult; + } + + namespace languages { + + /** + * Register a multi document highlight provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups sequentially asked for document highlights. + * The process stops when a provider returns a `non-falsy` or `non-failure` result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A multi-document highlight provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + * @stubbed + */ + export function registerMultiDocumentHighlightProvider(selector: DocumentSelector, provider: MultiDocumentHighlightProvider): Disposable; + } + +} From e0d49835a43a92eb0eb834886422feaf09e71773 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 15 Jan 2024 15:29:43 +0100 Subject: [PATCH 042/441] Memory Leak when using MessageService#showProgress on Backend #13253 (#13254) * dispose cancellation event listeners in RpcProtocol * introduce common DisposableWrapper --- packages/core/src/common/disposable.ts | 25 +++++++++++++++++++ .../src/common/message-rpc/rpc-protocol.ts | 24 ++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/core/src/common/disposable.ts b/packages/core/src/common/disposable.ts index 1920343c9073e..cfb07b03166b0 100644 --- a/packages/core/src/common/disposable.ts +++ b/packages/core/src/common/disposable.ts @@ -133,3 +133,28 @@ export function disposableTimeout(...args: Parameters): Dispo const handle = setTimeout(...args); return { dispose: () => clearTimeout(handle) }; } + +/** + * Wrapper for a {@link Disposable} that is not available immediately. + */ +export class DisposableWrapper implements Disposable { + + private disposed = false; + private disposable: Disposable | undefined = undefined; + + set(disposable: Disposable): void { + if (this.disposed) { + disposable.dispose(); + } else { + this.disposable = disposable; + } + } + + dispose(): void { + this.disposed = true; + if (this.disposable) { + this.disposable.dispose(); + this.disposable = undefined; + } + } +} diff --git a/packages/core/src/common/message-rpc/rpc-protocol.ts b/packages/core/src/common/message-rpc/rpc-protocol.ts index 9ebd529c4a3ed..f3f51decb3270 100644 --- a/packages/core/src/common/message-rpc/rpc-protocol.ts +++ b/packages/core/src/common/message-rpc/rpc-protocol.ts @@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { CancellationToken, CancellationTokenSource } from '../cancellation'; -import { Disposable, DisposableCollection } from '../disposable'; +import { DisposableWrapper, Disposable, DisposableCollection } from '../disposable'; import { Emitter, Event } from '../event'; import { Deferred } from '../promise-util'; import { Channel } from './channel'; @@ -57,6 +57,7 @@ export class RpcProtocol { static readonly CANCELLATION_TOKEN_KEY = 'add.cancellation.token'; protected readonly pendingRequests: Map> = new Map(); + protected readonly pendingRequestCancellationEventListeners: Map = new Map(); protected nextMessageId: number = 0; @@ -80,6 +81,8 @@ export class RpcProtocol { channel.onClose(event => { this.pendingRequests.forEach(pending => pending.reject(new Error(event.reason))); this.pendingRequests.clear(); + this.pendingRequestCancellationEventListeners.forEach(disposable => disposable.dispose()); + this.pendingRequestCancellationEventListeners.clear(); this.toDispose.dispose(); }); this.toDispose.push(channel.onMessage(readBuffer => this.handleMessage(this.decoder.parse(readBuffer())))); @@ -131,6 +134,7 @@ export class RpcProtocol { } else { throw new Error(`No reply handler for reply with id: ${id}`); } + this.disposeCancellationEventListener(id); } protected handleReplyErr(id: number, error: any): void { @@ -141,6 +145,15 @@ export class RpcProtocol { } else { throw new Error(`No reply handler for error reply with id: ${id}`); } + this.disposeCancellationEventListener(id); + } + + protected disposeCancellationEventListener(id: number): void { + const toDispose = this.pendingRequestCancellationEventListeners.get(id); + if (toDispose) { + this.pendingRequestCancellationEventListeners.delete(id); + toDispose.dispose(); + } } sendRequest(method: string, args: any[]): Promise { @@ -157,6 +170,10 @@ export class RpcProtocol { this.pendingRequests.set(id, reply); + // register disposable before output.commit() even when not available yet + const disposableWrapper = new DisposableWrapper(); + this.pendingRequestCancellationEventListeners.set(id, disposableWrapper); + const output = this.channel.getWriteBuffer(); this.encoder.request(output, id, method, args); output.commit(); @@ -164,7 +181,10 @@ export class RpcProtocol { if (cancellationToken?.isCancellationRequested) { this.sendCancel(id); } else { - cancellationToken?.onCancellationRequested(() => this.sendCancel(id)); + const disposable = cancellationToken?.onCancellationRequested(() => this.sendCancel(id)); + if (disposable) { + disposableWrapper.set(disposable); + } } return reply.promise; From abc49cd8a7e50c6ca4297bddc226851f70ec4162 Mon Sep 17 00:00:00 2001 From: Vlad Arama <86936229+vladarama@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:46:28 -0500 Subject: [PATCH 043/441] vsx-registry: implement verified extension filtering (#12995) This commit handles the contribution of the 'verified' field when searching for extensions in the vsx-registry. It implements a checkmark near the publisher name when the extension's publisher is verified. Adds a toggle button near the search bar in the vsx registry which allows for the filtering of extensions. When toggled, only verified extensions will be shown during the search which allows the user to quickly filter out extensions that might be deemed unsafe. Signed-off-by: Vlad Arama --- dev-packages/ovsx-client/src/ovsx-types.ts | 1 + .../vsx-registry/src/browser/style/index.css | 67 ++++++++++++++++- .../src/browser/vsx-extension.tsx | 49 +++++++++--- .../browser/vsx-extensions-contribution.ts | 6 ++ .../src/browser/vsx-extensions-model.ts | 75 +++++++++++++++---- .../src/browser/vsx-extensions-preferences.ts | 58 ++++++++++++++ .../src/browser/vsx-extensions-search-bar.tsx | 66 ++++++++++++---- .../browser/vsx-registry-frontend-module.ts | 2 + 8 files changed, 284 insertions(+), 40 deletions(-) create mode 100644 packages/vsx-registry/src/browser/vsx-extensions-preferences.ts diff --git a/dev-packages/ovsx-client/src/ovsx-types.ts b/dev-packages/ovsx-client/src/ovsx-types.ts index 1c0c2a197c252..21327fe9aa36b 100644 --- a/dev-packages/ovsx-client/src/ovsx-types.ts +++ b/dev-packages/ovsx-client/src/ovsx-types.ts @@ -211,6 +211,7 @@ export interface VSXExtensionRaw { version: string; timestamp: string; preview?: boolean; + verified?: boolean; displayName?: string; description?: string; categories?: string[]; diff --git a/packages/vsx-registry/src/browser/style/index.css b/packages/vsx-registry/src/browser/style/index.css index 70c619d240e33..ef05e93adaec3 100644 --- a/packages/vsx-registry/src/browser/style/index.css +++ b/packages/vsx-registry/src/browser/style/index.css @@ -21,6 +21,56 @@ ); } +.vsx-search-container { + display: flex; + align-items: center; + width: 100%; + background: var(--theia-input-background); + border-style: solid; + border-width: var(--theia-border-width); + border-color: var(--theia-input-background); + border-radius: 2px; +} + +.vsx-search-container:focus-within { + border-color: var(--theia-focusBorder); +} + +.vsx-search-container .option-buttons { + height: 23px; + display: flex; + align-items: center; + align-self: flex-start; + background-color: none; + margin: 2px; +} + +.vsx-search-container .option { + width: 21px; + height: 21px; + margin: 0 1px; + display: inline-block; + box-sizing: border-box; + align-items: center; + user-select: none; + background-repeat: no-repeat; + background-position: center; + border: var(--theia-border-width) solid transparent; + opacity: 0.7; + cursor: pointer; +} + +.vsx-search-container .option.enabled { + color: var(--theia-inputOption-activeForeground); + border: var(--theia-border-width) var(--theia-inputOption-activeBorder) solid; + background-color: var(--theia-inputOption-activeBackground); + opacity: 1; +} + +.vsx-search-container .option:hover { + opacity: 1; +} + .theia-vsx-extensions { height: 100%; } @@ -42,10 +92,14 @@ overflow: hidden; line-height: var(--theia-content-line-height); flex: 1; - padding-top: calc(var(--theia-ui-padding) / 2); - padding-bottom: calc(var(--theia-ui-padding) / 2); + margin-top: calc(var(--theia-ui-padding) / 2); + margin-bottom: calc(var(--theia-ui-padding) / 2); } +.theia-vsx-extensions-search-bar .theia-input:focus { + border: none; + outline: none; +} .theia-vsx-extension { display: flex; flex-direction: row; @@ -136,6 +190,15 @@ white-space: nowrap; } +.theia-vsx-extension-action-bar .codicon-verified-filled { + color: var(--theia-extensionIcon-verifiedForeground); + margin-right: 2px; +} + +.theia-vsx-extension-publisher-container { + display: flex; +} + .theia-vsx-extension-action-bar .action { font-size: 90%; min-width: auto !important; diff --git a/packages/vsx-registry/src/browser/vsx-extension.tsx b/packages/vsx-registry/src/browser/vsx-extension.tsx index 3e2c106c38d7f..8e33019909e93 100644 --- a/packages/vsx-registry/src/browser/vsx-extension.tsx +++ b/packages/vsx-registry/src/browser/vsx-extension.tsx @@ -28,7 +28,7 @@ import { Endpoint } from '@theia/core/lib/browser/endpoint'; import { VSXEnvironment } from '../common/vsx-environment'; import { VSXExtensionsSearchModel } from './vsx-extensions-search-model'; import { CommandRegistry, MenuPath, nls } from '@theia/core/lib/common'; -import { codicon, ContextMenuRenderer, HoverService, TreeWidget } from '@theia/core/lib/browser'; +import { codicon, ConfirmDialog, ContextMenuRenderer, HoverService, TreeWidget } from '@theia/core/lib/browser'; import { VSXExtensionNamespaceAccess, VSXUser } from '@theia/ovsx-client/lib/ovsx-types'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering'; @@ -57,6 +57,7 @@ export class VSXExtensionData { readonly license?: string; readonly readme?: string; readonly preview?: boolean; + readonly verified?: boolean; readonly namespaceAccess?: VSXExtensionNamespaceAccess; readonly publishedBy?: VSXUser; static KEYS: Set<(keyof VSXExtensionData)> = new Set([ @@ -75,6 +76,7 @@ export class VSXExtensionData { 'license', 'readme', 'preview', + 'verified', 'namespaceAccess', 'publishedBy' ]); @@ -265,6 +267,10 @@ export class VSXExtension implements VSXExtensionData, TreeElement { return this.getData('preview'); } + get verified(): boolean | undefined { + return this.getData('verified'); + } + get namespaceAccess(): VSXExtensionNamespaceAccess | undefined { return this.getData('namespaceAccess'); } @@ -297,13 +303,16 @@ export class VSXExtension implements VSXExtensionData, TreeElement { } async install(options?: PluginDeployOptions): Promise { - this._busy++; - try { - await this.progressService.withProgress(nls.localizeByDefault("Installing extension '{0}' v{1}...", this.id, this.version ?? 0), 'extensions', () => - this.pluginServer.deploy(this.uri.toString(), undefined, options) - ); - } finally { - this._busy--; + if (!this.verified) { + const choice = await new ConfirmDialog({ + title: nls.localize('theia/vsx-registry/confirmDialogTitle', 'Are you sure you want to proceed with the installation ?'), + msg: nls.localize('theia/vsx-registry/confirmDialogMessage', 'The extension "{0}" is unverified and might pose a security risk.', this.displayName) + }).open(); + if (choice) { + this.doInstall(options); + } + } else { + this.doInstall(options); } } @@ -322,6 +331,17 @@ export class VSXExtension implements VSXExtensionData, TreeElement { } } + protected async doInstall(options?: PluginDeployOptions): Promise { + this._busy++; + try { + await this.progressService.withProgress(nls.localizeByDefault("Installing extension '{0}' v{1}...", this.id, this.version ?? 0), 'extensions', () => + this.pluginServer.deploy(this.uri.toString(), undefined, options) + ); + } finally { + this._busy--; + } + } + handleContextMenu(e: React.MouseEvent): void { e.preventDefault(); this.contextMenuRenderer.render({ @@ -464,7 +484,7 @@ export namespace VSXExtensionComponent { export class VSXExtensionComponent extends AbstractVSXExtensionComponent { override render(): React.ReactNode { - const { iconUrl, publisher, displayName, description, version, downloadCount, averageRating, tooltip } = this.props.extension; + const { iconUrl, publisher, displayName, description, version, downloadCount, averageRating, tooltip, verified } = this.props.extension; return
{description}
- {publisher} +
+ {verified === true ? ( + + ) : verified === false ? ( + + ) : ( + + )} + {publisher} +
{this.renderAction(this.props.host)}
diff --git a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts index 9de6fe277faea..b301d3a0fc75d 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts +++ b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts @@ -182,6 +182,12 @@ export class VSXExtensionsContribution extends AbstractViewContribution { return this.doChange(async () => { - const searchResult = new Set(); + this._searchResult = new Set(); if (!param.query) { - this._searchResult = searchResult; return; } const client = await this.clientProvider(); @@ -225,20 +231,55 @@ export class VSXExtensionsModel { if (!allVersions) { continue; } - this.setExtension(id).update(Object.assign(data, { - publisher: data.namespace, - downloadUrl: data.files.download, - iconUrl: data.files.icon, - readmeUrl: data.files.readme, - licenseUrl: data.files.license, - version: allVersions.version - })); - searchResult.add(id); + if (this.preferences.get('extensions.onlyShowVerifiedExtensions')) { + this.fetchVerifiedStatus(id, client, allVersions).then(verified => { + this.doChange(() => { + this.addExtensions(data, id, allVersions, !!verified); + return Promise.resolve(); + }); + }); + } else { + this.addExtensions(data, id, allVersions); + this.fetchVerifiedStatus(id, client, allVersions).then(verified => { + this.doChange(() => { + let extension = this.getExtension(id); + extension = this.setExtension(id); + extension.update(Object.assign({ + verified: verified + })); + return Promise.resolve(); + }); + }); + } } - this._searchResult = searchResult; }, token); } + protected async fetchVerifiedStatus(id: string, client: OVSXClient, allVersions: VSXAllVersions): Promise { + const res = await client.query({ extensionId: id, extensionVersion: allVersions.version, includeAllVersions: true }); + let verified = res.extensions?.[0].verified; + if (!verified && res.extensions?.[0].publishedBy.loginName === 'open-vsx') { + verified = true; + } + return verified; + } + + protected addExtensions(data: VSXSearchEntry, id: string, allVersions: VSXAllVersions, verified?: boolean): void { + if (!this.preferences.get('extensions.onlyShowVerifiedExtensions') || verified) { + const extension = this.setExtension(id); + extension.update(Object.assign(data, { + publisher: data.namespace, + downloadUrl: data.files.download, + iconUrl: data.files.icon, + readmeUrl: data.files.readme, + licenseUrl: data.files.license, + version: allVersions.version, + verified: verified + })); + this._searchResult.add(id); + } + } + protected async updateInstalled(): Promise { const prevInstalled = this._installed; return this.doChange(async () => { @@ -331,6 +372,11 @@ export class VSXExtensionsModel { if (data.error) { return this.onDidFailRefresh(id, data.error); } + if (!data.verified) { + if (data.publishedBy.loginName === 'open-vsx') { + data.verified = true; + } + } extension = this.setExtension(id); extension.update(Object.assign(data, { publisher: data.namespace, @@ -338,7 +384,8 @@ export class VSXExtensionsModel { iconUrl: data.files.icon, readmeUrl: data.files.readme, licenseUrl: data.files.license, - version: data.version + version: data.version, + verified: data.verified })); return extension; } catch (e) { diff --git a/packages/vsx-registry/src/browser/vsx-extensions-preferences.ts b/packages/vsx-registry/src/browser/vsx-extensions-preferences.ts new file mode 100644 index 0000000000000..ba5406aa1c3e9 --- /dev/null +++ b/packages/vsx-registry/src/browser/vsx-extensions-preferences.ts @@ -0,0 +1,58 @@ +// ***************************************************************************** +// Copyright (C) 2023 Ericsson and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { + createPreferenceProxy, + PreferenceProxy, + PreferenceService, + PreferenceSchema, + PreferenceContribution +} from '@theia/core/lib/browser/preferences'; +import { nls } from '@theia/core'; + +export const VsxExtensionsPreferenceSchema: PreferenceSchema = { + 'type': 'object', + properties: { + 'extensions.onlyShowVerifiedExtensions': { + type: 'boolean', + default: false, + description: nls.localize('theia/vsx-registry/onlyShowVerifiedExtensionsDescription', 'This allows the {0} to only show verified extensions.', 'Open VSX Registry') + }, + } +}; + +export interface VsxExtensionsConfiguration { + 'extensions.onlyShowVerifiedExtensions': boolean; +} + +export const VsxExtensionsPreferenceContribution = Symbol('VsxExtensionsPreferenceContribution'); +export const VsxExtensionsPreferences = Symbol('VsxExtensionsPreferences'); +export type VsxExtensionsPreferences = PreferenceProxy; + +export function createVsxExtensionsPreferences(preferences: PreferenceService, schema: PreferenceSchema = VsxExtensionsPreferenceSchema): VsxExtensionsPreferences { + return createPreferenceProxy(preferences, schema); +} + +export function bindVsxExtensionsPreferences(bind: interfaces.Bind): void { + bind(VsxExtensionsPreferences).toDynamicValue(ctx => { + const preferences = ctx.container.get(PreferenceService); + const contribution = ctx.container.get(VsxExtensionsPreferenceContribution); + return createVsxExtensionsPreferences(preferences, contribution.schema); + }).inSingletonScope(); + bind(VsxExtensionsPreferenceContribution).toConstantValue({ schema: VsxExtensionsPreferenceSchema }); + bind(PreferenceContribution).toService(VsxExtensionsPreferenceContribution); +} diff --git a/packages/vsx-registry/src/browser/vsx-extensions-search-bar.tsx b/packages/vsx-registry/src/browser/vsx-extensions-search-bar.tsx index 240a480d862df..1702a41cab1f8 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-search-bar.tsx +++ b/packages/vsx-registry/src/browser/vsx-extensions-search-bar.tsx @@ -16,37 +16,57 @@ import * as React from '@theia/core/shared/react'; import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; -import { ReactWidget, Message } from '@theia/core/lib/browser/widgets'; +import { ReactWidget, Message, codicon } from '@theia/core/lib/browser/widgets'; +import { PreferenceService } from '@theia/core/lib/browser'; import { VSXExtensionsSearchModel } from './vsx-extensions-search-model'; +import { VSXExtensionsModel } from './vsx-extensions-model'; import { nls } from '@theia/core/lib/common/nls'; @injectable() export class VSXExtensionsSearchBar extends ReactWidget { + @inject(VSXExtensionsModel) + protected readonly extensionsModel: VSXExtensionsModel; + @inject(VSXExtensionsSearchModel) - protected readonly model: VSXExtensionsSearchModel; + protected readonly searchModel: VSXExtensionsSearchModel; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + protected input: HTMLInputElement | undefined; + protected onlyShowVerifiedExtensions: boolean | undefined; @postConstruct() protected init(): void { + this.onlyShowVerifiedExtensions = this.preferenceService.get('extensions.onlyShowVerifiedExtensions'); this.id = 'vsx-extensions-search-bar'; this.addClass('theia-vsx-extensions-search-bar'); - this.model.onDidChangeQuery((query: string) => this.updateSearchTerm(query)); + this.searchModel.onDidChangeQuery((query: string) => this.updateSearchTerm(query)); + this.preferenceService.onPreferenceChanged(change => { + if (change.preferenceName === 'extensions.onlyShowVerifiedExtensions') { + this.extensionsModel.setOnlyShowVerifiedExtensions(!!change.newValue); + this.onlyShowVerifiedExtensions = change.newValue; + this.update(); + } + }); } - protected input: HTMLInputElement | undefined; - protected render(): React.ReactNode { - return this.input = input || undefined} - defaultValue={this.model.query} - spellCheck={false} - className='theia-input' - placeholder={nls.localize('theia/vsx-registry/searchPlaceholder', 'Search Extensions in {0}', 'Open VSX Registry')} - onChange={this.updateQuery}> - ; + return
+ this.input = input || undefined} + defaultValue={this.searchModel.query} + spellCheck={false} + className='theia-input' + placeholder={nls.localize('theia/vsx-registry/searchPlaceholder', 'Search Extensions in {0}', 'Open VSX Registry')} + onChange={this.updateQuery}> + + {this.renderOptionContainer()} +
; } - protected updateQuery = (e: React.ChangeEvent) => this.model.query = e.target.value; + protected updateQuery = (e: React.ChangeEvent) => this.searchModel.query = e.target.value; protected updateSearchTerm(term: string): void { if (this.input) { @@ -54,6 +74,24 @@ export class VSXExtensionsSearchBar extends ReactWidget { } } + protected renderOptionContainer(): React.ReactNode { + const showVerifiedExtensions = this.renderShowVerifiedExtensions(); + return
{showVerifiedExtensions}
; + } + + protected renderShowVerifiedExtensions(): React.ReactNode { + return this.handleShowVerifiedExtensionsClick()}> + ; + } + + protected handleShowVerifiedExtensionsClick(): void { + this.extensionsModel.setOnlyShowVerifiedExtensions(!this.onlyShowVerifiedExtensions); + this.update(); + } + protected override onActivateRequest(msg: Message): void { super.onActivateRequest(msg); if (this.input) { diff --git a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts index f3ebbccd2e211..6999668966173 100644 --- a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts +++ b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts @@ -33,6 +33,7 @@ import { VSXExtensionsSourceOptions } from './vsx-extensions-source'; import { VSXExtensionsSearchModel } from './vsx-extensions-search-model'; import { bindExtensionPreferences } from './recommended-extensions/recommended-extensions-preference-contribution'; import { bindPreferenceProviderOverrides } from './recommended-extensions/preference-provider-overrides'; +import { bindVsxExtensionsPreferences } from './vsx-extensions-preferences'; import { VSXEnvironment, VSX_ENVIRONMENT_PATH } from '../common/vsx-environment'; import { LanguageQuickPickService } from '@theia/core/lib/browser/i18n/language-quick-pick-service'; import { VSXLanguageQuickPickService } from './vsx-language-quick-pick-service'; @@ -103,4 +104,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bindExtensionPreferences(bind); bindPreferenceProviderOverrides(bind, unbind); + bindVsxExtensionsPreferences(bind); }); From ded2a67e20b8e8d513157124e7e6eba0c3352608 Mon Sep 17 00:00:00 2001 From: safisa Date: Mon, 15 Jan 2024 17:47:34 +0200 Subject: [PATCH 044/441] fix for web socket disconnection: file explorer progress bar issue #13216 (#13268) Fixes #13216 --- packages/core/src/browser/tree/tree.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/browser/tree/tree.ts b/packages/core/src/browser/tree/tree.ts index 3e78cab724d47..432b67dac15f0 100644 --- a/packages/core/src/browser/tree/tree.ts +++ b/packages/core/src/browser/tree/tree.ts @@ -397,9 +397,10 @@ export class TreeImpl implements Tree { protected async doMarkAsBusy(node: Mutable, ms: number, token: CancellationToken): Promise { try { + token.onCancellationRequested(() => this.doResetBusy(node)); await timeout(ms, token); + if (token.isCancellationRequested) { return; } this.doSetBusy(node); - token.onCancellationRequested(() => this.doResetBusy(node)); } catch { /* no-op */ } From 6cd31cb6a11a9ad6970531cf106b04da157db928 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Tue, 16 Jan 2024 08:28:15 +0100 Subject: [PATCH 045/441] [RPC] Delay sending messages when there are handlers running initialization (#13180) * 'unknown document' error is thrown without mentioning what document is unknown #13158 * pause communication during init of handlers #13172 * introduce synchronizer allowing to implement this blocking mechanism --- .../plugin-ext/src/common/proxy-handler.ts | 15 ++++++- .../plugin-ext/src/common/rpc-protocol.ts | 42 ++++++++++++++++++- packages/plugin-ext/src/plugin/documents.ts | 4 +- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts index dcfe7d4a2e722..9a3f362ca7deb 100644 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ b/packages/plugin-ext/src/common/proxy-handler.ts @@ -25,11 +25,18 @@ export interface RpcHandlerOptions { } export interface ProxyHandlerOptions extends RpcHandlerOptions { channelProvider: () => Promise, + proxySynchronizer: ProxySynchronizer, } export interface InvocationHandlerOptions extends RpcHandlerOptions { target: any } + +export interface ProxySynchronizer { + startProxyInitialization(id: string, init: Promise): void + pendingProxyInitializations(): Promise +} + /** * A proxy handler that will send any method invocation on the proxied object * as a rcp protocol message over a channel. @@ -40,6 +47,7 @@ export class ClientProxyHandler implements ProxyHandler { readonly id: string; private readonly channelProvider: () => Promise; + private readonly proxySynchronizer: ProxySynchronizer; private readonly encoder: RpcMessageEncoder; private readonly decoder: RpcMessageDecoder; @@ -50,6 +58,7 @@ export class ClientProxyHandler implements ProxyHandler { private initializeRpc(): void { // we need to set the flag to true before waiting for the channel provider. Otherwise `get` might // get called again and we'll try to open a channel more than once + this.proxySynchronizer.startProxyInitialization(this.id, this.rpcDeferred.promise.then(() => { })); this.isRpcInitialized = true; const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; this.channelProvider().then(channel => { @@ -69,7 +78,7 @@ export class ClientProxyHandler implements ProxyHandler { const isNotify = this.isNotification(name); return (...args: any[]) => { const method = name.toString(); - return this.rpcDeferred.promise.then(async (connection: RpcProtocol) => { + return this.sendWhenNoInit(async (connection: RpcProtocol) => { if (isNotify) { connection.sendNotification(method, args); } else { @@ -79,6 +88,10 @@ export class ClientProxyHandler implements ProxyHandler { }; } + private sendWhenNoInit(send: (connection: RpcProtocol) => Promise): Promise { + return this.proxySynchronizer.pendingProxyInitializations().then(() => this.rpcDeferred.promise.then(send)); + } + /** * Return whether the given property represents a notification. If true, * the promise returned from the invocation will resolve immediately to `undefined` diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index 107c639ef2c60..6f2a8792437b8 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -27,11 +27,12 @@ import { Emitter, Event } from '@theia/core/lib/common/event'; import { ChannelMultiplexer, MessageProvider } from '@theia/core/lib/common/message-rpc/channel'; import { MsgPackMessageDecoder, MsgPackMessageEncoder } from '@theia/core/lib/common/message-rpc/rpc-message-encoder'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; -import { ClientProxyHandler, RpcInvocationHandler } from './proxy-handler'; +import { ClientProxyHandler, ProxySynchronizer, RpcInvocationHandler } from './proxy-handler'; import { MsgPackExtensionManager } from '@theia/core/lib/common/message-rpc/msg-pack-extension-manager'; import { URI as VSCodeURI } from '@theia/core/shared/vscode-uri'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { Range, Position } from '../plugin/types-impl'; +import { Deferred } from '@theia/core/lib/common/promise-util'; export interface MessageConnection { send(msg: string): void; @@ -83,6 +84,7 @@ export class RPCProtocolImpl implements RPCProtocol { private readonly multiplexer: ChannelMultiplexer; private readonly encoder = new MsgPackMessageEncoder(); private readonly decoder = new MsgPackMessageDecoder(); + private readonly initCallback: ProxySynchronizer; private readonly toDispose = new DisposableCollection( Disposable.create(() => { /* mark as no disposed */ }) @@ -91,6 +93,7 @@ export class RPCProtocolImpl implements RPCProtocol { constructor(channel: Channel) { this.toDispose.push(this.multiplexer = new ChannelMultiplexer(new BatchingChannel(channel))); this.toDispose.push(Disposable.create(() => this.proxies.clear())); + this.initCallback = new ProxySynchronizerImpl(); } dispose(): void { @@ -114,7 +117,9 @@ export class RPCProtocolImpl implements RPCProtocol { } protected createProxy(proxyId: string): T { - const handler = new ClientProxyHandler({ id: proxyId, encoder: this.encoder, decoder: this.decoder, channelProvider: () => this.multiplexer.open(proxyId) }); + const handler = new ClientProxyHandler({ + id: proxyId, encoder: this.encoder, decoder: this.decoder, channelProvider: () => this.multiplexer.open(proxyId), proxySynchronizer: this.initCallback + }); return new Proxy(Object.create(null), handler); } @@ -149,6 +154,39 @@ export class RPCProtocolImpl implements RPCProtocol { } } +export class ProxySynchronizerImpl implements ProxySynchronizer { + + private readonly runningInitializations = new Set(); + + private _pendingProxyInitializations: Deferred; + + constructor() { + this._pendingProxyInitializations = new Deferred(); + /* after creation no init is active */ + this._pendingProxyInitializations.resolve(); + } + + startProxyInitialization(id: string, init: Promise): void { + if (this.runningInitializations.size === 0) { + this._pendingProxyInitializations = new Deferred(); + } + init.then(() => this.finishedProxyInitialization(id)); + this.runningInitializations.add(id); + } + + protected finishedProxyInitialization(id: string): void { + this.runningInitializations.delete(id); + if (this.runningInitializations.size === 0) { + this._pendingProxyInitializations.resolve(); + } + } + + pendingProxyInitializations(): Promise { + return this._pendingProxyInitializations.promise; + } + +} + /** * Wraps and underlying channel to send/receive multiple messages in one go: * - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`. diff --git a/packages/plugin-ext/src/plugin/documents.ts b/packages/plugin-ext/src/plugin/documents.ts index 3eee7ade3a3d6..dc6bbbefe3ce6 100644 --- a/packages/plugin-ext/src/plugin/documents.ts +++ b/packages/plugin-ext/src/plugin/documents.ts @@ -158,7 +158,7 @@ export class DocumentsExtImpl implements DocumentsExt { const uriString = uri.toString(); const data = this.editorsAndDocuments.getDocument(uriString); if (!data) { - throw new Error('unknown document'); + throw new Error('unknown document: ' + uriString); } data.acceptIsDirty(isDirty); this._onDidChangeDocument.fire({ @@ -172,7 +172,7 @@ export class DocumentsExtImpl implements DocumentsExt { const uriString = uri.toString(); const data = this.editorsAndDocuments.getDocument(uriString); if (!data) { - throw new Error('unknown document'); + throw new Error('unknown document: ' + uriString); } data.acceptIsDirty(isDirty); data.onEvents(e); From 72a44322a4d1c4ab8d8d8ea05e5b6bf1990bb719 Mon Sep 17 00:00:00 2001 From: Haydar Metin Date: Tue, 16 Jan 2024 10:46:32 +0100 Subject: [PATCH 046/441] Add keybindings to toggle the tree checkbox (#13271) --- packages/core/src/browser/tree/tree-widget.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index 029ff65db4a02..163bdf561b4c9 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -1227,12 +1227,15 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { /** * Handle the `space key` keyboard event. - * - By default should be similar to a single-click action. + * - If the element has a checkbox, it will be toggled. + * - Otherwise, it should be similar to a single-click action. * @param event the `space key` keyboard event. */ protected handleSpace(event: KeyboardEvent): void { const { focusedNode } = this.focusService; - if (!this.props.multiSelect || (!event.ctrlKey && !event.metaKey && !event.shiftKey)) { + if (focusedNode && focusedNode.checkboxInfo) { + this.model.markAsChecked(focusedNode, !focusedNode.checkboxInfo.checked); + } else if (!this.props.multiSelect || (!event.ctrlKey && !event.metaKey && !event.shiftKey)) { this.tapNode(focusedNode); } } From b25357fee2e36506495fa149a9726b451632d487 Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Thu, 11 Jan 2024 10:53:06 +0100 Subject: [PATCH 047/441] fix: preserve formatting in .tsx files The formatting in .tsx files and the one applied by the formatter were mismatched. Contributed on behalf of STMicroelectronics Signed-off-by: Alexandra Buzila --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5b108a74ccc8b..8166ed2cb0656 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -59,4 +59,9 @@ 180 ], "typescript.preferences.quoteStyle": "single", + "[typescriptreact]": { + "editor.defaultFormatter": "vscode.typescript-language-features", + "typescript.preferences.quoteStyle": "single", + "editor.tabSize": 4, + } } From 3569baa6f79d3034012b1908be7384d9ca589f52 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 16 Jan 2024 14:05:55 +0100 Subject: [PATCH 048/441] Improve notebook error logging (#13256) --- .../service/notebook-kernel-history-service.ts | 12 +++++------- .../service/notebook-kernel-quick-pick-service.ts | 2 ++ .../src/plugin/notebook/notebook-kernels.ts | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-kernel-history-service.ts b/packages/notebook/src/browser/service/notebook-kernel-history-service.ts index 5115820134209..70a3c79389976 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-history-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-history-service.ts @@ -48,10 +48,8 @@ export class NotebookKernelHistoryService implements Disposable { @inject(CommandService) protected commandService: CommandService; - declare serviceBrand: undefined; - - private static STORAGE_KEY = 'notebook.kernelHistory'; - private mostRecentKernelsMap: KernelsList = {}; + protected static STORAGE_KEY = 'notebook.kernelHistory'; + protected mostRecentKernelsMap: KernelsList = {}; @postConstruct() protected init(): void { @@ -97,16 +95,16 @@ export class NotebookKernelHistoryService implements Disposable { this.saveState(); } - private saveState(): void { + protected saveState(): void { let notEmpty = false; - for (const [_, kernels] of Object.entries(this.mostRecentKernelsMap)) { + for (const kernels of Object.values(this.mostRecentKernelsMap)) { notEmpty = notEmpty || Object.entries(kernels).length > 0; } this.storageService.setData(NotebookKernelHistoryService.STORAGE_KEY, notEmpty ? this.mostRecentKernelsMap : undefined); } - private async loadState(): Promise { + protected async loadState(): Promise { const kernelMap = await this.storageService.getData(NotebookKernelHistoryService.STORAGE_KEY); if (kernelMap) { this.mostRecentKernelsMap = kernelMap as KernelsList; diff --git a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts index b1387fa7241d5..7b999993202d1 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts @@ -356,6 +356,7 @@ export class NotebookKernelQuickPickService { return this.displaySelectAnotherQuickPick(editor, false); } } catch (ex) { + console.error('Failed to select notebook kernel', ex); return false; } } else if (isKernelPick(selectedKernelPickItem)) { @@ -370,6 +371,7 @@ export class NotebookKernelQuickPickService { await selectedKernelPickItem.action.run(this.commandService); return true; } catch (ex) { + console.error('Failed to select notebook kernel', ex); return false; } } diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts index f03038eabebde..9dddde9768293 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts @@ -334,7 +334,6 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller); } catch (err) { console.error(`NotebookController[${handle}] execute cells FAILED`, err); - console.error(err); } } From a6d7e9b6250a14e008477436fe97592761525776 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Tue, 16 Jan 2024 14:18:27 +0100 Subject: [PATCH 049/441] Fix stack overflow for cyclic menu contribution (#13264) --- .../src/common/menu/menu-model-registry.ts | 27 +++++++++++++--- packages/core/src/common/menu/menu.spec.ts | 32 +++++++++++++++++-- .../menus/menus-contribution-handler.ts | 11 +++++-- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/packages/core/src/common/menu/menu-model-registry.ts b/packages/core/src/common/menu/menu-model-registry.ts index c29d69ecf3cad..42b4a93ac9b96 100644 --- a/packages/core/src/common/menu/menu-model-registry.ts +++ b/packages/core/src/common/menu/menu-model-registry.ts @@ -14,13 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, named } from 'inversify'; -import { Disposable } from '../disposable'; -import { CommandRegistry, Command } from '../command'; +import { inject, injectable, named } from 'inversify'; +import { Command, CommandRegistry } from '../command'; import { ContributionProvider } from '../contribution-provider'; -import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node'; -import { CompoundMenuNode, MenuAction, MenuNode, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types'; +import { Disposable } from '../disposable'; import { ActionMenuNode } from './action-menu-node'; +import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node'; +import { CompoundMenuNode, MenuAction, MenuNode, MenuNodeMetadata, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types'; export const MenuContribution = Symbol('MenuContribution'); @@ -157,6 +157,23 @@ export class MenuModelRegistry { linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable { const child = this.getMenuNode(childId); const parent = this.getMenuNode(parentPath, group); + + const isRecursive = (node: MenuNodeMetadata, childNode: MenuNodeMetadata): boolean => { + if (node.id === childNode.id) { + return true; + } + if (node.parent) { + return isRecursive(node.parent, childNode); + } + return false; + }; + + // check for menu contribution recursion + if (isRecursive(parent, child)) { + console.warn(`Recursive menu contribution detected: ${child.id} is already in hierarchy of ${parent.id}.`); + return Disposable.NULL; + } + const wrapper = new CompositeMenuNodeWrapper(child, parent, options); return parent.addNode(wrapper); } diff --git a/packages/core/src/common/menu/menu.spec.ts b/packages/core/src/common/menu/menu.spec.ts index 78b769c4d0da8..650ae274574d0 100644 --- a/packages/core/src/common/menu/menu.spec.ts +++ b/packages/core/src/common/menu/menu.spec.ts @@ -14,10 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { CommandContribution, CommandRegistry } from '../command'; -import { MenuContribution, MenuModelRegistry } from './menu-model-registry'; import * as chai from 'chai'; +import { CommandContribution, CommandRegistry } from '../command'; import { CompositeMenuNode } from './composite-menu-node'; +import { MenuContribution, MenuModelRegistry } from './menu-model-registry'; const expect = chai.expect; @@ -61,6 +61,25 @@ describe('menu-model-registry', () => { expect(openGroup.children.length).equals(2); expect(openGroup.label).undefined; }); + + it('Should not allow to register cyclic menus.', () => { + const fileMenu = ['main', 'File']; + const fileOpenMenu = [...fileMenu, '0_open']; + const fileCloseMenu = [...fileMenu, '1_close']; + const service = createMenuRegistry({ + registerMenus(menuRegistry: MenuModelRegistry): void { + menuRegistry.registerSubmenu(fileMenu, 'File'); + // open menu should not be added to open menu + menuRegistry.linkSubmenu(fileOpenMenu, fileOpenMenu); + // close menu should be added + menuRegistry.linkSubmenu(fileOpenMenu, fileCloseMenu); + } + }, { + registerCommands(reg: CommandRegistry): void { } + }); + const all = service.getMenu() as CompositeMenuNode; + expect(menuStructureToString(all.children[0] as CompositeMenuNode)).equals('File(0_open(1_close),1_close())'); + }); }); }); @@ -71,3 +90,12 @@ function createMenuRegistry(menuContrib: MenuContribution, commandContrib: Comma menuReg.onStart(); return menuReg; } + +function menuStructureToString(node: CompositeMenuNode): string { + return node.children.map(c => { + if (c instanceof CompositeMenuNode) { + return `${c.id}(${menuStructureToString(c)})`; + } + return c.id; + }).join(','); +} diff --git a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts index 2ffb27f5951cd..4c23f436a7b8e 100644 --- a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts @@ -99,9 +99,12 @@ export class MenusContributionPointHandler { const targets = this.getMatchingMenu(contributionPoint as ContributionPoint) ?? [contributionPoint]; const { group, order } = this.parseGroup(item.group); const { submenu, command } = item; - if (submenu) { - targets.forEach(target => toDispose.push(this.menuRegistry.linkSubmenu(target, submenu!, { order, when: item.when }, group))); - } else if (command) { + if (submenu && command) { + console.warn( + `Menu item ${command} from plugin ${plugin.metadata.model.id} contributed both submenu and command. Only command will be registered.` + ); + } + if (command) { toDispose.push(this.commandAdapter.addCommand(command)); targets.forEach(target => { const node = new ActionMenuNode({ @@ -112,6 +115,8 @@ export class MenusContributionPointHandler { const parent = this.menuRegistry.getMenuNode(target, group); toDispose.push(parent.addNode(node)); }); + } else if (submenu) { + targets.forEach(target => toDispose.push(this.menuRegistry.linkSubmenu(target, submenu!, { order, when: item.when }, group))); } } } catch (error) { From 6f01be59a6d14070a6169fcfdfaddb812611f8e0 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Tue, 16 Jan 2024 14:36:42 +0100 Subject: [PATCH 050/441] Support 'browser-only' Theia (#12853) Adds tooling, adapts the current code base and provides an example application for the new 'browser-only' Theia application target. This target generates a Theia frontend application which can run without a backend, transforming the Theia application to a static site. Adapts tooling to: - support new 'browser-only' application target - support new 'frontendOnly' and 'frontendOnlyPreload' module declarations - use http-server when starting 'browser-only' applications Replaces backend services with browser-only variants - implementation of BrowserFS-based filesystem - implementation of 'ServiceConnectionProvider' which timeouts for all requests, enabling loading all Theia packages Adds a browser-only example application and api-samples showcasing the customization of the new BrowserFS-based filesystem. Co-authored-by: Remi Schnekenburger Co-authored-by: Alexandra Buzila Co-authored-by: Tobias Ortmayr Co-authored-by: Eugen Neufeld --- CHANGELOG.md | 4 + dev-packages/application-manager/package.json | 1 + .../src/application-package-manager.ts | 22 + .../src/generator/abstract-generator.ts | 4 + .../src/generator/backend-generator.ts | 4 + .../src/generator/frontend-generator.ts | 16 +- .../src/generator/webpack-generator.ts | 11 +- .../application-manager/src/rebuild.ts | 2 +- .../src/application-package.ts | 39 +- .../src/application-props.ts | 5 +- .../src/extension-package.ts | 2 + dev-packages/cli/package.json | 1 + dev-packages/cli/src/theia.ts | 4 +- examples/api-samples/package.json | 3 + .../api-samples-frontend-only-module.ts | 27 + .../example-filesystem-initialization.ts | 51 ++ examples/browser-only/package.json | 78 +++ examples/browser-only/tsconfig.json | 141 ++++++ examples/browser-only/webpack.config.js | 18 + package.json | 3 +- packages/core/package.json | 4 + .../frontend-only-application-module.ts | 114 +++++ .../i18n/i18n-frontend-only-module.ts | 37 ++ .../logger-frontend-only-module.ts | 63 +++ ...ontend-only-service-connection-provider.ts | 39 ++ .../messaging-frontend-only-module.ts | 42 ++ .../preload/frontend-only-preload-module.ts | 49 ++ .../browser/frontend-application-module.ts | 1 - .../core/src/{node => common}/file-uri.ts | 4 +- .../electron-main-application.ts | 2 +- .../electron-main/theia-electron-window.ts | 2 +- .../env-variables/env-variables-server.ts | 2 +- packages/core/src/node/file-uri.spec.ts | 2 +- packages/core/src/node/index.ts | 2 +- .../linux-external-terminal-service.ts | 2 +- .../mac-external-terminal-service.ts | 2 +- .../windows-external-terminal-service.ts | 2 +- .../src/node/file-search-service-impl.ts | 2 +- packages/filesystem/package.json | 4 + ...browser-only-filesystem-frontend-module.ts | 38 ++ ...browser-only-filesystem-provider-server.ts | 32 ++ .../browserfs-filesystem-initialization.ts | 61 +++ .../browserfs-filesystem-provider.ts | 462 ++++++++++++++++++ .../electron-file-dialog-service.ts | 2 +- .../node/disk-file-system-provider.spec.ts | 2 +- .../src/node/disk-file-system-provider.ts | 2 +- .../node/download/directory-archiver.spec.ts | 2 +- .../src/node/download/directory-archiver.ts | 2 +- .../node/download/file-download-endpoint.ts | 2 +- .../node/download/file-download-handler.ts | 2 +- .../src/node/file-change-collection.spec.ts | 2 +- .../nsfw-watcher/nsfw-filesystem-service.ts | 2 +- .../src/node/dugite-git-watcher.slow-spec.ts | 2 +- packages/git/src/node/dugite-git.slow-spec.ts | 2 +- packages/git/src/node/dugite-git.spec.ts | 2 +- packages/git/src/node/dugite-git.ts | 2 +- .../src/node/mini-browser-endpoint.ts | 2 +- .../src/browser/navigator-diff.spec.ts | 2 +- .../src/node/hosted-instance-manager.ts | 2 +- .../src/node/plugin-vscode-file-handler.ts | 2 +- .../node/scanners/file-plugin-uri-factory.ts | 2 +- .../handlers/plugin-theia-file-handler.ts | 2 +- .../main/node/plugins-key-value-storage.ts | 2 +- ...ep-search-in-workspace-server.slow-spec.ts | 2 +- .../ripgrep-search-in-workspace-server.ts | 2 +- packages/terminal/src/node/shell-process.ts | 2 +- packages/workspace/package.json | 3 + .../browser-only-workspace-server.ts | 69 +++ .../workspace-frontend-only-module.ts | 28 ++ tsconfig.json | 3 + yarn.lock | 112 ++++- 71 files changed, 1602 insertions(+), 63 deletions(-) create mode 100644 examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts create mode 100644 examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts create mode 100644 examples/browser-only/package.json create mode 100644 examples/browser-only/tsconfig.json create mode 100644 examples/browser-only/webpack.config.js create mode 100644 packages/core/src/browser-only/frontend-only-application-module.ts create mode 100644 packages/core/src/browser-only/i18n/i18n-frontend-only-module.ts create mode 100644 packages/core/src/browser-only/logger-frontend-only-module.ts create mode 100644 packages/core/src/browser-only/messaging/frontend-only-service-connection-provider.ts create mode 100644 packages/core/src/browser-only/messaging/messaging-frontend-only-module.ts create mode 100644 packages/core/src/browser-only/preload/frontend-only-preload-module.ts rename packages/core/src/{node => common}/file-uri.ts (97%) create mode 100644 packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts create mode 100644 packages/filesystem/src/browser-only/browser-only-filesystem-provider-server.ts create mode 100644 packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts create mode 100644 packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts create mode 100644 packages/workspace/src/browser-only/browser-only-workspace-server.ts create mode 100644 packages/workspace/src/browser-only/workspace-frontend-only-module.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf2f5a2f8c03..505c6060e9f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ - [plugin] stub multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics +[Breaking Changes:](#breaking_changes_not_yet_released) + +- [core] moved `FileUri` from `node` package to `common` + ## v1.45.0 - 12/21/2023 - [application-manager] updated logic to allow rebinding messaging services in preload [#13199](https://github.com/eclipse-theia/theia/pull/13199) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 1d7cfbdf2478e..7ad70e5831baf 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -45,6 +45,7 @@ "css-loader": "^6.2.0", "electron-rebuild": "^3.2.7", "fs-extra": "^4.0.2", + "http-server": "^14.1.1", "ignore-loader": "^0.1.2", "less": "^3.0.3", "mini-css-extract-plugin": "^2.6.1", diff --git a/dev-packages/application-manager/src/application-package-manager.ts b/dev-packages/application-manager/src/application-package-manager.ts index 86b5126c8eb6b..09d3d26956789 100644 --- a/dev-packages/application-manager/src/application-package-manager.ts +++ b/dev-packages/application-manager/src/application-package-manager.ts @@ -119,10 +119,32 @@ export class ApplicationPackageManager { start(args: string[] = []): cp.ChildProcess { if (this.pck.isElectron()) { return this.startElectron(args); + } else if (this.pck.isBrowserOnly()) { + return this.startBrowserOnly(args); } return this.startBrowser(args); } + startBrowserOnly(args: string[]): cp.ChildProcess { + const { command, mainArgs, options } = this.adjustBrowserOnlyArgs(args); + return this.__process.spawnBin(command, mainArgs, options); + } + + adjustBrowserOnlyArgs(args: string[]): Readonly<{ command: string, mainArgs: string[]; options: cp.SpawnOptions }> { + let { mainArgs, options } = this.adjustArgs(args); + + // first parameter: path to generated frontend + // second parameter: disable cache to support watching + mainArgs = ['lib/frontend', '-c-1', ...mainArgs]; + + const portIndex = mainArgs.findIndex(v => v.startsWith('--port')); + if (portIndex === -1) { + mainArgs.push('--port=3000'); + } + + return { command: 'http-server', mainArgs, options }; + } + startElectron(args: string[]): cp.ChildProcess { // If possible, pass the project root directory to electron rather than the script file so that Electron // can determine the app name. This requires that the package.json has a main field. diff --git a/dev-packages/application-manager/src/generator/abstract-generator.ts b/dev-packages/application-manager/src/generator/abstract-generator.ts index 2ff52527ef3ec..da35c5d588849 100644 --- a/dev-packages/application-manager/src/generator/abstract-generator.ts +++ b/dev-packages/application-manager/src/generator/abstract-generator.ts @@ -37,6 +37,10 @@ export abstract class AbstractGenerator { return this.pck.ifElectron(value, defaultValue); } + protected ifBrowserOnly(value: string, defaultValue: string = ''): string { + return this.pck.ifBrowserOnly(value, defaultValue); + } + protected async write(path: string, content: string): Promise { await fs.ensureFile(path); await fs.writeFile(path, content); diff --git a/dev-packages/application-manager/src/generator/backend-generator.ts b/dev-packages/application-manager/src/generator/backend-generator.ts index 5f457d0d9dba5..a5df1bdb80b46 100644 --- a/dev-packages/application-manager/src/generator/backend-generator.ts +++ b/dev-packages/application-manager/src/generator/backend-generator.ts @@ -20,6 +20,10 @@ import { AbstractGenerator } from './abstract-generator'; export class BackendGenerator extends AbstractGenerator { async generate(): Promise { + if (this.pck.isBrowserOnly()) { + // no backend generation in case of browser-only target + return; + } const backendModules = this.pck.targetBackendModules; await this.write(this.pck.backend('server.js'), this.compileServer(backendModules)); await this.write(this.pck.backend('main.js'), this.compileMain(backendModules)); diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index d4218f3b174b6..672b11cbcb43e 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -24,7 +24,7 @@ export class FrontendGenerator extends AbstractGenerator { async generate(options?: GeneratorOptions): Promise { await this.write(this.pck.frontend('index.html'), this.compileIndexHtml(this.pck.targetFrontendModules)); - await this.write(this.pck.frontend('index.js'), this.compileIndexJs(this.pck.targetFrontendModules, this.pck.frontendPreloadModules)); + await this.write(this.pck.frontend('index.js'), this.compileIndexJs(this.pck.targetFrontendModules, this.pck.targetFrontendPreloadModules)); await this.write(this.pck.frontend('secondary-window.html'), this.compileSecondaryWindowHtml()); await this.write(this.pck.frontend('secondary-index.js'), this.compileSecondaryIndexJs(this.pck.secondaryWindowModules)); if (this.pck.isElectron()) { @@ -108,18 +108,26 @@ ${Array.from(frontendPreloadModules.values(), jsModulePath => `\ } module.exports = (async () => { - const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowser() - ? 'browser/messaging/messaging-frontend-module' - : 'electron-browser/messaging/electron-messaging-frontend-module'}'); + const { messagingFrontendModule } = require('@theia/core/lib/${!this.pck.isElectron() + ? 'browser/messaging/messaging-frontend-module' + : 'electron-browser/messaging/electron-messaging-frontend-module'}'); const container = new Container(); container.load(messagingFrontendModule); + ${this.ifBrowserOnly(`const { messagingFrontendOnlyModule } = require('@theia/core/lib/browser-only/messaging/messaging-frontend-only-module'); + container.load(messagingFrontendOnlyModule);`)} + await preload(container); const { FrontendApplication } = require('@theia/core/lib/browser'); const { frontendApplicationModule } = require('@theia/core/lib/browser/frontend-application-module'); const { loggerFrontendModule } = require('@theia/core/lib/browser/logger-frontend-module'); container.load(frontendApplicationModule); + ${this.pck.ifBrowserOnly(`const { frontendOnlyApplicationModule } = require('@theia/core/lib/browser-only/frontend-only-application-module'); + container.load(frontendOnlyApplicationModule);`)} + container.load(loggerFrontendModule); + ${this.ifBrowserOnly(`const { loggerFrontendOnlyModule } = require('@theia/core/lib/browser-only/logger-frontend-only-module'); + container.load(loggerFrontendOnlyModule);`)} try { ${Array.from(frontendModules.values(), jsModulePath => `\ diff --git a/dev-packages/application-manager/src/generator/webpack-generator.ts b/dev-packages/application-manager/src/generator/webpack-generator.ts index e650c7a7dbd09..fa942815843b9 100644 --- a/dev-packages/application-manager/src/generator/webpack-generator.ts +++ b/dev-packages/application-manager/src/generator/webpack-generator.ts @@ -22,7 +22,9 @@ export class WebpackGenerator extends AbstractGenerator { async generate(): Promise { await this.write(this.genConfigPath, this.compileWebpackConfig()); - await this.write(this.genNodeConfigPath, this.compileNodeWebpackConfig()); + if (!this.pck.isBrowserOnly()) { + await this.write(this.genNodeConfigPath, this.compileNodeWebpackConfig()); + } if (await this.shouldGenerateUserWebpackConfig()) { await this.write(this.configPath, this.compileUserWebpackConfig()); } @@ -332,7 +334,7 @@ module.exports = [{ */ // @ts-check const configs = require('./${paths.basename(this.genConfigPath)}'); -const nodeConfig = require('./${paths.basename(this.genNodeConfigPath)}'); +${this.ifBrowserOnly('', `const nodeConfig = require('./${paths.basename(this.genNodeConfigPath)}');`)} /** * Expose bundled modules on window.theia.moduleName namespace, e.g. @@ -343,10 +345,11 @@ configs[0].module.rules.push({ loader: require.resolve('@theia/application-manager/lib/expose-loader') }); */ -module.exports = [ +${this.ifBrowserOnly('module.exports = configs;', `module.exports = [ ...configs, nodeConfig.config -];`; +];`)} +`; } protected compileNodeWebpackConfig(): string { diff --git a/dev-packages/application-manager/src/rebuild.ts b/dev-packages/application-manager/src/rebuild.ts index 0784c315558ba..82e454be7c802 100644 --- a/dev-packages/application-manager/src/rebuild.ts +++ b/dev-packages/application-manager/src/rebuild.ts @@ -19,7 +19,7 @@ import fs = require('fs-extra'); import path = require('path'); import os = require('os'); -export type RebuildTarget = 'electron' | 'browser'; +export type RebuildTarget = 'electron' | 'browser' | 'browser-only'; const EXIT_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM']; diff --git a/dev-packages/application-package/src/application-package.ts b/dev-packages/application-package/src/application-package.ts index 68b5cb896c6ec..700dec43bb2d8 100644 --- a/dev-packages/application-package/src/application-package.ts +++ b/dev-packages/application-package/src/application-package.ts @@ -73,7 +73,7 @@ export class ApplicationPackage { theia.target = this.options.appTarget; } - if (theia.target && !(theia.target in ApplicationProps.ApplicationTarget)) { + if (theia.target && !(Object.values(ApplicationProps.ApplicationTarget).includes(theia.target))) { const defaultTarget = ApplicationProps.ApplicationTarget.browser; console.warn(`Unknown application target '${theia.target}', '${defaultTarget}' to be used instead`); theia.target = defaultTarget; @@ -140,10 +140,24 @@ export class ApplicationPackage { return this._frontendPreloadModules ??= this.computeModules('frontendPreload'); } + get frontendOnlyPreloadModules(): Map { + if (!this._frontendPreloadModules) { + this._frontendPreloadModules = this.computeModules('frontendOnlyPreload', 'frontendPreload'); + } + return this._frontendPreloadModules; + } + get frontendModules(): Map { return this._frontendModules ??= this.computeModules('frontend'); } + get frontendOnlyModules(): Map { + if (!this._frontendModules) { + this._frontendModules = this.computeModules('frontendOnly', 'frontend'); + } + return this._frontendModules; + } + get frontendElectronModules(): Map { return this._frontendElectronModules ??= this.computeModules('frontendElectron', 'frontend'); } @@ -227,6 +241,10 @@ export class ApplicationPackage { return this.target === ApplicationProps.ApplicationTarget.electron; } + isBrowserOnly(): boolean { + return this.target === ApplicationProps.ApplicationTarget.browserOnly; + } + ifBrowser(value: T): T | undefined; ifBrowser(value: T, defaultValue: T): T; ifBrowser(value: T, defaultValue?: T): T | undefined { @@ -239,14 +257,33 @@ export class ApplicationPackage { return this.isElectron() ? value : defaultValue; } + ifBrowserOnly(value: T): T | undefined; + ifBrowserOnly(value: T, defaultValue: T): T; + ifBrowserOnly(value: T, defaultValue?: T): T | undefined { + return this.isBrowserOnly() ? value : defaultValue; + } + get targetBackendModules(): Map { + if (this.isBrowserOnly()) { + return new Map(); + } return this.ifBrowser(this.backendModules, this.backendElectronModules); } get targetFrontendModules(): Map { + if (this.isBrowserOnly()) { + return this.frontendOnlyModules; + } return this.ifBrowser(this.frontendModules, this.frontendElectronModules); } + get targetFrontendPreloadModules(): Map { + if (this.isBrowserOnly()) { + return this.frontendOnlyPreloadModules; + } + return this.frontendPreloadModules; + } + get targetElectronMainModules(): Map { return this.ifElectron(this.electronMainModules, new Map()); } diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index 78bcece1c011e..a443a60df679d 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -241,10 +241,11 @@ export interface ApplicationProps extends NpmRegistryProps { }; } export namespace ApplicationProps { - export type Target = keyof typeof ApplicationTarget; + export type Target = `${ApplicationTarget}`; export enum ApplicationTarget { browser = 'browser', - electron = 'electron' + electron = 'electron', + browserOnly = 'browser-only' }; export const DEFAULT: ApplicationProps = { ...NpmRegistryProps.DEFAULT, diff --git a/dev-packages/application-package/src/extension-package.ts b/dev-packages/application-package/src/extension-package.ts index 8ccd8d661513a..dced483c99674 100644 --- a/dev-packages/application-package/src/extension-package.ts +++ b/dev-packages/application-package/src/extension-package.ts @@ -21,7 +21,9 @@ import { NpmRegistry, PublishedNodePackage, NodePackage } from './npm-registry'; export interface Extension { frontendPreload?: string; + frontendOnlyPreload?: string; frontend?: string; + frontendOnly?: string; frontendElectron?: string; secondaryWindow?: string; backend?: string; diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index ee50fc60bfde2..7b70f54e5d527 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -44,6 +44,7 @@ "decompress": "^4.2.1", "escape-string-regexp": "4.0.0", "glob": "^8.0.3", + "http-server": "^14.1.1", "limiter": "^2.1.0", "log-update": "^4.0.0", "mocha": "^10.1.0", diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index 41acf9d6e09e4..d215c578de288 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -94,12 +94,12 @@ function rebuildCommand(command: string, target: ApplicationProps.Target): yargs } function defineCommonOptions(cli: yargs.Argv): yargs.Argv { return cli .option('app-target', { description: 'The target application type. Overrides `theia.target` in the application\'s package.json', - choices: ['browser', 'electron'] as const, + choices: ['browser', 'electron', 'browser-only'] as const, }); } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index d300bb90c332b..28f584a2356e9 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -29,6 +29,9 @@ { "electronMain": "lib/electron-main/update/sample-updater-main-module", "frontendElectron": "lib/electron-browser/updater/sample-updater-frontend-module" + }, + { + "frontendOnly": "lib/browser-only/api-samples-frontend-only-module" } ], "keywords": [ diff --git a/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts b/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts new file mode 100644 index 0000000000000..2fe1703ffa8b1 --- /dev/null +++ b/examples/api-samples/src/browser-only/api-samples-frontend-only-module.ts @@ -0,0 +1,27 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; +import { bindBrowserFSInitialization } from './filesystem/example-filesystem-initialization'; + +export default new ContainerModule(( + bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + rebind: interfaces.Rebind, +) => { + bindBrowserFSInitialization(bind, rebind); +}); diff --git a/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts b/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts new file mode 100644 index 0000000000000..f048231f977a5 --- /dev/null +++ b/examples/api-samples/src/browser-only/filesystem/example-filesystem-initialization.ts @@ -0,0 +1,51 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { URI } from '@theia/core'; +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; +import { EncodingService } from '@theia/core/lib/common/encoding-service'; +import { BrowserFSInitialization, DefaultBrowserFSInitialization } from '@theia/filesystem/lib/browser-only/browserfs-filesystem-initialization'; +import { BrowserFSFileSystemProvider } from '@theia/filesystem/lib/browser-only/browserfs-filesystem-provider'; +import type { FSModule } from 'browserfs/dist/node/core/FS'; + +@injectable() +export class ExampleBrowserFSInitialization extends DefaultBrowserFSInitialization { + + @inject(EncodingService) + protected encodingService: EncodingService; + + override async initializeFS(fs: FSModule, provider: BrowserFSFileSystemProvider): Promise { + try { + if (!fs.existsSync('/home/workspace')) { + await provider.mkdir(new URI('/home/workspace')); + await provider.writeFile(new URI('/home/workspace/my-file.txt'), this.encodingService.encode('foo').buffer, { create: true, overwrite: false }); + await provider.writeFile(new URI('/home/workspace/my-file2.txt'), this.encodingService.encode('bar').buffer, { create: true, overwrite: false }); + } + if (!fs.existsSync('/home/workspace2')) { + await provider.mkdir(new URI('/home/workspace2')); + await provider.writeFile(new URI('/home/workspace2/my-file.json'), this.encodingService.encode('{ foo: true }').buffer, { create: true, overwrite: false }); + await provider.writeFile(new URI('/home/workspace2/my-file2.json'), this.encodingService.encode('{ bar: false }').buffer, { create: true, overwrite: false }); + } + } catch (e) { + console.error('An error occurred while initializing the demo workspaces', e); + } + } +} + +export const bindBrowserFSInitialization = (bind: interfaces.Bind, rebind: interfaces.Rebind): void => { + bind(ExampleBrowserFSInitialization).toSelf(); + rebind(BrowserFSInitialization).toService(ExampleBrowserFSInitialization); +}; diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json new file mode 100644 index 0000000000000..453fb7dae24a4 --- /dev/null +++ b/examples/browser-only/package.json @@ -0,0 +1,78 @@ +{ + "private": true, + "name": "@theia/example-browser-only", + "version": "1.45.0", + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "theia": { + "target": "browser-only", + "frontend": { + "config": { + "applicationName": "Theia Browser-Only Example", + "preferences": { + "files.enableTrash": false + } + } + } + }, + "dependencies": { + "@theia/api-samples": "1.45.0", + "@theia/bulk-edit": "1.45.0", + "@theia/callhierarchy": "1.45.0", + "@theia/console": "1.45.0", + "@theia/core": "1.45.0", + "@theia/debug": "1.45.0", + "@theia/editor": "1.45.0", + "@theia/editor-preview": "1.45.0", + "@theia/file-search": "1.45.0", + "@theia/filesystem": "1.45.0", + "@theia/getting-started": "1.45.0", + "@theia/git": "1.45.0", + "@theia/keymaps": "1.45.0", + "@theia/markers": "1.45.0", + "@theia/memory-inspector": "1.45.0", + "@theia/messages": "1.45.0", + "@theia/metrics": "1.45.0", + "@theia/mini-browser": "1.45.0", + "@theia/monaco": "1.45.0", + "@theia/navigator": "1.45.0", + "@theia/outline-view": "1.45.0", + "@theia/output": "1.45.0", + "@theia/plugin-dev": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/plugin-ext-vscode": "1.45.0", + "@theia/plugin-metrics": "1.45.0", + "@theia/preferences": "1.45.0", + "@theia/preview": "1.45.0", + "@theia/process": "1.45.0", + "@theia/property-view": "1.45.0", + "@theia/scm": "1.45.0", + "@theia/scm-extra": "1.45.0", + "@theia/search-in-workspace": "1.45.0", + "@theia/secondary-window": "1.45.0", + "@theia/task": "1.45.0", + "@theia/terminal": "1.45.0", + "@theia/timeline": "1.45.0", + "@theia/toolbar": "1.45.0", + "@theia/typehierarchy": "1.45.0", + "@theia/userstorage": "1.45.0", + "@theia/variable-resolver": "1.45.0", + "@theia/vsx-registry": "1.45.0", + "@theia/workspace": "1.45.0" + }, + "scripts": { + "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", + "clean": "theia clean", + "build": "yarn -s compile && yarn -s bundle", + "bundle": "theia build --mode development", + "compile": "tsc -b", + "start": "theia start", + "start:debug": "yarn -s start --log-level=debug", + "start:watch": "concurrently --kill-others -n tsc,bundle,run -c red,yellow,green \"tsc -b -w --preserveWatchOutput\" \"yarn -s watch:bundle\" \"yarn -s start\"", + "watch": "concurrently --kill-others -n tsc,bundle -c red,yellow \"tsc -b -w --preserveWatchOutput\" \"yarn -s watch:bundle\"", + "watch:bundle": "theia build --watch --mode development", + "watch:compile": "tsc -b -w" + }, + "devDependencies": { + "@theia/cli": "1.45.0" + } +} diff --git a/examples/browser-only/tsconfig.json b/examples/browser-only/tsconfig.json new file mode 100644 index 0000000000000..d4bcfc14426b9 --- /dev/null +++ b/examples/browser-only/tsconfig.json @@ -0,0 +1,141 @@ +{ + "extends": "../../configs/base.tsconfig", + "include": [], + "compilerOptions": { + "composite": true + }, + "references": [ + { + "path": "../../dev-packages/cli" + }, + { + "path": "../../packages/bulk-edit" + }, + { + "path": "../../packages/callhierarchy" + }, + { + "path": "../../packages/console" + }, + { + "path": "../../packages/core" + }, + { + "path": "../../packages/debug" + }, + { + "path": "../../packages/editor" + }, + { + "path": "../../packages/editor-preview" + }, + { + "path": "../../packages/file-search" + }, + { + "path": "../../packages/filesystem" + }, + { + "path": "../../packages/getting-started" + }, + { + "path": "../../packages/git" + }, + { + "path": "../../packages/keymaps" + }, + { + "path": "../../packages/markers" + }, + { + "path": "../../packages/memory-inspector" + }, + { + "path": "../../packages/messages" + }, + { + "path": "../../packages/metrics" + }, + { + "path": "../../packages/mini-browser" + }, + { + "path": "../../packages/monaco" + }, + { + "path": "../../packages/navigator" + }, + { + "path": "../../packages/outline-view" + }, + { + "path": "../../packages/output" + }, + { + "path": "../../packages/plugin-dev" + }, + { + "path": "../../packages/plugin-ext" + }, + { + "path": "../../packages/plugin-ext-vscode" + }, + { + "path": "../../packages/plugin-metrics" + }, + { + "path": "../../packages/preferences" + }, + { + "path": "../../packages/preview" + }, + { + "path": "../../packages/process" + }, + { + "path": "../../packages/property-view" + }, + { + "path": "../../packages/scm" + }, + { + "path": "../../packages/scm-extra" + }, + { + "path": "../../packages/search-in-workspace" + }, + { + "path": "../../packages/secondary-window" + }, + { + "path": "../../packages/task" + }, + { + "path": "../../packages/terminal" + }, + { + "path": "../../packages/timeline" + }, + { + "path": "../../packages/toolbar" + }, + { + "path": "../../packages/typehierarchy" + }, + { + "path": "../../packages/userstorage" + }, + { + "path": "../../packages/variable-resolver" + }, + { + "path": "../../packages/vsx-registry" + }, + { + "path": "../../packages/workspace" + }, + { + "path": "../api-samples" + } + ] +} diff --git a/examples/browser-only/webpack.config.js b/examples/browser-only/webpack.config.js new file mode 100644 index 0000000000000..40e4ee963ba9f --- /dev/null +++ b/examples/browser-only/webpack.config.js @@ -0,0 +1,18 @@ +/** + * This file can be edited to customize webpack configuration. + * To reset delete this file and rerun theia build again. + */ +// @ts-check +const configs = require('./gen-webpack.config.js'); + + +/** + * Expose bundled modules on window.theia.moduleName namespace, e.g. + * window['theia']['@theia/core/lib/common/uri']. + * Such syntax can be used by external code, for instance, for testing. +configs[0].module.rules.push({ + test: /\.js$/, + loader: require.resolve('@theia/application-manager/lib/expose-loader') +}); */ + +module.exports = configs; diff --git a/package.json b/package.json index eedb0306c48a5..250eb85206fa3 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,9 @@ "scripts": { "all": "yarn -s install && yarn -s lint && yarn -s build", "browser": "yarn -s --cwd examples/browser", + "browser-only": "yarn -s --cwd examples/browser-only", "build": "yarn -s compile && yarn -s build:examples", - "build:examples": "yarn browser build && yarn electron build", + "build:examples": "yarn browser build && yarn electron build && yarn browser-only build", "clean": "yarn -s rebuild:clean && yarn -s lint:clean && node scripts/run-reverse-topo.js yarn -s clean", "compile": "echo Compiling TypeScript sources... && yarn -s compile:clean && yarn -s compile:tsc", "compile:clean": "ts-clean dev-packages/* packages/*", diff --git a/packages/core/package.json b/packages/core/package.json index 85c21e0f86dce..babe4599f4599 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -134,8 +134,12 @@ "frontendPreload": "lib/browser/preload/preload-module", "preload": "lib/electron-browser/preload" }, + { + "frontendOnlyPreload": "lib/browser-only/preload/frontend-only-preload-module" + }, { "frontend": "lib/browser/i18n/i18n-frontend-module", + "frontendOnly": "lib/browser-only/i18n/i18n-frontend-only-module", "backend": "lib/node/i18n/i18n-backend-module" }, { diff --git a/packages/core/src/browser-only/frontend-only-application-module.ts b/packages/core/src/browser-only/frontend-only-application-module.ts new file mode 100644 index 0000000000000..680e7d227c83a --- /dev/null +++ b/packages/core/src/browser-only/frontend-only-application-module.ts @@ -0,0 +1,114 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from 'inversify'; +import { BackendStopwatch, CommandRegistry, Emitter, MeasurementOptions, OS } from '../common'; +import { ApplicationInfo, ApplicationServer, ExtensionInfo } from '../common/application-protocol'; +import { EnvVariable, EnvVariablesServer } from './../common/env-variables'; +import { bindMessageService } from '../browser/frontend-application-bindings'; +import { KeyStoreService } from '../common/key-store'; +import { QuickPickService } from '../common/quick-pick-service'; +import { QuickPickServiceImpl } from '../browser/quick-input'; +import { BackendRequestService, RequestService } from '@theia/request'; +import { ConnectionStatus, ConnectionStatusService } from '../browser/connection-status-service'; + +export { bindMessageService }; + +// is loaded directly after the regular frontend module +export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind, isBound, rebind) => { + + if (isBound(CommandRegistry)) { + rebind(CommandRegistry).toSelf().inSingletonScope(); + } else { + bind(CommandRegistry).toSelf().inSingletonScope(); + } + + const stopwatch: BackendStopwatch = { + start: async (_name: string, _options?: MeasurementOptions | undefined): Promise => -1, + stop: async (_measurement: number, _message: string, _messageArgs: unknown[]): Promise => { } + }; + if (isBound(BackendStopwatch)) { + rebind(BackendStopwatch).toConstantValue(stopwatch); + } else { + bind(BackendStopwatch).toConstantValue(stopwatch); + } + + if (isBound(CommandRegistry)) { + rebind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope(); + } else { + bind(QuickPickService).to(QuickPickServiceImpl).inSingletonScope(); + } + + const mockedApplicationServer: ApplicationServer = { + getExtensionsInfos: async (): Promise => [], + getApplicationInfo: async (): Promise => undefined, + getBackendOS: async (): Promise => OS.Type.Linux + }; + if (isBound(ApplicationServer)) { + rebind(ApplicationServer).toConstantValue(mockedApplicationServer); + } else { + bind(ApplicationServer).toConstantValue(mockedApplicationServer); + } + + const varServer: EnvVariablesServer = { + getExecPath: async (): Promise => '', + getVariables: async (): Promise => [], + getValue: async (_key: string): Promise => undefined, + getConfigDirUri: async (): Promise => '', + getHomeDirUri: async (): Promise => '', + getDrives: async (): Promise => [] + }; + if (isBound(EnvVariablesServer)) { + rebind(EnvVariablesServer).toConstantValue(varServer); + } else { + bind(EnvVariablesServer).toConstantValue(varServer); + } + + const keyStoreService: KeyStoreService = { + deletePassword: () => Promise.resolve(false), + findCredentials: () => Promise.resolve([]), + findPassword: () => Promise.resolve(undefined), + setPassword: () => Promise.resolve(), + getPassword: () => Promise.resolve(undefined) + }; + if (isBound(KeyStoreService)) { + rebind(KeyStoreService).toConstantValue(keyStoreService); + } else { + bind(KeyStoreService).toConstantValue(keyStoreService); + } + + const requestService: RequestService = { + configure: () => Promise.resolve(), + request: () => Promise.reject(), + resolveProxy: () => Promise.resolve(undefined) + }; + if (isBound(BackendRequestService)) { + rebind(BackendRequestService).toConstantValue(requestService); + } else { + bind(BackendRequestService).toConstantValue(requestService); + } + + const connectionStatusService: ConnectionStatusService = { + currentStatus: ConnectionStatus.ONLINE, + onStatusChange: new Emitter().event + }; + if (isBound(ConnectionStatusService)) { + rebind(ConnectionStatusService).toConstantValue(connectionStatusService); + } else { + bind(ConnectionStatusService).toConstantValue(connectionStatusService); + } + +}); diff --git a/packages/core/src/browser-only/i18n/i18n-frontend-only-module.ts b/packages/core/src/browser-only/i18n/i18n-frontend-only-module.ts new file mode 100644 index 0000000000000..e88337fcb65b7 --- /dev/null +++ b/packages/core/src/browser-only/i18n/i18n-frontend-only-module.ts @@ -0,0 +1,37 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from 'inversify'; +import { AsyncLocalizationProvider, LanguageInfo, Localization } from '../../common/i18n/localization'; +import { LanguageQuickPickService } from '../../browser/i18n/language-quick-pick-service'; + +export default new ContainerModule(bind => { + const i18nMock: AsyncLocalizationProvider = { + getCurrentLanguage: async (): Promise => 'en', + setCurrentLanguage: async (_languageId: string): Promise => { + + }, + getAvailableLanguages: async (): Promise => + [] + , + loadLocalization: async (_languageId: string): Promise => ({ + translations: {}, + languageId: 'en' + }) + }; + bind(AsyncLocalizationProvider).toConstantValue(i18nMock); + bind(LanguageQuickPickService).toSelf().inSingletonScope(); +}); diff --git a/packages/core/src/browser-only/logger-frontend-only-module.ts b/packages/core/src/browser-only/logger-frontend-only-module.ts new file mode 100644 index 0000000000000..e5fc84021476d --- /dev/null +++ b/packages/core/src/browser-only/logger-frontend-only-module.ts @@ -0,0 +1,63 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule, Container } from 'inversify'; +import { ILoggerServer, ILoggerClient, LogLevel, ConsoleLogger } from '../common/logger-protocol'; +import { ILogger, Logger, LoggerFactory, LoggerName } from '../common/logger'; + +// is loaded directly after the regular logger frontend module +export const loggerFrontendOnlyModule = new ContainerModule((bind, unbind, isBound, rebind) => { + const logger: ILoggerServer = { + setLogLevel: async (_name: string, _logLevel: number): Promise => { }, + getLogLevel: async (_name: string): Promise => LogLevel.INFO, + log: async (name: string, logLevel: number, message: string, params: unknown[]): Promise => { + ConsoleLogger.log(name, logLevel, message, params); + + }, + child: async (_name: string): Promise => { }, + dispose: (): void => { + }, + setClient: (_client: ILoggerClient | undefined): void => { + } + }; + if (isBound(ILoggerServer)) { + rebind(ILoggerServer).toConstantValue(logger); + } else { + bind(ILoggerServer).toConstantValue(logger); + } + + if (isBound(ILoggerServer)) { + rebind(LoggerFactory).toFactory(ctx => + (name: string) => { + const child = new Container({ defaultScope: 'Singleton' }); + child.parent = ctx.container; + child.bind(ILogger).to(Logger).inTransientScope(); + child.bind(LoggerName).toConstantValue(name); + return child.get(ILogger); + } + ); + } else { + bind(LoggerFactory).toFactory(ctx => + (name: string) => { + const child = new Container({ defaultScope: 'Singleton' }); + child.parent = ctx.container; + child.bind(ILogger).to(Logger).inTransientScope(); + child.bind(LoggerName).toConstantValue(name); + return child.get(ILogger); + } + ); + } +}); diff --git a/packages/core/src/browser-only/messaging/frontend-only-service-connection-provider.ts b/packages/core/src/browser-only/messaging/frontend-only-service-connection-provider.ts new file mode 100644 index 0000000000000..e2208565be4eb --- /dev/null +++ b/packages/core/src/browser-only/messaging/frontend-only-service-connection-provider.ts @@ -0,0 +1,39 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { Event, RpcProxy, Channel, RpcProxyFactory, Emitter } from '../../common'; +import { injectable } from 'inversify'; +import { ServiceConnectionProvider } from '../../browser/messaging/service-connection-provider'; +import { ConnectionSource } from '../../browser/messaging/connection-source'; + +@injectable() +export class FrontendOnlyConnectionSource implements ConnectionSource { + onConnectionDidOpen = new Emitter().event; +} + +@injectable() +export class FrontendOnlyServiceConnectionProvider extends ServiceConnectionProvider { + onSocketDidOpen = Event.None; + onSocketDidClose = Event.None; + onIncomingMessageActivity = Event.None; + override createProxy(path: unknown, target?: unknown): RpcProxy { + console.debug(`[Frontend-Only Fallback] Created proxy connection for ${path}`); + const factory = target instanceof RpcProxyFactory ? target : new RpcProxyFactory(target); + return factory.createProxy(); + } + override listen(path: string, handler: ServiceConnectionProvider.ConnectionHandler, reconnect: boolean): void { + console.debug('[Frontend-Only Fallback] Listen to websocket connection requested'); + } +} diff --git a/packages/core/src/browser-only/messaging/messaging-frontend-only-module.ts b/packages/core/src/browser-only/messaging/messaging-frontend-only-module.ts new file mode 100644 index 0000000000000..55029105c81d8 --- /dev/null +++ b/packages/core/src/browser-only/messaging/messaging-frontend-only-module.ts @@ -0,0 +1,42 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ContainerModule } from 'inversify'; +import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source'; +import { FrontendOnlyConnectionSource, FrontendOnlyServiceConnectionProvider } from './frontend-only-service-connection-provider'; +import { ConnectionSource } from '../../browser/messaging/connection-source'; +import { LocalConnectionProvider, RemoteConnectionProvider } from '../../browser/messaging/service-connection-provider'; + +// is loaded directly after the regular message frontend module +export const messagingFrontendOnlyModule = new ContainerModule((bind, unbind, isBound, rebind) => { + unbind(WebSocketConnectionSource); + bind(FrontendOnlyConnectionSource).toSelf().inSingletonScope(); + if (isBound(ConnectionSource)) { + rebind(ConnectionSource).toService(FrontendOnlyConnectionSource); + } else { + bind(ConnectionSource).toService(FrontendOnlyConnectionSource); + } + bind(FrontendOnlyServiceConnectionProvider).toSelf().inSingletonScope(); + if (isBound(LocalConnectionProvider)) { + rebind(LocalConnectionProvider).toService(FrontendOnlyServiceConnectionProvider); + } else { + bind(LocalConnectionProvider).toService(FrontendOnlyServiceConnectionProvider); + } + if (isBound(RemoteConnectionProvider)) { + rebind(RemoteConnectionProvider).toService(FrontendOnlyServiceConnectionProvider); + } else { + bind(RemoteConnectionProvider).toService(FrontendOnlyServiceConnectionProvider); + } +}); diff --git a/packages/core/src/browser-only/preload/frontend-only-preload-module.ts b/packages/core/src/browser-only/preload/frontend-only-preload-module.ts new file mode 100644 index 0000000000000..ea0ed3fcb7e2a --- /dev/null +++ b/packages/core/src/browser-only/preload/frontend-only-preload-module.ts @@ -0,0 +1,49 @@ +// ***************************************************************************** +// Copyright (C) 2023 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from 'inversify'; +import { LocalizationServer } from '../../common/i18n/localization-server'; +import { OS, OSBackendProvider } from '../../common/os'; +import { Localization } from '../../common/i18n/localization'; + +// loaded after regular preload module +export default new ContainerModule((bind, unbind, isBound, rebind) => { + const frontendOnlyLocalizationServer: LocalizationServer = { + loadLocalization: async (languageId: string): Promise => ({ translations: {}, languageId }) + }; + if (isBound(LocalizationServer)) { + rebind(LocalizationServer).toConstantValue(frontendOnlyLocalizationServer); + } else { + bind(LocalizationServer).toConstantValue(frontendOnlyLocalizationServer); + } + + const frontendOnlyOSBackendProvider: OSBackendProvider = { + getBackendOS: async (): Promise => { + if (window.navigator.platform.startsWith('Win')) { + return OS.Type.Windows; + } else if (window.navigator.platform.startsWith('Mac')) { + return OS.Type.OSX; + } else { + return OS.Type.Linux; + } + } + }; + if (isBound(OSBackendProvider)) { + rebind(OSBackendProvider).toConstantValue(frontendOnlyOSBackendProvider); + } else { + bind(OSBackendProvider).toConstantValue(frontendOnlyOSBackendProvider); + } +}); diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index ca1bb7f3ab971..34e857decdf2b 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -346,7 +346,6 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is }); bind(FrontendConnectionStatusService).toSelf().inSingletonScope(); bind(ConnectionStatusService).toService(FrontendConnectionStatusService); - bind(FrontendApplicationContribution).toService(FrontendConnectionStatusService); bind(ApplicationConnectionStatusContribution).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(ApplicationConnectionStatusContribution); diff --git a/packages/core/src/node/file-uri.ts b/packages/core/src/common/file-uri.ts similarity index 97% rename from packages/core/src/node/file-uri.ts rename to packages/core/src/common/file-uri.ts index 99a86fbb7b5ca..f92af9fc0fdaf 100644 --- a/packages/core/src/node/file-uri.ts +++ b/packages/core/src/common/file-uri.ts @@ -15,8 +15,8 @@ // ***************************************************************************** import { URI as Uri } from 'vscode-uri'; -import URI from '../common/uri'; -import { isWindows } from '../common/os'; +import URI from './uri'; +import { isWindows } from './os'; export namespace FileUri { diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index cc9b03039b15c..2efa9fce419e2 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -24,7 +24,7 @@ import { existsSync, mkdirSync } from 'fs-extra'; import { fork, ForkOptions } from 'child_process'; import { DefaultTheme, FrontendApplicationConfig } from '@theia/application-package/lib/application-props'; import URI from '../common/uri'; -import { FileUri } from '../node/file-uri'; +import { FileUri } from '../common/file-uri'; import { Deferred } from '../common/promise-util'; import { MaybePromise } from '../common/types'; import { ContributionProvider } from '../common/contribution-provider'; diff --git a/packages/core/src/electron-main/theia-electron-window.ts b/packages/core/src/electron-main/theia-electron-window.ts index 855939e1ca9dc..17c82fa9a4511 100644 --- a/packages/core/src/electron-main/theia-electron-window.ts +++ b/packages/core/src/electron-main/theia-electron-window.ts @@ -22,7 +22,7 @@ import { ElectronMainApplicationGlobals } from './electron-main-constants'; import { DisposableCollection, Emitter, Event } from '../common'; import { createDisposableListener } from './event-utils'; import { URI } from '../common/uri'; -import { FileUri } from '../node/file-uri'; +import { FileUri } from '../common/file-uri'; import { TheiaRendererAPI } from './electron-api-main'; /** diff --git a/packages/core/src/node/env-variables/env-variables-server.ts b/packages/core/src/node/env-variables/env-variables-server.ts index 3d795a9063dde..7c337992ac90b 100644 --- a/packages/core/src/node/env-variables/env-variables-server.ts +++ b/packages/core/src/node/env-variables/env-variables-server.ts @@ -21,7 +21,7 @@ import * as drivelist from 'drivelist'; import { pathExists, mkdir } from 'fs-extra'; import { EnvVariable, EnvVariablesServer } from '../../common/env-variables'; import { isWindows } from '../../common/os'; -import { FileUri } from '../file-uri'; +import { FileUri } from '../../common/file-uri'; @injectable() export class EnvVariablesServerImpl implements EnvVariablesServer { diff --git a/packages/core/src/node/file-uri.spec.ts b/packages/core/src/node/file-uri.spec.ts index 378fe71edfc99..f4689d6a58672 100644 --- a/packages/core/src/node/file-uri.spec.ts +++ b/packages/core/src/node/file-uri.spec.ts @@ -17,7 +17,7 @@ import * as os from 'os'; import * as path from 'path'; import * as chai from 'chai'; -import { FileUri } from './file-uri'; +import { FileUri } from '../common/file-uri'; import { isWindows } from '../common/os'; const expect = chai.expect; diff --git a/packages/core/src/node/index.ts b/packages/core/src/node/index.ts index 192f8089d99de..c7d735f4f03d6 100644 --- a/packages/core/src/node/index.ts +++ b/packages/core/src/node/index.ts @@ -16,7 +16,7 @@ export * from './backend-application'; export * from './debug'; -export * from './file-uri'; +export * from '../common/file-uri'; export * from './messaging'; export * from './cli'; export { FileSystemLocking } from './filesystem-locking'; diff --git a/packages/external-terminal/src/electron-node/linux-external-terminal-service.ts b/packages/external-terminal/src/electron-node/linux-external-terminal-service.ts index 44180a1fa1743..2c0b34b4d1035 100644 --- a/packages/external-terminal/src/electron-node/linux-external-terminal-service.ts +++ b/packages/external-terminal/src/electron-node/linux-external-terminal-service.ts @@ -18,7 +18,7 @@ import * as cp from 'child_process'; import * as fs from '@theia/core/shared/fs-extra'; import { injectable } from '@theia/core/shared/inversify'; import { OS } from '@theia/core/lib/common/os'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { ExternalTerminalService, ExternalTerminalConfiguration } from '../common/external-terminal'; /*--------------------------------------------------------------------------------------------- diff --git a/packages/external-terminal/src/electron-node/mac-external-terminal-service.ts b/packages/external-terminal/src/electron-node/mac-external-terminal-service.ts index 9dd06d706fb89..7c5e8bd43acc4 100644 --- a/packages/external-terminal/src/electron-node/mac-external-terminal-service.ts +++ b/packages/external-terminal/src/electron-node/mac-external-terminal-service.ts @@ -16,7 +16,7 @@ import * as cp from 'child_process'; import { injectable } from '@theia/core/shared/inversify'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { ExternalTerminalService, ExternalTerminalConfiguration } from '../common/external-terminal'; /*--------------------------------------------------------------------------------------------- diff --git a/packages/external-terminal/src/electron-node/windows-external-terminal-service.ts b/packages/external-terminal/src/electron-node/windows-external-terminal-service.ts index d169339c38e95..7e8c54a1a900e 100644 --- a/packages/external-terminal/src/electron-node/windows-external-terminal-service.ts +++ b/packages/external-terminal/src/electron-node/windows-external-terminal-service.ts @@ -17,7 +17,7 @@ import * as cp from 'child_process'; import * as path from 'path'; import { injectable } from '@theia/core/shared/inversify'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { ExternalTerminalService, ExternalTerminalConfiguration } from '../common/external-terminal'; /*--------------------------------------------------------------------------------------------- diff --git a/packages/file-search/src/node/file-search-service-impl.ts b/packages/file-search/src/node/file-search-service-impl.ts index 8169a5077b788..609be17abef81 100644 --- a/packages/file-search/src/node/file-search-service-impl.ts +++ b/packages/file-search/src/node/file-search-service-impl.ts @@ -20,7 +20,7 @@ import * as readline from 'readline'; import { rgPath } from '@vscode/ripgrep'; import { injectable, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { CancellationTokenSource, CancellationToken, ILogger, isWindows } from '@theia/core'; import { RawProcessFactory } from '@theia/process/lib/node'; import { FileSearchService, WHITESPACE_QUERY_SEPARATOR } from '../common/file-search-service'; diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index a1517d8fe786f..997fca2738f1f 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -11,6 +11,7 @@ "@types/uuid": "^7.0.3", "async-mutex": "^0.3.1", "body-parser": "^1.18.3", + "browserfs": "^1.4.3", "http-status-codes": "^1.3.0", "minimatch": "^5.1.0", "multer": "1.4.4-lts.1", @@ -33,6 +34,9 @@ "frontend": "lib/browser/filesystem-frontend-module", "backend": "lib/node/filesystem-backend-module" }, + { + "frontendOnly": "lib/browser-only/browser-only-filesystem-frontend-module" + }, { "frontend": "lib/browser/download/file-download-frontend-module", "backend": "lib/node/download/file-download-backend-module" diff --git a/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts b/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts new file mode 100644 index 0000000000000..eae27435b6d90 --- /dev/null +++ b/packages/filesystem/src/browser-only/browser-only-filesystem-frontend-module.ts @@ -0,0 +1,38 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { FileSystemProvider } from '../common/files'; +import { BrowserFSFileSystemProvider } from './browserfs-filesystem-provider'; +import { RemoteFileSystemProvider, RemoteFileSystemServer } from '../common/remote-file-system-provider'; +import { BrowserFSInitialization, DefaultBrowserFSInitialization } from './browserfs-filesystem-initialization'; +import { BrowserOnlyFileSystemProviderServer } from './browser-only-filesystem-provider-server'; + +export default new ContainerModule((bind, _unbind, isBound, rebind) => { + bind(DefaultBrowserFSInitialization).toSelf(); + bind(BrowserFSFileSystemProvider).toSelf(); + bind(BrowserFSInitialization).toService(DefaultBrowserFSInitialization); + if (isBound(FileSystemProvider)) { + rebind(FileSystemProvider).to(BrowserFSFileSystemProvider).inSingletonScope(); + } else { + bind(FileSystemProvider).to(BrowserFSFileSystemProvider).inSingletonScope(); + } + if (isBound(RemoteFileSystemProvider)) { + rebind(RemoteFileSystemServer).to(BrowserOnlyFileSystemProviderServer).inSingletonScope(); + } else { + bind(RemoteFileSystemServer).to(BrowserOnlyFileSystemProviderServer).inSingletonScope(); + } +}); diff --git a/packages/filesystem/src/browser-only/browser-only-filesystem-provider-server.ts b/packages/filesystem/src/browser-only/browser-only-filesystem-provider-server.ts new file mode 100644 index 0000000000000..5b3c2b4a3a555 --- /dev/null +++ b/packages/filesystem/src/browser-only/browser-only-filesystem-provider-server.ts @@ -0,0 +1,32 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { FileSystemProviderServer } from '../common/remote-file-system-provider'; +import { Event } from '@theia/core'; + +/** + * Backend component. + * + * JSON-RPC server exposing a wrapped file system provider remotely. + */ +@injectable() +export class BrowserOnlyFileSystemProviderServer extends FileSystemProviderServer { + + // needed because users expect implicitly the RemoteFileSystemServer to be a RemoteFileSystemProxyFactory + onDidOpenConnection = Event.None; + onDidCloseConnection = Event.None; +} diff --git a/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts b/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts new file mode 100644 index 0000000000000..0b17d3076a468 --- /dev/null +++ b/packages/filesystem/src/browser-only/browserfs-filesystem-initialization.ts @@ -0,0 +1,61 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import type { FSModule } from 'browserfs/dist/node/core/FS'; +import type { BrowserFSFileSystemProvider } from './browserfs-filesystem-provider'; +import { injectable } from '@theia/core/shared/inversify'; +import { FileSystem, initialize } from 'browserfs'; +import MountableFileSystem from 'browserfs/dist/node/backend/MountableFileSystem'; + +export const BrowserFSInitialization = Symbol('BrowserFSInitialization'); +export interface BrowserFSInitialization { + createMountableFileSystem(): Promise + initializeFS: (fs: FSModule, provider: BrowserFSFileSystemProvider) => Promise; +} + +@injectable() +export class DefaultBrowserFSInitialization implements BrowserFSInitialization { + + createMountableFileSystem(): Promise { + return new Promise(resolve => { + FileSystem.IndexedDB.Create({}, (e, persistedFS) => { + if (e) { + throw e; + } + if (!persistedFS) { + throw Error('Could not create filesystem'); + } + FileSystem.MountableFileSystem.Create({ + '/home': persistedFS + + }, (error, mountableFS) => { + if (error) { + throw error; + } + if (!mountableFS) { + throw Error('Could not create filesystem'); + } + initialize(mountableFS); + resolve(mountableFS); + }); + }); + }); + } + + async initializeFS(fs: FSModule, provider: BrowserFSFileSystemProvider): Promise { + + } +} diff --git a/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts b/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts new file mode 100644 index 0000000000000..ca833d9536313 --- /dev/null +++ b/packages/filesystem/src/browser-only/browserfs-filesystem-provider.ts @@ -0,0 +1,462 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// based on https://github.com/microsoft/vscode/blob/04c36be045a94fee58e5f8992d3e3fd980294a84/src/vs/platform/files/node/diskFileSystemProvider.ts + +/* eslint-disable no-null/no-null */ + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { + FileChange, FileDeleteOptions, FileOpenOptions, + FileOverwriteOptions, FileReadStreamOptions, FileSystemProviderCapabilities, + FileSystemProviderError, + FileSystemProviderErrorCode, + FileSystemProviderWithFileReadWriteCapability, + FileType, FileUpdateOptions, FileUpdateResult, FileWriteOptions, Stat, WatchOptions, createFileSystemProviderError +} from '../common/files'; +import { Event, URI, Disposable, CancellationToken } from '@theia/core'; +import { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol'; +import { ReadableStreamEvents } from '@theia/core/lib/common/stream'; +import { BFSRequire } from 'browserfs'; +import type { FSModule } from 'browserfs/dist/node/core/FS'; +import type { FileSystem } from 'browserfs/dist/node/core/file_system'; +import MountableFileSystem from 'browserfs/dist/node/backend/MountableFileSystem'; +import { basename, dirname, normalize } from 'path'; +import Stats from 'browserfs/dist/node/core/node_fs_stats'; +import { retry } from '@theia/core/lib/common/promise-util'; +import { BrowserFSInitialization } from './browserfs-filesystem-initialization'; + +// adapted from DiskFileSystemProvider +@injectable() +export class BrowserFSFileSystemProvider implements FileSystemProviderWithFileReadWriteCapability { + capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; + onDidChangeCapabilities: Event = Event.None; + onDidChangeFile: Event = Event.None; + onFileWatchError: Event = Event.None; + private mapHandleToPos: Map = new Map(); + private writeHandles: Set = new Set(); + private canFlush: boolean = true; + + private fs: FSModule; + private mountableFS: MountableFileSystem; + private initialized: Promise; + + constructor(@inject(BrowserFSInitialization) readonly initialization: BrowserFSInitialization) { + const init = async (): Promise => { + this.mountableFS = await initialization.createMountableFileSystem(); + this.fs = BFSRequire('fs'); + await initialization.initializeFS(this.fs, new Proxy(this, { + get(target, prop, receiver): unknown { + if (prop === 'initialized') { + return Promise.resolve(true); + } + return Reflect.get(target, prop, receiver); + } + })); + return true; + }; + this.initialized = init(); + } + + async mount(mountPoint: string, fs: FileSystem): Promise { + await this.initialized; + this.mountableFS.mount(mountPoint, fs); + }; + + watch(_resource: URI, _opts: WatchOptions): Disposable { + return Disposable.NULL; + } + async stat(resource: URI): Promise { + await this.initialized; + const path = this.toFilePath(resource); + + let stats: Stats; + try { + stats = await this.promisify(this.fs.stat)(path) as Stats; + } catch (error) { + throw this.toFileSystemProviderError(error); + } + if (stats === undefined) { + throw new Error(`Could not read file stat for resource '${path}'`); + } + return { + type: this.toType(stats, /* symbolicLink */undefined), // FIXME: missing symbolicLink + ctime: stats.birthtime.getTime(), // intentionally not using ctime here, we want the creation time + mtime: stats.mtime.getTime(), + size: stats.size, + // FIXME: missing mode, permissions + }; + + } + async mkdir(resource: URI): Promise { + await this.initialized; + try { + await this.promisify(this.fs.mkdir)(this.toFilePath(resource)); + } catch (error) { + throw this.toFileSystemProviderError(error); + } + } + async readdir(resource: URI): Promise<[string, FileType][]> { + await this.initialized; + try { + + const children = await this.promisify(this.fs.readdir)(this.toFilePath(resource)) as string[]; + const result: [string, FileType][] = []; + await Promise.all(children.map(async child => { + try { + const stat = await this.stat(resource.resolve(child)); + result.push([child, stat.type]); + } catch (error) { + console.trace(error); // ignore errors for individual entries that can arise from permission denied + } + })); + + return result; + } catch (error) { + throw this.toFileSystemProviderError(error); + } + } + async delete(resource: URI, _opts: FileDeleteOptions): Promise { + await this.initialized; + // FIXME use options + try { + await this.promisify(this.fs.unlink)(this.toFilePath(resource)); + } catch (error) { + throw this.toFileSystemProviderError(error); + } + } + async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + await this.initialized; + const fromFilePath = this.toFilePath(from); + const toFilePath = this.toFilePath(to); + if (fromFilePath === toFilePath) { + return; // simulate node.js behaviour here and do a no-op if paths match + } + try { + // assume FS is path case sensitive - correct? + const targetExists = await this.promisify(this.fs.exists)(toFilePath); + if (targetExists) { + throw Error(`File '${toFilePath}' already exists.`); + } + if (fromFilePath === toFilePath) { + return Promise.resolve(); + } + + await this.promisify(this.fs.rename)(fromFilePath, toFilePath); + + const stat = await this.promisify(this.fs.lstat)(toFilePath) as Stats; + if (stat.isDirectory() || stat.isSymbolicLink()) { + return Promise.resolve(); // only for files + } + const fd = await this.promisify(open)(toFilePath, 'a'); + try { + await this.promisify(this.fs.futimes)(fd, stat.atime, new Date()); + } catch (error) { + // ignore + } + + this.promisify(this.fs.close)(fd); + } catch (error) { + // rewrite some typical errors that can happen especially around symlinks + // to something the user can better understand + if (error.code === 'EINVAL' || error.code === 'EBUSY' || error.code === 'ENAMETOOLONG') { + error = new Error(`Unable to move '${basename(fromFilePath)}' into '${basename(dirname(toFilePath))}' (${error.toString()}).`); + } + + throw this.toFileSystemProviderError(error); + } + } + async copy?(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + await this.initialized; + throw new Error('Method not implemented.'); + } + async readFile(resource: URI): Promise { + await this.initialized; + try { + const filePath = this.toFilePath(resource); + return await this.promisify(this.fs.readFile)(filePath) as Uint8Array; + } catch (error) { + throw this.toFileSystemProviderError(error); + } + } + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + await this.initialized; + let handle: number | undefined = undefined; + try { + const filePath = this.toFilePath(resource); + + // Validate target unless { create: true, overwrite: true } + if (!opts.create || !opts.overwrite) { + const fileExists = await this.promisify(this.fs.exists)(filePath); + if (fileExists) { + if (!opts.overwrite) { + throw createFileSystemProviderError('File already exists', FileSystemProviderErrorCode.FileExists); + } + } else { + if (!opts.create) { + throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound); + } + } + } + + // Open + handle = await this.open(resource, { create: true }); + + // Write content at once + await this.write(handle, 0, content, 0, content.byteLength); + } catch (error) { + throw this.toFileSystemProviderError(error); + } finally { + if (typeof handle === 'number') { + await this.close(handle); + } + } + } + readFileStream?(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { + throw new Error('Method not implemented.'); + } + async open(resource: URI, opts: FileOpenOptions): Promise { + await this.initialized; + try { + const filePath = this.toFilePath(resource); + + let flags: string | undefined = undefined; + if (opts.create) { + // we take opts.create as a hint that the file is opened for writing + // as such we use 'w' to truncate an existing or create the + // file otherwise. we do not allow reading. + if (!flags) { + flags = 'w'; + } + } else { + // otherwise we assume the file is opened for reading + // as such we use 'r' to neither truncate, nor create + // the file. + flags = 'r'; + } + + const handle = await this.promisify(this.fs.open)(filePath, flags) as number; + + // remember this handle to track file position of the handle + // we init the position to 0 since the file descriptor was + // just created and the position was not moved so far (see + // also http://man7.org/linux/man-pages/man2/open.2.html - + // "The file offset is set to the beginning of the file.") + this.mapHandleToPos.set(handle, 0); + + // remember that this handle was used for writing + if (opts.create) { + this.writeHandles.add(handle); + } + + return handle; + } catch (error) { + throw this.toFileSystemProviderError(error); + } + } + async close(fd: number): Promise { + await this.initialized; + // remove this handle from map of positions + this.mapHandleToPos.delete(fd); + + // if a handle is closed that was used for writing, ensure + // to flush the contents to disk if possible. + if (this.writeHandles.delete(fd) && this.canFlush) { + try { + await this.promisify(this.fs.fdatasync)(fd); + } catch (error) { + // In some exotic setups it is well possible that node fails to sync + // In that case we disable flushing and log the error to our logger + this.canFlush = false; + console.error(error); + } + } + + await this.promisify(this.fs.close)(fd); + } + async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { + await this.initialized; + const normalizedPos = this.normalizePos(fd, pos); + + let bytesRead: number | null = null; + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: { bytesRead: number, buffer: Uint8Array } | number = (await this.promisify(this.fs.read)(fd, data, offset, length, normalizedPos)) as any; + + if (typeof result === 'number') { + bytesRead = result; // node.d.ts fail + } else { + bytesRead = result.bytesRead; + } + + return bytesRead; + } catch (error) { + throw this.toFileSystemProviderError(error); + } finally { + this.updatePos(fd, normalizedPos, bytesRead); + } + } + async write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { + await this.initialized; + // we know at this point that the file to write to is truncated and thus empty + // if the write now fails, the file remains empty. as such we really try hard + // to ensure the write succeeds by retrying up to three times. + return retry(() => this.doWrite(fd, pos, data, offset, length), 100 /* ms delay */, 3 /* retries */); + + } + private async doWrite(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { + await this.initialized; + const normalizedPos = this.normalizePos(fd, pos); + + let bytesWritten: number | null = null; + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result: { bytesWritten: number, buffer: Uint8Array } | number = (await this.promisify(this.fs.write)(fd, data, offset, length, normalizedPos)) as any; + + if (typeof result === 'number') { + bytesWritten = result; // node.d.ts fail + } else { + bytesWritten = result.bytesWritten; + } + + return bytesWritten; + } catch (error) { + throw this.toFileSystemProviderError(error); + } finally { + this.updatePos(fd, normalizedPos, bytesWritten); + } + } + private normalizePos(fd: number, pos: number): number | null { + + // when calling fs.read/write we try to avoid passing in the "pos" argument and + // rather prefer to pass in "null" because this avoids an extra seek(pos) + // call that in some cases can even fail (e.g. when opening a file over FTP - + // see https://github.com/microsoft/vscode/issues/73884). + // + // as such, we compare the passed in position argument with our last known + // position for the file descriptor and use "null" if they match. + if (pos === this.mapHandleToPos.get(fd)) { + return null; + } + + return pos; + } + private updatePos(fd: number, pos: number | null, bytesLength: number | null): void { + const lastKnownPos = this.mapHandleToPos.get(fd); + if (typeof lastKnownPos === 'number') { + + // pos !== null signals that previously a position was used that is + // not null. node.js documentation explains, that in this case + // the internal file pointer is not moving and as such we do not move + // our position pointer. + // + // Docs: "If position is null, data will be read from the current file position, + // and the file position will be updated. If position is an integer, the file position + // will remain unchanged." + if (typeof pos === 'number') { + // do not modify the position + } else if (typeof bytesLength === 'number') { + this.mapHandleToPos.set(fd, lastKnownPos + bytesLength); + } else { + this.mapHandleToPos.delete(fd); + } + } + } + async access?(resource: URI, mode?: number | undefined): Promise { + await this.initialized; + throw new Error('Method not implemented.'); + } + async fsPath?(resource: URI): Promise { + await this.initialized; + throw new Error('Method not implemented.'); + } + async updateFile?(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise { + await this.initialized; + throw new Error('Method not implemented.'); + } + + private toFilePath(resource: URI): string { + return normalize(resource.path.toString()); + } + + private toType(entry: Stats, symbolicLink?: { dangling: boolean }): FileType { + // Signal file type by checking for file / directory, except: + // - symbolic links pointing to non-existing files are FileType.Unknown + // - files that are neither file nor directory are FileType.Unknown + let type: FileType; + if (symbolicLink?.dangling) { + type = FileType.Unknown; + } else if (entry.isFile()) { + type = FileType.File; + } else if (entry.isDirectory()) { + type = FileType.Directory; + } else { + type = FileType.Unknown; + } + + // Always signal symbolic link as file type additionally + if (symbolicLink) { + type |= FileType.SymbolicLink; + } + + return type; + } + + // FIXME typing + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private promisify(f: Function): (...args: any[]) => Promise { + // eslint-disable-next-line @typescript-eslint/tslint/config, @typescript-eslint/no-explicit-any + return function (...args: any[]) { + return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + f(...args, (err: Error, result: T) => err ? reject(err) : resolve(result)); + }); + }; + } + + private toFileSystemProviderError(error: NodeJS.ErrnoException): FileSystemProviderError { + if (error instanceof FileSystemProviderError) { + return error; // avoid double conversion + } + + let code: FileSystemProviderErrorCode; + switch (error.code) { + case 'ENOENT': + code = FileSystemProviderErrorCode.FileNotFound; + break; + case 'EISDIR': + code = FileSystemProviderErrorCode.FileIsADirectory; + break; + case 'ENOTDIR': + code = FileSystemProviderErrorCode.FileNotADirectory; + break; + case 'EEXIST': + code = FileSystemProviderErrorCode.FileExists; + break; + case 'EPERM': + case 'EACCES': + code = FileSystemProviderErrorCode.NoPermissions; + break; + default: + code = FileSystemProviderErrorCode.Unknown; + } + + return createFileSystemProviderError(error, code); + } +} diff --git a/packages/filesystem/src/electron-browser/file-dialog/electron-file-dialog-service.ts b/packages/filesystem/src/electron-browser/file-dialog/electron-file-dialog-service.ts index c3edec5afd967..a5cd9a7a4ad63 100644 --- a/packages/filesystem/src/electron-browser/file-dialog/electron-file-dialog-service.ts +++ b/packages/filesystem/src/electron-browser/file-dialog/electron-file-dialog-service.ts @@ -28,7 +28,7 @@ import { DefaultFileDialogService, OpenFileDialogProps, SaveFileDialogProps } fr // solution. // // eslint-disable-next-line @theia/runtime-import-check -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { OpenDialogOptions, SaveDialogOptions } from '../../electron-common/electron-api'; import '@theia/core/lib/electron-common/electron-api'; diff --git a/packages/filesystem/src/node/disk-file-system-provider.spec.ts b/packages/filesystem/src/node/disk-file-system-provider.spec.ts index ed0011db27e92..f5f434fb7b0fd 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.spec.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.spec.ts @@ -18,7 +18,7 @@ import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposa import { EncodingService } from '@theia/core/lib/common/encoding-service'; import { ILogger } from '@theia/core/lib/common/logger'; import { MockLogger } from '@theia/core/lib/common/test/mock-logger'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { IPCConnectionProvider } from '@theia/core/lib/node/messaging/ipc-connection-provider'; import { Container, ContainerModule } from '@theia/core/shared/inversify'; import { equal, fail } from 'assert'; diff --git a/packages/filesystem/src/node/disk-file-system-provider.ts b/packages/filesystem/src/node/disk-file-system-provider.ts index b5bdf2234934c..e1e1e64e532a3 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.ts @@ -35,7 +35,7 @@ import { import { promisify } from 'util'; import URI from '@theia/core/lib/common/uri'; import { Path } from '@theia/core/lib/common/path'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { OS, isWindows } from '@theia/core/lib/common/os'; diff --git a/packages/filesystem/src/node/download/directory-archiver.spec.ts b/packages/filesystem/src/node/download/directory-archiver.spec.ts index a1c1e2c410c21..12a2752f25a8c 100644 --- a/packages/filesystem/src/node/download/directory-archiver.spec.ts +++ b/packages/filesystem/src/node/download/directory-archiver.spec.ts @@ -21,7 +21,7 @@ import { extract } from 'tar-fs'; import { expect } from 'chai'; import URI from '@theia/core/lib/common/uri'; import { MockDirectoryArchiver } from './test/mock-directory-archiver'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; const track = temp.track(); diff --git a/packages/filesystem/src/node/download/directory-archiver.ts b/packages/filesystem/src/node/download/directory-archiver.ts index c8c99526e8af8..40513d74f720e 100644 --- a/packages/filesystem/src/node/download/directory-archiver.ts +++ b/packages/filesystem/src/node/download/directory-archiver.ts @@ -18,7 +18,7 @@ import { injectable } from '@theia/core/shared/inversify'; import * as fs from '@theia/core/shared/fs-extra'; import { pack } from 'tar-fs'; import URI from '@theia/core/lib/common/uri'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; @injectable() export class DirectoryArchiver { diff --git a/packages/filesystem/src/node/download/file-download-endpoint.ts b/packages/filesystem/src/node/download/file-download-endpoint.ts index 6237c1e7ef050..a33523ef82bd0 100644 --- a/packages/filesystem/src/node/download/file-download-endpoint.ts +++ b/packages/filesystem/src/node/download/file-download-endpoint.ts @@ -21,7 +21,7 @@ import { injectable, inject, named } from '@theia/core/shared/inversify'; import { json } from 'body-parser'; import { Application, Router } from '@theia/core/shared/express'; import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { FileDownloadHandler } from './file-download-handler'; @injectable() diff --git a/packages/filesystem/src/node/download/file-download-handler.ts b/packages/filesystem/src/node/download/file-download-handler.ts index f1ae5e478c366..7a54513e6bcae 100644 --- a/packages/filesystem/src/node/download/file-download-handler.ts +++ b/packages/filesystem/src/node/download/file-download-handler.ts @@ -24,7 +24,7 @@ import { OK, BAD_REQUEST, METHOD_NOT_ALLOWED, NOT_FOUND, INTERNAL_SERVER_ERROR, import URI from '@theia/core/lib/common/uri'; import { isEmpty } from '@theia/core/lib/common/objects'; import { ILogger } from '@theia/core/lib/common/logger'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { DirectoryArchiver } from './directory-archiver'; import { FileDownloadData } from '../../common/download/file-download-data'; import { FileDownloadCache, DownloadStorageItem } from './file-download-cache'; diff --git a/packages/filesystem/src/node/file-change-collection.spec.ts b/packages/filesystem/src/node/file-change-collection.spec.ts index 30f218a5e0eeb..a745e20899084 100644 --- a/packages/filesystem/src/node/file-change-collection.spec.ts +++ b/packages/filesystem/src/node/file-change-collection.spec.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import * as assert from 'assert'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { FileChangeCollection } from './file-change-collection'; import { FileChangeType } from '../common/files'; diff --git a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts b/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts index 6e4723076fea3..ebc947846e804 100644 --- a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts +++ b/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts @@ -18,7 +18,7 @@ import nsfw = require('@theia/core/shared/nsfw'); import path = require('path'); import { promises as fsp } from 'fs'; import { IMinimatch, Minimatch } from 'minimatch'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { FileChangeType, FileSystemWatcherService, FileSystemWatcherServiceClient, WatchOptions } from '../../common/filesystem-watcher-protocol'; diff --git a/packages/git/src/node/dugite-git-watcher.slow-spec.ts b/packages/git/src/node/dugite-git-watcher.slow-spec.ts index 5a4f0e95bdd4f..843e092029471 100644 --- a/packages/git/src/node/dugite-git-watcher.slow-spec.ts +++ b/packages/git/src/node/dugite-git-watcher.slow-spec.ts @@ -18,7 +18,7 @@ import * as fs from '@theia/core/shared/fs-extra'; import * as temp from 'temp'; import * as path from 'path'; import { expect } from 'chai'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { Git } from '../common/git'; import { DugiteGit } from './dugite-git'; import { Repository } from '../common'; diff --git a/packages/git/src/node/dugite-git.slow-spec.ts b/packages/git/src/node/dugite-git.slow-spec.ts index 888507b401d54..b4954dd321188 100644 --- a/packages/git/src/node/dugite-git.slow-spec.ts +++ b/packages/git/src/node/dugite-git.slow-spec.ts @@ -16,7 +16,7 @@ import * as temp from 'temp'; import { expect } from 'chai'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { GitFileStatus } from '../common'; import { createGit } from './test/binding-helper'; diff --git a/packages/git/src/node/dugite-git.spec.ts b/packages/git/src/node/dugite-git.spec.ts index db3592f321c7c..340fafaf6e1b2 100644 --- a/packages/git/src/node/dugite-git.spec.ts +++ b/packages/git/src/node/dugite-git.spec.ts @@ -22,7 +22,7 @@ import * as fs from '@theia/core/shared/fs-extra'; import { expect } from 'chai'; import { Git } from '../common/git'; import { git as gitExec } from 'dugite-extra/lib/core/git'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { WorkingDirectoryStatus, Repository, GitUtils, GitFileStatus, GitFileChange } from '../common'; import { initRepository, createTestRepository } from 'dugite-extra/lib/command/test-helper'; import { createGit } from './test/binding-helper'; diff --git a/packages/git/src/node/dugite-git.ts b/packages/git/src/node/dugite-git.ts index e72ad9dee1724..857c757b12ba6 100644 --- a/packages/git/src/node/dugite-git.ts +++ b/packages/git/src/node/dugite-git.ts @@ -24,7 +24,7 @@ import { clone } from 'dugite-extra/lib/command/clone'; import { fetch } from 'dugite-extra/lib/command/fetch'; import { stash } from 'dugite-extra/lib/command/stash'; import { merge } from 'dugite-extra/lib/command/merge'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { getStatus } from 'dugite-extra/lib/command/status'; import { createCommit } from 'dugite-extra/lib/command/commit'; import { stage, unstage } from 'dugite-extra/lib/command/stage'; diff --git a/packages/mini-browser/src/node/mini-browser-endpoint.ts b/packages/mini-browser/src/node/mini-browser-endpoint.ts index f8400b862bfff..1c3420bf5eba7 100644 --- a/packages/mini-browser/src/node/mini-browser-endpoint.ts +++ b/packages/mini-browser/src/node/mini-browser-endpoint.ts @@ -20,7 +20,7 @@ import * as fs from '@theia/core/shared/fs-extra'; import { lookup } from 'mime-types'; import { injectable, inject, named } from '@theia/core/shared/inversify'; import { Application, Request, Response } from '@theia/core/shared/express'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { ILogger } from '@theia/core/lib/common/logger'; import { MaybePromise } from '@theia/core/lib/common/types'; import { ContributionProvider } from '@theia/core/lib/common/contribution-provider'; diff --git a/packages/navigator/src/browser/navigator-diff.spec.ts b/packages/navigator/src/browser/navigator-diff.spec.ts index ced5d792c6678..2f70af9ace45f 100644 --- a/packages/navigator/src/browser/navigator-diff.spec.ts +++ b/packages/navigator/src/browser/navigator-diff.spec.ts @@ -31,7 +31,7 @@ import { OpenerService } from '@theia/core/lib/browser'; import { MockOpenerService } from '@theia/core/lib/browser/test/mock-opener-service'; import { MessageService } from '@theia/core/lib/common/message-service'; import { MessageClient } from '@theia/core/lib/common/message-service-protocol'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { DiskFileSystemProvider } from '@theia/filesystem/lib/node/disk-file-system-provider'; diff --git a/packages/plugin-dev/src/node/hosted-instance-manager.ts b/packages/plugin-dev/src/node/hosted-instance-manager.ts index dc11d18603478..e56ad1d1932f7 100644 --- a/packages/plugin-dev/src/node/hosted-instance-manager.ts +++ b/packages/plugin-dev/src/node/hosted-instance-manager.ts @@ -24,7 +24,7 @@ import URI from '@theia/core/lib/common/uri'; import { ContributionProvider } from '@theia/core/lib/common/contribution-provider'; import { HostedPluginUriPostProcessor, HostedPluginUriPostProcessorSymbolName } from './hosted-plugin-uri-postprocessor'; import { environment, isWindows } from '@theia/core'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { LogType } from '@theia/plugin-ext/lib/common/types'; import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin'; import { MetadataScanner } from '@theia/plugin-ext/lib/hosted/node/metadata-scanner'; diff --git a/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts b/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts index f3f6459aa95cc..599d61fae2158 100644 --- a/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts +++ b/packages/plugin-ext-vscode/src/node/plugin-vscode-file-handler.ts @@ -18,8 +18,8 @@ import { PluginDeployerFileHandler, PluginDeployerEntry, PluginDeployerFileHandl import * as filenamify from 'filenamify'; import { inject, injectable } from '@theia/core/shared/inversify'; import * as fs from '@theia/core/shared/fs-extra'; -import { FileUri } from '@theia/core/lib/node'; import { PluginVSCodeEnvironment } from '../common/plugin-vscode-environment'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { unpackToDeploymentDir } from './plugin-vscode-utils'; export const isVSCodePluginFile = (pluginPath?: string) => Boolean(pluginPath && (pluginPath.endsWith('.vsix') || pluginPath.endsWith('.tgz'))); diff --git a/packages/plugin-ext/src/hosted/node/scanners/file-plugin-uri-factory.ts b/packages/plugin-ext/src/hosted/node/scanners/file-plugin-uri-factory.ts index 3ad460efd6c78..74518f5097d66 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/file-plugin-uri-factory.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/file-plugin-uri-factory.ts @@ -17,7 +17,7 @@ import { injectable } from '@theia/core/shared/inversify'; import * as path from 'path'; import URI from '@theia/core/lib/common/uri'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { PluginPackage } from '../../../common'; import { PluginUriFactory } from './plugin-uri-factory'; /** diff --git a/packages/plugin-ext/src/main/node/handlers/plugin-theia-file-handler.ts b/packages/plugin-ext/src/main/node/handlers/plugin-theia-file-handler.ts index 28983e18b15af..367bdf5aa4d01 100644 --- a/packages/plugin-ext/src/main/node/handlers/plugin-theia-file-handler.ts +++ b/packages/plugin-ext/src/main/node/handlers/plugin-theia-file-handler.ts @@ -21,7 +21,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util'; import { getTempDirPathAsync } from '../temp-dir-util'; import * as fs from '@theia/core/shared/fs-extra'; import * as filenamify from 'filenamify'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { PluginTheiaEnvironment } from '../../common/plugin-theia-environment'; @injectable() diff --git a/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts b/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts index 833654c727e98..48aa75ebc3fa1 100644 --- a/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts +++ b/packages/plugin-ext/src/main/node/plugins-key-value-storage.ts @@ -18,7 +18,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify' import { FileSystemLocking } from '@theia/core/lib/node'; import * as fs from '@theia/core/shared/fs-extra'; import * as path from 'path'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { PluginPaths } from './paths/const'; diff --git a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.slow-spec.ts b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.slow-spec.ts index 70f659b355bd0..d33d0e8c1a736 100644 --- a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.slow-spec.ts +++ b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.slow-spec.ts @@ -16,7 +16,7 @@ import { Container } from '@theia/core/shared/inversify'; import { ILogger, isWindows } from '@theia/core'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { MockLogger } from '@theia/core/lib/common/test/mock-logger'; import { RawProcessFactory, RawProcessOptions, RawProcess, ProcessManager } from '@theia/process/lib/node'; import { RipgrepSearchInWorkspaceServer, RgPath } from './ripgrep-search-in-workspace-server'; diff --git a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts index a10fc6588a15c..dc55caa616149 100644 --- a/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts +++ b/packages/search-in-workspace/src/node/ripgrep-search-in-workspace-server.ts @@ -18,7 +18,7 @@ import * as fs from '@theia/core/shared/fs-extra'; import * as path from 'path'; import { ILogger } from '@theia/core'; import { RawProcess, RawProcessFactory, RawProcessOptions } from '@theia/process/lib/node'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import URI from '@theia/core/lib/common/uri'; import { inject, injectable } from '@theia/core/shared/inversify'; import { SearchInWorkspaceServer, SearchInWorkspaceOptions, SearchInWorkspaceResult, SearchInWorkspaceClient, LinePreview } from '../common/search-in-workspace-interface'; diff --git a/packages/terminal/src/node/shell-process.ts b/packages/terminal/src/node/shell-process.ts index 524a697082c7e..4bf64080756c5 100644 --- a/packages/terminal/src/node/shell-process.ts +++ b/packages/terminal/src/node/shell-process.ts @@ -20,7 +20,7 @@ import { ILogger } from '@theia/core/lib/common/logger'; import { TerminalProcess, TerminalProcessOptions, ProcessManager, MultiRingBuffer } from '@theia/process/lib/node'; import { isWindows, isOSX } from '@theia/core/lib/common'; import URI from '@theia/core/lib/common/uri'; -import { FileUri } from '@theia/core/lib/node/file-uri'; +import { FileUri } from '@theia/core/lib/common/file-uri'; import { EnvironmentUtils } from '@theia/core/lib/node/environment-utils'; import { parseArgs } from '@theia/process/lib/node/utils'; diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 797c43fedbdb2..542c9a1b56205 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -16,6 +16,9 @@ { "frontend": "lib/browser/workspace-frontend-module", "backend": "lib/node/workspace-backend-module" + }, + { + "frontendOnly": "lib/browser-only/workspace-frontend-only-module" } ], "keywords": [ diff --git a/packages/workspace/src/browser-only/browser-only-workspace-server.ts b/packages/workspace/src/browser-only/browser-only-workspace-server.ts new file mode 100644 index 0000000000000..2c94b3b997791 --- /dev/null +++ b/packages/workspace/src/browser-only/browser-only-workspace-server.ts @@ -0,0 +1,69 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable } from '@theia/core/shared/inversify'; +import { WorkspaceServer } from '../common/workspace-protocol'; +import { ILogger, isStringArray } from '@theia/core'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; + +export const RECENT_WORKSPACES_LOCAL_STORAGE_KEY = 'workspaces'; + +@injectable() +export class BrowserOnlyWorkspaceServer implements WorkspaceServer { + + @inject(ILogger) + protected logger: ILogger; + + @inject(FileService) + protected readonly fileService: FileService; + + async getRecentWorkspaces(): Promise { + const storedWorkspaces = localStorage.getItem(RECENT_WORKSPACES_LOCAL_STORAGE_KEY); + if (!storedWorkspaces) { + return []; + } + try { + const parsedWorkspaces = JSON.parse(storedWorkspaces); + if (isStringArray(parsedWorkspaces)) { + return parsedWorkspaces; + } + } catch (e) { + this.logger.error(e); + return []; + } + return []; + } + + async getMostRecentlyUsedWorkspace(): Promise { + const workspaces = await this.getRecentWorkspaces(); + return workspaces[0]; + } + + async setMostRecentlyUsedWorkspace(uri: string): Promise { + const workspaces = await this.getRecentWorkspaces(); + if (workspaces.includes(uri)) { + workspaces.splice(workspaces.indexOf(uri), 1); + } + localStorage.setItem(RECENT_WORKSPACES_LOCAL_STORAGE_KEY, JSON.stringify([uri, ...workspaces])); + } + + async removeRecentWorkspace(uri: string): Promise { + const workspaces = await this.getRecentWorkspaces(); + if (workspaces.includes(uri)) { + workspaces.splice(workspaces.indexOf(uri), 1); + } + localStorage.setItem(RECENT_WORKSPACES_LOCAL_STORAGE_KEY, JSON.stringify(workspaces)); + } +} diff --git a/packages/workspace/src/browser-only/workspace-frontend-only-module.ts b/packages/workspace/src/browser-only/workspace-frontend-only-module.ts new file mode 100644 index 0000000000000..2953f66fdfd2d --- /dev/null +++ b/packages/workspace/src/browser-only/workspace-frontend-only-module.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; +import { BrowserOnlyWorkspaceServer } from './browser-only-workspace-server'; +import { WorkspaceServer } from '../common'; + +export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind) => { + bind(BrowserOnlyWorkspaceServer).toSelf().inSingletonScope(); + if (isBound(WorkspaceServer)) { + rebind(WorkspaceServer).toService(BrowserOnlyWorkspaceServer); + } else { + bind(WorkspaceServer).toService(BrowserOnlyWorkspaceServer); + } +}); diff --git a/tsconfig.json b/tsconfig.json index 732d0029c6500..ac07be3ca619e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,6 +42,9 @@ { "path": "examples/browser" }, + { + "path": "examples/browser-only" + }, { "path": "examples/electron" }, diff --git a/yarn.lock b/yarn.lock index 4a9da1bdbf274..011b396fd23e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3160,6 +3160,13 @@ async-mutex@^0.4.0: dependencies: tslib "^2.4.0" +async@^2.1.4, async@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + async@^3.2.3, async@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -3295,6 +3302,13 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== +basic-auth@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -3446,6 +3460,14 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserfs@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/browserfs/-/browserfs-1.4.3.tgz#92ffc6063967612daccdb8566d3fc03f521205fb" + integrity sha512-tz8HClVrzTJshcyIu8frE15cjqjcBIu15Bezxsvl/i+6f59iNCN3kznlWjz0FEb3DlnDx3gW5szxeT6D1x0s0w== + dependencies: + async "^2.1.4" + pako "^1.0.4" + browserslist@^4.14.5, browserslist@^4.21.9, browserslist@^4.22.1: version "4.22.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" @@ -3702,7 +3724,7 @@ chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4200,6 +4222,11 @@ cors@~2.8.5: object-assign "^4" vary "^1" +corser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ== + cosmiconfig@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" @@ -5315,7 +5342,7 @@ event-stream@=3.3.4: stream-combiner "~0.0.4" through "~2.3.1" -eventemitter3@^4.0.4: +eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -5670,7 +5697,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.14.0, follow-redirects@^1.15.0: +follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== @@ -6258,7 +6285,7 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" -he@1.2.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -6355,6 +6382,34 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-server@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e" + integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A== + dependencies: + basic-auth "^2.0.1" + chalk "^4.1.2" + corser "^2.0.1" + he "^1.2.0" + html-encoding-sniffer "^3.0.0" + http-proxy "^1.18.1" + mime "^1.6.0" + minimist "^1.2.6" + opener "^1.5.1" + portfinder "^1.0.28" + secure-compare "3.0.1" + union "~0.5.0" + url-join "^4.0.1" + http-status-codes@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" @@ -7558,7 +7613,7 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw== -lodash@^4.17.15, lodash@^4.17.21, lodash@^4.5.1: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.5.1: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7821,7 +7876,7 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24, dependencies: mime-db "1.52.0" -mime@1.6.0, mime@^1.3.4, mime@^1.4.1: +mime@1.6.0, mime@^1.3.4, mime@^1.4.1, mime@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -8018,7 +8073,7 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@^0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8786,6 +8841,11 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +opener@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + optionator@^0.9.1: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -8992,6 +9052,11 @@ pacote@^15.2.0: ssri "^10.0.0" tar "^6.1.11" +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -9234,6 +9299,15 @@ playwright@1.38.1: optionalDependencies: fsevents "2.3.2" +portfinder@^1.0.28: + version "1.0.32" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" + integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== + dependencies: + async "^2.6.4" + debug "^3.2.7" + mkdirp "^0.5.6" + postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" @@ -9530,7 +9604,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.9.1, qs@^6.9.4: +qs@^6.4.0, qs@^6.9.1, qs@^6.9.4: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -10058,16 +10132,16 @@ safe-array-concat@^1.0.1: has-symbols "^1.0.3" isarray "^2.0.5" +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -10141,6 +10215,11 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +secure-compare@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" + integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== + seek-bzip@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" @@ -11429,6 +11508,13 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +union@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075" + integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA== + dependencies: + qs "^6.4.0" + unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" From f6157709a2bf8c07ae413cda28e0e5b5b05d6538 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 16 Jan 2024 14:42:05 +0100 Subject: [PATCH 051/441] Bump API version to 1.85.1 (#13276) Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- dev-packages/application-package/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index a61f3c2a095e2..eecbf198febeb 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.84.2'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.85.1'; From 15caac52e82f0707c9c0183fb4b3400f4a65054b Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 16 Jan 2024 15:03:45 +0100 Subject: [PATCH 052/441] Rename Terminal.sendText addNewLine parameter to align with vscode api (#13236) fixes #13235 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/plugin-ext/src/common/plugin-api-rpc.ts | 4 ++-- packages/plugin-ext/src/main/browser/terminal-main.ts | 4 ++-- packages/plugin-ext/src/plugin/terminal-ext.ts | 4 ++-- packages/plugin/src/theia.d.ts | 7 ++++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 505c6060e9f59..d36802d4511a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [plugin] stub multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics +- [terminal] rename terminal.sendText() parameter from addNewLine to shouldExecute [#13236](https://github.com/eclipse-theia/theia/pull/13236) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_not_yet_released) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 5516220d17cd3..c0616d5177df6 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -327,9 +327,9 @@ export interface TerminalServiceMain { * Send text to the terminal by id. * @param id - terminal widget id. * @param text - text content. - * @param addNewLine - in case true - add new line after the text, otherwise - don't apply new line. + * @param shouldExecute - in case true - Indicates that the text being sent should be executed rather than just inserted in the terminal. */ - $sendText(id: string, text: string, addNewLine?: boolean): void; + $sendText(id: string, text: string, shouldExecute?: boolean): void; /** * Write data to the terminal by id. diff --git a/packages/plugin-ext/src/main/browser/terminal-main.ts b/packages/plugin-ext/src/main/browser/terminal-main.ts index fe1876c7bf8b4..590955fc27faa 100644 --- a/packages/plugin-ext/src/main/browser/terminal-main.ts +++ b/packages/plugin-ext/src/main/browser/terminal-main.ts @@ -181,11 +181,11 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin return undefined; } - $sendText(id: string, text: string, addNewLine?: boolean): void { + $sendText(id: string, text: string, shouldExecute?: boolean): void { const terminal = this.terminals.getById(id); if (terminal) { text = text.replace(/\r?\n/g, '\r'); - if (addNewLine && text.charAt(text.length - 1) !== '\r') { + if (shouldExecute && text.charAt(text.length - 1) !== '\r') { text += '\r'; } terminal.sendText(text); diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index 7c158fd36a8cb..627c322b4ec4b 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -463,8 +463,8 @@ export class TerminalExtImpl implements Terminal { this.creationOptions = this.options; } - sendText(text: string, addNewLine: boolean = true): void { - this.id.promise.then(id => this.proxy.$sendText(id, text, addNewLine)); + sendText(text: string, shouldExecute: boolean = true): void { + this.id.promise.then(id => this.proxy.$sendText(id, text, shouldExecute)); } show(preserveFocus?: boolean): void { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index c27a73a6b6ddc..e1cbea324ebed 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -3043,10 +3043,11 @@ export module '@theia/plugin' { /** * Send text to the terminal. - * @param text - text content. - * @param addNewLine - in case true - apply new line after the text, otherwise don't apply new line. This defaults to `true`. + * @param text - The text to send. + * @param shouldExecute - Indicates that the text being sent should be executed rather than just inserted in the terminal. + * The character added is \r, independent from the platform (compared to platform specific in vscode). This defaults to `true`. */ - sendText(text: string, addNewLine?: boolean): void; + sendText(text: string, shouldExecute?: boolean): void; /** * Show created terminal on the UI. From 0dbca07941b43db33a36c71fd135e993175b4125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 16 Jan 2024 17:31:09 +0000 Subject: [PATCH 053/441] Fix leak when reconnecting to back end without reload. Fixes #13215 (#13250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../browser/messaging/ws-connection-source.ts | 62 ++++++++++++------- .../websocket-frontend-connection-service.ts | 15 +++-- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/packages/core/src/browser/messaging/ws-connection-source.ts b/packages/core/src/browser/messaging/ws-connection-source.ts index a1e9b83814035..24d7d7920143f 100644 --- a/packages/core/src/browser/messaging/ws-connection-source.ts +++ b/packages/core/src/browser/messaging/ws-connection-source.ts @@ -87,38 +87,58 @@ export class WebSocketConnectionSource implements ConnectionSource { this._socket.connect(); } - protected handleSocketConnected(): void { - if (this.currentChannel) { - const reconnectListener = (hasConnection: boolean) => { - this._socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); - if (hasConnection) { - this.writeBuffer.flush(this.socket); + protected negogiateReconnect(): void { + const reconnectListener = (hasConnection: boolean) => { + this._socket.off(ConnectionManagementMessages.RECONNECT, reconnectListener); + if (hasConnection) { + console.info(`reconnect succeeded on ${this.socket.id}`); + this.writeBuffer!.flush(this.socket); + } else { + if (FrontendApplicationConfigProvider.get().reloadOnReconnect) { + window.location.reload(); // this might happen in the preload module, when we have no window service yet } else { - if (FrontendApplicationConfigProvider.get().reloadOnReconnect) { - window.location.reload(); // this might happen in the preload module, when we have no window service yet - } else { - this.connectNewChannel(); - } + console.info(`reconnect failed on ${this.socket.id}`); + this.currentChannel.onCloseEmitter.fire({ reason: 'reconnecting channel' }); + this.currentChannel.close(); + this.writeBuffer.drain(); + this.socket.disconnect(); + this.socket.connect(); + this.negotiateInitialConnect(); } - }; - this._socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); - this._socket.emit(ConnectionManagementMessages.RECONNECT, this.frontendIdProvider.getId()); + } + }; + this._socket.on(ConnectionManagementMessages.RECONNECT, reconnectListener); + console.info(`sending reconnect on ${this.socket.id}`); + this._socket.emit(ConnectionManagementMessages.RECONNECT, this.frontendIdProvider.getId()); + } + + protected negotiateInitialConnect(): void { + const initialConnectListener = () => { + console.info(`initial connect received on ${this.socket.id}`); + + this._socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + this.connectNewChannel(); + }; + this._socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); + console.info(`sending initial connect on ${this.socket.id}`); + + this._socket.emit(ConnectionManagementMessages.INITIAL_CONNECT, this.frontendIdProvider.getId()); + } + + protected handleSocketConnected(): void { + if (this.currentChannel) { + this.negogiateReconnect(); } else { - const initialConnectListener = () => { - this._socket.off(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); - this.connectNewChannel(); - }; - this._socket.on(ConnectionManagementMessages.INITIAL_CONNECT, initialConnectListener); - this._socket.emit(ConnectionManagementMessages.INITIAL_CONNECT, this.frontendIdProvider.getId()); + this.negotiateInitialConnect(); } } connectNewChannel(): void { if (this.currentChannel) { - this.writeBuffer.drain(); this.currentChannel.close(); this.currentChannel.onCloseEmitter.fire({ reason: 'reconnecting channel' }); } + this.writeBuffer.drain(); this.currentChannel = this.createChannel(); this.onConnectionDidOpenEmitter.fire(this.currentChannel); } diff --git a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts index 648edd0f14a55..633dc1d410aff 100644 --- a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts +++ b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts @@ -51,7 +51,9 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer if (this.connectionsByFrontend.has(frontEndId)) { this.closeConnection(frontEndId, 'reconnecting same front end'); } - channelCreatedHandler(this.createConnection(socket, frontEndId)); + const channel = this.createConnection(socket, frontEndId); + this.handleSocketDisconnect(socket, channel, frontEndId); + channelCreatedHandler(channel); socket.emit(ConnectionManagementMessages.INITIAL_CONNECT); }; @@ -63,6 +65,7 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer console.info(`Reconnecting to front end ${frontEndId}`); socket.emit(ConnectionManagementMessages.RECONNECT, true); channel.connect(socket); + this.handleSocketDisconnect(socket, channel, frontEndId); const pendingTimeout = this.closeTimeouts.get(frontEndId); clearTimeout(pendingTimeout); this.closeTimeouts.delete(frontEndId); @@ -89,11 +92,16 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer connection.close(); } - protected createConnection(socket: Socket, frontEndId: string): Channel { + protected createConnection(socket: Socket, frontEndId: string): ReconnectableSocketChannel { console.info(`creating connection for ${frontEndId}`); const channel = new ReconnectableSocketChannel(); channel.connect(socket); + this.connectionsByFrontend.set(frontEndId, channel); + return channel; + } + + handleSocketDisconnect(socket: Socket, channel: ReconnectableSocketChannel, frontEndId: string): void { socket.on('disconnect', evt => { console.info('socked closed'); channel.disconnect(); @@ -111,9 +119,6 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer // timeout < 0: never close the back end } }); - - this.connectionsByFrontend.set(frontEndId, channel); - return channel; } markForClose(channelId: string): void { From 90211ed10db3ad7a8be5936ab6cae9b993a5ec5e Mon Sep 17 00:00:00 2001 From: John Cortell <109228283+jcortell68@users.noreply.github.com> Date: Wed, 17 Jan 2024 02:20:24 -0600 Subject: [PATCH 054/441] Address gap in coding guideliness. (#13278) The guidelines discourage the use of @multiInject and lists the benefits of ContributionProvider, but neglects to mention the filtering feature of that utility. --- doc/coding-guidelines.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/coding-guidelines.md b/doc/coding-guidelines.md index fae57fc42dcfd..b3706d79e0aa7 100644 --- a/doc/coding-guidelines.md +++ b/doc/coding-guidelines.md @@ -301,11 +301,12 @@ export namespace DirtyDiffModel { } ``` -* [5.](#no-multi-inject) Don't use multi-inject, use `ContributionProvider` to inject multiple instances. +* [5.](#no-multi-inject) Don't use InversifyJS's `@multiInject`, use Theia's utility `ContributionProvider` to inject multiple instances. > Why? > - `ContributionProvider` is a documented way to introduce contribution points. See `Contribution-Points`: https://www.theia-ide.org/docs/services_and_contributions > - If nothing is bound to an identifier, multi-inject resolves to `undefined`, not an empty array. `ContributionProvider` provides an empty array. > - Multi-inject does not guarantee the same instances are injected if an extender does not use `inSingletonScope`. `ContributionProvider` caches instances to ensure uniqueness. +> - `ContributionProvider` supports filtering. See `ContributionFilterRegistry`. ## CSS From f7b1a7874daf0decfe46cd5c80c3c5a63f9fed3e Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Wed, 17 Jan 2024 13:48:49 +0100 Subject: [PATCH 055/441] Ensure we do not spam the log in the RpcProxyFactory (#13191) Fixes https://github.com/eclipse-theia/theia/issues/13189 --- packages/core/src/common/messaging/proxy-factory.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/core/src/common/messaging/proxy-factory.ts b/packages/core/src/common/messaging/proxy-factory.ts index ab654ca871a46..f24f6bbb1bdd7 100644 --- a/packages/core/src/common/messaging/proxy-factory.ts +++ b/packages/core/src/common/messaging/proxy-factory.ts @@ -168,14 +168,7 @@ export class RpcProxyFactory implements ProxyHandler { throw new Error(`no target was set to handle ${method}`); } } catch (error) { - const e = this.serializeError(error); - if (e instanceof ResponseError) { - throw e; - } - const reason = e.message || ''; - const stack = e.stack || ''; - console.error(`Request ${method} failed with error: ${reason}`, stack); - throw e; + throw this.serializeError(error); } } From 6a844443b39e462349c720af96afa56cd8a7f3f8 Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Wed, 17 Jan 2024 22:19:08 +0100 Subject: [PATCH 056/441] Make tree widget indentation configurable (#13179) Make the indentation of the tree widget configurable via preference. Contributed on behalf of STMicroelectronics Signed-off-by: Alexandra Buzila --- CHANGELOG.md | 1 + .../browser/frontend-application-module.ts | 2 + packages/core/src/browser/tree/index.ts | 1 + .../core/src/browser/tree/tree-preference.ts | 50 +++++++++++++++++++ .../core/src/browser/tree/tree-widget.tsx | 22 ++++++-- .../browser/abstract-navigator-tree-widget.ts | 4 -- 6 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/browser/tree/tree-preference.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d36802d4511a2..459ea9036d021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [plugin] stub multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics - [terminal] rename terminal.sendText() parameter from addNewLine to shouldExecute [#13236](https://github.com/eclipse-theia/theia/pull/13236) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics +- [core] added preference 'workbench.tree.indent' to control the indentation in the tree widget [#13179](https://github.com/eclipse-theia/theia/pull/13179) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_not_yet_released) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 34e857decdf2b..3dd79dd12d489 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -139,6 +139,7 @@ import { bindCommonStylingParticipants } from './common-styling-participants'; import { HoverService } from './hover-service'; import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './shell/additional-views-menu-widget'; import { LanguageIconLabelProvider } from './language-icon-provider'; +import { bindTreePreferences } from './tree'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -365,6 +366,7 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(ThemeService).toSelf().inSingletonScope(); bindCorePreferences(bind); + bindTreePreferences(bind); bind(MimeService).toSelf().inSingletonScope(); diff --git a/packages/core/src/browser/tree/index.ts b/packages/core/src/browser/tree/index.ts index 2361b0b62280f..03ce159fb9324 100644 --- a/packages/core/src/browser/tree/index.ts +++ b/packages/core/src/browser/tree/index.ts @@ -26,3 +26,4 @@ export * from './tree-container'; export * from './tree-decorator'; export * from './tree-search'; export * from './tree-compression'; +export * from './tree-preference'; diff --git a/packages/core/src/browser/tree/tree-preference.ts b/packages/core/src/browser/tree/tree-preference.ts new file mode 100644 index 0000000000000..424939be9fee3 --- /dev/null +++ b/packages/core/src/browser/tree/tree-preference.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from 'inversify'; +import { PreferenceProxy, PreferenceContribution, PreferenceSchema } from '../preferences'; +import { PreferenceProxyFactory } from '../preferences/injectable-preference-proxy'; +import { nls } from '../../common/nls'; + +export const PREFERENCE_NAME_TREE_INDENT = 'workbench.tree.indent'; + +export const treePreferencesSchema: PreferenceSchema = { + type: 'object', + properties: { + [PREFERENCE_NAME_TREE_INDENT]: { + description: nls.localizeByDefault('Controls tree indentation in pixels.'), + type: 'number', + default: 8, + minimum: 4, + maximum: 40 + }, + } +}; + +export class TreeConfiguration { + [PREFERENCE_NAME_TREE_INDENT]: number; +} + +export const TreePreferences = Symbol('treePreferences'); +export type TreePreferences = PreferenceProxy; + +export function bindTreePreferences(bind: interfaces.Bind): void { + bind(TreePreferences).toDynamicValue(ctx => { + const factory = ctx.container.get(PreferenceProxyFactory); + return factory(treePreferencesSchema); + }).inSingletonScope(); + bind(PreferenceContribution).toConstantValue({ schema: treePreferencesSchema }); +} diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index 163bdf561b4c9..0c4f0c4cc7bdc 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -43,6 +43,8 @@ import { LabelProvider } from '../label-provider'; import { CorePreferences } from '../core-preferences'; import { TreeFocusService } from './tree-focus-service'; import { useEffect } from 'react'; +import { PreferenceService, PreferenceChange } from '../preferences'; +import { PREFERENCE_NAME_TREE_INDENT } from './tree-preference'; const debounce = require('lodash.debounce'); @@ -73,8 +75,7 @@ export interface TreeProps { readonly contextMenuPath?: MenuPath; /** - * The size of the padding (in pixels) per hierarchy depth. The root element won't have left padding but - * the padding for the children will be calculated as `leftPadding * hierarchyDepth` and so on. + * The size of the padding (in pixels) for the root node of the tree. */ readonly leftPadding: number; @@ -174,6 +175,9 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { @inject(SelectionService) protected readonly selectionService: SelectionService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @@ -182,6 +186,8 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { protected shouldScrollToRow = true; + protected treeIndent: number = 8; + constructor( @inject(TreeProps) readonly props: TreeProps, @inject(TreeModel) readonly model: TreeModel, @@ -198,6 +204,7 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { @postConstruct() protected init(): void { + this.treeIndent = this.preferenceService.get(PREFERENCE_NAME_TREE_INDENT, this.treeIndent); if (this.props.search) { this.searchBox = this.searchBoxFactory({ ...SearchBoxProps.DEFAULT, showButtons: true, showFilter: true }); this.searchBox.node.addEventListener('focus', () => { @@ -264,6 +271,12 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { return; } } + }), + this.preferenceService.onPreferenceChanged((event: PreferenceChange) => { + if (event.preferenceName === PREFERENCE_NAME_TREE_INDENT) { + this.treeIndent = event.newValue; + this.update(); + } }) ]); setTimeout(() => { @@ -1500,7 +1513,10 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { return this.labelProvider.getLongName(node); } protected getDepthPadding(depth: number): number { - return depth * this.props.leftPadding; + if (depth === 1) { + return this.props.leftPadding; + } + return depth * this.treeIndent; } } export namespace TreeWidget { diff --git a/packages/navigator/src/browser/abstract-navigator-tree-widget.ts b/packages/navigator/src/browser/abstract-navigator-tree-widget.ts index 73c5898bea0ff..56d7962326ebd 100644 --- a/packages/navigator/src/browser/abstract-navigator-tree-widget.ts +++ b/packages/navigator/src/browser/abstract-navigator-tree-widget.ts @@ -16,7 +16,6 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { FileNavigatorPreferences } from './navigator-preferences'; -import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { FileTreeWidget } from '@theia/filesystem/lib/browser'; import { Attributes, HTMLAttributes } from '@theia/core/shared/react'; import { TreeNode } from '@theia/core/lib/browser'; @@ -24,9 +23,6 @@ import { TreeNode } from '@theia/core/lib/browser'; @injectable() export class AbstractNavigatorTreeWidget extends FileTreeWidget { - @inject(PreferenceService) - protected readonly preferenceService: PreferenceService; - @inject(FileNavigatorPreferences) protected readonly navigatorPreferences: FileNavigatorPreferences; From ff8148f1811be2220500ddbae59f894d73eea16c Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Thu, 18 Jan 2024 10:05:44 +0100 Subject: [PATCH 057/441] Improve documentation on passing objects across RPC (#13238) Fixes https://github.com/eclipse-theia/theia/issues/13224 --- doc/Plugin-API.md | 42 +++++++++++-------- .../message-rpc/msg-pack-extension-manager.ts | 2 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/doc/Plugin-API.md b/doc/Plugin-API.md index fc63f73818803..539b67bfa3b44 100644 --- a/doc/Plugin-API.md +++ b/doc/Plugin-API.md @@ -66,12 +66,20 @@ To communicate with each other, the implementation of each side of the API - `Ma The proxy is based on the interface of the other side: `Main` implementation has a proxy of the `Ext` interface and vice versa. The implementations do not have explicit dependencies to each other. -Communication via RPC only supports transferring plain JSON objects: Only pure DTO objects without any functions can be transmitted. -Consequently, objects with functions need to be cached and references to such objects need to be transmitted as handles (ids) that are resolved to the original object later on to invoke functions. +### Encoding and Decoding RPC Messages -For instance, in [LanguagesExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts)#registerCodeActionsProvider a new code action provider is cached on the `Ext` side and then registered on the `Main` side via its handle. -When the code action provider’s methods are later invoked on the `Main` side (e.g. in [LanguagesMainImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/languages-main.ts)#provideCodeActions), it calls the `Ext` side with this handle. -The `Ext` side then gets the cached object, executes appropriate functions and returns the results back to the `Main` side (e.g. in [LanguagesExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts)#$provideCodeActions). +The communication between each side of the API is goverend by proxies that use a `RpcProtocol` on a given channel to transmit RPC messages such as requests and notifications. +In Theia, the encoding and decoding process of RPC messages can be customized through dedicated `RpcMessageEncoder` and `RpcMessageDecoder` classes that can be provided when a new RpcProtocol is created. +The `RpcMessageEncoder` writes RPC messages to a buffer whereas the `RpcMessageDecoder` parses a binary message from a buffer into a RPC message. + +By default, Theia uses an encoder and decoder based on [msgpackr](https://www.npmjs.com/package/msgpackr) that already properly handles many JavaScript built-in types, such as arrays and maps. +We can separately extend the encoding and decoding of our own classes by installing extensions using the `MsgPackExtensionManager` singleton, accessible from both ends of the channel. +Examples of this can be found in the [extension for Errors](https://github.com/eclipse-theia/theia/blob/72421be24d0461f811a39324579913e91056d7c4/packages/core/src/common/message-rpc/rpc-message-encoder.ts#L171) and the [extension for URI, Range and other classes](https://github.com/eclipse-theia/theia/blob/72421be24d0461f811a39324579913e91056d7c4/packages/plugin-ext/src/common/rpc-protocol.ts#L223). +We call the registration of these extensions in `index.ts` to ensure they are available early on. +Please note that msgpackr [always registers extensions globally](https://github.com/kriszyp/msgpackr/issues/93) so the extensions leak into all connections within Theia, i.e., also non-API related areas such as the frontend-backend connection. +And while the number of custom extensions is [limited to 100](https://github.com/kriszyp/msgpackr?tab=readme-ov-file#custom-extensions), checking the extensions for every single message may impact performance if there are many extensions or the conversion is very expensive. +Another hurdle is that we need to ensure that we always detect the correct type of object purely from the object shape which may prove difficult if there are hundreds of objects and custom instances from user code. +Consequently, we mainly use the msgpackr extension mechanism for very few common classes but rely on custom data transfer objects (DTOs) for the Theia plugin API, see also [Complex objects and RPC](#complex-objects-and-rpc). ## Adding new API @@ -147,27 +155,25 @@ They each create the proxy to the other side in their constructors by using the ### Complex objects and RPC -Only pure DTO objects without any functions or references to other objects can be transmitted via RPC. -This often makes it impossible to just transfer objects provided by a plugin directly via RPC. -In this case a DTO interface is necessary. -These are defined [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts). -Utility functions to convert between DTO and API types on the `Ext` side are usually added to [plugin-ext/src/plugin/type-converters.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/type-converters.ts). -Thus, this is also a good starting point to look for conversion utilities for existing types. +When [encoding and decoding RPC messages](#encoding-and-decoding-rpc-messages) pure DTO objects that only carry properties can always be transmitted safely. +However, due to the necessary serialization process, functions and references to other objects can never be transmitted safely. If functions of objects need to be invoked on the opposite side of their creation, the object needs to be cached on the creation side. -The other side receives a handle (basically an id) that can be used to invoke the functionality on the creation side. +The other side receives a handle (usually an id) that can be used to invoke the functionality on the creation side. As all cached objects are kept in memory, they should be disposed of when they are no longer needed. - -For instance, in [LanguagesExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts)#registerCodeActionsProvider a new code action provider is created and cached on the `Ext` side and then registered on the `Main` side via its handle. -When the code action provider’s methods are later invoked on the `Main` side (e.g. in [LanguagesMainImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/languages-main.ts)#provideCodeActions), it calls the `Ext` side with this handle. -The `Ext` side then gets the cached object, executes appropriate functions and returns the results back to the `Main` side (e.g. in [LanguageExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts)#$provideCodeActions). - +For instance, in [LanguagesExtImpl#registerCodeActionsProvider](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts) a new code action provider is created and cached on the `Ext` side and then registered on the `Main` side via its handle. +When the code action provider’s methods are later invoked on the `Main` side (e.g. in [LanguagesMainImpl#provideCodeActions](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/languages-main.ts)), it calls the `Ext` side with this handle. +The `Ext` side then gets the cached object, executes appropriate functions and returns the results back to the `Main` side (e.g. in [LanguagesExtImpl#$provideCodeActions](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts)). Another example to browse are the [TaskExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/tasks/tasks.ts) and [TaskMainImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/tasks-main.ts) classes. +To [ensure correct type conversion](#encoding-and-decoding-rpc-messages) between the Theia backend and the plugin host we define an API protocol based on types and DTOs that can be transmitted safely. +The plugin API and it's types are defined in [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts) with some additional conversion on the `Ext` side being defined in [plugin-ext/src/plugin/type-converters.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/type-converters.ts). +Thus, this is also a good starting point to look for conversion utilities for existing types. + ### Adding new types New classes and other types such as enums are usually implemented in [plugin-ext/src/plugin/types-impl.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/types-impl.ts). -They can be added here and the added to the API object created in the API factory. +They can be added there and then we can add them to the API object created in the API factory. ## Additional Links diff --git a/packages/core/src/common/message-rpc/msg-pack-extension-manager.ts b/packages/core/src/common/message-rpc/msg-pack-extension-manager.ts index 5844c1f684bfb..c6be687ae6ec5 100644 --- a/packages/core/src/common/message-rpc/msg-pack-extension-manager.ts +++ b/packages/core/src/common/message-rpc/msg-pack-extension-manager.ts @@ -21,7 +21,7 @@ import { addExtension } from 'msgpackr'; * required for the default RPC communication. MsgPackR extensions * are installed globally on both ends of the communication channel. * (frontend-backend, pluginExt-pluginMain). - * Is implemented as singleton as it is also used in plugin child processes which have no access to inversify. + * Is implemented as singleton as it is also used in plugin child processes which have no access to inversify. */ export class MsgPackExtensionManager { private static readonly INSTANCE = new MsgPackExtensionManager(); From f4ad0cd33880b2134d88b356c6ba0543abc6ad01 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Thu, 18 Jan 2024 12:40:48 +0100 Subject: [PATCH 058/441] Support `activeCustomEditorId` context key (#13267) --- .../browser/webview/webview-context-keys.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/webview/webview-context-keys.ts b/packages/plugin-ext/src/main/browser/webview/webview-context-keys.ts index d85da096c04b7..e5b109dc1e7f7 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-context-keys.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-context-keys.ts @@ -14,9 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { ApplicationShell, FocusTracker, Widget } from '@theia/core/lib/browser'; +import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { CustomEditorWidget } from '../custom-editors/custom-editor-widget'; import { WebviewWidget } from './webview'; @injectable() @@ -27,6 +28,11 @@ export class WebviewContextKeys { */ activeWebviewPanelId: ContextKey; + /** + * Context key representing the `viewType` of the active `CustomEditorWidget`, if any. + */ + activeCustomEditorId: ContextKey; + @inject(ApplicationShell) protected applicationShell: ApplicationShell; @@ -36,12 +42,19 @@ export class WebviewContextKeys { @postConstruct() protected init(): void { this.activeWebviewPanelId = this.contextKeyService.createKey('activeWebviewPanelId', ''); + this.activeCustomEditorId = this.contextKeyService.createKey('activeCustomEditorId', ''); this.applicationShell.onDidChangeCurrentWidget(this.handleDidChangeCurrentWidget, this); } protected handleDidChangeCurrentWidget(change: FocusTracker.IChangedArgs): void { - if (change.newValue instanceof WebviewWidget) { - this.activeWebviewPanelId.set(change.newValue.viewType); + const { newValue } = change; + if (newValue instanceof CustomEditorWidget) { + this.activeCustomEditorId.set(newValue.viewType); + } else { + this.activeCustomEditorId.set(''); + } + if (newValue instanceof WebviewWidget) { + this.activeWebviewPanelId.set(newValue.viewType); } else { this.activeWebviewPanelId.set(''); } From eb8148bdebaa7fa291109293878b331c93655342 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 18 Jan 2024 13:42:37 +0100 Subject: [PATCH 059/441] Empty VSCode plugin webview editor windows #13258 (#13265) * introduce widget manager method to find a widget based on testing its options * use this in webviews-main to also check pending widgets * use an origin that is computed based on the view type * add uuid v5 hash method to core --- packages/core/src/browser/widget-manager.ts | 25 +++++++++++++++++++ packages/core/src/common/uuid.ts | 13 ++++++++++ .../browser/webview/webview-widget-factory.ts | 17 ++----------- .../src/main/browser/webviews-main.ts | 7 +++++- .../plugin-ext/src/plugin/webview-views.ts | 3 ++- packages/plugin-ext/src/plugin/webviews.ts | 17 +++++++------ 6 files changed, 58 insertions(+), 24 deletions(-) diff --git a/packages/core/src/browser/widget-manager.ts b/packages/core/src/browser/widget-manager.ts index 56c1ba61e2dbb..77a4a2bb2e265 100644 --- a/packages/core/src/browser/widget-manager.ts +++ b/packages/core/src/browser/widget-manager.ts @@ -194,6 +194,31 @@ export class WidgetManager { return widget; } + /** + * Finds a widget that matches the given test predicate. + * @param factoryId The widget factory id. + * @param predicate The test predicate. + * + * @returns a promise resolving to the widget if available, else `undefined`. + */ + async findWidget(factoryId: string, predicate: (options?: any) => boolean): Promise { + for (const [key, widget] of this.widgets.entries()) { + if (this.testPredicate(key, factoryId, predicate)) { + return widget as T; + } + } + for (const [key, widget] of this.pendingWidgetPromises.entries()) { + if (this.testPredicate(key, factoryId, predicate)) { + return widget as T; + } + } + } + + protected testPredicate(key: string, factoryId: string, predicate: (options?: any) => boolean): boolean { + const constructionOptions = this.fromKey(key); + return constructionOptions.factoryId === factoryId && predicate(constructionOptions.options); + } + protected doGetWidget(key: string): MaybePromise | undefined { const pendingWidget = this.widgets.get(key) ?? this.pendingWidgetPromises.get(key); if (pendingWidget) { diff --git a/packages/core/src/common/uuid.ts b/packages/core/src/common/uuid.ts index 1325fa7249d83..1994e29282d30 100644 --- a/packages/core/src/common/uuid.ts +++ b/packages/core/src/common/uuid.ts @@ -21,6 +21,8 @@ // based on https://github.com/microsoft/vscode/blob/1.72.2/src/vs/base/common/uuid.ts +import { v5 } from 'uuid'; + const _UUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; export function isUUID(value: string): boolean { @@ -97,3 +99,14 @@ export const generateUuid = (function (): () => string { return result; }; })(); + +const NAMESPACE = '4c90ee4f-d952-44b1-83ca-f04121ab8e05'; +/** + * This function will hash the given value using SHA1. The result will be a uuid. + * @param value the string to hash + * @returns a uuid + */ +export function hashValue(value: string): string { + // as opposed to v4, v5 is deterministic and uses SHA1 hashing + return v5(value, NAMESPACE); +} diff --git a/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts b/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts index aec77f37c448e..6b029253af7da 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-widget-factory.ts @@ -17,8 +17,7 @@ import { interfaces } from '@theia/core/shared/inversify'; import { WebviewWidget, WebviewWidgetIdentifier, WebviewWidgetExternalEndpoint } from './webview'; import { WebviewEnvironment } from './webview-environment'; -import { StorageService } from '@theia/core/lib/browser'; -import { v4 } from 'uuid'; +import { hashValue } from '@theia/core/lib/common/uuid'; export class WebviewWidgetFactory { @@ -32,7 +31,7 @@ export class WebviewWidgetFactory { async createWidget(identifier: WebviewWidgetIdentifier): Promise { const externalEndpoint = await this.container.get(WebviewEnvironment).externalEndpoint(); - let endpoint = externalEndpoint.replace('{{uuid}}', identifier.viewId ? await this.getOrigin(this.container.get(StorageService), identifier.viewId) : identifier.id); + let endpoint = externalEndpoint.replace('{{uuid}}', identifier.viewId ? hashValue(identifier.viewId) : identifier.id); if (endpoint[endpoint.length - 1] === '/') { endpoint = endpoint.slice(0, endpoint.length - 1); } @@ -42,16 +41,4 @@ export class WebviewWidgetFactory { return child.get(WebviewWidget); } - protected async getOrigin(storageService: StorageService, viewId: string): Promise { - const key = 'plugin-view-registry.origin.' + viewId; - const origin = await storageService.getData(key); - if (!origin) { - const newOrigin = v4(); - storageService.setData(key, newOrigin); - return newOrigin; - } else { - return origin; - } - } - } diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index aa534de603d0e..cf0f319950ba4 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -269,7 +269,12 @@ export class WebviewsMainImpl implements WebviewsMain, Disposable { } private async tryGetWebview(id: string): Promise { - const webview = this.widgetManager.getWidgets(WebviewWidget.FACTORY_ID).find(widget => widget instanceof WebviewWidget && widget.identifier.id === id) as WebviewWidget + const webview = await this.widgetManager.findWidget(WebviewWidget.FACTORY_ID, options => { + if (options) { + return options.id === id; + } + return false; + }) || await this.widgetManager.getWidget(CustomEditorWidget.FACTORY_ID, { id }); return webview; } diff --git a/packages/plugin-ext/src/plugin/webview-views.ts b/packages/plugin-ext/src/plugin/webview-views.ts index 04d66415b79e3..16d659c691c29 100644 --- a/packages/plugin-ext/src/plugin/webview-views.ts +++ b/packages/plugin-ext/src/plugin/webview-views.ts @@ -27,6 +27,7 @@ import { WebviewImpl, WebviewsExtImpl } from './webviews'; import { WebviewViewProvider } from '@theia/plugin'; import { Emitter, Event } from '@theia/core/lib/common/event'; import * as theia from '@theia/plugin'; +import { hashValue } from '@theia/core/lib/common/uuid'; export class WebviewViewsExtImpl implements WebviewViewsExt { @@ -82,7 +83,7 @@ export class WebviewViewsExtImpl implements WebviewViewsExt { const { provider, plugin } = entry; - const webviewNoPanel = this.webviewsExt.createNewWebview({}, plugin, handle); + const webviewNoPanel = this.webviewsExt.createNewWebview({}, plugin, handle, hashValue(viewType)); const revivedView = new WebviewViewExtImpl(handle, this.proxy, viewType, title, webviewNoPanel, true); this.webviewViews.set(handle, revivedView); await provider.resolveWebviewView(revivedView, { state }, cancellation); diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index c107ced1af6be..8a1fe9da7f6a0 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -23,6 +23,7 @@ import { fromViewColumn, toViewColumn, toWebviewPanelShowOptions } from './type- import { Disposable, WebviewPanelTargetArea, URI } from './types-impl'; import { WorkspaceExtImpl } from './workspace'; import { PluginIconPath } from './plugin-icon-path'; +import { hashValue } from '@theia/core/lib/common/uuid'; export class WebviewsExtImpl implements WebviewsExt { private readonly proxy: WebviewsMain; @@ -96,7 +97,7 @@ export class WebviewsExtImpl implements WebviewsExt { } const { serializer, plugin } = entry; - const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin); + const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin, hashValue(viewType)); const revivedPanel = new WebviewPanelImpl(viewId, this.proxy, viewType, title, toViewColumn(viewState.position)!, options, webview); revivedPanel.setActive(viewState.active); revivedPanel.setVisible(viewState.visible); @@ -131,7 +132,7 @@ export class WebviewsExtImpl implements WebviewsExt { throw new Error('Webviews are not initialized'); } const webviewShowOptions = toWebviewPanelShowOptions(showOptions); - const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin); + const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin, hashValue(viewType)); const panel = new WebviewPanelImpl(viewId, this.proxy, viewType, title, webviewShowOptions, options, webview); this.webviewPanels.set(viewId, panel); return panel; @@ -140,12 +141,13 @@ export class WebviewsExtImpl implements WebviewsExt { createNewWebview( options: theia.WebviewPanelOptions & theia.WebviewOptions, plugin: Plugin, - viewId: string + viewId: string, + origin?: string ): WebviewImpl { if (!this.initData) { throw new Error('Webviews are not initialized'); } - const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin); + const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin, origin); this.webviews.set(viewId, webview); return webview; } @@ -201,7 +203,8 @@ export class WebviewImpl implements theia.Webview { options: theia.WebviewOptions, private readonly initData: WebviewInitData, private readonly workspace: WorkspaceExtImpl, - readonly plugin: Plugin + readonly plugin: Plugin, + private readonly origin?: string ) { this._options = options; } @@ -219,12 +222,12 @@ export class WebviewImpl implements theia.Webview { .replace('{{scheme}}', resource.scheme) .replace('{{authority}}', resource.authority) .replace('{{path}}', resource.path.replace(/^\//, '')) - .replace('{{uuid}}', this.viewId); + .replace('{{uuid}}', this.origin ?? this.viewId); return URI.parse(uri); } get cspSource(): string { - return this.initData.webviewCspSource.replace('{{uuid}}', this.viewId); + return this.initData.webviewCspSource.replace('{{uuid}}', this.origin ?? this.viewId); } get html(): string { From 8659f9f3e56d237cc686fb6e3cd33856a3b0c4ac Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 18 Jan 2024 16:36:54 +0100 Subject: [PATCH 060/441] Improve notebook output rendering (#13239) Signed-off-by: Jonah Iden --- .../browser/view/notebook-code-cell-view.tsx | 41 ++++++++++----- .../renderers/cell-output-webview.tsx | 50 ++++++++++--------- .../renderers/output-webview-internal.ts | 24 +++++---- .../renderers/webview-communication.ts | 3 +- 4 files changed, 70 insertions(+), 48 deletions(-) diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 4ef29d2c375e8..d219addef2bf2 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -149,6 +149,9 @@ interface NotebookCellOutputProps { export class NotebookCodeCellOutputs extends React.Component { protected outputsWebview: CellOutputWebview | undefined; + protected outputsWebviewPromise: Promise | undefined; + + protected toDispose = new DisposableCollection(); constructor(props: NotebookCellOutputProps) { super(props); @@ -156,27 +159,41 @@ export class NotebookCodeCellOutputs extends React.Component { const { cell, outputWebviewFactory } = this.props; - cell.onDidChangeOutputs(async () => { - if (!this.outputsWebview && cell.outputs.length > 0) { - this.outputsWebview = await outputWebviewFactory(cell); - } else if (this.outputsWebview && cell.outputs.length === 0) { - this.outputsWebview.dispose(); + this.toDispose.push(cell.onDidChangeOutputs(async () => { + if (!this.outputsWebviewPromise && cell.outputs.length > 0) { + this.outputsWebviewPromise = outputWebviewFactory(cell).then(webview => { + this.outputsWebview = webview; + this.forceUpdate(); + return webview; + }); + this.forceUpdate(); + } else if (this.outputsWebviewPromise && cell.outputs.length === 0 && cell.internalMetadata.runEndTime) { + (await this.outputsWebviewPromise).dispose(); this.outputsWebview = undefined; + this.outputsWebviewPromise = undefined; + this.forceUpdate(); } - this.forceUpdate(); - }); + })); if (cell.outputs.length > 0) { - this.outputsWebview = await outputWebviewFactory(cell); - this.forceUpdate(); + this.outputsWebviewPromise = outputWebviewFactory(cell).then(webview => { + this.outputsWebview = webview; + this.forceUpdate(); + return webview; + }); } } - override componentDidUpdate(): void { - if (!this.outputsWebview?.isAttached()) { - this.outputsWebview?.attachWebview(); + override async componentDidUpdate(): Promise { + if (!(await this.outputsWebviewPromise)?.isAttached()) { + (await this.outputsWebviewPromise)?.attachWebview(); } } + override async componentWillUnmount(): Promise { + this.toDispose.dispose(); + (await this.outputsWebviewPromise)?.dispose(); + } + override render(): React.ReactNode { return this.outputsWebview ? <> diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index fbd832304adcb..dbf7179be3df1 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -72,12 +72,14 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { protected webviewWidget: WebviewWidget; + protected toDispose = new DisposableCollection(); + @postConstruct() protected async init(): Promise { - this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange)); - this.cell.onDidChangeOutputItems(output => { + this.toDispose.push(this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange))); + this.toDispose.push(this.cell.onDidChangeOutputItems(output => { this.updateOutput({start: this.cell.outputs.findIndex(o => o.outputId === output.outputId), deleteCount: 1, newOutputs: [output]}); - }); + })); this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); this.webviewWidget.setContentOptions({ allowScripts: true }); @@ -106,29 +108,28 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } updateOutput(update: NotebookCellOutputsSplice): void { - if (this.cell.outputs.length > 0) { - if (this.webviewWidget.isHidden) { - this.webviewWidget.show(); - } - - this.outputPresentationListeners.dispose(); - this.outputPresentationListeners = new DisposableCollection(); - for (const output of this.cell.outputs) { - this.outputPresentationListeners.push(output.onRequestOutputPresentationChange(() => this.requestOutputPresentationUpdate(output))); - } + if (this.webviewWidget.isHidden) { + this.webviewWidget.show(); + } - const updateOutputMessage: OutputChangedMessage = { - type: 'outputChanged', - newOutputs: update.newOutputs.map(output => ({ - id: output.outputId, - items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), - metadata: output.metadata - })), - deletedOutputIds: this.cell.outputs.slice(update.start, update.start + update.deleteCount).map(output => output.outputId) - }; - - this.webviewWidget.sendMessage(updateOutputMessage); + this.outputPresentationListeners.dispose(); + this.outputPresentationListeners = new DisposableCollection(); + for (const output of this.cell.outputs) { + this.outputPresentationListeners.push(output.onRequestOutputPresentationChange(() => this.requestOutputPresentationUpdate(output))); } + + const updateOutputMessage: OutputChangedMessage = { + type: 'outputChanged', + newOutputs: update.newOutputs.map(output => ({ + id: output.outputId, + items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), + metadata: output.metadata + })), + deleteStart: update.start, + deleteCount: update.deleteCount + }; + + this.webviewWidget.sendMessage(updateOutputMessage); } private async requestOutputPresentationUpdate(output: NotebookCellOutputModel): Promise { @@ -195,6 +196,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } dispose(): void { + this.toDispose.dispose(); this.outputPresentationListeners.dispose(); this.webviewWidget.dispose(); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index b8ac9bdd68939..7c77c3de51284 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -114,7 +114,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { this.element.style.paddingRight = '10px'; this.element.id = output.id; document.body.appendChild(this.element); - + this.outputId = output.id; this.allItems = items; } @@ -134,7 +134,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } } - const outputs = new Map(); + const outputs: Output[] = []; class Renderer { @@ -378,15 +378,14 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } }(); - function clearOutput(outputId: string): void { - outputs.get(outputId)?.clear(); - outputs.delete(outputId); - document.getElementById(outputId)?.remove(); + function clearOutput(output: Output): void { + output.clear(); + output.element.remove(); } function outputsChanged(changedEvent: webviewCommunication.OutputChangedMessage): void { - for (const outputId of changedEvent.deletedOutputIds ?? []) { - clearOutput(outputId); + for (const output of outputs.splice(changedEvent.deleteStart ?? 0, changedEvent.deleteCount ?? 0)) { + clearOutput(output); } for (const outputData of changedEvent.newOutputs ?? []) { @@ -410,7 +409,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { })); const output = new Output(outputData, apiItems); - outputs.set(outputData.id, output); + outputs.push(output); renderers.render(output, undefined, undefined, new AbortController().signal); } @@ -466,8 +465,11 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { renderers.getRenderer(event.data.rendererId)?.receiveMessage(event.data.message); break; case 'changePreferredMimetype': - clearOutput(event.data.outputId); - renderers.render(outputs.get(event.data.outputId)!, event.data.mimeType, undefined, new AbortController().signal); + const outputId = event.data.outputId; + const index = outputs.findIndex(output => output.outputId === outputId); + outputs.splice(index, 1); + clearOutput(outputs.splice(index, 1)[0]); + renderers.render(outputs[index], event.data.mimeType, undefined, new AbortController().signal); break; } }); diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts index 4fee027832c22..b514145d854fa 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts @@ -39,7 +39,8 @@ export interface UpdateRenderersMessage { export interface OutputChangedMessage { readonly type: 'outputChanged'; readonly newOutputs?: Output[]; - readonly deletedOutputIds?: string[]; + readonly deleteStart?: number; + readonly deleteCount?: number; } export interface ChangePreferredMimetypeMessage { From b4bbecd8385d2ad1f797e5c217ec8753ece5b831 Mon Sep 17 00:00:00 2001 From: Matthias Seiffert Date: Thu, 18 Jan 2024 16:37:24 +0100 Subject: [PATCH 061/441] Resize terminal process after resizing widget (#13281) Resize the terminal process in the debounced doResizeTerminal (after the terminal widget has the new size). Contributed on behalf of Elektrobit Automotive GmbH Signed-off-by: Matthias Seiffert --- packages/terminal/src/browser/terminal-widget-impl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 7c233c7fe6a6a..ba7f8935a7000 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -611,8 +611,6 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget if (this.needsResize) { this.resizeTerminal(); this.needsResize = false; - - this.resizeTerminalProcess(); } } @@ -797,6 +795,8 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget const cols = geo.cols; const rows = geo.rows - 1; // subtract one row for margin this.term.resize(cols, rows); + + this.resizeTerminalProcess(); } protected resizeTerminalProcess(): void { From 56cf3dbe76693735bfef8b8ac0ec424c38567931 Mon Sep 17 00:00:00 2001 From: "Christian W. Damus" Date: Fri, 19 Jan 2024 06:46:19 -0500 Subject: [PATCH 062/441] Implement "headless plugins" in a new plugin host outside of frontend connections (#13138) Define a 'theiaHeadlessPlugin' engine to identify headless plugins that support only the headless plugin host. Ordinary backend plugins that also run in the headless host can add a 'headless' entry-point in the 'theiaPlugin' object in package.json - update the Theia application webpack generator to handle packing the headless-plugin API init script - implement a headless entry-point in the PluginModel as a peer to frontend and backend entry-points - factor out common plugin manager behaviour into an abstract class - define distinct plugin manager (vscode/theia) and headless plugin manager implementations - similarly for the HostedPluginSupport - define the minimal TerminalExt/Main APIs for access to the default shell to support the 'env.shell' API - implement nested Inversify container for headless plugins to isolate them and their plugin host from the connection-scoped plugins - add examples for headless plugin - add a "Greeting of the Day" example custom API provider - add a plugin-gotd example headless plugin that uses the custom API example to greet the world on activation and also illustrates simple vscode API usage - Support headless entrypoint in VSCode plugins - support the headless entrypoint in an otherwise VS Code plugin - prefer the VS Code names for start/stop functions - update the example plugin to use be a dual VSCode/headless plugin using the vscode API in the usual backend entrypoint - Support distinct and application-specific headless activation events - extend the PluginPackage interface to add a "headless" property initially defining only an optional "activationEvents" string array property for headless mode. The idea being that eventually other things like "contributions" might also be defined, here - support application injection of activation events that it will trigger in its plugins via the HeadlessHostedPluginSupport class's activateByEvent(activationEvent: string) API - adapt the example GotD plugin's package manifest to use the new "headless" property - Differentiate provision of ext APIs to headless and backend plugin hosts - define a distinct headless ext API initialization function protocol and headless API initialization script path to target the headless plugin host specifically in the ext API provider - refactor the initialization of ext APIs in the plugin host to make use of this new distinction - update the Greeting-of-the-Day example API provider to support both headless and backend plugins - define the index for the common plugin-ext-headless API exports - fix up other minor details - Inversify injection in the Plugin Host - implement an Inversify container in the plugin hosts - inject Ext interfaces and the RPC framework - get container modules in plugin entrypoints to configure the container - the plugin container module can provide for API initialization in a simpler, more reusable way than the current initialization function, which is still supported for backwards compatibility Signed-off-by: Christian W. Damus Co-authored-by: Martin Fleck Co-authored-by: Mark Sujew --- .../src/generator/webpack-generator.ts | 3 + examples/api-provider-sample/.eslintrc.js | 10 + examples/api-provider-sample/README.md | 48 ++ examples/api-provider-sample/package.json | 42 ++ .../src/common/plugin-api-rpc.ts | 70 +++ examples/api-provider-sample/src/gotd.d.ts | 49 ++ .../src/node/ext-plugin-gotd-api-provider.ts | 33 ++ .../src/node/gotd-backend-module.ts | 28 ++ .../src/node/gotd-main-plugin-provider.ts | 29 ++ .../src/node/greeting-main-impl.ts | 72 +++ .../src/plugin/gotd-api-init.ts | 98 ++++ .../src/plugin/greeting-ext-impl.ts | 86 ++++ examples/api-provider-sample/tsconfig.json | 22 + examples/browser/package.json | 2 + examples/browser/tsconfig.json | 8 +- examples/electron/package.json | 2 + examples/electron/tsconfig.json | 6 + .../src/node/hosted-plugin-reader.ts | 2 +- packages/plugin-ext-headless/.eslintrc.js | 13 + packages/plugin-ext-headless/README.md | 32 ++ packages/plugin-ext-headless/package.json | 55 +++ .../src/common/headless-plugin-container.ts | 23 + .../src/common/headless-plugin-protocol.ts | 38 ++ .../src/common/headless-plugin-rpc.ts | 46 ++ .../plugin-ext-headless/src/common/index.ts | 23 + .../plugin-ext-headless-api-contribution.ts | 60 +++ ...gin-ext-headless-hosted-electron-module.ts | 22 + .../src/hosted/node/headless-hosted-plugin.ts | 199 ++++++++ .../node/plugin-ext-headless-hosted-module.ts | 75 +++ .../node/plugin-host-headless-module.ts | 76 +++ .../hosted/node/plugin-host-headless-rpc.ts | 80 +++ .../src/hosted/node/plugin-host-headless.ts | 111 +++++ .../node/scanners/scanner-theia-headless.ts | 85 ++++ packages/plugin-ext-headless/src/index.ts | 17 + ...plugin-theia-headless-directory-handler.ts | 35 ++ .../src/main/node/headless-progress-client.ts | 44 ++ .../src/main/node/main-context.ts | 35 ++ .../node/plugin-ext-headless-main-module.ts | 42 ++ .../plugin-ext-headless/src/package.spec.ts | 25 + .../plugin-ext-headless-electron-module.ts | 32 ++ .../src/plugin-ext-headless-module.ts | 31 ++ .../src/plugin/headless-plugin-manager.ts | 50 ++ packages/plugin-ext-headless/tsconfig.json | 27 ++ .../src/node/scanner-vscode.ts | 4 + .../plugin-ext/src/common/plugin-api-rpc.ts | 9 +- .../src/common/plugin-ext-api-contribution.ts | 43 +- .../plugin-ext/src/common/plugin-protocol.ts | 9 +- .../plugin-ext/src/common/rpc-protocol.ts | 2 +- .../src/hosted/browser/hosted-plugin.ts | 374 ++------------ .../src/hosted/browser/worker/debug-stub.ts | 7 +- .../hosted/browser/worker/worker-env-ext.ts | 7 +- .../src/hosted/browser/worker/worker-main.ts | 218 ++++----- .../browser/worker/worker-plugin-module.ts | 73 +++ .../src/hosted/common/hosted-plugin.ts | 456 ++++++++++++++++++ .../src/hosted/node/hosted-plugin-process.ts | 8 +- .../node/plugin-ext-hosted-backend-module.ts | 4 +- .../src/hosted/node/plugin-host-module.ts | 69 +++ .../src/hosted/node/plugin-host-rpc.ts | 305 +++++++++--- .../plugin-ext/src/hosted/node/plugin-host.ts | 16 +- .../src/hosted/node/plugin-reader.ts | 3 + .../src/hosted/node/plugin-service.ts | 25 +- .../src/hosted/node/scanners/scanner-theia.ts | 58 ++- .../plugin-ext/src/main/browser/env-main.ts | 29 +- .../src/main/browser/message-registry-main.ts | 33 +- .../src/main/browser/notification-main.ts | 68 +-- .../common/basic-message-registry-main.ts | 53 ++ .../main/common/basic-notification-main.ts | 86 ++++ .../plugin-ext/src/main/common/env-main.ts | 44 ++ .../plugin-theia-directory-handler.ts | 54 ++- .../src/main/node/plugin-deployer-impl.ts | 6 +- .../plugin-ext/src/plugin/clipboard-ext.ts | 12 +- .../plugin-ext/src/plugin/debug/debug-ext.ts | 13 +- .../src/plugin/editors-and-documents.ts | 8 +- packages/plugin-ext/src/plugin/env.ts | 13 +- .../plugin-ext/src/plugin/localization-ext.ts | 11 +- .../plugin-ext/src/plugin/message-registry.ts | 9 +- .../src/plugin/node/debug/debug.spec.ts | 6 +- .../src/plugin/node/env-node-ext.ts | 8 +- .../plugin/node/plugin-container-module.ts | 165 +++++++ .../plugin-ext/src/plugin/plugin-manager.ts | 143 ++++-- .../plugin-ext/src/plugin/plugin-storage.ts | 26 +- .../src/plugin/preference-registry.spec.ts | 7 +- .../src/plugin/preference-registry.ts | 16 +- packages/plugin-ext/src/plugin/secrets-ext.ts | 30 +- .../plugin-ext/src/plugin/terminal-ext.ts | 4 +- packages/plugin-ext/src/plugin/webviews.ts | 18 +- packages/plugin-ext/src/plugin/workspace.ts | 18 +- .../sample-namespace/plugin-gotd/.gitignore | 1 + .../sample-namespace/plugin-gotd/LICENSE | 277 +++++++++++ .../sample-namespace/plugin-gotd/README.md | 44 ++ .../sample-namespace/plugin-gotd/extension.js | 41 ++ .../sample-namespace/plugin-gotd/headless.js | 88 ++++ .../sample-namespace/plugin-gotd/package.json | 34 ++ tsconfig.json | 8 +- 94 files changed, 4155 insertions(+), 794 deletions(-) create mode 100644 examples/api-provider-sample/.eslintrc.js create mode 100644 examples/api-provider-sample/README.md create mode 100644 examples/api-provider-sample/package.json create mode 100644 examples/api-provider-sample/src/common/plugin-api-rpc.ts create mode 100644 examples/api-provider-sample/src/gotd.d.ts create mode 100644 examples/api-provider-sample/src/node/ext-plugin-gotd-api-provider.ts create mode 100644 examples/api-provider-sample/src/node/gotd-backend-module.ts create mode 100644 examples/api-provider-sample/src/node/gotd-main-plugin-provider.ts create mode 100644 examples/api-provider-sample/src/node/greeting-main-impl.ts create mode 100644 examples/api-provider-sample/src/plugin/gotd-api-init.ts create mode 100644 examples/api-provider-sample/src/plugin/greeting-ext-impl.ts create mode 100644 examples/api-provider-sample/tsconfig.json create mode 100644 packages/plugin-ext-headless/.eslintrc.js create mode 100644 packages/plugin-ext-headless/README.md create mode 100644 packages/plugin-ext-headless/package.json create mode 100644 packages/plugin-ext-headless/src/common/headless-plugin-container.ts create mode 100644 packages/plugin-ext-headless/src/common/headless-plugin-protocol.ts create mode 100644 packages/plugin-ext-headless/src/common/headless-plugin-rpc.ts create mode 100644 packages/plugin-ext-headless/src/common/index.ts create mode 100644 packages/plugin-ext-headless/src/common/plugin-ext-headless-api-contribution.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node-electron/plugin-ext-headless-hosted-electron-module.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/headless-hosted-plugin.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-module.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-rpc.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/plugin-host-headless.ts create mode 100644 packages/plugin-ext-headless/src/hosted/node/scanners/scanner-theia-headless.ts create mode 100644 packages/plugin-ext-headless/src/index.ts create mode 100644 packages/plugin-ext-headless/src/main/node/handlers/plugin-theia-headless-directory-handler.ts create mode 100644 packages/plugin-ext-headless/src/main/node/headless-progress-client.ts create mode 100644 packages/plugin-ext-headless/src/main/node/main-context.ts create mode 100644 packages/plugin-ext-headless/src/main/node/plugin-ext-headless-main-module.ts create mode 100644 packages/plugin-ext-headless/src/package.spec.ts create mode 100644 packages/plugin-ext-headless/src/plugin-ext-headless-electron-module.ts create mode 100644 packages/plugin-ext-headless/src/plugin-ext-headless-module.ts create mode 100644 packages/plugin-ext-headless/src/plugin/headless-plugin-manager.ts create mode 100644 packages/plugin-ext-headless/tsconfig.json create mode 100644 packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts create mode 100644 packages/plugin-ext/src/hosted/common/hosted-plugin.ts create mode 100644 packages/plugin-ext/src/hosted/node/plugin-host-module.ts create mode 100644 packages/plugin-ext/src/main/common/basic-message-registry-main.ts create mode 100644 packages/plugin-ext/src/main/common/basic-notification-main.ts create mode 100644 packages/plugin-ext/src/main/common/env-main.ts create mode 100644 packages/plugin-ext/src/plugin/node/plugin-container-module.ts create mode 100644 sample-plugins/sample-namespace/plugin-gotd/.gitignore create mode 100644 sample-plugins/sample-namespace/plugin-gotd/LICENSE create mode 100644 sample-plugins/sample-namespace/plugin-gotd/README.md create mode 100644 sample-plugins/sample-namespace/plugin-gotd/extension.js create mode 100644 sample-plugins/sample-namespace/plugin-gotd/headless.js create mode 100644 sample-plugins/sample-namespace/plugin-gotd/package.json diff --git a/dev-packages/application-manager/src/generator/webpack-generator.ts b/dev-packages/application-manager/src/generator/webpack-generator.ts index fa942815843b9..0eeb0b38dc269 100644 --- a/dev-packages/application-manager/src/generator/webpack-generator.ts +++ b/dev-packages/application-manager/src/generator/webpack-generator.ts @@ -378,6 +378,7 @@ for (const [entryPointName, entryPointPath] of Object.entries({ ${this.ifPackage('@theia/plugin-ext', "'backend-init-theia': '@theia/plugin-ext/lib/hosted/node/scanners/backend-init-theia',")} ${this.ifPackage('@theia/filesystem', "'nsfw-watcher': '@theia/filesystem/lib/node/nsfw-watcher',")} ${this.ifPackage('@theia/plugin-ext-vscode', "'plugin-vscode-init': '@theia/plugin-ext-vscode/lib/node/plugin-vscode-init',")} + ${this.ifPackage('@theia/api-provider-sample', "'gotd-api-init': '@theia/api-provider-sample/lib/plugin/gotd-api-init',")} })) { commonJsLibraries[entryPointName] = { import: require.resolve(entryPointPath), @@ -429,6 +430,8 @@ const config = { 'ipc-bootstrap': require.resolve('@theia/core/lib/node/messaging/ipc-bootstrap'), ${this.ifPackage('@theia/plugin-ext', () => `// VS Code extension support: 'plugin-host': require.resolve('@theia/plugin-ext/lib/hosted/node/plugin-host'),`)} + ${this.ifPackage('@theia/plugin-ext-headless', () => `// Theia Headless Plugin support: + 'plugin-host-headless': require.resolve('@theia/plugin-ext-headless/lib/hosted/node/plugin-host-headless'),`)} ${this.ifPackage('@theia/process', () => `// Make sure the node-pty thread worker can be executed: 'worker/conoutSocketWorker': require.resolve('node-pty/lib/worker/conoutSocketWorker'),`)} ${this.ifPackage('@theia/git', () => `// Ensure the git locator process can the started diff --git a/examples/api-provider-sample/.eslintrc.js b/examples/api-provider-sample/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/examples/api-provider-sample/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/examples/api-provider-sample/README.md b/examples/api-provider-sample/README.md new file mode 100644 index 0000000000000..dff79d5296502 --- /dev/null +++ b/examples/api-provider-sample/README.md @@ -0,0 +1,48 @@ +
+ +
+ +theia-ext-logo + +

ECLIPSE THEIA - API PROVIDER SAMPLE

+ +
+ +
+ +## Description + +The `@theia/api-provider-sample` extension is a programming example showing how to define and provide a custom API object for _plugins_ to use. +The purpose of the extension is to: +- provide developers with realistic coding examples of providing custom API objects +- provide easy-to-use and test examples for features when reviewing pull requests + +The extension is for reference and test purposes only and is not published on `npm` (`private: true`). + +### Greeting of the Day + +The sample defines a `gotd` API that plugins can import and use to obtain tailored messages with which to greet the world, for example in their activation function. + +The source code is laid out in the `src/` tree as follows: + +- `gotd.d.ts` — the TypeScript definition of the `gotd` API object that plugins import to interact with the "Greeting of the Day" service +- `plugin/` — the API initialization script and the implementation of the API objects (`GreetingExt` and similar interfaces). + All code in this directory runs exclusively in the separate plugin-host Node process, isolated from the main Theia process, together with either headless plugins or the backend of VS Code plugins. + The `GreetingExtImpl` and similar classes communicate with the actual API implementation (`GreetingMainImpl` etc.) classes in the main Theia process via RPC +- `node/` — the API classes implementing `GreetingMain` and similar interfaces and the Inversify bindings that register the API provider. + All code in this directory runs in the main Theia Node process + - `common/` — the RPC API Ext/Main interface definitions corresponding to the backend of the `gotd` plugin API + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json new file mode 100644 index 0000000000000..7f3f6c9bbfd50 --- /dev/null +++ b/examples/api-provider-sample/package.json @@ -0,0 +1,42 @@ +{ + "private": true, + "name": "@theia/api-provider-sample", + "version": "1.45.0", + "description": "Theia - Example code to demonstrate Theia API Provider Extensions", + "dependencies": { + "@theia/core": "1.45.0", + "@theia/plugin-ext-headless": "1.45.0", + "@theia/plugin-ext": "1.45.0" + }, + "theiaExtensions": [ + { + "backend": "lib/node/gotd-backend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "types": "src/gotd.d.ts", + "scripts": { + "lint": "theiaext lint", + "build": "theiaext build", + "watch": "theiaext watch", + "clean": "theiaext clean" + }, + "devDependencies": { + "@theia/ext-scripts": "1.45.0" + } +} diff --git a/examples/api-provider-sample/src/common/plugin-api-rpc.ts b/examples/api-provider-sample/src/common/plugin-api-rpc.ts new file mode 100644 index 0000000000000..87f34925ce0c0 --- /dev/null +++ b/examples/api-provider-sample/src/common/plugin-api-rpc.ts @@ -0,0 +1,70 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { createProxyIdentifier } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import type { greeting } from '../gotd'; +import { Event } from '@theia/core'; + +export enum GreetingKind { + DIRECT = 1, + QUIRKY = 2, + SNARKY = 3, +} + +export interface GreeterData { + readonly uuid: string; + greetingKinds: greeting.GreetingKind[]; +}; + +export const GreetingMain = Symbol('GreetingMain'); +export interface GreetingMain { + $getMessage(greeterId: string): Promise; + + $createGreeter(): Promise; + $destroyGreeter(greeterId: GreeterData['uuid']): Promise; + + $updateGreeter(data: GreeterData): void; +} + +export const GreetingExt = Symbol('GreetingExt'); +export interface GreetingExt { + + // + // External protocol + // + + registerGreeter(): Promise; + unregisterGreeter(uuid: string): Promise; + + getMessage(greeterId: string): Promise; + getGreetingKinds(greeterId: string): readonly greeting.GreetingKind[]; + setGreetingKindEnabled(greeterId: string, greetingKind: greeting.GreetingKind, enable: boolean): void; + onGreetingKindsChanged(greeterId: string): Event; + + // + // Internal protocol + // + + $greeterUpdated(data: GreeterData): void; + +} + +export const PLUGIN_RPC_CONTEXT = { + GREETING_MAIN: createProxyIdentifier('GreetingMain'), +}; + +export const MAIN_RPC_CONTEXT = { + GREETING_EXT: createProxyIdentifier('GreetingExt'), +}; diff --git a/examples/api-provider-sample/src/gotd.d.ts b/examples/api-provider-sample/src/gotd.d.ts new file mode 100644 index 0000000000000..52bbc7a469a5e --- /dev/null +++ b/examples/api-provider-sample/src/gotd.d.ts @@ -0,0 +1,49 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +// Strictly speaking, the 'greeting' namespace is an unnecessary level of organization +// but it serves to illustrate how API namespaces are implemented in the backend. +export namespace greeting { + export function createGreeter(): Promise; + + export enum GreetingKind { + DIRECT = 1, + QUIRKY = 2, + SNARKY = 3, + } + + export interface Greeter extends Disposable { + greetingKinds: readonly GreetingKind[]; + + getMessage(): Promise; + + setGreetingKind(kind: GreetingKind, enable = true): void; + + onGreetingKindsChanged: Event; + } +} + +export interface Event { + (listener: (e: T) => unknown, thisArg?: unknown): Disposable; +} + +export interface Disposable { + dispose(): void; +} + +namespace Disposable { + export function create(func: () => void): Disposable; +} diff --git a/examples/api-provider-sample/src/node/ext-plugin-gotd-api-provider.ts b/examples/api-provider-sample/src/node/ext-plugin-gotd-api-provider.ts new file mode 100644 index 0000000000000..e16c7d902e879 --- /dev/null +++ b/examples/api-provider-sample/src/node/ext-plugin-gotd-api-provider.ts @@ -0,0 +1,33 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import * as path from 'path'; +import { injectable } from '@theia/core/shared/inversify'; +import { ExtPluginApi, ExtPluginApiProvider } from '@theia/plugin-ext-headless'; + +@injectable() +export class ExtPluginGotdApiProvider implements ExtPluginApiProvider { + provideApi(): ExtPluginApi { + // We can support both backend plugins and headless plugins, so we have only one + // entry-point script. Moreover, the application build packages that script in + // the `../backend/` directory from its source `../plugin/` location, alongside + // the scripts for all other plugin API providers. + const universalInitPath = path.join(__dirname, '../backend/gotd-api-init'); + return { + backendInitPath: universalInitPath, + headlessInitPath: universalInitPath + }; + } +} diff --git a/examples/api-provider-sample/src/node/gotd-backend-module.ts b/examples/api-provider-sample/src/node/gotd-backend-module.ts new file mode 100644 index 0000000000000..08b54a7f556f1 --- /dev/null +++ b/examples/api-provider-sample/src/node/gotd-backend-module.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ContainerModule } from '@theia/core/shared/inversify'; +import { ExtPluginApiProvider } from '@theia/plugin-ext'; +import { ExtPluginGotdApiProvider } from './ext-plugin-gotd-api-provider'; +import { MainPluginApiProvider } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution'; +import { GotdMainPluginApiProvider } from './gotd-main-plugin-provider'; +import { GreetingMain } from '../common/plugin-api-rpc'; +import { GreetingMainImpl } from './greeting-main-impl'; + +export default new ContainerModule(bind => { + bind(Symbol.for(ExtPluginApiProvider)).to(ExtPluginGotdApiProvider).inSingletonScope(); + bind(MainPluginApiProvider).to(GotdMainPluginApiProvider).inSingletonScope(); + bind(GreetingMain).to(GreetingMainImpl).inSingletonScope(); +}); diff --git a/examples/api-provider-sample/src/node/gotd-main-plugin-provider.ts b/examples/api-provider-sample/src/node/gotd-main-plugin-provider.ts new file mode 100644 index 0000000000000..14778112cb4a9 --- /dev/null +++ b/examples/api-provider-sample/src/node/gotd-main-plugin-provider.ts @@ -0,0 +1,29 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { MainPluginApiProvider } from '@theia/plugin-ext/lib/common/plugin-ext-api-contribution'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { GreetingMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; + +@injectable() +export class GotdMainPluginApiProvider implements MainPluginApiProvider { + @inject(GreetingMain) + protected readonly greetingMain: GreetingMain; + + initialize(rpc: RPCProtocol): void { + rpc.set(PLUGIN_RPC_CONTEXT.GREETING_MAIN, this.greetingMain); + } +} diff --git a/examples/api-provider-sample/src/node/greeting-main-impl.ts b/examples/api-provider-sample/src/node/greeting-main-impl.ts new file mode 100644 index 0000000000000..02bf2d2eb9f45 --- /dev/null +++ b/examples/api-provider-sample/src/node/greeting-main-impl.ts @@ -0,0 +1,72 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { generateUuid } from '@theia/core/lib/common/uuid'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { GreetingKind, GreeterData, GreetingExt, GreetingMain, MAIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; + +const GREETINGS = { + [GreetingKind.DIRECT]: ['Hello, world!', "I'm here!", 'Good day!'], + [GreetingKind.QUIRKY]: ['Howdy doody, world?', "What's crack-a-lackin'?", 'Wazzup werld?'], + [GreetingKind.SNARKY]: ["Oh, it's you, world.", 'You again, world?!', 'Whatever.'], +} as const; + +@injectable() +export class GreetingMainImpl implements GreetingMain { + protected proxy: GreetingExt; + + private greeterData: Record = {}; + + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { + this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.GREETING_EXT); + } + + async $createGreeter(): Promise { + const result: GreeterData = { + uuid: generateUuid(), + greetingKinds: [GreetingKind.DIRECT] + }; + this.greeterData[result.uuid] = result; + return result; + } + + async $destroyGreeter(greeterId: string): Promise { + delete this.greeterData[greeterId]; + } + + $updateGreeter(data: GreeterData): void { + const myData = this.greeterData[data.uuid]; + if (myData) { + myData.greetingKinds = [...data.greetingKinds]; + this.proxy.$greeterUpdated({ ...myData }); + } + } + + async $getMessage(greeterId: string): Promise { + const data = this.greeterData[greeterId]; + if (data.greetingKinds.length === 0) { + throw new Error(`No greetings are available for greeter ${greeterId}`); + } + + // Get a random one of our supported greeting kinds. + const kind = data.greetingKinds[(Math.floor(Math.random() * data.greetingKinds.length))]; + // And a random greeting of that kind + const greetingIdx = Math.floor(Math.random() * GREETINGS[kind].length); + + return GREETINGS[kind][greetingIdx]; + } +} diff --git a/examples/api-provider-sample/src/plugin/gotd-api-init.ts b/examples/api-provider-sample/src/plugin/gotd-api-init.ts new file mode 100644 index 0000000000000..118410785db71 --- /dev/null +++ b/examples/api-provider-sample/src/plugin/gotd-api-init.ts @@ -0,0 +1,98 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { Plugin } from '@theia/plugin-ext/lib/common/plugin-api-rpc'; +import type * as gotd from '../gotd'; +import { GreetingKind, GreetingExt, MAIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; +import { GreetingExtImpl } from './greeting-ext-impl'; +import { Disposable, DisposableCollection } from '@theia/core'; +import { ApiFactory, PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; + +// This script is responsible for creating and returning the extension's +// custom API object when a plugin's module imports it. Keep in mind that +// all of the code here runs in the plugin-host node process, whether that +// be the backend host dedicated to some frontend connection or the single +// host for headless plugins, which is where the plugin itself is running. + +type Gotd = typeof gotd; +const GotdApiFactory = Symbol('GotdApiFactory'); +type GotdApiFactory = ApiFactory; + +// Retrieved by Theia to configure the Inversify DI container when the plugin is initialized. +// This is called when the plugin-host process is forked. +export const containerModule = PluginContainerModule.create(({ bind, bindApiFactory }) => { + bind(GreetingExt).to(GreetingExtImpl).inSingletonScope(); + bindApiFactory('@theia/api-provider-sample', GotdApiFactory, GotdApiFactoryImpl); +}); + +// Creates the Greeting of the Day API object +@injectable() +class GotdApiFactoryImpl { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + + @inject(GreetingExt) + protected readonly greetingExt: GreetingExt; + + @postConstruct() + initialize(): void { + this.rpc.set(MAIN_RPC_CONTEXT.GREETING_EXT, this.greetingExt); + } + + createApi(plugin: Plugin): Gotd { + const self = this; + async function createGreeter(): Promise { + const toDispose = new DisposableCollection(); + + const uuid = await self.greetingExt.registerGreeter(); + toDispose.push(Disposable.create(() => self.greetingExt.unregisterGreeter(uuid))); + + const onGreetingKindsChanged = self.greetingExt.onGreetingKindsChanged(uuid); + + const result: gotd.greeting.Greeter = { + get greetingKinds(): readonly GreetingKind[] { + return self.greetingExt.getGreetingKinds(uuid); + }, + + setGreetingKind(greetingKind: GreetingKind, enable = true): void { + self.greetingExt.setGreetingKindEnabled(uuid, greetingKind, enable); + }, + + getMessage(): Promise { + return self.greetingExt.getMessage(uuid); + }, + + onGreetingKindsChanged, + + dispose: toDispose.dispose.bind(toDispose), + }; + + return result; + } + + const greeting: Gotd['greeting'] = { + createGreeter, + GreetingKind + }; + + return { + greeting, + Disposable, + }; + }; +} diff --git a/examples/api-provider-sample/src/plugin/greeting-ext-impl.ts b/examples/api-provider-sample/src/plugin/greeting-ext-impl.ts new file mode 100644 index 0000000000000..56b5dabb35f03 --- /dev/null +++ b/examples/api-provider-sample/src/plugin/greeting-ext-impl.ts @@ -0,0 +1,86 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable } from '@theia/core/shared/inversify'; +import { GreetingKind, GreeterData, GreetingExt, GreetingMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { Event, Emitter } from '@theia/core'; + +type LocalGreeterData = GreeterData & { + onGreetingKindsChangedEmitter: Emitter +}; + +@injectable() +export class GreetingExtImpl implements GreetingExt { + private readonly proxy: GreetingMain; + + private greeterData: Record = {}; + + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { + this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.GREETING_MAIN); + } + + async registerGreeter(): Promise { + const newGreeter = await this.proxy.$createGreeter(); + this.greeterData[newGreeter.uuid] = { + ...newGreeter, + onGreetingKindsChangedEmitter: new Emitter() + }; + return newGreeter.uuid; + } + + unregisterGreeter(uuid: string): Promise { + delete this.greeterData[uuid]; + return this.proxy.$destroyGreeter(uuid); + } + + getGreetingKinds(greeterId: string): readonly GreetingKind[] { + const data = this.greeterData[greeterId]; + return data ? [...data.greetingKinds] : []; + } + + setGreetingKindEnabled(greeterId: string, greetingKind: GreetingKind, enable: boolean): void { + const data = this.greeterData[greeterId]; + + if (data.greetingKinds.includes(greetingKind) === enable) { + return; // Nothing to change + } + + if (enable) { + data.greetingKinds.push(greetingKind); + } else { + const index = data.greetingKinds.indexOf(greetingKind); + data.greetingKinds.splice(index, 1); + } + + this.proxy.$updateGreeter({uuid: greeterId, greetingKinds: [...data.greetingKinds] }); + } + + onGreetingKindsChanged(greeterId: string): Event { + return this.greeterData[greeterId].onGreetingKindsChangedEmitter.event; + } + + getMessage(greeterId: string): Promise { + return this.proxy.$getMessage(greeterId); + } + + $greeterUpdated(data: GreeterData): void { + const myData = this.greeterData[data.uuid]; + if (myData) { + myData.greetingKinds = [...data.greetingKinds]; + myData.onGreetingKindsChangedEmitter.fire([...data.greetingKinds]); + } + } +} diff --git a/examples/api-provider-sample/tsconfig.json b/examples/api-provider-sample/tsconfig.json new file mode 100644 index 0000000000000..a13a4e663a43d --- /dev/null +++ b/examples/api-provider-sample/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../../packages/core" + }, + { + "path": "../../packages/plugin-ext" + }, + { + "path": "../../packages/plugin-ext-headless" + } + ] +} diff --git a/examples/browser/package.json b/examples/browser/package.json index f26ff0b0ff168..125daa00f26b3 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -20,7 +20,9 @@ } }, "dependencies": { + "@theia/api-provider-sample": "1.45.0", "@theia/api-samples": "1.45.0", + "@theia/plugin-ext-headless": "1.45.0", "@theia/bulk-edit": "1.45.0", "@theia/callhierarchy": "1.45.0", "@theia/console": "1.45.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index e3756e4f8c7de..0debed71dc386 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../configs/base.tsconfig", - "include": [ ], + "include": [], "compilerOptions": { "composite": true }, @@ -80,6 +80,9 @@ { "path": "../../packages/plugin-ext" }, + { + "path": "../../packages/plugin-ext-headless" + }, { "path": "../../packages/plugin-ext-vscode" }, @@ -143,6 +146,9 @@ { "path": "../../packages/workspace" }, + { + "path": "../api-provider-sample" + }, { "path": "../api-samples" } diff --git a/examples/electron/package.json b/examples/electron/package.json index 0f2fe49b360e7..49e3f629d2422 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -20,7 +20,9 @@ } }, "dependencies": { + "@theia/api-provider-sample": "1.45.0", "@theia/api-samples": "1.45.0", + "@theia/plugin-ext-headless": "1.45.0", "@theia/bulk-edit": "1.45.0", "@theia/callhierarchy": "1.45.0", "@theia/console": "1.45.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index edac6071a7d07..c1c637281a2a3 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -83,6 +83,9 @@ { "path": "../../packages/plugin-ext" }, + { + "path": "../../packages/plugin-ext-headless" + }, { "path": "../../packages/plugin-ext-vscode" }, @@ -140,6 +143,9 @@ { "path": "../../packages/workspace" }, + { + "path": "../api-provider-sample" + }, { "path": "../api-samples" } diff --git a/packages/plugin-dev/src/node/hosted-plugin-reader.ts b/packages/plugin-dev/src/node/hosted-plugin-reader.ts index 7aeec23cdeb74..d4bcbf87061c0 100644 --- a/packages/plugin-dev/src/node/hosted-plugin-reader.ts +++ b/packages/plugin-dev/src/node/hosted-plugin-reader.ts @@ -42,7 +42,7 @@ export class HostedPluginReader implements BackendApplicationContribution { const hostedPlugin = new PluginDeployerEntryImpl('Hosted Plugin', pluginPath!, pluginPath); hostedPlugin.storeValue('isUnderDevelopment', true); const hostedMetadata = await this.hostedPlugin.promise; - if (hostedMetadata!.model.entryPoint && hostedMetadata!.model.entryPoint.backend) { + if (hostedMetadata!.model.entryPoint && (hostedMetadata!.model.entryPoint.backend || hostedMetadata!.model.entryPoint.headless)) { this.deployerHandler.deployBackendPlugins([hostedPlugin]); } diff --git a/packages/plugin-ext-headless/.eslintrc.js b/packages/plugin-ext-headless/.eslintrc.js new file mode 100644 index 0000000000000..c452b0b44baec --- /dev/null +++ b/packages/plugin-ext-headless/.eslintrc.js @@ -0,0 +1,13 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + }, + rules: { + 'no-null/no-null': 'off', + } +}; diff --git a/packages/plugin-ext-headless/README.md b/packages/plugin-ext-headless/README.md new file mode 100644 index 0000000000000..f77c932c07d09 --- /dev/null +++ b/packages/plugin-ext-headless/README.md @@ -0,0 +1,32 @@ +
+ +
+ +theia-ext-logo + +

ECLIPSE THEIA - HEADLESS PLUGIN-EXT EXTENSION

+ +
+ +
+ +## Description + +The `@theia/plugin-ext-headless` extension contributes functionality for the backend-only "headless `plugin`" API. +The plugin extension host managed by this extension is scoped to the single Theia NodeJS instance. +This is unlike the plugin extension hosts managed by the [`@theia/plugin-ext` extension][plugin-ext] which are scoped on a per-frontend-connection basis. + +[plugin-ext]: ../plugin-ext/README.md + +## Implementation + +The implementation is derived from the [`@theia/plugin-ext` extension][plugin-ext] for frontend-scoped plugins. + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json new file mode 100644 index 0000000000000..913519140f197 --- /dev/null +++ b/packages/plugin-ext-headless/package.json @@ -0,0 +1,55 @@ +{ + "name": "@theia/plugin-ext-headless", + "version": "1.45.0", + "description": "Theia - Headless (Backend-only) Plugin Extension", + "main": "lib/common/index.js", + "typings": "lib/common/index.d.ts", + "dependencies": { + "@theia/core": "1.45.0", + "@theia/plugin-ext": "1.45.0", + "@theia/terminal": "1.45.0" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "backend": "lib/plugin-ext-headless-module", + "backendElectron": "lib/plugin-ext-headless-electron-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.45.0", + "@types/decompress": "^4.2.2", + "@types/escape-html": "^0.0.20", + "@types/lodash.clonedeep": "^4.5.3", + "@types/ps-tree": "^1.1.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/plugin-ext-headless/src/common/headless-plugin-container.ts b/packages/plugin-ext-headless/src/common/headless-plugin-container.ts new file mode 100644 index 0000000000000..d7b91419a73a4 --- /dev/null +++ b/packages/plugin-ext-headless/src/common/headless-plugin-container.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/** + * Service identifier for Inversify container modules that are used + * to configure the child Container in which scope the services supporting + * the Headless Plugin Host are isolated from connection-scoped frontend/backend + * plugin hosts and the rest of the Theia Node server. + */ +export const HeadlessPluginContainerModule = Symbol('HeadlessPluginContainerModule'); diff --git a/packages/plugin-ext-headless/src/common/headless-plugin-protocol.ts b/packages/plugin-ext-headless/src/common/headless-plugin-protocol.ts new file mode 100644 index 0000000000000..362ca39d87814 --- /dev/null +++ b/packages/plugin-ext-headless/src/common/headless-plugin-protocol.ts @@ -0,0 +1,38 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from '@theia/plugin-ext'; + +declare module '@theia/plugin-ext' { + /** + * Extension of the package manifest interface defined by the core plugin framework. + */ + interface PluginPackage { + /** + * Analogues of declarations offered by VS Code plugins, but for the headless instantiation. + */ + headless?: { + /** Activation events supported in headless mode, if any. */ + activationEvents?: string[]; + } + } +} + +/** + * Name for a `string[]` injection binding contributing headless activation event names + * supported by the application. + */ +export const SupportedHeadlessActivationEvents = Symbol('SupportedHeadlessActivationEvents'); diff --git a/packages/plugin-ext-headless/src/common/headless-plugin-rpc.ts b/packages/plugin-ext-headless/src/common/headless-plugin-rpc.ts new file mode 100644 index 0000000000000..8fd578ffd62d7 --- /dev/null +++ b/packages/plugin-ext-headless/src/common/headless-plugin-rpc.ts @@ -0,0 +1,46 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { createProxyIdentifier } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { AbstractPluginManagerExt, EnvInit } from '@theia/plugin-ext'; +import { KeysToKeysToAnyValue } from '@theia/plugin-ext/lib/common/types'; +import { + MAIN_RPC_CONTEXT, PLUGIN_RPC_CONTEXT +} from '@theia/plugin-ext/lib/common/plugin-api-rpc'; +import { ExtPluginApi } from './plugin-ext-headless-api-contribution'; + +export const HEADLESSPLUGIN_RPC_CONTEXT = { + MESSAGE_REGISTRY_MAIN: PLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN, + ENV_MAIN: PLUGIN_RPC_CONTEXT.ENV_MAIN, + NOTIFICATION_MAIN: PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, + LOCALIZATION_MAIN: PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN, +}; + +export const HEADLESSMAIN_RPC_CONTEXT = { + HOSTED_PLUGIN_MANAGER_EXT: createProxyIdentifier('HeadlessPluginManagerExt'), + NOTIFICATION_EXT: MAIN_RPC_CONTEXT.NOTIFICATION_EXT, +}; + +export type HeadlessEnvInit = Pick; + +export interface HeadlessPluginManagerInitializeParams { + activationEvents: string[]; + globalState: KeysToKeysToAnyValue; + env: HeadlessEnvInit; + extApi?: ExtPluginApi[]; +} + +export interface HeadlessPluginManagerExt extends AbstractPluginManagerExt { } diff --git a/packages/plugin-ext-headless/src/common/index.ts b/packages/plugin-ext-headless/src/common/index.ts new file mode 100644 index 0000000000000..279c170ea1f20 --- /dev/null +++ b/packages/plugin-ext-headless/src/common/index.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './headless-plugin-container'; +export { + ExtPluginApi, ExtPluginHeadlessApi, ExtPluginApiProvider, + ExtPluginHeadlessApiProvider +} from './plugin-ext-headless-api-contribution'; +export { PluginPackage, SupportedHeadlessActivationEvents } from './headless-plugin-protocol'; +export * from './headless-plugin-rpc'; diff --git a/packages/plugin-ext-headless/src/common/plugin-ext-headless-api-contribution.ts b/packages/plugin-ext-headless/src/common/plugin-ext-headless-api-contribution.ts new file mode 100644 index 0000000000000..fe9726188f6bb --- /dev/null +++ b/packages/plugin-ext-headless/src/common/plugin-ext-headless-api-contribution.ts @@ -0,0 +1,60 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PluginManager } from '@theia/plugin-ext'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; + +export * from '@theia/plugin-ext'; + +declare module '@theia/plugin-ext' { + /** + * Plugin API extension description. + * This interface describes scripts for all three plugin runtimes: frontend (WebWorker), backend (NodeJs), and headless (NodeJs). + */ + interface ExtPluginApi extends ExtPluginHeadlessApi { + // Note that the frontendInitPath and backendInitPath properties are included by + // Typescript interface merge from the @theia/plugin-ext::ExtPluginApi interface. + } +} + +/** + * Provider for headless extension API description. + */ +export interface ExtPluginHeadlessApiProvider { + /** + * Provide API description. + */ + provideApi(): ExtPluginHeadlessApi; +} + +/** + * Headless Plugin API extension description. + * This interface describes a script for the headless (NodeJs) runtime outside of the scope of frontend connections. + */ +export interface ExtPluginHeadlessApi { + /** + * Path to the script which should be loaded to provide api, module should export `provideApi` function with + * [ExtPluginApiBackendInitializationFn](#ExtPluginApiBackendInitializationFn) signature + */ + headlessInitPath?: string; +} + +/** + * Signature of the extension API initialization function for APIs contributed to headless plugins. + */ +export interface ExtPluginApiHeadlessInitializationFn { + (rpc: RPCProtocol, pluginManager: PluginManager): void; +} diff --git a/packages/plugin-ext-headless/src/hosted/node-electron/plugin-ext-headless-hosted-electron-module.ts b/packages/plugin-ext-headless/src/hosted/node-electron/plugin-ext-headless-hosted-electron-module.ts new file mode 100644 index 0000000000000..64a026d80bf2c --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node-electron/plugin-ext-headless-hosted-electron-module.ts @@ -0,0 +1,22 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { bindCommonHostedBackend } from '../node/plugin-ext-headless-hosted-module'; + +export function bindElectronBackend(bind: interfaces.Bind): void { + bindCommonHostedBackend(bind); +} diff --git a/packages/plugin-ext-headless/src/hosted/node/headless-hosted-plugin.ts b/packages/plugin-ext-headless/src/hosted/node/headless-hosted-plugin.ts new file mode 100644 index 0000000000000..f33d583ed2403 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/headless-hosted-plugin.ts @@ -0,0 +1,199 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// some code copied and modified from https://github.com/microsoft/vscode/blob/da5fb7d5b865aa522abc7e82c10b746834b98639/src/vs/workbench/api/node/extHostExtensionService.ts + +import { generateUuid } from '@theia/core/lib/common/uuid'; +import { injectable, inject, named } from '@theia/core/shared/inversify'; +import { getPluginId, DeployedPlugin, HostedPluginServer, PluginDeployer } from '@theia/plugin-ext/lib/common/plugin-protocol'; +import { setUpPluginApi } from '../../main/node/main-context'; +import { RPCProtocol, RPCProtocolImpl } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { ContributionProvider, Disposable, DisposableCollection, nls } from '@theia/core'; +import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; +import { IPCChannel } from '@theia/core/lib/node'; +import { BackendApplicationConfigProvider } from '@theia/core/lib/node/backend-application-config-provider'; +import { HostedPluginProcess } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-process'; +import { IShellTerminalServer } from '@theia/terminal/lib/common/shell-terminal-protocol'; +import { HeadlessPluginManagerExt, HEADLESSMAIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc'; +import { AbstractHostedPluginSupport, PluginContributions } from '@theia/plugin-ext/lib/hosted/common/hosted-plugin'; +import { TheiaHeadlessPluginScanner } from './scanners/scanner-theia-headless'; +import { SupportedHeadlessActivationEvents } from '../../common/headless-plugin-protocol'; +import { PluginDeployerImpl } from '@theia/plugin-ext/lib/main/node/plugin-deployer-impl'; + +import URI from '@theia/core/lib/common/uri'; +import * as fs from 'fs'; +import * as asyncFs from 'fs/promises'; + +export type HeadlessPluginHost = string; + +export function isHeadlessPlugin(plugin: DeployedPlugin): boolean { + return !!plugin.metadata.model.entryPoint.headless; +} + +@injectable() +export class HeadlessHostedPluginSupport extends AbstractHostedPluginSupport { + + @inject(HostedPluginProcess) + protected readonly pluginProcess: HostedPluginProcess; + + @inject(IShellTerminalServer) + protected readonly shellTerminalServer: IShellTerminalServer; + + @inject(TheiaHeadlessPluginScanner) + protected readonly scanner: TheiaHeadlessPluginScanner; + + @inject(PluginDeployer) + protected readonly pluginDeployer: PluginDeployerImpl; + + @inject(ContributionProvider) + @named(SupportedHeadlessActivationEvents) + protected readonly supportedActivationEventsContributions: ContributionProvider; + + constructor() { + super(generateUuid()); + } + + shutDown(): void { + this.pluginProcess.terminatePluginServer(); + } + + protected createTheiaReadyPromise(): Promise { + return Promise.all([this.envServer.getVariables()]); + } + + // Only load headless plugins + protected acceptPlugin(plugin: DeployedPlugin): boolean | DeployedPlugin { + if (!isHeadlessPlugin(plugin)) { + return false; + } + + if (plugin.metadata.model.engine.type === this.scanner.apiType) { + // Easy case: take it as it is + return true; + } + + // Adapt it for headless + return this.scanner.adaptForHeadless(plugin); + } + + protected handleContributions(_plugin: DeployedPlugin): Disposable { + // We have no contribution points, yet, for headless plugins + return Disposable.NULL; + } + + protected override async beforeSyncPlugins(toDisconnect: DisposableCollection): Promise { + await super.beforeSyncPlugins(toDisconnect); + + // Plugin deployment is asynchronous, so wait until that's finished. + return new Promise((resolve, reject) => { + this.pluginDeployer.onDidDeploy(resolve); + toDisconnect.push(Disposable.create(reject)); + }); + } + + protected async obtainManager(host: string, hostContributions: PluginContributions[], toDisconnect: DisposableCollection): Promise { + let manager = this.managers.get(host); + if (!manager) { + const pluginId = getPluginId(hostContributions[0].plugin.metadata.model); + const rpc = this.initRpc(host, pluginId); + toDisconnect.push(rpc); + + manager = rpc.getProxy(HEADLESSMAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT); + this.managers.set(host, manager); + toDisconnect.push(Disposable.create(() => this.managers.delete(host))); + + const [extApi, globalState] = await Promise.all([ + this.server.getExtPluginAPI(), + this.pluginServer.getAllStorageValues(undefined) + ]); + if (toDisconnect.disposed) { + return undefined; + } + + const activationEvents = this.supportedActivationEventsContributions.getContributions().flatMap(array => array); + const shell = await this.shellTerminalServer.getDefaultShell(); + const isElectron = environment.electron.is(); + + await manager.$init({ + activationEvents, + globalState, + env: { + language: nls.locale || nls.defaultLocale, + shell, + appName: BackendApplicationConfigProvider.get().applicationName, + appHost: isElectron ? 'desktop' : 'web' // TODO: 'web' could be the embedder's name, e.g. 'github.dev' + }, + extApi + }); + if (toDisconnect.disposed) { + return undefined; + } + } + return manager; + } + + protected initRpc(host: HeadlessPluginHost, pluginId: string): RPCProtocol { + const rpc = this.createServerRpc(host); + this.container.bind(RPCProtocol).toConstantValue(rpc); + setUpPluginApi(rpc, this.container); + this.mainPluginApiProviders.getContributions().forEach(p => p.initialize(rpc, this.container)); + return rpc; + } + + protected createServerRpc(pluginHostId: string): RPCProtocol { + const channel = new IPCChannel(this.pluginProcess['childProcess']); + + return new RPCProtocolImpl(channel); + } + + protected async getStoragePath(): Promise { + // Headless plugins are associated with the main Node process, so + // their storage is the global storage. + return this.getHostGlobalStoragePath(); + } + + protected async getHostGlobalStoragePath(): Promise { + const configDirUri = await this.envServer.getConfigDirUri(); + const globalStorageFolderUri = new URI(configDirUri).resolve('globalStorage'); + const globalStorageFolderUrl = new URL(globalStorageFolderUri.toString()); + + let stat: fs.Stats | undefined; + + try { + stat = await asyncFs.stat(globalStorageFolderUrl); + } catch (_) { + // OK, no such directory + } + + if (stat && !stat.isDirectory()) { + throw new Error(`Global storage folder is not a directory: ${globalStorageFolderUri}`); + } + + // Make sure that folder by the path exists + if (!stat) { + await asyncFs.mkdir(globalStorageFolderUrl, { recursive: true }); + } + + const globalStorageFolderFsPath = await asyncFs.realpath(globalStorageFolderUrl); + if (!globalStorageFolderFsPath) { + throw new Error(`Could not resolve the FS path for URI: ${globalStorageFolderUri}`); + } + return globalStorageFolderFsPath; + } +} diff --git a/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts b/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts new file mode 100644 index 0000000000000..51a031ee91d3b --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts @@ -0,0 +1,75 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as path from 'path'; +import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider'; +import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; +import { ExtPluginApiProvider, HostedPluginServer, PluginHostEnvironmentVariable, PluginScanner } from '@theia/plugin-ext'; +import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin'; +import { HostedPluginProcess, HostedPluginProcessConfiguration } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-process'; +import { BackendPluginHostableFilter, HostedPluginServerImpl } from '@theia/plugin-ext/lib/hosted/node/plugin-service'; +import { MaybePromise } from '@theia/core'; +import { HeadlessPluginContainerModule } from '../../common/headless-plugin-container'; +import { HeadlessHostedPluginSupport, isHeadlessPlugin } from './headless-hosted-plugin'; +import { TheiaHeadlessPluginScanner } from './scanners/scanner-theia-headless'; +import { SupportedHeadlessActivationEvents } from '../../common/headless-plugin-protocol'; + +export function bindCommonHostedBackend(bind: interfaces.Bind): void { + bind(HostedPluginProcess).toSelf().inSingletonScope(); + bind(HostedPluginSupport).toSelf().inSingletonScope(); + + bindContributionProvider(bind, Symbol.for(ExtPluginApiProvider)); + bindContributionProvider(bind, PluginHostEnvironmentVariable); + bindContributionProvider(bind, SupportedHeadlessActivationEvents); + + bind(HostedPluginServerImpl).toSelf().inSingletonScope(); + bind(HostedPluginServer).toService(HostedPluginServerImpl); + bind(HeadlessHostedPluginSupport).toSelf().inSingletonScope(); + bind(BackendPluginHostableFilter).toConstantValue(isHeadlessPlugin); + + bind(HostedPluginProcessConfiguration).toConstantValue({ + path: path.join(__dirname, 'plugin-host-headless'), + }); +} + +export function bindHeadlessHosted(bind: interfaces.Bind): void { + bind(TheiaHeadlessPluginScanner).toSelf().inSingletonScope(); + bind(PluginScanner).toService(TheiaHeadlessPluginScanner); + bind(SupportedHeadlessActivationEvents).toConstantValue(['*', 'onStartupFinished']); + + bind(BackendApplicationContribution).toDynamicValue(({container}) => { + let hostedPluginSupport: HeadlessHostedPluginSupport | undefined; + + return { + onStart(): MaybePromise { + // Create a child container to isolate the Headless Plugin hosting stack + // from all connection-scoped frontend/backend plugin hosts and + // also to avoid leaking it into the global container scope + const headlessPluginsContainer = container.createChild(); + const modules = container.getAll(HeadlessPluginContainerModule); + headlessPluginsContainer.load(...modules); + + hostedPluginSupport = headlessPluginsContainer.get(HeadlessHostedPluginSupport); + hostedPluginSupport.onStart(headlessPluginsContainer); + }, + + onStop(): void { + hostedPluginSupport?.shutDown(); + } + }; + }); +} diff --git a/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-module.ts b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-module.ts new file mode 100644 index 0000000000000..0237e1662ead9 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-module.ts @@ -0,0 +1,76 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import '@theia/core/shared/reflect-metadata'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { RPCProtocol, RPCProtocolImpl } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { AbstractPluginHostRPC, PluginContainerModuleLoader } from '@theia/plugin-ext/lib/hosted/node/plugin-host-rpc'; +import { AbstractPluginManagerExtImpl, MinimalTerminalServiceExt } from '@theia/plugin-ext/lib/plugin/plugin-manager'; +import { HeadlessPluginHostRPC } from './plugin-host-headless-rpc'; +import { HeadlessPluginManagerExtImpl } from '../../plugin/headless-plugin-manager'; +import { IPCChannel } from '@theia/core/lib/node'; +import { InternalPluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; + +import { EnvExtImpl } from '@theia/plugin-ext/lib/plugin/env'; +import { EnvNodeExtImpl } from '@theia/plugin-ext/lib/plugin/node/env-node-ext'; +import { LocalizationExt } from '@theia/plugin-ext'; +import { LocalizationExtImpl } from '@theia/plugin-ext/lib/plugin/localization-ext'; +import { InternalStorageExt } from '@theia/plugin-ext/lib/plugin/plugin-storage'; +import { InternalSecretsExt } from '@theia/plugin-ext/lib/plugin/secrets-ext'; +import { EnvironmentVariableCollectionImpl } from '@theia/plugin-ext/lib/plugin/terminal-ext'; +import { Disposable } from '@theia/core'; + +export default new ContainerModule(bind => { + const channel = new IPCChannel(); + bind(RPCProtocol).toConstantValue(new RPCProtocolImpl(channel)); + + bind(PluginContainerModuleLoader).toDynamicValue(({ container }) => + (module: ContainerModule) => { + container.load(module); + const internalModule = module as InternalPluginContainerModule; + const pluginApiCache = internalModule.initializeApi?.(container); + return pluginApiCache; + }).inSingletonScope(); + + bind(AbstractPluginHostRPC).toService(HeadlessPluginHostRPC); + bind(HeadlessPluginHostRPC).toSelf().inSingletonScope(); + bind(AbstractPluginManagerExtImpl).toService(HeadlessPluginManagerExtImpl); + bind(HeadlessPluginManagerExtImpl).toSelf().inSingletonScope(); + bind(EnvExtImpl).to(EnvNodeExtImpl).inSingletonScope(); + bind(LocalizationExt).to(LocalizationExtImpl).inSingletonScope(); + + const dummySecrets: InternalSecretsExt = { + get: () => Promise.resolve(undefined), + store: () => Promise.resolve(undefined), + delete: () => Promise.resolve(undefined), + $onDidChangePassword: () => Promise.resolve(), + onDidChangePassword: () => Disposable.NULL, + }; + const dummyStorage: InternalStorageExt = { + init: () => undefined, + setPerPluginData: () => Promise.resolve(false), + getPerPluginData: () => ({}), + storageDataChangedEvent: () => Disposable.NULL, + $updatePluginsWorkspaceData: () => undefined + }; + const dummyTerminalService: MinimalTerminalServiceExt = { + $initEnvironmentVariableCollections: () => undefined, + $setShell: () => undefined, + getEnvironmentVariableCollection: () => new EnvironmentVariableCollectionImpl(false), + }; + bind(InternalSecretsExt).toConstantValue(dummySecrets); + bind(InternalStorageExt).toConstantValue(dummyStorage); + bind(MinimalTerminalServiceExt).toConstantValue(dummyTerminalService); +}); diff --git a/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-rpc.ts b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-rpc.ts new file mode 100644 index 0000000000000..175fbebcd8ca9 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless-rpc.ts @@ -0,0 +1,80 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { dynamicRequire } from '@theia/core/lib/node/dynamic-require'; +import { ContainerModule, injectable, inject } from '@theia/core/shared/inversify'; +import { EnvExtImpl } from '@theia/plugin-ext/lib/plugin/env'; +import { LocalizationExt } from '@theia/plugin-ext'; +import { LocalizationExtImpl } from '@theia/plugin-ext/lib/plugin/localization-ext'; +import { HEADLESSMAIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc'; +import { HeadlessPluginManagerExtImpl } from '../../plugin/headless-plugin-manager'; +import { AbstractPluginHostRPC, ExtInterfaces } from '@theia/plugin-ext/lib/hosted/node/plugin-host-rpc'; +import { PluginModel } from '@theia/plugin-ext/lib/common/plugin-protocol'; +import { ExtPluginApi, ExtPluginApiHeadlessInitializationFn } from '../../common/plugin-ext-headless-api-contribution'; + +type HeadlessExtInterfaces = Pick; + +/** + * The RPC handler for headless plugins. + */ +@injectable() +export class HeadlessPluginHostRPC extends AbstractPluginHostRPC { + @inject(EnvExtImpl) + protected readonly envExt: EnvExtImpl; + + @inject(LocalizationExt) + protected readonly localizationExt: LocalizationExtImpl; + + constructor() { + super('HEADLESS_PLUGIN_HOST', undefined, + { + $pluginManager: HEADLESSMAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, + } + ); + } + + protected createExtInterfaces(): HeadlessExtInterfaces { + return { + envExt: this.envExt, + localizationExt: this.localizationExt + }; + } + + protected createAPIFactory(_extInterfaces: HeadlessExtInterfaces): null { + // As yet there is no default API namespace for backend plugins to access the Theia framework + return null; + } + + protected override getBackendPluginPath(pluginModel: PluginModel): string | undefined { + return pluginModel.entryPoint.headless; + } + + protected initExtApi(extApi: ExtPluginApi): void { + interface PluginExports { + containerModule?: ContainerModule; + provideApi?: ExtPluginApiHeadlessInitializationFn; + } + if (extApi.headlessInitPath) { + const { containerModule, provideApi } = dynamicRequire(extApi.headlessInitPath); + if (containerModule) { + this.loadContainerModule(containerModule); + } + if (provideApi) { + provideApi(this.rpc, this.pluginManager); + } + } + } +} diff --git a/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless.ts b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless.ts new file mode 100644 index 0000000000000..9d0556db2d635 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/plugin-host-headless.ts @@ -0,0 +1,111 @@ +// ***************************************************************************** +// Copyright (C) 2018 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import '@theia/core/shared/reflect-metadata'; +import { Container } from '@theia/core/shared/inversify'; +import { ConnectionClosedError, RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { ProcessTerminatedMessage, ProcessTerminateMessage } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-protocol'; +import { HeadlessPluginHostRPC } from './plugin-host-headless-rpc'; +import pluginHostModule from './plugin-host-headless-module'; + +const banner = `HEADLESS_PLUGIN_HOST(${process.pid}):`; +console.log(banner, 'Starting instance'); + +// override exit() function, to do not allow plugin kill this node +process.exit = function (code?: number): void { + const err = new Error('A plugin called process.exit() but it was blocked.'); + console.warn(banner, err.stack); +} as (code?: number) => never; + +// same for 'crash'(works only in electron) +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const proc = process as any; +if (proc.crash) { + proc.crash = function (): void { + const err = new Error('A plugin called process.crash() but it was blocked.'); + console.warn(banner, err.stack); + }; +} + +process.on('uncaughtException', (err: Error) => { + console.error(banner, err); +}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const unhandledPromises: Promise[] = []; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +process.on('unhandledRejection', (reason: any, promise: Promise) => { + unhandledPromises.push(promise); + setTimeout(() => { + const index = unhandledPromises.indexOf(promise); + if (index >= 0) { + promise.catch(err => { + unhandledPromises.splice(index, 1); + if (terminating && (ConnectionClosedError.is(err) || ConnectionClosedError.is(reason))) { + // during termination it is expected that pending rpc request are rejected + return; + } + console.error(banner, `Promise rejection not handled in one second: ${err} , reason: ${reason}`); + if (err && err.stack) { + console.error(banner, `With stack trace: ${err.stack}`); + } + }); + } + }, 1000); +}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +process.on('rejectionHandled', (promise: Promise) => { + const index = unhandledPromises.indexOf(promise); + if (index >= 0) { + unhandledPromises.splice(index, 1); + } +}); + +let terminating = false; + +const container = new Container(); +container.load(pluginHostModule); + +const rpc: RPCProtocol = container.get(RPCProtocol); +const pluginHostRPC = container.get(HeadlessPluginHostRPC); + +process.on('message', async (message: string) => { + if (terminating) { + return; + } + try { + const msg = JSON.parse(message); + if (ProcessTerminateMessage.is(msg)) { + terminating = true; + if (msg.stopTimeout) { + await Promise.race([ + pluginHostRPC.terminate(), + new Promise(resolve => setTimeout(resolve, msg.stopTimeout)) + ]); + } else { + await pluginHostRPC.terminate(); + } + rpc.dispose(); + if (process.send) { + process.send(JSON.stringify({ type: ProcessTerminatedMessage.TYPE })); + } + + } + } catch (e) { + console.error(banner, e); + } +}); diff --git a/packages/plugin-ext-headless/src/hosted/node/scanners/scanner-theia-headless.ts b/packages/plugin-ext-headless/src/hosted/node/scanners/scanner-theia-headless.ts new file mode 100644 index 0000000000000..58fa0f26837e0 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/scanners/scanner-theia-headless.ts @@ -0,0 +1,85 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* eslint-disable @theia/localization-check */ + +import { injectable } from '@theia/core/shared/inversify'; +import { DeployedPlugin, PluginPackage, PluginEntryPoint } from '@theia/plugin-ext'; +import { AbstractPluginScanner } from '@theia/plugin-ext/lib/hosted/node/scanners/scanner-theia'; +import { deepClone } from '@theia/core/lib/common/objects'; + +@injectable() +export class TheiaHeadlessPluginScanner extends AbstractPluginScanner { + + constructor() { + super('theiaHeadlessPlugin'); + } + + protected getEntryPoint(plugin: PluginPackage): PluginEntryPoint { + if (plugin?.theiaPlugin?.headless) { + return { + headless: plugin.theiaPlugin.headless + }; + }; + + return { + headless: plugin.main + }; + } + + /** + * Adapt the given `plugin`'s metadata for headless deployment, where it does not + * already natively specify its headless deployment, such as is the case for plugins + * declaring the `"vscode"` or `"theiaPlugin"` engine. This consists of cloning the + * relevant properties of its deployment metadata and modifying them as required, + * including but not limited to: + * + * - renaming the `lifecycle` start and stop functions as 'activate' and 'deactivate' + * following the VS Code naming convention (in case the `plugin` is a Theia-style + * plugin that uses 'start' and 'stop') + * - deleting inapplicable information such as frontend and backend init script paths + * - filtering/rewriting contributions and/or activation events + * + * The cloning is necessary to retain the original information for the non-headless + * deployments that the plugin also supports. + */ + adaptForHeadless(plugin: DeployedPlugin): DeployedPlugin { + return { + type: plugin.type, + metadata: this.adaptMetadataForHeadless(plugin), + contributes: this.adaptContributesForHeadless(plugin) + }; + } + + protected adaptMetadataForHeadless(plugin: DeployedPlugin): DeployedPlugin['metadata'] { + const result = deepClone(plugin.metadata); + + const lifecycle = result.lifecycle; + delete lifecycle.frontendInitPath; + delete lifecycle.backendInitPath; + + // Same as in VS Code + lifecycle.startMethod = 'activate'; + lifecycle.stopMethod = 'deactivate'; + + return result; + } + + protected adaptContributesForHeadless(plugin: DeployedPlugin): DeployedPlugin['contributes'] { + // We don't yet support and contribution points in headless plugins + return undefined; + } +} diff --git a/packages/plugin-ext-headless/src/index.ts b/packages/plugin-ext-headless/src/index.ts new file mode 100644 index 0000000000000..30d6ae1a4b8a8 --- /dev/null +++ b/packages/plugin-ext-headless/src/index.ts @@ -0,0 +1,17 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './common'; diff --git a/packages/plugin-ext-headless/src/main/node/handlers/plugin-theia-headless-directory-handler.ts b/packages/plugin-ext-headless/src/main/node/handlers/plugin-theia-headless-directory-handler.ts new file mode 100644 index 0000000000000..547d80b5a8364 --- /dev/null +++ b/packages/plugin-ext-headless/src/main/node/handlers/plugin-theia-headless-directory-handler.ts @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; + +import { PluginDeployerDirectoryHandlerContext, PluginDeployerEntryType, PluginPackage } from '@theia/plugin-ext'; +import { AbstractPluginDirectoryHandler } from '@theia/plugin-ext/lib/main/node/handlers/plugin-theia-directory-handler'; + +@injectable() +export class PluginTheiaHeadlessDirectoryHandler extends AbstractPluginDirectoryHandler { + + protected acceptManifest(plugin: PluginPackage): boolean { + return plugin?.engines?.theiaPlugin === undefined && 'theiaHeadlessPlugin' in plugin.engines; + } + + async handle(context: PluginDeployerDirectoryHandlerContext): Promise { + await this.copyDirectory(context); + const types: PluginDeployerEntryType[] = [PluginDeployerEntryType.HEADLESS]; + context.pluginEntry().accept(...types); + } + +} diff --git a/packages/plugin-ext-headless/src/main/node/headless-progress-client.ts b/packages/plugin-ext-headless/src/main/node/headless-progress-client.ts new file mode 100644 index 0000000000000..7822402aed6d3 --- /dev/null +++ b/packages/plugin-ext-headless/src/main/node/headless-progress-client.ts @@ -0,0 +1,44 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { + CancellationToken, + ProgressClient, ProgressMessage, ProgressUpdate +} from '@theia/core'; + +/** + * A simple progress client for headless plugins that just writes debug messages to the console + * because there is no one connected frontend to which it is appropriate to send the messages. + */ +@injectable() +export class HeadlessProgressClient implements ProgressClient { + async showProgress(_progressId: string, message: ProgressMessage, cancellationToken: CancellationToken): Promise { + if (cancellationToken.isCancellationRequested) { + return ProgressMessage.Cancel; + } + console.debug(message.text); + } + + async reportProgress(_progressId: string, update: ProgressUpdate, message: ProgressMessage, cancellationToken: CancellationToken): Promise { + if (cancellationToken.isCancellationRequested) { + return; + } + const progress = update.work && update.work.total ? `[${100 * Math.min(update.work.done, update.work.total) / update.work.total}%]` : ''; + const text = `${progress} ${update.message ?? 'completed ...'}`; + console.debug(text); + } +} diff --git a/packages/plugin-ext-headless/src/main/node/main-context.ts b/packages/plugin-ext-headless/src/main/node/main-context.ts new file mode 100644 index 0000000000000..2f848d3be5d48 --- /dev/null +++ b/packages/plugin-ext-headless/src/main/node/main-context.ts @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { interfaces } from '@theia/core/shared/inversify'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { EnvMainImpl } from '@theia/plugin-ext/lib/main/common/env-main'; +import { BasicMessageRegistryMainImpl } from '@theia/plugin-ext/lib/main/common/basic-message-registry-main'; +import { BasicNotificationMainImpl } from '@theia/plugin-ext/lib/main/common/basic-notification-main'; + +import { HEADLESSMAIN_RPC_CONTEXT, HEADLESSPLUGIN_RPC_CONTEXT } from '../../common/headless-plugin-rpc'; + +// This sets up only the minimal plugin API required by the plugin manager to report +// messages and notifications to the main side and to initialize plugins. +export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void { + const envMain = new EnvMainImpl(rpc, container); + rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.ENV_MAIN, envMain); + + const messageRegistryMain = new BasicMessageRegistryMainImpl(container); + rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN, messageRegistryMain); + + const notificationMain = new BasicNotificationMainImpl(rpc, container, HEADLESSMAIN_RPC_CONTEXT.NOTIFICATION_EXT); + rpc.set(HEADLESSPLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN, notificationMain); +} diff --git a/packages/plugin-ext-headless/src/main/node/plugin-ext-headless-main-module.ts b/packages/plugin-ext-headless/src/main/node/plugin-ext-headless-main-module.ts new file mode 100644 index 0000000000000..19faa51517862 --- /dev/null +++ b/packages/plugin-ext-headless/src/main/node/plugin-ext-headless-main-module.ts @@ -0,0 +1,42 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { + MessageClient, MessageService, + ProgressClient, ProgressService, + bindContributionProvider +} from '@theia/core'; +import { MainPluginApiProvider, PluginDeployerDirectoryHandler } from '@theia/plugin-ext'; +import { PluginTheiaHeadlessDirectoryHandler } from './handlers/plugin-theia-headless-directory-handler'; +import { HeadlessProgressClient } from './headless-progress-client'; + +export function bindHeadlessMain(bind: interfaces.Bind): void { + bind(PluginDeployerDirectoryHandler).to(PluginTheiaHeadlessDirectoryHandler).inSingletonScope(); +} + +export function bindBackendMain(bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind): void { + bindContributionProvider(bind, MainPluginApiProvider); + + // + // Main API dependencies + // + + bind(MessageService).toSelf().inSingletonScope(); + bind(MessageClient).toSelf().inSingletonScope(); // Just logs to console + bind(ProgressService).toSelf().inSingletonScope(); + bind(ProgressClient).to(HeadlessProgressClient).inSingletonScope(); +} diff --git a/packages/plugin-ext-headless/src/package.spec.ts b/packages/plugin-ext-headless/src/package.spec.ts new file mode 100644 index 0000000000000..b918a55863c16 --- /dev/null +++ b/packages/plugin-ext-headless/src/package.spec.ts @@ -0,0 +1,25 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* + * This is a placeholder for tests that the extension package should implement + * but as yet does not. + * Please delete this file when a real test is implemented. + */ + +describe('plugin-ext-headless package', () => { + it('placeholder to enable mocha', () => true); +}); diff --git a/packages/plugin-ext-headless/src/plugin-ext-headless-electron-module.ts b/packages/plugin-ext-headless/src/plugin-ext-headless-electron-module.ts new file mode 100644 index 0000000000000..489d884e8cdcf --- /dev/null +++ b/packages/plugin-ext-headless/src/plugin-ext-headless-electron-module.ts @@ -0,0 +1,32 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { HeadlessPluginContainerModule } from './common/headless-plugin-container'; +import { bindElectronBackend } from './hosted/node-electron/plugin-ext-headless-hosted-electron-module'; +import { bindHeadlessMain, bindBackendMain } from './main/node/plugin-ext-headless-main-module'; +import { bindHeadlessHosted } from './hosted/node/plugin-ext-headless-hosted-module'; + +const backendElectronModule = new ContainerModule((bind, unbind, isBound, rebind) => { + bindBackendMain(bind, unbind, isBound, rebind); + bindElectronBackend(bind); +}); + +export default new ContainerModule(bind => { + bind(HeadlessPluginContainerModule).toConstantValue(backendElectronModule); + bindHeadlessMain(bind); + bindHeadlessHosted(bind); +}); diff --git a/packages/plugin-ext-headless/src/plugin-ext-headless-module.ts b/packages/plugin-ext-headless/src/plugin-ext-headless-module.ts new file mode 100644 index 0000000000000..61c0844f6ae0a --- /dev/null +++ b/packages/plugin-ext-headless/src/plugin-ext-headless-module.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { HeadlessPluginContainerModule } from './common/headless-plugin-container'; +import { bindHeadlessHosted, bindCommonHostedBackend } from './hosted/node/plugin-ext-headless-hosted-module'; +import { bindHeadlessMain, bindBackendMain } from './main/node/plugin-ext-headless-main-module'; + +const backendModule = new ContainerModule((bind, unbind, isBound, rebind) => { + bindBackendMain(bind, unbind, isBound, rebind); + bindCommonHostedBackend(bind); +}); + +export default new ContainerModule(bind => { + bind(HeadlessPluginContainerModule).toConstantValue(backendModule); + bindHeadlessMain(bind); + bindHeadlessHosted(bind); +}); diff --git a/packages/plugin-ext-headless/src/plugin/headless-plugin-manager.ts b/packages/plugin-ext-headless/src/plugin/headless-plugin-manager.ts new file mode 100644 index 0000000000000..5f2872f4e9168 --- /dev/null +++ b/packages/plugin-ext-headless/src/plugin/headless-plugin-manager.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { AbstractPluginManagerExtImpl } from '@theia/plugin-ext/lib/plugin/plugin-manager'; +import { HeadlessPluginManagerExt, HeadlessPluginManagerInitializeParams } from '../common/headless-plugin-rpc'; +import { Plugin } from '@theia/plugin-ext'; + +@injectable() +export class HeadlessPluginManagerExtImpl extends AbstractPluginManagerExtImpl implements HeadlessPluginManagerExt { + + private readonly supportedActivationEvents = new Set(); + + async $init(params: HeadlessPluginManagerInitializeParams): Promise { + params.activationEvents?.forEach(event => this.supportedActivationEvents.add(event)); + + this.storage.init(params.globalState, {}); + + this.envExt.setLanguage(params.env.language); + this.envExt.setApplicationName(params.env.appName); + this.envExt.setAppHost(params.env.appHost); + + if (params.extApi) { + this.host.initExtApi(params.extApi); + } + } + + protected override getActivationEvents(plugin: Plugin): string[] | undefined { + const result = plugin.rawModel?.headless?.activationEvents; + return Array.isArray(result) ? result : undefined; + } + + protected isSupportedActivationEvent(activationEvent: string): boolean { + return this.supportedActivationEvents.has(activationEvent.split(':')[0]); + } + +} diff --git a/packages/plugin-ext-headless/tsconfig.json b/packages/plugin-ext-headless/tsconfig.json new file mode 100644 index 0000000000000..e61a769a6936a --- /dev/null +++ b/packages/plugin-ext-headless/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib", + "lib": [ + "es6", + "dom", + "webworker" + ] + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../core" + }, + { + "path": "../plugin-ext" + }, + { + "path": "../terminal" + } + ] +} diff --git a/packages/plugin-ext-vscode/src/node/scanner-vscode.ts b/packages/plugin-ext-vscode/src/node/scanner-vscode.ts index 7b1049eea1e5d..cc49cc7f6bf22 100644 --- a/packages/plugin-ext-vscode/src/node/scanner-vscode.ts +++ b/packages/plugin-ext-vscode/src/node/scanner-vscode.ts @@ -51,6 +51,10 @@ export class VsCodePluginScanner extends TheiaPluginScanner implements PluginSca // Default to using backend entryPoint.backend = plugin.main; } + if (plugin.theiaPlugin?.headless) { + // Support the Theia-specific extension for headless plugins + entryPoint.headless = plugin.theiaPlugin?.headless; + } const result: PluginModel = { packagePath: plugin.packagePath, diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index c0616d5177df6..32140ec1c0625 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -172,6 +172,7 @@ export interface PluginAPI { } +export const PluginManager = Symbol.for('PluginManager'); export interface PluginManager { getAllPlugins(): Plugin[]; getPluginById(pluginId: string): Plugin | undefined; @@ -243,10 +244,9 @@ export interface PluginManagerStartParams { activationEvents: string[] } -export interface PluginManagerExt { - +export interface AbstractPluginManagerExt

> { /** initialize the manager, should be called only once */ - $init(params: PluginManagerInitializeParams): Promise; + $init(params: P): Promise; /** load and activate plugins */ $start(params: PluginManagerStartParams): Promise; @@ -264,6 +264,8 @@ export interface PluginManagerExt { $activatePlugin(id: string): Promise; } +export interface PluginManagerExt extends AbstractPluginManagerExt { } + export interface CommandRegistryMain { $registerCommand(command: theia.CommandDescription): void; $unregisterCommand(id: string): void; @@ -2651,6 +2653,7 @@ export interface IdentifiableInlineCompletion extends InlineCompletion { idx: number; } +export const LocalizationExt = Symbol('LocalizationExt'); export interface LocalizationExt { translateMessage(pluginId: string, details: StringDetails): string; getBundle(pluginId: string): Record | undefined; diff --git a/packages/plugin-ext/src/common/plugin-ext-api-contribution.ts b/packages/plugin-ext/src/common/plugin-ext-api-contribution.ts index 41455e26e98b3..522987b7ca89b 100644 --- a/packages/plugin-ext/src/common/plugin-ext-api-contribution.ts +++ b/packages/plugin-ext/src/common/plugin-ext-api-contribution.ts @@ -19,26 +19,53 @@ import { interfaces } from '@theia/core/shared/inversify'; export const ExtPluginApiProvider = 'extPluginApi'; /** - * Provider for extension API description + * Provider for extension API description. */ export interface ExtPluginApiProvider { /** - * Provide API description + * Provide API description. */ provideApi(): ExtPluginApi; } /** - * Plugin API extension description. - * This interface describes scripts for both plugin runtimes: frontend(WebWorker) and backend(NodeJs) + * Provider for backend extension API description. */ -export interface ExtPluginApi { +export interface ExtPluginBackendApiProvider { + /** + * Provide API description. + */ + provideApi(): ExtPluginBackendApi; +} + +/** + * Provider for frontend extension API description. + */ +export interface ExtPluginFrontendApiProvider { + /** + * Provide API description. + */ + provideApi(): ExtPluginFrontendApi; +} + +/** + * Backend Plugin API extension description. + * This interface describes a script for the backend(NodeJs) runtime. + */ +export interface ExtPluginBackendApi { /** * Path to the script which should be loaded to provide api, module should export `provideApi` function with * [ExtPluginApiBackendInitializationFn](#ExtPluginApiBackendInitializationFn) signature */ backendInitPath?: string; +} + +/** + * Frontend Plugin API extension description. + * This interface describes a script for the frontend(WebWorker) runtime. + */ +export interface ExtPluginFrontendApi { /** * Initialization information for frontend part of Plugin API @@ -46,6 +73,12 @@ export interface ExtPluginApi { frontendExtApi?: FrontendExtPluginApi; } +/** + * Plugin API extension description. + * This interface describes scripts for both plugin runtimes: frontend(WebWorker) and backend(NodeJs) + */ +export interface ExtPluginApi extends ExtPluginBackendApi, ExtPluginFrontendApi { } + export interface ExtPluginApiFrontendInitializationFn { (rpc: RPCProtocol, plugins: Map): void; } diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts index 19f6e0d7920f4..547074215bd60 100644 --- a/packages/plugin-ext/src/common/plugin-protocol.ts +++ b/packages/plugin-ext/src/common/plugin-protocol.ts @@ -31,7 +31,7 @@ export { PluginIdentifiers }; export const hostedServicePath = '/services/hostedPlugin'; /** - * Plugin engine (API) type, i.e. 'theiaPlugin', 'vscode', etc. + * Plugin engine (API) type, i.e. 'theiaPlugin', 'vscode', 'theiaHeadlessPlugin', etc. */ export type PluginEngine = string; @@ -49,6 +49,8 @@ export interface PluginPackage { theiaPlugin?: { frontend?: string; backend?: string; + /* Requires the `@theia/plugin-ext-headless` extension. */ + headless?: string; }; main?: string; browser?: string; @@ -445,7 +447,9 @@ export enum PluginDeployerEntryType { FRONTEND, - BACKEND + BACKEND, + + HEADLESS // Deployed in the Theia Node server outside the context of a frontend/backend connection } /** @@ -571,6 +575,7 @@ export interface PluginModel { export interface PluginEntryPoint { frontend?: string; backend?: string; + headless?: string; } /** diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index 6f2a8792437b8..fb8ae1c3ca28a 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -39,7 +39,7 @@ export interface MessageConnection { onMessage: Event; } -export const RPCProtocol = Symbol('RPCProtocol'); +export const RPCProtocol = Symbol.for('RPCProtocol'); export interface RPCProtocol extends Disposable { /** * Returns a proxy to an object addressable/named in the plugin process or in the main process. diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index fc454e5a58966..d9e957a986af5 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -21,26 +21,24 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import debounce = require('@theia/core/shared/lodash.debounce'); -import { UUID } from '@theia/core/shared/@phosphor/coreutils'; -import { injectable, inject, interfaces, named, postConstruct } from '@theia/core/shared/inversify'; +import { generateUuid } from '@theia/core/lib/common/uuid'; +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { PluginWorker } from './plugin-worker'; -import { PluginMetadata, getPluginId, HostedPluginServer, DeployedPlugin, PluginServer, PluginIdentifiers } from '../../common/plugin-protocol'; +import { getPluginId, DeployedPlugin, HostedPluginServer } from '../../common/plugin-protocol'; import { HostedPluginWatcher } from './hosted-plugin-watcher'; -import { MAIN_RPC_CONTEXT, PluginManagerExt, ConfigStorage, UIKind } from '../../common/plugin-api-rpc'; +import { MAIN_RPC_CONTEXT, PluginManagerExt, UIKind } from '../../common/plugin-api-rpc'; import { setUpPluginApi } from '../../main/browser/main-context'; import { RPCProtocol, RPCProtocolImpl } from '../../common/rpc-protocol'; import { - Disposable, DisposableCollection, Emitter, isCancelled, - ILogger, ContributionProvider, CommandRegistry, WillExecuteCommandEvent, - CancellationTokenSource, RpcProxy, ProgressService, nls + Disposable, DisposableCollection, isCancelled, + CommandRegistry, WillExecuteCommandEvent, + CancellationTokenSource, ProgressService, nls, + RpcProxy } from '@theia/core'; import { PreferenceServiceImpl, PreferenceProviderProvider } from '@theia/core/lib/browser/preferences'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { PluginContributionHandler } from '../../main/browser/plugin-contribution-handler'; import { getQueryParameters } from '../../main/browser/env-main'; -import { MainPluginApiProvider } from '../../common/plugin-ext-api-contribution'; -import { PluginPathsService } from '../../main/common/plugin-paths-protocol'; import { getPreferences } from '../../main/browser/preference-registry-main'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager'; @@ -55,7 +53,6 @@ import { WebviewEnvironment } from '../../main/browser/webview/webview-environme import { WebviewWidget } from '../../main/browser/webview/webview'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import URI from '@theia/core/lib/common/uri'; import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; @@ -66,19 +63,20 @@ import { CustomEditorWidget } from '../../main/browser/custom-editors/custom-edi import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/language'; import { LanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageService'; -import { Measurement, Stopwatch } from '@theia/core/lib/common'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel'; import { NotebookTypeRegistry, NotebookService, NotebookRendererMessagingService } from '@theia/notebook/lib/browser'; +import { + AbstractHostedPluginSupport, PluginContributions, PluginHost, + ALL_ACTIVATION_EVENT, isConnectionScopedBackendPlugin +} from '../common/hosted-plugin'; -export type PluginHost = 'frontend' | string; export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker' | 'onDebugDynamicConfigurations'; export const PluginProgressLocation = 'plugin'; -export const ALL_ACTIVATION_EVENT = '*'; @injectable() -export class HostedPluginSupport { +export class HostedPluginSupport extends AbstractHostedPluginSupport> { protected static ADDITIONAL_ACTIVATION_EVENTS_ENV = 'ADDITIONAL_ACTIVATION_EVENTS'; protected static BUILTIN_ACTIVATION_EVENTS = [ @@ -104,38 +102,18 @@ export class HostedPluginSupport { 'onNotebookSerializer' ]; - protected readonly clientId = UUID.uuid4(); - - protected container: interfaces.Container; - - @inject(ILogger) - protected readonly logger: ILogger; - - @inject(HostedPluginServer) - protected readonly server: RpcProxy; - @inject(HostedPluginWatcher) protected readonly watcher: HostedPluginWatcher; @inject(PluginContributionHandler) protected readonly contributionHandler: PluginContributionHandler; - @inject(ContributionProvider) - @named(MainPluginApiProvider) - protected readonly mainPluginApiProviders: ContributionProvider; - - @inject(PluginServer) - protected readonly pluginServer: PluginServer; - @inject(PreferenceProviderProvider) protected readonly preferenceProviderProvider: PreferenceProviderProvider; @inject(PreferenceServiceImpl) protected readonly preferenceServiceImpl: PreferenceServiceImpl; - @inject(PluginPathsService) - protected readonly pluginPathsService: PluginPathsService; - @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -190,48 +168,20 @@ export class HostedPluginSupport { @inject(TerminalService) protected readonly terminalService: TerminalService; - @inject(EnvVariablesServer) - protected readonly envServer: EnvVariablesServer; - @inject(JsonSchemaStore) protected readonly jsonSchemaStore: JsonSchemaStore; @inject(PluginCustomEditorRegistry) protected readonly customEditorRegistry: PluginCustomEditorRegistry; - @inject(Stopwatch) - protected readonly stopwatch: Stopwatch; - - protected theiaReadyPromise: Promise; - - protected readonly managers = new Map(); - - protected readonly contributions = new Map(); - - protected readonly activationEvents = new Set(); - - protected readonly onDidChangePluginsEmitter = new Emitter(); - readonly onDidChangePlugins = this.onDidChangePluginsEmitter.event; - - protected readonly deferredWillStart = new Deferred(); - /** - * Resolves when the initial plugins are loaded and about to be started. - */ - get willStart(): Promise { - return this.deferredWillStart.promise; - } - - protected readonly deferredDidStart = new Deferred(); - /** - * Resolves when the initial plugins are started. - */ - get didStart(): Promise { - return this.deferredDidStart.promise; + constructor() { + super(generateUuid()); } @postConstruct() - protected init(): void { - this.theiaReadyPromise = Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]); + protected override init(): void { + super.init(); + this.workspaceService.onWorkspaceChanged(() => this.updateStoragePath()); const languageService = (StandaloneServices.get(ILanguageService) as LanguageService); @@ -277,254 +227,49 @@ export class HostedPluginSupport { }); } - get plugins(): PluginMetadata[] { - const plugins: PluginMetadata[] = []; - this.contributions.forEach(contributions => plugins.push(contributions.plugin.metadata)); - return plugins; + protected createTheiaReadyPromise(): Promise { + return Promise.all([this.preferenceServiceImpl.ready, this.workspaceService.roots]); } - getPlugin(id: PluginIdentifiers.UnversionedId): DeployedPlugin | undefined { - const contributions = this.contributions.get(id); - return contributions && contributions.plugin; + protected override runOperation(operation: () => Promise): Promise { + return this.progressService.withProgress('', PluginProgressLocation, () => this.doLoad()); } - /** do not call it, except from the plugin frontend contribution */ - onStart(container: interfaces.Container): void { - this.container = container; - this.load(); - this.watcher.onDidDeploy(() => this.load()); + protected override afterStart(): void { this.server.onDidOpenConnection(() => this.load()); } - protected loadQueue: Promise = Promise.resolve(undefined); - load = debounce(() => this.loadQueue = this.loadQueue.then(async () => { - try { - await this.progressService.withProgress('', PluginProgressLocation, () => this.doLoad()); - } catch (e) { - console.error('Failed to load plugins:', e); - } - }), 50, { leading: true }); + // Only load connection-scoped plugins + protected acceptPlugin(plugin: DeployedPlugin): boolean { + return isConnectionScopedBackendPlugin(plugin); + } + + protected override async beforeSyncPlugins(toDisconnect: DisposableCollection): Promise { + await super.beforeSyncPlugins(toDisconnect); - protected async doLoad(): Promise { - const toDisconnect = new DisposableCollection(Disposable.create(() => { /* mark as connected */ })); toDisconnect.push(Disposable.create(() => this.preserveWebviews())); this.server.onDidCloseConnection(() => toDisconnect.dispose()); + } - // process empty plugins as well in order to properly remove stale plugin widgets - await this.syncPlugins(); - - // it has to be resolved before awaiting layout is initialized - // otherwise clients can hang forever in the initialization phase - this.deferredWillStart.resolve(); - + protected override async beforeLoadContributions(toDisconnect: DisposableCollection): Promise { // make sure that the previous state, including plugin widgets, is restored // and core layout is initialized, i.e. explorer, scm, debug views are already added to the shell // but shell is not yet revealed await this.appState.reachedState('initialized_layout'); + } - if (toDisconnect.disposed) { - // if disconnected then don't try to load plugin contributions - return; - } - const contributionsByHost = this.loadContributions(toDisconnect); - + protected override async afterLoadContributions(toDisconnect: DisposableCollection): Promise { await this.viewRegistry.initWidgets(); // remove restored plugin widgets which were not registered by contributions this.viewRegistry.removeStaleWidgets(); - await this.theiaReadyPromise; - - if (toDisconnect.disposed) { - // if disconnected then don't try to init plugin code and dynamic contributions - return; - } - await this.startPlugins(contributionsByHost, toDisconnect); - - this.deferredDidStart.resolve(); - } - - /** - * Sync loaded and deployed plugins: - * - undeployed plugins are unloaded - * - newly deployed plugins are initialized - */ - protected async syncPlugins(): Promise { - let initialized = 0; - const waitPluginsMeasurement = this.measure('waitForDeployment'); - let syncPluginsMeasurement: Measurement | undefined; - - const toUnload = new Set(this.contributions.keys()); - let didChangeInstallationStatus = false; - try { - const newPluginIds: PluginIdentifiers.VersionedId[] = []; - const [deployedPluginIds, uninstalledPluginIds] = await Promise.all([this.server.getDeployedPluginIds(), this.server.getUninstalledPluginIds()]); - waitPluginsMeasurement.log('Waiting for backend deployment'); - syncPluginsMeasurement = this.measure('syncPlugins'); - for (const versionedId of deployedPluginIds) { - const unversionedId = PluginIdentifiers.unversionedFromVersioned(versionedId); - toUnload.delete(unversionedId); - if (!this.contributions.has(unversionedId)) { - newPluginIds.push(versionedId); - } - } - for (const pluginId of toUnload) { - this.contributions.get(pluginId)?.dispose(); - } - for (const versionedId of uninstalledPluginIds) { - const plugin = this.getPlugin(PluginIdentifiers.unversionedFromVersioned(versionedId)); - if (plugin && PluginIdentifiers.componentsToVersionedId(plugin.metadata.model) === versionedId && !plugin.metadata.outOfSync) { - plugin.metadata.outOfSync = didChangeInstallationStatus = true; - } - } - for (const contribution of this.contributions.values()) { - if (contribution.plugin.metadata.outOfSync && !uninstalledPluginIds.includes(PluginIdentifiers.componentsToVersionedId(contribution.plugin.metadata.model))) { - contribution.plugin.metadata.outOfSync = false; - didChangeInstallationStatus = true; - } - } - if (newPluginIds.length) { - const plugins = await this.server.getDeployedPlugins({ pluginIds: newPluginIds }); - for (const plugin of plugins) { - const pluginId = PluginIdentifiers.componentsToUnversionedId(plugin.metadata.model); - const contributions = new PluginContributions(plugin); - this.contributions.set(pluginId, contributions); - contributions.push(Disposable.create(() => this.contributions.delete(pluginId))); - initialized++; - } - } - } finally { - if (initialized || toUnload.size || didChangeInstallationStatus) { - this.onDidChangePluginsEmitter.fire(undefined); - } - - if (!syncPluginsMeasurement) { - // await didn't complete normally - waitPluginsMeasurement.error('Backend deployment failed.'); - } - } - if (initialized > 0) { - // Only log sync measurement if there are were plugins to sync. - syncPluginsMeasurement?.log(`Sync of ${this.getPluginCount(initialized)}`); - } else { - syncPluginsMeasurement.stop(); - } } - /** - * Always synchronous in order to simplify handling disconnections. - * @throws never - */ - protected loadContributions(toDisconnect: DisposableCollection): Map { - let loaded = 0; - const loadPluginsMeasurement = this.measure('loadPlugins'); - - const hostContributions = new Map(); - console.log(`[${this.clientId}] Loading plugin contributions`); - for (const contributions of this.contributions.values()) { - const plugin = contributions.plugin.metadata; - const pluginId = plugin.model.id; - - if (contributions.state === PluginContributions.State.INITIALIZING) { - contributions.state = PluginContributions.State.LOADING; - contributions.push(Disposable.create(() => console.log(`[${pluginId}]: Unloaded plugin.`))); - contributions.push(this.contributionHandler.handleContributions(this.clientId, contributions.plugin)); - contributions.state = PluginContributions.State.LOADED; - console.debug(`[${this.clientId}][${pluginId}]: Loaded contributions.`); - loaded++; - } - - if (contributions.state === PluginContributions.State.LOADED) { - contributions.state = PluginContributions.State.STARTING; - const host = plugin.model.entryPoint.frontend ? 'frontend' : plugin.host; - const dynamicContributions = hostContributions.get(host) || []; - dynamicContributions.push(contributions); - hostContributions.set(host, dynamicContributions); - toDisconnect.push(Disposable.create(() => { - contributions!.state = PluginContributions.State.LOADED; - console.debug(`[${this.clientId}][${pluginId}]: Disconnected.`); - })); - } - } - if (loaded > 0) { - // Only log load measurement if there are were plugins to load. - loadPluginsMeasurement?.log(`Load contributions of ${this.getPluginCount(loaded)}`); - } else { - loadPluginsMeasurement.stop(); - } - - return hostContributions; + protected handleContributions(plugin: DeployedPlugin): Disposable { + return this.contributionHandler.handleContributions(this.clientId, plugin); } - protected async startPlugins(contributionsByHost: Map, toDisconnect: DisposableCollection): Promise { - let started = 0; - const startPluginsMeasurement = this.measure('startPlugins'); - - const [hostLogPath, hostStoragePath, hostGlobalStoragePath] = await Promise.all([ - this.pluginPathsService.getHostLogPath(), - this.getStoragePath(), - this.getHostGlobalStoragePath() - ]); - - if (toDisconnect.disposed) { - return; - } - - const thenable: Promise[] = []; - const configStorage: ConfigStorage = { - hostLogPath, - hostStoragePath, - hostGlobalStoragePath - }; - - for (const [host, hostContributions] of contributionsByHost) { - // do not start plugins for electron browser - if (host === 'frontend' && environment.electron.is()) { - continue; - } - - const manager = await this.obtainManager(host, hostContributions, toDisconnect); - if (!manager) { - continue; - } - - const plugins = hostContributions.map(contributions => contributions.plugin.metadata); - thenable.push((async () => { - try { - const activationEvents = [...this.activationEvents]; - await manager.$start({ plugins, configStorage, activationEvents }); - if (toDisconnect.disposed) { - return; - } - console.log(`[${this.clientId}] Starting plugins.`); - for (const contributions of hostContributions) { - started++; - const plugin = contributions.plugin; - const id = plugin.metadata.model.id; - contributions.state = PluginContributions.State.STARTED; - console.debug(`[${this.clientId}][${id}]: Started plugin.`); - toDisconnect.push(contributions.push(Disposable.create(() => { - console.debug(`[${this.clientId}][${id}]: Stopped plugin.`); - manager.$stop(id); - }))); - - this.activateByWorkspaceContains(manager, plugin); - } - } catch (e) { - console.error(`Failed to start plugins for '${host}' host`, e); - } - })()); - } - - await Promise.all(thenable); - await this.activateByEvent('onStartupFinished'); - if (toDisconnect.disposed) { - return; - } - - if (started > 0) { - startPluginsMeasurement.log(`Start of ${this.getPluginCount(started)}`); - } else { - startPluginsMeasurement.stop(); - } + protected override handlePluginStarted(manager: PluginManagerExt, plugin: DeployedPlugin): void { + this.activateByWorkspaceContains(manager, plugin); } protected async obtainManager(host: string, hostContributions: PluginContributions[], toDisconnect: DisposableCollection): Promise { @@ -646,14 +391,6 @@ export class HostedPluginSupport { return globalStorageFolderFsPath; } - async activateByEvent(activationEvent: string): Promise { - if (this.activationEvents.has(activationEvent)) { - return; - } - this.activationEvents.add(activationEvent); - await Promise.all(Array.from(this.managers.values(), manager => manager.$activateByEvent(activationEvent))); - } - async activateByViewContainer(viewContainerId: string): Promise { await Promise.all(this.viewRegistry.getContainerViews(viewContainerId).map(viewId => this.activateByView(viewId))); } @@ -808,22 +545,6 @@ export class HostedPluginSupport { } } - async activatePlugin(id: string): Promise { - const activation = []; - for (const manager of this.managers.values()) { - activation.push(manager.$activatePlugin(id)); - } - await Promise.all(activation); - } - - protected measure(name: string): Measurement { - return this.stopwatch.start(name, { context: this.clientId }); - } - - protected getPluginCount(plugins: number): string { - return `${plugins} plugin${plugins === 1 ? '' : 's'}`; - } - protected readonly webviewsToRestore = new Map(); protected readonly webviewRevivers = new Map Promise>(); @@ -889,22 +610,3 @@ export class HostedPluginSupport { } } - -export class PluginContributions extends DisposableCollection { - constructor( - readonly plugin: DeployedPlugin - ) { - super(); - } - state: PluginContributions.State = PluginContributions.State.INITIALIZING; -} - -export namespace PluginContributions { - export enum State { - INITIALIZING = 0, - LOADING = 1, - LOADED = 2, - STARTING = 3, - STARTED = 4 - } -} diff --git a/packages/plugin-ext/src/hosted/browser/worker/debug-stub.ts b/packages/plugin-ext/src/hosted/browser/worker/debug-stub.ts index 9dd3e48b64440..f37b9b0a2f431 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/debug-stub.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/debug-stub.ts @@ -15,12 +15,13 @@ // ***************************************************************************** // eslint-disable-next-line @theia/runtime-import-check +import { interfaces } from '@theia/core/shared/inversify'; import { DebugExtImpl } from '../../../plugin/debug/debug-ext'; -import { RPCProtocol } from '../../../common/rpc-protocol'; /* eslint-disable @typescript-eslint/no-explicit-any */ -export function createDebugExtStub(rpc: RPCProtocol): DebugExtImpl { - return new Proxy(new DebugExtImpl(rpc), { +export function createDebugExtStub(container: interfaces.Container): DebugExtImpl { + const delegate = container.get(DebugExtImpl); + return new Proxy(delegate, { apply: function (target, that, args): void { console.error('Debug API works only in plugin container'); } diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts index 993827d6bd50c..bab5d3e39dfa5 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts @@ -14,17 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { injectable } from '@theia/core/shared/inversify'; import { EnvExtImpl } from '../../../plugin/env'; -import { RPCProtocol } from '../../../common/rpc-protocol'; /** * Worker specific implementation not returning any FileSystem details * Extending the common class */ +@injectable() export class WorkerEnvExtImpl extends EnvExtImpl { - constructor(rpc: RPCProtocol) { - super(rpc); + constructor() { + super(); } /** diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts index caae9776e2f18..f24f83223e6d2 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts @@ -15,13 +15,12 @@ // ***************************************************************************** // eslint-disable-next-line import/no-extraneous-dependencies import 'reflect-metadata'; -import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel'; -import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; +import { Container } from '@theia/core/shared/inversify'; import * as theia from '@theia/plugin'; -import { emptyPlugin, MAIN_RPC_CONTEXT, Plugin, TerminalServiceExt } from '../../../common/plugin-api-rpc'; +import { emptyPlugin, MAIN_RPC_CONTEXT, Plugin } from '../../../common/plugin-api-rpc'; import { ExtPluginApi } from '../../../common/plugin-ext-api-contribution'; import { getPluginId, PluginMetadata } from '../../../common/plugin-protocol'; -import { RPCProtocolImpl } from '../../../common/rpc-protocol'; +import { RPCProtocol, RPCProtocolImpl } from '../../../common/rpc-protocol'; import { ClipboardExt } from '../../../plugin/clipboard-ext'; import { EditorsAndDocumentsExtImpl } from '../../../plugin/editors-and-documents'; import { MessageRegistryExt } from '../../../plugin/message-registry'; @@ -29,14 +28,13 @@ import { createAPIFactory } from '../../../plugin/plugin-context'; import { PluginManagerExtImpl } from '../../../plugin/plugin-manager'; import { KeyValueStorageProxy } from '../../../plugin/plugin-storage'; import { PreferenceRegistryExtImpl } from '../../../plugin/preference-registry'; -import { SecretsExtImpl } from '../../../plugin/secrets-ext'; -import { TerminalServiceExtImpl } from '../../../plugin/terminal-ext'; import { WebviewsExtImpl } from '../../../plugin/webviews'; import { WorkspaceExtImpl } from '../../../plugin/workspace'; -import { createDebugExtStub } from './debug-stub'; import { loadManifest } from './plugin-manifest-loader'; -import { WorkerEnvExtImpl } from './worker-env-ext'; +import { EnvExtImpl } from '../../../plugin/env'; +import { DebugExtImpl } from '../../../plugin/debug/debug-ext'; import { LocalizationExtImpl } from '../../../plugin/localization-ext'; +import pluginHostModule from './worker-plugin-module'; // eslint-disable-next-line @typescript-eslint/no-explicit-any const ctx = self as any; @@ -44,20 +42,6 @@ const ctx = self as any; const pluginsApiImpl = new Map(); const pluginsModulesNames = new Map(); -const channel = new BasicChannel(() => { - const writeBuffer = new Uint8ArrayWriteBuffer(); - writeBuffer.onCommit(buffer => { - ctx.postMessage(buffer); - }); - return writeBuffer; -}); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -addEventListener('message', (message: any) => { - channel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(message.data)); -}); - -const rpc = new RPCProtocolImpl(channel); - const scripts = new Set(); function initialize(contextPath: string, pluginMetadata: PluginMetadata): void { @@ -68,112 +52,116 @@ function initialize(contextPath: string, pluginMetadata: PluginMetadata): void { scripts.add(path); } } -const envExt = new WorkerEnvExtImpl(rpc); -const storageProxy = new KeyValueStorageProxy(rpc); -const editorsAndDocuments = new EditorsAndDocumentsExtImpl(rpc); -const messageRegistryExt = new MessageRegistryExt(rpc); -const workspaceExt = new WorkspaceExtImpl(rpc, editorsAndDocuments, messageRegistryExt); -const preferenceRegistryExt = new PreferenceRegistryExtImpl(rpc, workspaceExt); -const debugExt = createDebugExtStub(rpc); -const clipboardExt = new ClipboardExt(rpc); -const webviewExt = new WebviewsExtImpl(rpc, workspaceExt); -const secretsExt = new SecretsExtImpl(rpc); -const localizationExt = new LocalizationExtImpl(rpc); -const terminalService: TerminalServiceExt = new TerminalServiceExtImpl(rpc); - -const pluginManager = new PluginManagerExtImpl({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - loadPlugin(plugin: Plugin): any { - if (plugin.pluginPath) { - if (isElectron()) { - ctx.importScripts(plugin.pluginPath); - } else { - if (plugin.lifecycle.frontendModuleName) { - // Set current module name being imported - ctx.frontendModuleName = plugin.lifecycle.frontendModuleName; - } - ctx.importScripts('/hostedPlugin/' + getPluginId(plugin.model) + '/' + plugin.pluginPath); - } - } +const container = new Container(); +container.load(pluginHostModule); + +const rpc: RPCProtocol = container.get(RPCProtocolImpl); +const pluginManager = container.get(PluginManagerExtImpl); +pluginManager.setPluginHost({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + loadPlugin(plugin: Plugin): any { + if (plugin.pluginPath) { + if (isElectron()) { + ctx.importScripts(plugin.pluginPath); + } else { + if (plugin.lifecycle.frontendModuleName) { + // Set current module name being imported + ctx.frontendModuleName = plugin.lifecycle.frontendModuleName; + } - if (plugin.lifecycle.frontendModuleName) { - if (!ctx[plugin.lifecycle.frontendModuleName]) { - console.error(`WebWorker: Cannot start plugin "${plugin.model.name}". Frontend plugin not found: "${plugin.lifecycle.frontendModuleName}"`); - return; + ctx.importScripts('/hostedPlugin/' + getPluginId(plugin.model) + '/' + plugin.pluginPath); + } } - return ctx[plugin.lifecycle.frontendModuleName]; - } - }, - async init(rawPluginData: PluginMetadata[]): Promise<[Plugin[], Plugin[]]> { - const result: Plugin[] = []; - const foreign: Plugin[] = []; - // Process the plugins concurrently, making sure to keep the order. - const plugins = await Promise.all<{ - /** Where to push the plugin: `result` or `foreign` */ - target: Plugin[], - plugin: Plugin - }>(rawPluginData.map(async plg => { - const pluginModel = plg.model; - const pluginLifecycle = plg.lifecycle; - if (pluginModel.entryPoint!.frontend) { - let frontendInitPath = pluginLifecycle.frontendInitPath; - if (frontendInitPath) { - initialize(frontendInitPath, plg); - } else { - frontendInitPath = ''; + + if (plugin.lifecycle.frontendModuleName) { + if (!ctx[plugin.lifecycle.frontendModuleName]) { + console.error(`WebWorker: Cannot start plugin "${plugin.model.name}". Frontend plugin not found: "${plugin.lifecycle.frontendModuleName}"`); + return; } - const rawModel = await loadManifest(pluginModel); - const plugin: Plugin = { - pluginPath: pluginModel.entryPoint.frontend!, - pluginFolder: pluginModel.packagePath, - pluginUri: pluginModel.packageUri, - model: pluginModel, - lifecycle: pluginLifecycle, - rawModel, - isUnderDevelopment: !!plg.isUnderDevelopment - }; - const apiImpl = apiFactory(plugin); - pluginsApiImpl.set(plugin.model.id, apiImpl); - pluginsModulesNames.set(plugin.lifecycle.frontendModuleName!, plugin); - return { target: result, plugin }; - } else { - return { - target: foreign, - plugin: { - pluginPath: pluginModel.entryPoint.backend, + return ctx[plugin.lifecycle.frontendModuleName]; + } + }, + async init(rawPluginData: PluginMetadata[]): Promise<[Plugin[], Plugin[]]> { + const result: Plugin[] = []; + const foreign: Plugin[] = []; + // Process the plugins concurrently, making sure to keep the order. + const plugins = await Promise.all<{ + /** Where to push the plugin: `result` or `foreign` */ + target: Plugin[], + plugin: Plugin + }>(rawPluginData.map(async plg => { + const pluginModel = plg.model; + const pluginLifecycle = plg.lifecycle; + if (pluginModel.entryPoint!.frontend) { + let frontendInitPath = pluginLifecycle.frontendInitPath; + if (frontendInitPath) { + initialize(frontendInitPath, plg); + } else { + frontendInitPath = ''; + } + const rawModel = await loadManifest(pluginModel); + const plugin: Plugin = { + pluginPath: pluginModel.entryPoint.frontend!, pluginFolder: pluginModel.packagePath, pluginUri: pluginModel.packageUri, model: pluginModel, lifecycle: pluginLifecycle, - get rawModel(): never { - throw new Error('not supported'); - }, + rawModel, isUnderDevelopment: !!plg.isUnderDevelopment - } - }; - } - })); - // Collect the ordered plugins and insert them in the target array: - for (const { target, plugin } of plugins) { - target.push(plugin); - } - return [result, foreign]; - }, - initExtApi(extApi: ExtPluginApi[]): void { - for (const api of extApi) { - try { - if (api.frontendExtApi) { - ctx.importScripts(api.frontendExtApi.initPath); - ctx[api.frontendExtApi.initVariable][api.frontendExtApi.initFunction](rpc, pluginsModulesNames); + }; + const apiImpl = apiFactory(plugin); + pluginsApiImpl.set(plugin.model.id, apiImpl); + pluginsModulesNames.set(plugin.lifecycle.frontendModuleName!, plugin); + return { target: result, plugin }; + } else { + return { + target: foreign, + plugin: { + pluginPath: pluginModel.entryPoint.backend, + pluginFolder: pluginModel.packagePath, + pluginUri: pluginModel.packageUri, + model: pluginModel, + lifecycle: pluginLifecycle, + get rawModel(): never { + throw new Error('not supported'); + }, + isUnderDevelopment: !!plg.isUnderDevelopment + } + }; } + })); + // Collect the ordered plugins and insert them in the target array: + for (const { target, plugin } of plugins) { + target.push(plugin); + } + return [result, foreign]; + }, + initExtApi(extApi: ExtPluginApi[]): void { + for (const api of extApi) { + try { + if (api.frontendExtApi) { + ctx.importScripts(api.frontendExtApi.initPath); + ctx[api.frontendExtApi.initVariable][api.frontendExtApi.initFunction](rpc, pluginsModulesNames); + } - } catch (e) { - console.error(e); + } catch (e) { + console.error(e); + } } } - } -}, envExt, terminalService, storageProxy, secretsExt, preferenceRegistryExt, webviewExt, localizationExt, rpc); + }); + +const envExt = container.get(EnvExtImpl); +const debugExt = container.get(DebugExtImpl); +const preferenceRegistryExt = container.get(PreferenceRegistryExtImpl); +const editorsAndDocuments = container.get(EditorsAndDocumentsExtImpl); +const workspaceExt = container.get(WorkspaceExtImpl); +const messageRegistryExt = container.get(MessageRegistryExt); +const clipboardExt = container.get(ClipboardExt); +const webviewExt = container.get(WebviewsExtImpl); +const localizationExt = container.get(LocalizationExtImpl); +const storageProxy = container.get(KeyValueStorageProxy); const apiFactory = createAPIFactory( rpc, diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts new file mode 100644 index 0000000000000..ebace3685d069 --- /dev/null +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts @@ -0,0 +1,73 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +// eslint-disable-next-line import/no-extraneous-dependencies +import 'reflect-metadata'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel'; +import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; +import { LocalizationExt } from '../../../common/plugin-api-rpc'; +import { RPCProtocol, RPCProtocolImpl } from '../../../common/rpc-protocol'; +import { ClipboardExt } from '../../../plugin/clipboard-ext'; +import { EditorsAndDocumentsExtImpl } from '../../../plugin/editors-and-documents'; +import { MessageRegistryExt } from '../../../plugin/message-registry'; +import { PluginManagerExtImpl } from '../../../plugin/plugin-manager'; +import { KeyValueStorageProxy } from '../../../plugin/plugin-storage'; +import { PreferenceRegistryExtImpl } from '../../../plugin/preference-registry'; +import { SecretsExtImpl } from '../../../plugin/secrets-ext'; +import { TerminalServiceExtImpl } from '../../../plugin/terminal-ext'; +import { WebviewsExtImpl } from '../../../plugin/webviews'; +import { WorkspaceExtImpl } from '../../../plugin/workspace'; +import { createDebugExtStub } from './debug-stub'; +import { EnvExtImpl } from '../../../plugin/env'; +import { WorkerEnvExtImpl } from './worker-env-ext'; +import { DebugExtImpl } from '../../../plugin/debug/debug-ext'; +import { LocalizationExtImpl } from '../../../plugin/localization-ext'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const ctx = self as any; + +export default new ContainerModule(bind => { + const channel = new BasicChannel(() => { + const writeBuffer = new Uint8ArrayWriteBuffer(); + writeBuffer.onCommit(buffer => { + ctx.postMessage(buffer); + }); + return writeBuffer; + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + addEventListener('message', (message: any) => { + channel.onMessageEmitter.fire(() => new Uint8ArrayReadBuffer(message.data)); + }); + + const rpc = new RPCProtocolImpl(channel); + + bind(RPCProtocol).toConstantValue(rpc); + + bind(PluginManagerExtImpl).toSelf().inSingletonScope(); + bind(EnvExtImpl).to(WorkerEnvExtImpl).inSingletonScope(); + bind(LocalizationExt).to(LocalizationExtImpl).inSingletonScope(); + bind(KeyValueStorageProxy).toSelf().inSingletonScope(); + bind(SecretsExtImpl).toSelf().inSingletonScope(); + bind(PreferenceRegistryExtImpl).toSelf().inSingletonScope(); + bind(DebugExtImpl).toDynamicValue(({ container }) => createDebugExtStub(container)) + .inSingletonScope(); + bind(EditorsAndDocumentsExtImpl).toSelf().inSingletonScope(); + bind(WorkspaceExtImpl).toSelf().inSingletonScope(); + bind(MessageRegistryExt).toSelf().inSingletonScope(); + bind(ClipboardExt).toSelf().inSingletonScope(); + bind(WebviewsExtImpl).toSelf().inSingletonScope(); + bind(TerminalServiceExtImpl).toSelf().inSingletonScope(); +}); diff --git a/packages/plugin-ext/src/hosted/common/hosted-plugin.ts b/packages/plugin-ext/src/hosted/common/hosted-plugin.ts new file mode 100644 index 0000000000000..422bd76a4fe1a --- /dev/null +++ b/packages/plugin-ext/src/hosted/common/hosted-plugin.ts @@ -0,0 +1,456 @@ +// ***************************************************************************** +// Copyright (C) 2018 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// some code copied and modified from https://github.com/microsoft/vscode/blob/da5fb7d5b865aa522abc7e82c10b746834b98639/src/vs/workbench/api/node/extHostExtensionService.ts + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import debounce = require('@theia/core/shared/lodash.debounce'); +import { injectable, inject, interfaces, named, postConstruct } from '@theia/core/shared/inversify'; +import { PluginMetadata, HostedPluginServer, DeployedPlugin, PluginServer, PluginIdentifiers } from '../../common/plugin-protocol'; +import { AbstractPluginManagerExt, ConfigStorage } from '../../common/plugin-api-rpc'; +import { + Disposable, DisposableCollection, Emitter, + ILogger, ContributionProvider, + RpcProxy +} from '@theia/core'; +import { MainPluginApiProvider } from '../../common/plugin-ext-api-contribution'; +import { PluginPathsService } from '../../main/common/plugin-paths-protocol'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; +import { Measurement, Stopwatch } from '@theia/core/lib/common'; + +export type PluginHost = 'frontend' | string; + +export const ALL_ACTIVATION_EVENT = '*'; + +export function isConnectionScopedBackendPlugin(plugin: DeployedPlugin): boolean { + const entryPoint = plugin.metadata.model.entryPoint; + + // A plugin doesn't have to have any entry-point if it doesn't need the activation handler, + // in which case it's assumed to be a backend plugin. + return !entryPoint.headless || !!entryPoint.backend; +} + +@injectable() +export abstract class AbstractHostedPluginSupport, HPS extends HostedPluginServer | RpcProxy> { + + protected container: interfaces.Container; + + @inject(ILogger) + protected readonly logger: ILogger; + + @inject(HostedPluginServer) + protected readonly server: HPS; + + @inject(ContributionProvider) + @named(MainPluginApiProvider) + protected readonly mainPluginApiProviders: ContributionProvider; + + @inject(PluginServer) + protected readonly pluginServer: PluginServer; + + @inject(PluginPathsService) + protected readonly pluginPathsService: PluginPathsService; + + @inject(EnvVariablesServer) + protected readonly envServer: EnvVariablesServer; + + @inject(Stopwatch) + protected readonly stopwatch: Stopwatch; + + protected theiaReadyPromise: Promise; + + protected readonly managers = new Map(); + + protected readonly contributions = new Map(); + + protected readonly activationEvents = new Set(); + + protected readonly onDidChangePluginsEmitter = new Emitter(); + readonly onDidChangePlugins = this.onDidChangePluginsEmitter.event; + + protected readonly deferredWillStart = new Deferred(); + /** + * Resolves when the initial plugins are loaded and about to be started. + */ + get willStart(): Promise { + return this.deferredWillStart.promise; + } + + protected readonly deferredDidStart = new Deferred(); + /** + * Resolves when the initial plugins are started. + */ + get didStart(): Promise { + return this.deferredDidStart.promise; + } + + constructor(protected readonly clientId: string) { } + + @postConstruct() + protected init(): void { + this.theiaReadyPromise = this.createTheiaReadyPromise(); + } + + protected abstract createTheiaReadyPromise(): Promise; + + get plugins(): PluginMetadata[] { + const plugins: PluginMetadata[] = []; + this.contributions.forEach(contributions => plugins.push(contributions.plugin.metadata)); + return plugins; + } + + getPlugin(id: PluginIdentifiers.UnversionedId): DeployedPlugin | undefined { + const contributions = this.contributions.get(id); + return contributions && contributions.plugin; + } + + /** do not call it, except from the plugin frontend contribution */ + onStart(container: interfaces.Container): void { + this.container = container; + this.load(); + this.afterStart(); + } + + protected afterStart(): void { + // Nothing to do in the abstract + } + + protected loadQueue: Promise = Promise.resolve(undefined); + load = debounce(() => this.loadQueue = this.loadQueue.then(async () => { + try { + await this.runOperation(() => this.doLoad()); + } catch (e) { + console.error('Failed to load plugins:', e); + } + }), 50, { leading: true }); + + protected runOperation(operation: () => Promise): Promise { + return operation(); + } + + protected async doLoad(): Promise { + const toDisconnect = new DisposableCollection(Disposable.create(() => { /* mark as connected */ })); + + await this.beforeSyncPlugins(toDisconnect); + + // process empty plugins as well in order to properly remove stale plugin widgets + await this.syncPlugins(); + + // it has to be resolved before awaiting layout is initialized + // otherwise clients can hang forever in the initialization phase + this.deferredWillStart.resolve(); + + await this.beforeLoadContributions(toDisconnect); + + if (toDisconnect.disposed) { + // if disconnected then don't try to load plugin contributions + return; + } + const contributionsByHost = this.loadContributions(toDisconnect); + + await this.afterLoadContributions(toDisconnect); + + await this.theiaReadyPromise; + if (toDisconnect.disposed) { + // if disconnected then don't try to init plugin code and dynamic contributions + return; + } + await this.startPlugins(contributionsByHost, toDisconnect); + + this.deferredDidStart.resolve(); + } + + protected beforeSyncPlugins(toDisconnect: DisposableCollection): Promise { + // Nothing to do in the abstract + return Promise.resolve(); + } + + protected beforeLoadContributions(toDisconnect: DisposableCollection): Promise { + // Nothing to do in the abstract + return Promise.resolve(); + } + + protected afterLoadContributions(toDisconnect: DisposableCollection): Promise { + // Nothing to do in the abstract + return Promise.resolve(); + } + + /** + * Sync loaded and deployed plugins: + * - undeployed plugins are unloaded + * - newly deployed plugins are initialized + */ + protected async syncPlugins(): Promise { + let initialized = 0; + const waitPluginsMeasurement = this.measure('waitForDeployment'); + let syncPluginsMeasurement: Measurement | undefined; + + const toUnload = new Set(this.contributions.keys()); + let didChangeInstallationStatus = false; + try { + const newPluginIds: PluginIdentifiers.VersionedId[] = []; + const [deployedPluginIds, uninstalledPluginIds] = await Promise.all([this.server.getDeployedPluginIds(), this.server.getUninstalledPluginIds()]); + waitPluginsMeasurement.log('Waiting for backend deployment'); + syncPluginsMeasurement = this.measure('syncPlugins'); + for (const versionedId of deployedPluginIds) { + const unversionedId = PluginIdentifiers.unversionedFromVersioned(versionedId); + toUnload.delete(unversionedId); + if (!this.contributions.has(unversionedId)) { + newPluginIds.push(versionedId); + } + } + for (const pluginId of toUnload) { + this.contributions.get(pluginId)?.dispose(); + } + for (const versionedId of uninstalledPluginIds) { + const plugin = this.getPlugin(PluginIdentifiers.unversionedFromVersioned(versionedId)); + if (plugin && PluginIdentifiers.componentsToVersionedId(plugin.metadata.model) === versionedId && !plugin.metadata.outOfSync) { + plugin.metadata.outOfSync = didChangeInstallationStatus = true; + } + } + for (const contribution of this.contributions.values()) { + if (contribution.plugin.metadata.outOfSync && !uninstalledPluginIds.includes(PluginIdentifiers.componentsToVersionedId(contribution.plugin.metadata.model))) { + contribution.plugin.metadata.outOfSync = false; + didChangeInstallationStatus = true; + } + } + if (newPluginIds.length) { + const deployedPlugins = await this.server.getDeployedPlugins({ pluginIds: newPluginIds }); + + const plugins: DeployedPlugin[] = []; + for (const plugin of deployedPlugins) { + const accepted = this.acceptPlugin(plugin); + if (typeof accepted === 'object') { + plugins.push(accepted); + } else if (accepted) { + plugins.push(plugin); + } + } + + for (const plugin of plugins) { + const pluginId = PluginIdentifiers.componentsToUnversionedId(plugin.metadata.model); + const contributions = new PluginContributions(plugin); + this.contributions.set(pluginId, contributions); + contributions.push(Disposable.create(() => this.contributions.delete(pluginId))); + initialized++; + } + } + } finally { + if (initialized || toUnload.size || didChangeInstallationStatus) { + this.onDidChangePluginsEmitter.fire(undefined); + } + + if (!syncPluginsMeasurement) { + // await didn't complete normally + waitPluginsMeasurement.error('Backend deployment failed.'); + } + } + if (initialized > 0) { + // Only log sync measurement if there are were plugins to sync. + syncPluginsMeasurement?.log(`Sync of ${this.getPluginCount(initialized)}`); + } else { + syncPluginsMeasurement?.stop(); + } + } + + /** + * Accept a deployed plugin to load in this host, or reject it, or adapt it for loading. + * The result may be a boolean to accept (`true`) or reject (`false`) the plugin as is, + * or else an adaptation of the original `plugin` to load in its stead. + */ + protected abstract acceptPlugin(plugin: DeployedPlugin): boolean | DeployedPlugin; + + /** + * Always synchronous in order to simplify handling disconnections. + * @throws never + */ + protected loadContributions(toDisconnect: DisposableCollection): Map { + let loaded = 0; + const loadPluginsMeasurement = this.measure('loadPlugins'); + + const hostContributions = new Map(); + console.log(`[${this.clientId}] Loading plugin contributions`); + for (const contributions of this.contributions.values()) { + const plugin = contributions.plugin.metadata; + const pluginId = plugin.model.id; + + if (contributions.state === PluginContributions.State.INITIALIZING) { + contributions.state = PluginContributions.State.LOADING; + contributions.push(Disposable.create(() => console.log(`[${pluginId}]: Unloaded plugin.`))); + contributions.push(this.handleContributions(contributions.plugin)); + contributions.state = PluginContributions.State.LOADED; + console.debug(`[${this.clientId}][${pluginId}]: Loaded contributions.`); + loaded++; + } + + if (contributions.state === PluginContributions.State.LOADED) { + contributions.state = PluginContributions.State.STARTING; + const host = plugin.model.entryPoint.frontend ? 'frontend' : plugin.host; + const dynamicContributions = hostContributions.get(host) || []; + dynamicContributions.push(contributions); + hostContributions.set(host, dynamicContributions); + toDisconnect.push(Disposable.create(() => { + contributions!.state = PluginContributions.State.LOADED; + console.debug(`[${this.clientId}][${pluginId}]: Disconnected.`); + })); + } + } + if (loaded > 0) { + // Only log load measurement if there are were plugins to load. + loadPluginsMeasurement?.log(`Load contributions of ${this.getPluginCount(loaded)}`); + } else { + loadPluginsMeasurement.stop(); + } + + return hostContributions; + } + + protected abstract handleContributions(plugin: DeployedPlugin): Disposable; + + protected async startPlugins(contributionsByHost: Map, toDisconnect: DisposableCollection): Promise { + let started = 0; + const startPluginsMeasurement = this.measure('startPlugins'); + + const [hostLogPath, hostStoragePath, hostGlobalStoragePath] = await Promise.all([ + this.pluginPathsService.getHostLogPath(), + this.getStoragePath(), + this.getHostGlobalStoragePath() + ]); + + if (toDisconnect.disposed) { + return; + } + + const thenable: Promise[] = []; + const configStorage: ConfigStorage = { + hostLogPath, + hostStoragePath, + hostGlobalStoragePath + }; + + for (const [host, hostContributions] of contributionsByHost) { + // do not start plugins for electron browser + if (host === 'frontend' && environment.electron.is()) { + continue; + } + + const manager = await this.obtainManager(host, hostContributions, toDisconnect); + if (!manager) { + continue; + } + + const plugins = hostContributions.map(contributions => contributions.plugin.metadata); + thenable.push((async () => { + try { + const activationEvents = [...this.activationEvents]; + await manager.$start({ plugins, configStorage, activationEvents }); + if (toDisconnect.disposed) { + return; + } + console.log(`[${this.clientId}] Starting plugins.`); + for (const contributions of hostContributions) { + started++; + const plugin = contributions.plugin; + const id = plugin.metadata.model.id; + contributions.state = PluginContributions.State.STARTED; + console.debug(`[${this.clientId}][${id}]: Started plugin.`); + toDisconnect.push(contributions.push(Disposable.create(() => { + console.debug(`[${this.clientId}][${id}]: Stopped plugin.`); + manager.$stop(id); + }))); + + this.handlePluginStarted(manager, plugin); + } + } catch (e) { + console.error(`Failed to start plugins for '${host}' host`, e); + } + })()); + } + + await Promise.all(thenable); + await this.activateByEvent('onStartupFinished'); + if (toDisconnect.disposed) { + return; + } + + if (started > 0) { + startPluginsMeasurement.log(`Start of ${this.getPluginCount(started)}`); + } else { + startPluginsMeasurement.stop(); + } + } + + protected abstract obtainManager(host: string, hostContributions: PluginContributions[], + toDisconnect: DisposableCollection): Promise; + + protected abstract getStoragePath(): Promise; + + protected abstract getHostGlobalStoragePath(): Promise; + + async activateByEvent(activationEvent: string): Promise { + if (this.activationEvents.has(activationEvent)) { + return; + } + this.activationEvents.add(activationEvent); + await Promise.all(Array.from(this.managers.values(), manager => manager.$activateByEvent(activationEvent))); + } + + async activatePlugin(id: string): Promise { + const activation = []; + for (const manager of this.managers.values()) { + activation.push(manager.$activatePlugin(id)); + } + await Promise.all(activation); + } + + protected handlePluginStarted(manager: PM, plugin: DeployedPlugin): void { + // Nothing to do in the abstract + } + + protected measure(name: string): Measurement { + return this.stopwatch.start(name, { context: this.clientId }); + } + + protected getPluginCount(plugins: number): string { + return `${plugins} plugin${plugins === 1 ? '' : 's'}`; + } + +} + +export class PluginContributions extends DisposableCollection { + constructor( + readonly plugin: DeployedPlugin + ) { + super(); + } + state: PluginContributions.State = PluginContributions.State.INITIALIZING; +} + +export namespace PluginContributions { + export enum State { + INITIALIZING = 0, + LOADING = 1, + LOADED = 2, + STARTING = 3, + STARTED = 4 + } +} diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts index b3a3f660fbfcc..aaf11b2dd7315 100644 --- a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts +++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts @@ -16,16 +16,16 @@ import { ConnectionErrorHandler, ContributionProvider, ILogger, MessageService } from '@theia/core/lib/common'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { BinaryMessagePipe } from '@theia/core/lib/node/messaging/binary-message-pipe'; import { createIpcEnv } from '@theia/core/lib/node/messaging/ipc-protocol'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import * as cp from 'child_process'; +import { Duplex } from 'stream'; +import { DeployedPlugin, HostedPluginClient, PLUGIN_HOST_BACKEND, PluginHostEnvironmentVariable, PluginIdentifiers, ServerPluginRunner } from '../../common/plugin-protocol'; import { HostedPluginCliContribution } from './hosted-plugin-cli-contribution'; import { HostedPluginLocalizationService } from './hosted-plugin-localization-service'; -import { ProcessTerminatedMessage, ProcessTerminateMessage } from './hosted-plugin-protocol'; -import { BinaryMessagePipe } from '@theia/core/lib/node/messaging/binary-message-pipe'; -import { DeployedPlugin, HostedPluginClient, PluginHostEnvironmentVariable, PluginIdentifiers, PLUGIN_HOST_BACKEND, ServerPluginRunner } from '../../common/plugin-protocol'; +import { ProcessTerminateMessage, ProcessTerminatedMessage } from './hosted-plugin-protocol'; import psTree = require('ps-tree'); -import { Duplex } from 'stream'; export interface IPCConnectionOptions { readonly serverName: string; diff --git a/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts b/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts index 78ce8fe2175e5..ae10070f58ea1 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-ext-hosted-backend-module.ts @@ -21,7 +21,7 @@ import { CliContribution } from '@theia/core/lib/node/cli'; import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module'; import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application'; import { MetadataScanner } from './metadata-scanner'; -import { HostedPluginServerImpl } from './plugin-service'; +import { BackendPluginHostableFilter, HostedPluginServerImpl } from './plugin-service'; import { HostedPluginReader } from './plugin-reader'; import { HostedPluginSupport } from './hosted-plugin'; import { TheiaPluginScanner } from './scanners/scanner-theia'; @@ -38,6 +38,7 @@ import { LanguagePackService, languagePackServicePath } from '../../common/langu import { PluginLanguagePackService } from './plugin-language-pack-service'; import { RpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory'; import { ConnectionHandler } from '@theia/core/lib/common/messaging/handler'; +import { isConnectionScopedBackendPlugin } from '../common/hosted-plugin'; const commonHostedConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(HostedPluginProcess).toSelf().inSingletonScope(); @@ -48,6 +49,7 @@ const commonHostedConnectionModule = ConnectionContainerModule.create(({ bind, b bind(HostedPluginServerImpl).toSelf().inSingletonScope(); bind(HostedPluginServer).toService(HostedPluginServerImpl); + bind(BackendPluginHostableFilter).toConstantValue(isConnectionScopedBackendPlugin); bindBackendService(hostedServicePath, HostedPluginServer, (server, client) => { server.setClient(client); client.onDidCloseConnection(() => server.dispose()); diff --git a/packages/plugin-ext/src/hosted/node/plugin-host-module.ts b/packages/plugin-ext/src/hosted/node/plugin-host-module.ts new file mode 100644 index 0000000000000..94d22e0ddc474 --- /dev/null +++ b/packages/plugin-ext/src/hosted/node/plugin-host-module.ts @@ -0,0 +1,69 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import '@theia/core/shared/reflect-metadata'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { RPCProtocol, RPCProtocolImpl } from '../../common/rpc-protocol'; +import { AbstractPluginHostRPC, PluginHostRPC, PluginContainerModuleLoader } from './plugin-host-rpc'; +import { AbstractPluginManagerExtImpl, MinimalTerminalServiceExt, PluginManagerExtImpl } from '../../plugin/plugin-manager'; +import { IPCChannel } from '@theia/core/lib/node'; +import { InternalPluginContainerModule } from '../../plugin/node/plugin-container-module'; +import { LocalizationExt } from '../../common/plugin-api-rpc'; +import { EnvExtImpl } from '../../plugin/env'; +import { EnvNodeExtImpl } from '../../plugin/node/env-node-ext'; +import { LocalizationExtImpl } from '../../plugin/localization-ext'; +import { PreferenceRegistryExtImpl } from '../../plugin/preference-registry'; +import { DebugExtImpl } from '../../plugin/debug/debug-ext'; +import { EditorsAndDocumentsExtImpl } from '../../plugin/editors-and-documents'; +import { WorkspaceExtImpl } from '../../plugin/workspace'; +import { MessageRegistryExt } from '../../plugin/message-registry'; +import { ClipboardExt } from '../../plugin/clipboard-ext'; +import { KeyValueStorageProxy, InternalStorageExt } from '../../plugin/plugin-storage'; +import { WebviewsExtImpl } from '../../plugin/webviews'; +import { TerminalServiceExtImpl } from '../../plugin/terminal-ext'; +import { InternalSecretsExt, SecretsExtImpl } from '../../plugin/secrets-ext'; + +export default new ContainerModule(bind => { + const channel = new IPCChannel(); + bind(RPCProtocol).toConstantValue(new RPCProtocolImpl(channel)); + + bind(PluginContainerModuleLoader).toDynamicValue(({ container }) => + (module: ContainerModule) => { + container.load(module); + const internalModule = module as InternalPluginContainerModule; + const pluginApiCache = internalModule.initializeApi?.(container); + return pluginApiCache; + }).inSingletonScope(); + + bind(AbstractPluginHostRPC).toService(PluginHostRPC); + bind(AbstractPluginManagerExtImpl).toService(PluginManagerExtImpl); + bind(PluginManagerExtImpl).toSelf().inSingletonScope(); + bind(PluginHostRPC).toSelf().inSingletonScope(); + bind(EnvExtImpl).to(EnvNodeExtImpl).inSingletonScope(); + bind(LocalizationExt).to(LocalizationExtImpl).inSingletonScope(); + bind(InternalStorageExt).toService(KeyValueStorageProxy); + bind(KeyValueStorageProxy).toSelf().inSingletonScope(); + bind(InternalSecretsExt).toService(SecretsExtImpl); + bind(SecretsExtImpl).toSelf().inSingletonScope(); + bind(PreferenceRegistryExtImpl).toSelf().inSingletonScope(); + bind(DebugExtImpl).toSelf().inSingletonScope(); + bind(EditorsAndDocumentsExtImpl).toSelf().inSingletonScope(); + bind(WorkspaceExtImpl).toSelf().inSingletonScope(); + bind(MessageRegistryExt).toSelf().inSingletonScope(); + bind(ClipboardExt).toSelf().inSingletonScope(); + bind(WebviewsExtImpl).toSelf().inSingletonScope(); + bind(MinimalTerminalServiceExt).toService(TerminalServiceExtImpl); + bind(TerminalServiceExtImpl).toSelf().inSingletonScope(); +}); diff --git a/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts b/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts index eefeddaf5e7d0..05c6764a5c3bb 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-host-rpc.ts @@ -14,10 +14,15 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { dynamicRequire, removeFromCache } from '@theia/core/lib/node/dynamic-require'; -import { PluginManagerExtImpl } from '../../plugin/plugin-manager'; -import { LocalizationExt, MAIN_RPC_CONTEXT, Plugin, PluginAPIFactory } from '../../common/plugin-api-rpc'; -import { PluginMetadata } from '../../common/plugin-protocol'; +import { ContainerModule, inject, injectable, postConstruct, unmanaged } from '@theia/core/shared/inversify'; +import { AbstractPluginManagerExtImpl, PluginHost, PluginManagerExtImpl } from '../../plugin/plugin-manager'; +import { MAIN_RPC_CONTEXT, Plugin, PluginAPIFactory, PluginManager, + LocalizationExt +} from '../../common/plugin-api-rpc'; +import { PluginMetadata, PluginModel } from '../../common/plugin-protocol'; import { createAPIFactory } from '../../plugin/plugin-context'; import { EnvExtImpl } from '../../plugin/env'; import { PreferenceRegistryExtImpl } from '../../plugin/preference-registry'; @@ -26,95 +31,139 @@ import { DebugExtImpl } from '../../plugin/debug/debug-ext'; import { EditorsAndDocumentsExtImpl } from '../../plugin/editors-and-documents'; import { WorkspaceExtImpl } from '../../plugin/workspace'; import { MessageRegistryExt } from '../../plugin/message-registry'; -import { EnvNodeExtImpl } from '../../plugin/node/env-node-ext'; import { ClipboardExt } from '../../plugin/clipboard-ext'; import { loadManifest } from './plugin-manifest-loader'; import { KeyValueStorageProxy } from '../../plugin/plugin-storage'; import { WebviewsExtImpl } from '../../plugin/webviews'; import { TerminalServiceExtImpl } from '../../plugin/terminal-ext'; import { SecretsExtImpl } from '../../plugin/secrets-ext'; -import { BackendInitializationFn } from '../../common'; import { connectProxyResolver } from './plugin-host-proxy'; import { LocalizationExtImpl } from '../../plugin/localization-ext'; +import { RPCProtocol, ProxyIdentifier } from '../../common/rpc-protocol'; +import { PluginApiCache } from '../../plugin/node/plugin-container-module'; + +/** + * The full set of all possible `Ext` interfaces that a plugin manager can support. + */ +export interface ExtInterfaces { + envExt: EnvExtImpl, + storageExt: KeyValueStorageProxy, + debugExt: DebugExtImpl, + editorsAndDocumentsExt: EditorsAndDocumentsExtImpl, + messageRegistryExt: MessageRegistryExt, + workspaceExt: WorkspaceExtImpl, + preferenceRegistryExt: PreferenceRegistryExtImpl, + clipboardExt: ClipboardExt, + webviewExt: WebviewsExtImpl, + terminalServiceExt: TerminalServiceExtImpl, + secretsExt: SecretsExtImpl, + localizationExt: LocalizationExtImpl +} + +/** + * The RPC proxy identifier keys to set in the RPC object to register our `Ext` interface implementations. + */ +export type RpcKeys> = Partial>> & { + $pluginManager: ProxyIdentifier; +}; + +export const PluginContainerModuleLoader = Symbol('PluginContainerModuleLoader'); +/** + * A function that loads a `PluginContainerModule` exported by a plugin's entry-point + * script, returning the per-`Container` cache of its exported API instances if the + * module has an API factory registered. + */ +export type PluginContainerModuleLoader = (module: ContainerModule) => PluginApiCache | undefined; /** * Handle the RPC calls. + * + * @template PM is the plugin manager (ext) type + * @template PAF is the plugin API factory type + * @template EXT is the type identifying the `Ext` interfaces supported by the plugin manager */ -export class PluginHostRPC { +@injectable() +export abstract class AbstractPluginHostRPC, PAF, EXT extends Partial> { + + @inject(RPCProtocol) + protected readonly rpc: any; - private apiFactory: PluginAPIFactory; + @inject(PluginContainerModuleLoader) + protected readonly loadContainerModule: PluginContainerModuleLoader; - private pluginManager: PluginManagerExtImpl; + @inject(AbstractPluginManagerExtImpl) + protected readonly pluginManager: PM; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - constructor(protected readonly rpc: any) { + protected readonly banner: string; + + protected apiFactory: PAF; + + constructor( + @unmanaged() name: string, + @unmanaged() private readonly backendInitPath: string | undefined, + @unmanaged() private readonly extRpc: RpcKeys) { + this.banner = `${name}(${process.pid}):`; } + @postConstruct() initialize(): void { - const envExt = new EnvNodeExtImpl(this.rpc); - const storageProxy = new KeyValueStorageProxy(this.rpc); - const debugExt = new DebugExtImpl(this.rpc); - const editorsAndDocumentsExt = new EditorsAndDocumentsExtImpl(this.rpc); - const messageRegistryExt = new MessageRegistryExt(this.rpc); - const workspaceExt = new WorkspaceExtImpl(this.rpc, editorsAndDocumentsExt, messageRegistryExt); - const preferenceRegistryExt = new PreferenceRegistryExtImpl(this.rpc, workspaceExt); - const clipboardExt = new ClipboardExt(this.rpc); - const webviewExt = new WebviewsExtImpl(this.rpc, workspaceExt); - const terminalService = new TerminalServiceExtImpl(this.rpc); - const secretsExt = new SecretsExtImpl(this.rpc); - const localizationExt = new LocalizationExtImpl(this.rpc); - this.pluginManager = this.createPluginManager(envExt, terminalService, storageProxy, preferenceRegistryExt, webviewExt, secretsExt, localizationExt, this.rpc); - this.rpc.set(MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, this.pluginManager); - this.rpc.set(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT, editorsAndDocumentsExt); - this.rpc.set(MAIN_RPC_CONTEXT.WORKSPACE_EXT, workspaceExt); - this.rpc.set(MAIN_RPC_CONTEXT.PREFERENCE_REGISTRY_EXT, preferenceRegistryExt); - this.rpc.set(MAIN_RPC_CONTEXT.STORAGE_EXT, storageProxy); - this.rpc.set(MAIN_RPC_CONTEXT.WEBVIEWS_EXT, webviewExt); - this.rpc.set(MAIN_RPC_CONTEXT.SECRETS_EXT, secretsExt); - - this.apiFactory = createAPIFactory( - this.rpc, - this.pluginManager, - envExt, - debugExt, - preferenceRegistryExt, - editorsAndDocumentsExt, - workspaceExt, - messageRegistryExt, - clipboardExt, - webviewExt, - localizationExt - ); - connectProxyResolver(workspaceExt, preferenceRegistryExt); + this.pluginManager.setPluginHost(this.createPluginHost()); + + const extInterfaces = this.createExtInterfaces(); + this.registerExtInterfaces(extInterfaces); + + this.apiFactory = this.createAPIFactory(extInterfaces); + + this.loadContainerModule(new ContainerModule(bind => bind(PluginManager).toConstantValue(this.pluginManager))); } async terminate(): Promise { await this.pluginManager.terminate(); } + protected abstract createAPIFactory(extInterfaces: EXT): PAF; + + protected abstract createExtInterfaces(): EXT; + + protected registerExtInterfaces(extInterfaces: EXT): void { + for (const _key in this.extRpc) { + if (Object.hasOwnProperty.call(this.extRpc, _key)) { + const key = _key as keyof ExtInterfaces; + // In case of present undefineds + if (extInterfaces[key]) { + this.rpc.set(this.extRpc[key], extInterfaces[key]); + } + } + } + this.rpc.set(this.extRpc.$pluginManager, this.pluginManager); + } + initContext(contextPath: string, plugin: Plugin): void { const { name, version } = plugin.rawModel; - console.debug('PLUGIN_HOST(' + process.pid + '): initializing(' + name + '@' + version + ' with ' + contextPath + ')'); + console.debug(this.banner, 'initializing(' + name + '@' + version + ' with ' + contextPath + ')'); try { - const backendInit = dynamicRequire<{ doInitialization: BackendInitializationFn }>(contextPath); + type BackendInitFn = (pluginApiFactory: PAF, plugin: Plugin) => void; + const backendInit = dynamicRequire<{ doInitialization: BackendInitFn }>(contextPath); backendInit.doInitialization(this.apiFactory, plugin); } catch (e) { console.error(e); } } - createPluginManager( - envExt: EnvExtImpl, terminalService: TerminalServiceExtImpl, storageProxy: KeyValueStorageProxy, - preferencesManager: PreferenceRegistryExtImpl, webview: WebviewsExtImpl, secretsExt: SecretsExtImpl, localization: LocalizationExt, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - rpc: any - ): PluginManagerExtImpl { + protected getBackendPluginPath(pluginModel: PluginModel): string | undefined { + return pluginModel.entryPoint.backend; + } + + /** + * Create the {@link PluginHost} that is required by my plugin manager ext interface to delegate + * critical behaviour such as loading and initializing plugins to me. + */ + createPluginHost(): PluginHost { const { extensionTestsPath } = process.env; const self = this; - const pluginManager = new PluginManagerExtImpl({ - // eslint-disable-next-line @typescript-eslint/no-explicit-any + return { loadPlugin(plugin: Plugin): any { - console.debug('PLUGIN_HOST(' + process.pid + '): PluginManagerExtImpl/loadPlugin(' + plugin.pluginPath + ')'); + console.debug(self.banner, 'PluginManagerExtImpl/loadPlugin(' + plugin.pluginPath + ')'); // cleaning the cache for all files of that plug-in. // this prevents a memory leak on plugin host restart. See for reference: // https://github.com/eclipse-theia/theia/pull/4931 @@ -125,7 +174,7 @@ export class PluginHostRPC { } }, async init(raw: PluginMetadata[]): Promise<[Plugin[], Plugin[]]> { - console.log('PLUGIN_HOST(' + process.pid + '): PluginManagerExtImpl/init()'); + console.log(self.banner, 'PluginManagerExtImpl/init()'); const result: Plugin[] = []; const foreign: Plugin[] = []; for (const plg of raw) { @@ -146,14 +195,16 @@ export class PluginHostRPC { isUnderDevelopment: !!plg.isUnderDevelopment }); } else { + // Headless and backend plugins are, for now, very similar let backendInitPath = pluginLifecycle.backendInitPath; // if no init path, try to init as regular Theia plugin - if (!backendInitPath) { - backendInitPath = __dirname + '/scanners/backend-init-theia.js'; + if (!backendInitPath && self.backendInitPath) { + backendInitPath = __dirname + self.backendInitPath; } + const pluginPath = self.getBackendPluginPath(pluginModel); const plugin: Plugin = { - pluginPath: pluginModel.entryPoint.backend!, + pluginPath, pluginFolder: pluginModel.packagePath, pluginUri: pluginModel.packageUri, model: pluginModel, @@ -162,30 +213,30 @@ export class PluginHostRPC { isUnderDevelopment: !!plg.isUnderDevelopment }; - self.initContext(backendInitPath, plugin); - + if (backendInitPath) { + self.initContext(backendInitPath, plugin); + } else { + const { name, version } = plugin.rawModel; + console.debug(self.banner, 'initializing(' + name + '@' + version + ' without any default API)'); + } result.push(plugin); } } catch (e) { - console.error(`Failed to initialize ${plg.model.id} plugin.`, e); + console.error(self.banner, `Failed to initialize ${plg.model.id} plugin.`, e); } } return [result, foreign]; }, initExtApi(extApi: ExtPluginApi[]): void { for (const api of extApi) { - if (api.backendInitPath) { - try { - const extApiInit = dynamicRequire<{ provideApi: ExtPluginApiBackendInitializationFn }>(api.backendInitPath); - extApiInit.provideApi(rpc, pluginManager); - } catch (e) { - console.error(e); - } + try { + self.initExtApi(api); + } catch (e) { + console.error(e); } } }, loadTests: extensionTestsPath ? async () => { - /* eslint-disable @typescript-eslint/no-explicit-any */ // Require the test runner via node require from the provided path let testRunner: any; let requireError: Error | undefined; @@ -212,7 +263,115 @@ export class PluginHostRPC { `Path ${extensionTestsPath} does not point to a valid extension test runner.` ); } : undefined - }, envExt, terminalService, storageProxy, secretsExt, preferencesManager, webview, localization, rpc); - return pluginManager; + }; + } + + /** + * Initialize the end of the given provided extension API applicable to the current plugin host. + * Errors should be propagated to the caller. + * + * @param extApi the extension API to initialize, if appropriate + * @throws if any error occurs in initializing the extension API + */ + protected abstract initExtApi(extApi: ExtPluginApi): void; +} + +/** + * The RPC handler for frontend-connection-scoped plugins (Theia and VSCode plugins). + */ +@injectable() +export class PluginHostRPC extends AbstractPluginHostRPC { + @inject(EnvExtImpl) + protected readonly envExt: EnvExtImpl; + + @inject(LocalizationExt) + protected readonly localizationExt: LocalizationExtImpl; + + @inject(KeyValueStorageProxy) + protected readonly keyValueStorageProxy: KeyValueStorageProxy; + + @inject(DebugExtImpl) + protected readonly debugExt: DebugExtImpl; + + @inject(EditorsAndDocumentsExtImpl) + protected readonly editorsAndDocumentsExt: EditorsAndDocumentsExtImpl; + + @inject(MessageRegistryExt) + protected readonly messageRegistryExt: MessageRegistryExt; + + @inject(WorkspaceExtImpl) + protected readonly workspaceExt: WorkspaceExtImpl; + + @inject(PreferenceRegistryExtImpl) + protected readonly preferenceRegistryExt: PreferenceRegistryExtImpl; + + @inject(ClipboardExt) + protected readonly clipboardExt: ClipboardExt; + + @inject(WebviewsExtImpl) + protected readonly webviewExt: WebviewsExtImpl; + + @inject(TerminalServiceExtImpl) + protected readonly terminalServiceExt: TerminalServiceExtImpl; + + @inject(SecretsExtImpl) + protected readonly secretsExt: SecretsExtImpl; + + constructor() { + super('PLUGIN_HOST', '/scanners/backend-init-theia.js', + { + $pluginManager: MAIN_RPC_CONTEXT.HOSTED_PLUGIN_MANAGER_EXT, + editorsAndDocumentsExt: MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT, + workspaceExt: MAIN_RPC_CONTEXT.WORKSPACE_EXT, + preferenceRegistryExt: MAIN_RPC_CONTEXT.PREFERENCE_REGISTRY_EXT, + storageExt: MAIN_RPC_CONTEXT.STORAGE_EXT, + webviewExt: MAIN_RPC_CONTEXT.WEBVIEWS_EXT, + secretsExt: MAIN_RPC_CONTEXT.SECRETS_EXT + } + ); + } + + protected createExtInterfaces(): ExtInterfaces { + connectProxyResolver(this.workspaceExt, this.preferenceRegistryExt); + return { + envExt: this.envExt, + storageExt: this.keyValueStorageProxy, + debugExt: this.debugExt, + editorsAndDocumentsExt: this.editorsAndDocumentsExt, + messageRegistryExt: this.messageRegistryExt, + workspaceExt: this.workspaceExt, + preferenceRegistryExt: this.preferenceRegistryExt, + clipboardExt: this.clipboardExt, + webviewExt: this.webviewExt, + terminalServiceExt: this.terminalServiceExt, + secretsExt: this.secretsExt, + localizationExt: this.localizationExt + }; + } + + protected createAPIFactory(extInterfaces: ExtInterfaces): PluginAPIFactory { + const { + envExt, debugExt, preferenceRegistryExt, editorsAndDocumentsExt, workspaceExt, + messageRegistryExt, clipboardExt, webviewExt, localizationExt + } = extInterfaces; + return createAPIFactory(this.rpc, this.pluginManager, envExt, debugExt, preferenceRegistryExt, + editorsAndDocumentsExt, workspaceExt, messageRegistryExt, clipboardExt, webviewExt, + localizationExt); + } + + protected initExtApi(extApi: ExtPluginApi): void { + interface PluginExports { + containerModule?: ContainerModule; + provideApi?: ExtPluginApiBackendInitializationFn; + } + if (extApi.backendInitPath) { + const { containerModule, provideApi } = dynamicRequire(extApi.backendInitPath); + if (containerModule) { + this.loadContainerModule(containerModule); + } + if (provideApi) { + provideApi(this.rpc, this.pluginManager); + } + } } } diff --git a/packages/plugin-ext/src/hosted/node/plugin-host.ts b/packages/plugin-ext/src/hosted/node/plugin-host.ts index 0159a153e52fe..cee2ad5b75d5b 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-host.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-host.ts @@ -14,10 +14,11 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import '@theia/core/shared/reflect-metadata'; -import { ConnectionClosedError, RPCProtocolImpl } from '../../common/rpc-protocol'; +import { Container } from '@theia/core/shared/inversify'; +import { ConnectionClosedError, RPCProtocol } from '../../common/rpc-protocol'; import { ProcessTerminatedMessage, ProcessTerminateMessage } from './hosted-plugin-protocol'; import { PluginHostRPC } from './plugin-host-rpc'; -import { IPCChannel } from '@theia/core/lib/node'; +import pluginHostModule from './plugin-host-module'; console.log('PLUGIN_HOST(' + process.pid + ') starting instance'); @@ -74,8 +75,12 @@ process.on('rejectionHandled', (promise: Promise) => { }); let terminating = false; -const channel = new IPCChannel(); -const rpc = new RPCProtocolImpl(channel); + +const container = new Container(); +container.load(pluginHostModule); + +const rpc: RPCProtocol = container.get(RPCProtocol); +const pluginHostRPC = container.get(PluginHostRPC); process.on('message', async (message: string) => { if (terminating) { @@ -103,6 +108,3 @@ process.on('message', async (message: string) => { console.error(e); } }); - -const pluginHostRPC = new PluginHostRPC(rpc); -pluginHostRPC.initialize(); diff --git a/packages/plugin-ext/src/hosted/node/plugin-reader.ts b/packages/plugin-ext/src/hosted/node/plugin-reader.ts index b137673a3acfd..f90df35151083 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-reader.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-reader.ts @@ -108,6 +108,9 @@ export class HostedPluginReader implements BackendApplicationContribution { if (pluginMetadata.model.entryPoint.backend) { pluginMetadata.model.entryPoint.backend = path.resolve(plugin.packagePath, pluginMetadata.model.entryPoint.backend); } + if (pluginMetadata.model.entryPoint.headless) { + pluginMetadata.model.entryPoint.headless = path.resolve(plugin.packagePath, pluginMetadata.model.entryPoint.headless); + } if (pluginMetadata) { // Add post processor if (this.metadataProcessors) { diff --git a/packages/plugin-ext/src/hosted/node/plugin-service.ts b/packages/plugin-ext/src/hosted/node/plugin-service.ts index 1c67db3e5109d..916009baa0857 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-service.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-service.ts @@ -13,7 +13,7 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, named, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject, named, optional, postConstruct } from '@theia/core/shared/inversify'; import { HostedPluginServer, HostedPluginClient, PluginDeployer, GetDeployedPluginsParams, DeployedPlugin, PluginIdentifiers } from '../../common/plugin-protocol'; import { HostedPluginSupport } from './hosted-plugin'; import { ILogger, Disposable, ContributionProvider, DisposableCollection } from '@theia/core'; @@ -23,6 +23,14 @@ import { PluginDeployerImpl } from '../../main/node/plugin-deployer-impl'; import { HostedPluginLocalizationService } from './hosted-plugin-localization-service'; import { PluginUninstallationManager } from '../../main/node/plugin-uninstallation-manager'; +export const BackendPluginHostableFilter = Symbol('BackendPluginHostableFilter'); +/** + * A filter matching backend plugins that are hostable in my plugin host process. + * Only if at least one backend plugin is deployed that matches my filter will I + * start the host process. + */ +export type BackendPluginHostableFilter = (plugin: DeployedPlugin) => boolean; + @injectable() export class HostedPluginServerImpl implements HostedPluginServer { @inject(ILogger) @@ -43,6 +51,10 @@ export class HostedPluginServerImpl implements HostedPluginServer { @inject(PluginUninstallationManager) protected readonly uninstallationManager: PluginUninstallationManager; + @inject(BackendPluginHostableFilter) + @optional() + protected backendPluginHostableFilter: BackendPluginHostableFilter; + protected client: HostedPluginClient | undefined; protected toDispose = new DisposableCollection(); @@ -63,6 +75,10 @@ export class HostedPluginServerImpl implements HostedPluginServer { @postConstruct() protected init(): void { + if (!this.backendPluginHostableFilter) { + this.backendPluginHostableFilter = () => true; + } + this.toDispose.pushAll([ this.pluginDeployer.onDidDeploy(() => this.client?.onDidDeploy()), this.uninstallationManager.onDidChangeUninstalledPlugins(currentUninstalled => { @@ -90,8 +106,9 @@ export class HostedPluginServerImpl implements HostedPluginServer { } async getDeployedPluginIds(): Promise { - const backendMetadata = await this.deployerHandler.getDeployedBackendPluginIds(); - if (backendMetadata.length > 0) { + const backendPlugins = (await this.deployerHandler.getDeployedBackendPlugins()) + .filter(this.backendPluginHostableFilter); + if (backendPlugins.length > 0) { this.hostedPlugin.runPluginServer(); } const plugins = new Set(); @@ -103,7 +120,7 @@ export class HostedPluginServerImpl implements HostedPluginServer { } }; addIds(await this.deployerHandler.getDeployedFrontendPluginIds()); - addIds(backendMetadata); + addIds(await this.deployerHandler.getDeployedBackendPluginIds()); addIds(await this.hostedPlugin.getExtraDeployedPluginIds()); return Array.from(plugins); } diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 6f05afe4f8d1c..af527a5e283b5 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -62,7 +62,9 @@ import { Translation, PluginIdentifiers, TerminalProfile, - PluginIconContribution + PluginIconContribution, + PluginEntryPoint, + PluginPackageContribution } from '../../../common/plugin-protocol'; import { promises as fs } from 'fs'; import * as path from 'path'; @@ -87,17 +89,19 @@ function getFileExtension(filePath: string): string { return index === -1 ? '' : filePath.substring(index + 1); } -@injectable() -export class TheiaPluginScanner implements PluginScanner { +type PluginPackageWithContributes = PluginPackage & { contributes: PluginPackageContribution }; - private readonly _apiType: PluginEngine = 'theiaPlugin'; +@injectable() +export abstract class AbstractPluginScanner implements PluginScanner { @inject(GrammarsReader) - private readonly grammarsReader: GrammarsReader; + protected readonly grammarsReader: GrammarsReader; @inject(PluginUriFactory) protected readonly pluginUriFactory: PluginUriFactory; + constructor(private readonly _apiType: PluginEngine, private readonly _backendInitPath?: string) { } + get apiType(): PluginEngine { return this._apiType; } @@ -119,22 +123,25 @@ export class TheiaPluginScanner implements PluginScanner { type: this._apiType, version: plugin.engines[this._apiType] }, - entryPoint: { - frontend: plugin.theiaPlugin!.frontend, - backend: plugin.theiaPlugin!.backend - } + entryPoint: this.getEntryPoint(plugin) }; return result; } + protected abstract getEntryPoint(plugin: PluginPackage): PluginEntryPoint; + getLifecycle(plugin: PluginPackage): PluginLifecycle { - return { + const result: PluginLifecycle = { startMethod: 'start', stopMethod: 'stop', frontendModuleName: buildFrontendModuleName(plugin), - - backendInitPath: path.join(__dirname, 'backend-init-theia') }; + + if (this._backendInitPath) { + result.backendInitPath = path.join(__dirname, this._backendInitPath); + } + + return result; } getDependencies(rawPlugin: PluginPackage): Map | undefined { @@ -155,6 +162,33 @@ export class TheiaPluginScanner implements PluginScanner { return contributions; } + return this.readContributions(rawPlugin as PluginPackageWithContributes, contributions); + } + + protected async readContributions(rawPlugin: PluginPackageWithContributes, contributions: PluginContribution): Promise { + return contributions; + } + +} + +@injectable() +export class TheiaPluginScanner extends AbstractPluginScanner { + constructor() { + super('theiaPlugin', 'backend-init-theia'); + } + + protected getEntryPoint(plugin: PluginPackage): PluginEntryPoint { + const result: PluginEntryPoint = { + frontend: plugin.theiaPlugin!.frontend, + backend: plugin.theiaPlugin!.backend + }; + if (plugin.theiaPlugin?.headless) { + result.headless = plugin.theiaPlugin.headless; + } + return result; + } + + protected override async readContributions(rawPlugin: PluginPackageWithContributes, contributions: PluginContribution): Promise { try { if (rawPlugin.contributes.configuration) { const configurations = Array.isArray(rawPlugin.contributes.configuration) ? rawPlugin.contributes.configuration : [rawPlugin.contributes.configuration]; diff --git a/packages/plugin-ext/src/main/browser/env-main.ts b/packages/plugin-ext/src/main/browser/env-main.ts index af9a9fa7335c9..12254b3fc7dd7 100644 --- a/packages/plugin-ext/src/main/browser/env-main.ts +++ b/packages/plugin-ext/src/main/browser/env-main.ts @@ -14,35 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { interfaces } from '@theia/core/shared/inversify'; -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; -import { RPCProtocol } from '../../common/rpc-protocol'; -import { EnvMain } from '../../common/plugin-api-rpc'; import { QueryParameters } from '../../common/env'; -import { isWindows, isOSX } from '@theia/core'; -import { OperatingSystem } from '../../plugin/types-impl'; - -export class EnvMainImpl implements EnvMain { - private envVariableServer: EnvVariablesServer; - - constructor(rpc: RPCProtocol, container: interfaces.Container) { - this.envVariableServer = container.get(EnvVariablesServer); - } - - $getEnvVariable(envVarName: string): Promise { - return this.envVariableServer.getValue(envVarName).then(result => result ? result.value : undefined); - } - - async $getClientOperatingSystem(): Promise { - if (isWindows) { - return OperatingSystem.Windows; - } - if (isOSX) { - return OperatingSystem.OSX; - } - return OperatingSystem.Linux; - } -} +export { EnvMainImpl } from '../common/env-main'; /** * Returns query parameters from current page. diff --git a/packages/plugin-ext/src/main/browser/message-registry-main.ts b/packages/plugin-ext/src/main/browser/message-registry-main.ts index 5deaf5c747550..53af9a6c37dad 100644 --- a/packages/plugin-ext/src/main/browser/message-registry-main.ts +++ b/packages/plugin-ext/src/main/browser/message-registry-main.ts @@ -15,26 +15,21 @@ // ***************************************************************************** import { interfaces } from '@theia/core/shared/inversify'; -import { MessageService } from '@theia/core/lib/common/message-service'; -import { MessageRegistryMain, MainMessageType, MainMessageOptions, MainMessageItem } from '../../common/plugin-api-rpc'; +import { MainMessageType, MainMessageOptions, MainMessageItem } from '../../common/plugin-api-rpc'; import { ModalNotification, MessageType } from './dialogs/modal-notification'; +import { BasicMessageRegistryMainImpl } from '../common/basic-message-registry-main'; -export class MessageRegistryMainImpl implements MessageRegistryMain { - private readonly messageService: MessageService; - +/** + * Message registry implementation that adds support for the model option via dialog in the browser. + */ +export class MessageRegistryMainImpl extends BasicMessageRegistryMainImpl { constructor(container: interfaces.Container) { - this.messageService = container.get(MessageService); + super(container); } - async $showMessage(type: MainMessageType, message: string, options: MainMessageOptions, actions: MainMessageItem[]): Promise { - const action = await this.doShowMessage(type, message, options, actions); - const handle = action - ? actions.map(a => a.title).indexOf(action) - : undefined; - return handle === undefined && options.modal ? options.onCloseActionHandle : handle; - } + protected override async doShowMessage(type: MainMessageType, message: string, + options: MainMessageOptions, actions: MainMessageItem[]): Promise { - protected async doShowMessage(type: MainMessageType, message: string, options: MainMessageOptions, actions: MainMessageItem[]): Promise { if (options.modal) { const messageType = type === MainMessageType.Error ? MessageType.Error : type === MainMessageType.Warning ? MessageType.Warning : @@ -42,15 +37,7 @@ export class MessageRegistryMainImpl implements MessageRegistryMain { const modalNotification = new ModalNotification(); return modalNotification.showDialog(messageType, message, options, actions); } - switch (type) { - case MainMessageType.Info: - return this.messageService.info(message, ...actions.map(a => a.title)); - case MainMessageType.Warning: - return this.messageService.warn(message, ...actions.map(a => a.title)); - case MainMessageType.Error: - return this.messageService.error(message, ...actions.map(a => a.title)); - } - throw new Error(`Message type '${type}' is not supported yet!`); + return super.doShowMessage(type, message, options, actions); } } diff --git a/packages/plugin-ext/src/main/browser/notification-main.ts b/packages/plugin-ext/src/main/browser/notification-main.ts index 90ab72eb7203c..eb3379f866099 100644 --- a/packages/plugin-ext/src/main/browser/notification-main.ts +++ b/packages/plugin-ext/src/main/browser/notification-main.ts @@ -14,73 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { NotificationExt, NotificationMain, MAIN_RPC_CONTEXT } from '../../common'; -import { ProgressService, Progress, ProgressMessage } from '@theia/core/lib/common'; +import { MAIN_RPC_CONTEXT } from '../../common'; import { interfaces } from '@theia/core/shared/inversify'; import { RPCProtocol } from '../../common/rpc-protocol'; -import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; - -export class NotificationMainImpl implements NotificationMain, Disposable { - private readonly progressService: ProgressService; - private readonly progressMap = new Map(); - private readonly progress2Work = new Map(); - private readonly proxy: NotificationExt; - - protected readonly toDispose = new DisposableCollection( - Disposable.create(() => { /* mark as not disposed */ }) - ); +import { BasicNotificationMainImpl } from '../common/basic-notification-main'; +export class NotificationMainImpl extends BasicNotificationMainImpl { constructor(rpc: RPCProtocol, container: interfaces.Container) { - this.progressService = container.get(ProgressService); - this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTIFICATION_EXT); - } - - dispose(): void { - this.toDispose.dispose(); - } - - async $startProgress(options: NotificationMain.StartProgressOptions): Promise { - const onDidCancel = () => { - // If the map does not contain current id, it has already stopped and should not be cancelled - if (this.progressMap.has(id)) { - this.proxy.$acceptProgressCanceled(id); - } - }; - - const progressMessage = this.mapOptions(options); - const progress = await this.progressService.showProgress(progressMessage, onDidCancel); - const id = progress.id; - this.progressMap.set(id, progress); - this.progress2Work.set(id, 0); - if (this.toDispose.disposed) { - this.$stopProgress(id); - } else { - this.toDispose.push(Disposable.create(() => this.$stopProgress(id))); - } - return id; - } - protected mapOptions(options: NotificationMain.StartProgressOptions): ProgressMessage { - const { title, location, cancellable } = options; - return { text: title, options: { location, cancelable: cancellable } }; - } - - $stopProgress(id: string): void { - const progress = this.progressMap.get(id); - - if (progress) { - this.progressMap.delete(id); - this.progress2Work.delete(id); - progress.cancel(); - } - } - - $updateProgress(id: string, item: NotificationMain.ProgressReport): void { - const progress = this.progressMap.get(id); - if (!progress) { - return; - } - const done = Math.min((this.progress2Work.get(id) || 0) + (item.increment || 0), 100); - this.progress2Work.set(id, done); - progress.report({ message: item.message, work: done ? { done, total: 100 } : undefined }); + super(rpc, container, MAIN_RPC_CONTEXT.NOTIFICATION_EXT); } } diff --git a/packages/plugin-ext/src/main/common/basic-message-registry-main.ts b/packages/plugin-ext/src/main/common/basic-message-registry-main.ts new file mode 100644 index 0000000000000..d9e60d413c117 --- /dev/null +++ b/packages/plugin-ext/src/main/common/basic-message-registry-main.ts @@ -0,0 +1,53 @@ +// ***************************************************************************** +// Copyright (C) 2018 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { MessageService } from '@theia/core/lib/common/message-service'; +import { MessageRegistryMain, MainMessageType, MainMessageOptions, MainMessageItem } from '../../common/plugin-api-rpc'; + +/** + * A basic implementation of the message registry that does not support the modal option + * as that requires an UI. + */ +export class BasicMessageRegistryMainImpl implements MessageRegistryMain { + protected readonly messageService: MessageService; + + constructor(container: interfaces.Container) { + this.messageService = container.get(MessageService); + } + + async $showMessage(type: MainMessageType, message: string, options: MainMessageOptions, actions: MainMessageItem[]): Promise { + const action = await this.doShowMessage(type, message, options, actions); + const handle = action + ? actions.map(a => a.title).indexOf(action) + : undefined; + return handle === undefined && options.modal ? options.onCloseActionHandle : handle; + } + + protected async doShowMessage(type: MainMessageType, message: string, options: MainMessageOptions, actions: MainMessageItem[]): Promise { + // Modal notifications are not supported in this context + switch (type) { + case MainMessageType.Info: + return this.messageService.info(message, ...actions.map(a => a.title)); + case MainMessageType.Warning: + return this.messageService.warn(message, ...actions.map(a => a.title)); + case MainMessageType.Error: + return this.messageService.error(message, ...actions.map(a => a.title)); + } + throw new Error(`Message type '${type}' is not supported yet!`); + } + +} diff --git a/packages/plugin-ext/src/main/common/basic-notification-main.ts b/packages/plugin-ext/src/main/common/basic-notification-main.ts new file mode 100644 index 0000000000000..4cb73d47f0a2d --- /dev/null +++ b/packages/plugin-ext/src/main/common/basic-notification-main.ts @@ -0,0 +1,86 @@ +// ***************************************************************************** +// Copyright (C) 2018 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { NotificationExt, NotificationMain } from '../../common'; +import { ProgressService, Progress, ProgressMessage } from '@theia/core/lib/common'; +import { interfaces } from '@theia/core/shared/inversify'; +import { ProxyIdentifier, RPCProtocol } from '../../common/rpc-protocol'; +import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; + +export class BasicNotificationMainImpl implements NotificationMain, Disposable { + protected readonly progressService: ProgressService; + protected readonly progressMap = new Map(); + protected readonly progress2Work = new Map(); + protected readonly proxy: NotificationExt; + + protected readonly toDispose = new DisposableCollection( + Disposable.create(() => { /* mark as not disposed */ }) + ); + + constructor(rpc: RPCProtocol, container: interfaces.Container, extIdentifier: ProxyIdentifier) { + this.progressService = container.get(ProgressService); + this.proxy = rpc.getProxy(extIdentifier); + } + + dispose(): void { + this.toDispose.dispose(); + } + + async $startProgress(options: NotificationMain.StartProgressOptions): Promise { + const onDidCancel = () => { + // If the map does not contain current id, it has already stopped and should not be cancelled + if (this.progressMap.has(id)) { + this.proxy.$acceptProgressCanceled(id); + } + }; + + const progressMessage = this.mapOptions(options); + const progress = await this.progressService.showProgress(progressMessage, onDidCancel); + const id = progress.id; + this.progressMap.set(id, progress); + this.progress2Work.set(id, 0); + if (this.toDispose.disposed) { + this.$stopProgress(id); + } else { + this.toDispose.push(Disposable.create(() => this.$stopProgress(id))); + } + return id; + } + protected mapOptions(options: NotificationMain.StartProgressOptions): ProgressMessage { + const { title, location, cancellable } = options; + return { text: title, options: { location, cancelable: cancellable } }; + } + + $stopProgress(id: string): void { + const progress = this.progressMap.get(id); + + if (progress) { + this.progressMap.delete(id); + this.progress2Work.delete(id); + progress.cancel(); + } + } + + $updateProgress(id: string, item: NotificationMain.ProgressReport): void { + const progress = this.progressMap.get(id); + if (!progress) { + return; + } + const done = Math.min((this.progress2Work.get(id) || 0) + (item.increment || 0), 100); + this.progress2Work.set(id, done); + progress.report({ message: item.message, work: done ? { done, total: 100 } : undefined }); + } +} diff --git a/packages/plugin-ext/src/main/common/env-main.ts b/packages/plugin-ext/src/main/common/env-main.ts new file mode 100644 index 0000000000000..a5014bd563208 --- /dev/null +++ b/packages/plugin-ext/src/main/common/env-main.ts @@ -0,0 +1,44 @@ +// ***************************************************************************** +// Copyright (C) 2018 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { RPCProtocol } from '../../common/rpc-protocol'; +import { EnvMain } from '../../common/plugin-api-rpc'; +import { isWindows, isOSX } from '@theia/core'; +import { OperatingSystem } from '../../plugin/types-impl'; + +export class EnvMainImpl implements EnvMain { + private envVariableServer: EnvVariablesServer; + + constructor(rpc: RPCProtocol, container: interfaces.Container) { + this.envVariableServer = container.get(EnvVariablesServer); + } + + $getEnvVariable(envVarName: string): Promise { + return this.envVariableServer.getValue(envVarName).then(result => result ? result.value : undefined); + } + + async $getClientOperatingSystem(): Promise { + if (isWindows) { + return OperatingSystem.Windows; + } + if (isOSX) { + return OperatingSystem.OSX; + } + return OperatingSystem.Linux; + } +} diff --git a/packages/plugin-ext/src/main/node/handlers/plugin-theia-directory-handler.ts b/packages/plugin-ext/src/main/node/handlers/plugin-theia-directory-handler.ts index a33fa57a72f23..84c4e7025ddeb 100644 --- a/packages/plugin-ext/src/main/node/handlers/plugin-theia-directory-handler.ts +++ b/packages/plugin-ext/src/main/node/handlers/plugin-theia-directory-handler.ts @@ -28,7 +28,7 @@ import { PluginCliContribution } from '../plugin-cli-contribution'; import { getTempDirPathAsync } from '../temp-dir-util'; @injectable() -export class PluginTheiaDirectoryHandler implements PluginDeployerDirectoryHandler { +export abstract class AbstractPluginDirectoryHandler implements PluginDeployerDirectoryHandler { protected readonly deploymentDirectory: Deferred; @@ -42,15 +42,20 @@ export class PluginTheiaDirectoryHandler implements PluginDeployerDirectoryHandl async accept(resolvedPlugin: PluginDeployerEntry): Promise { - console.debug('PluginTheiaDirectoryHandler: accepting plugin with path', resolvedPlugin.path()); + console.debug(`Plugin directory handler: accepting plugin with path ${resolvedPlugin.path()}`); // handle only directories if (await resolvedPlugin.isFile()) { return false; } + // Was this directory unpacked from an NPM tarball? + const wasTarball = resolvedPlugin.originalPath().endsWith('.tgz'); + const rootPath = resolvedPlugin.path(); + const basePath = wasTarball ? path.resolve(rootPath, 'package') : rootPath; + // is there a package.json ? - const packageJsonPath = path.resolve(resolvedPlugin.path(), 'package.json'); + const packageJsonPath = path.resolve(basePath, 'package.json'); try { let packageJson = resolvedPlugin.getValue('package.json'); @@ -60,26 +65,20 @@ export class PluginTheiaDirectoryHandler implements PluginDeployerDirectoryHandl resolvedPlugin.storeValue('package.json', packageJson); } - if (packageJson?.engines?.theiaPlugin) { + if (this.acceptManifest(packageJson)) { + if (wasTarball) { + resolvedPlugin.updatePath(basePath); + resolvedPlugin.rootPath = rootPath; + } return true; } } catch { /* Failed to read file. Fall through. */ } return false; } - async handle(context: PluginDeployerDirectoryHandlerContext): Promise { - await this.copyDirectory(context); - const types: PluginDeployerEntryType[] = []; - const packageJson = context.pluginEntry().getValue('package.json'); - if (packageJson.theiaPlugin && packageJson.theiaPlugin.backend) { - types.push(PluginDeployerEntryType.BACKEND); - } - if (packageJson.theiaPlugin && packageJson.theiaPlugin.frontend) { - types.push(PluginDeployerEntryType.FRONTEND); - } + protected abstract acceptManifest(plugin: PluginPackage): boolean; - context.pluginEntry().accept(...types); - } + abstract handle(context: PluginDeployerDirectoryHandlerContext): Promise; protected async copyDirectory(context: PluginDeployerDirectoryHandlerContext): Promise { if (this.pluginCli.copyUncompressedPlugins() && context.pluginEntry().type === PluginType.User) { @@ -112,4 +111,27 @@ export class PluginTheiaDirectoryHandler implements PluginDeployerDirectoryHandl const deploymentDirectory = await this.deploymentDirectory.promise; return FileUri.fsPath(deploymentDirectory.resolve(filenamify(context.pluginEntry().id(), { replacement: '_' }))); } + +} + +@injectable() +export class PluginTheiaDirectoryHandler extends AbstractPluginDirectoryHandler { + + protected acceptManifest(plugin: PluginPackage): boolean { + return plugin?.engines?.theiaPlugin !== undefined; + } + + async handle(context: PluginDeployerDirectoryHandlerContext): Promise { + await this.copyDirectory(context); + const types: PluginDeployerEntryType[] = []; + const packageJson = context.pluginEntry().getValue('package.json'); + if (packageJson.theiaPlugin && packageJson.theiaPlugin.backend) { + types.push(PluginDeployerEntryType.BACKEND); + } + if (packageJson.theiaPlugin && packageJson.theiaPlugin.frontend) { + types.push(PluginDeployerEntryType.FRONTEND); + } + + context.pluginEntry().accept(...types); + } } diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts index 1eb032c397741..8c60671b78b88 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts @@ -271,20 +271,24 @@ export class PluginDeployerImpl implements PluginDeployer { const acceptedPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted()); const acceptedFrontendPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.FRONTEND)); const acceptedBackendPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.BACKEND)); + const acceptedHeadlessPlugins = pluginsToDeploy.filter(pluginDeployerEntry => pluginDeployerEntry.isAccepted(PluginDeployerEntryType.HEADLESS)); this.logger.debug('the accepted plugins are', acceptedPlugins); this.logger.debug('the acceptedFrontendPlugins plugins are', acceptedFrontendPlugins); this.logger.debug('the acceptedBackendPlugins plugins are', acceptedBackendPlugins); + this.logger.debug('the acceptedHeadlessPlugins plugins are', acceptedHeadlessPlugins); acceptedPlugins.forEach(plugin => { this.logger.debug('will deploy plugin', plugin.id(), 'with changes', JSON.stringify(plugin.getChanges()), 'and this plugin has been resolved by', plugin.resolvedBy()); }); // local path to launch - const pluginPaths = acceptedBackendPlugins.map(pluginEntry => pluginEntry.path()); + const pluginPaths = [...acceptedBackendPlugins, ...acceptedHeadlessPlugins].map(pluginEntry => pluginEntry.path()); this.logger.debug('local path to deploy on remote instance', pluginPaths); const deployments = await Promise.all([ + // headless plugins are deployed like backend plugins + this.pluginDeployerHandler.deployBackendPlugins(acceptedHeadlessPlugins), // start the backend plugins this.pluginDeployerHandler.deployBackendPlugins(acceptedBackendPlugins), this.pluginDeployerHandler.deployFrontendPlugins(acceptedFrontendPlugins) diff --git a/packages/plugin-ext/src/plugin/clipboard-ext.ts b/packages/plugin-ext/src/plugin/clipboard-ext.ts index 363f83085be88..65ed8e7d39af7 100644 --- a/packages/plugin-ext/src/plugin/clipboard-ext.ts +++ b/packages/plugin-ext/src/plugin/clipboard-ext.ts @@ -15,15 +15,21 @@ // ***************************************************************************** import * as theia from '@theia/plugin'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { RPCProtocol } from '../common/rpc-protocol'; import { PLUGIN_RPC_CONTEXT, ClipboardMain } from '../common'; +@injectable() export class ClipboardExt implements theia.Clipboard { - protected readonly proxy: ClipboardMain; + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; - constructor(rpc: RPCProtocol) { - this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.CLIPBOARD_MAIN); + protected proxy: ClipboardMain; + + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.CLIPBOARD_MAIN); } readText(): Promise { diff --git a/packages/plugin-ext/src/plugin/debug/debug-ext.ts b/packages/plugin-ext/src/plugin/debug/debug-ext.ts index bc896a9be928a..d982385f30b82 100644 --- a/packages/plugin-ext/src/plugin/debug/debug-ext.ts +++ b/packages/plugin-ext/src/plugin/debug/debug-ext.ts @@ -13,6 +13,7 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { Emitter } from '@theia/core/lib/common/event'; import { Path } from '@theia/core/lib/common/path'; import * as theia from '@theia/plugin'; @@ -43,7 +44,11 @@ interface ConfigurationProviderRecord { /* eslint-disable @typescript-eslint/no-explicit-any */ +@injectable() export class DebugExtImpl implements DebugExt { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + // debug sessions by sessionId private sessions = new Map(); private configurationProviderHandleGenerator: number; @@ -83,8 +88,7 @@ export class DebugExtImpl implements DebugExt { return [...this._breakpoints.values()]; } - constructor(rpc: RPCProtocol) { - this.proxy = rpc.getProxy(Ext.DEBUG_MAIN); + constructor() { this.activeDebugConsole = { append: (value: string) => this.proxy.$appendToDebugConsole(value), appendLine: (value: string) => this.proxy.$appendLineToDebugConsole(value) @@ -93,6 +97,11 @@ export class DebugExtImpl implements DebugExt { this.configurationProviders = []; } + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(Ext.DEBUG_MAIN); + } + /** * Sets dependencies. */ diff --git a/packages/plugin-ext/src/plugin/editors-and-documents.ts b/packages/plugin-ext/src/plugin/editors-and-documents.ts index 4d3255e9f5507..d5a98a9863fa3 100644 --- a/packages/plugin-ext/src/plugin/editors-and-documents.ts +++ b/packages/plugin-ext/src/plugin/editors-and-documents.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { inject, injectable } from '@theia/core/shared/inversify'; import { EditorsAndDocumentsExt, EditorsAndDocumentsDelta, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { TextEditorExt } from './text-editor'; import { RPCProtocol } from '../common/rpc-protocol'; @@ -24,7 +25,11 @@ import * as Converter from './type-converters'; import { dispose } from '../common/disposable-util'; import { URI } from './types-impl'; +@injectable() export class EditorsAndDocumentsExtImpl implements EditorsAndDocumentsExt { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + private activeEditorId: string | null = null; private readonly _onDidAddDocuments = new Emitter(); @@ -40,9 +45,6 @@ export class EditorsAndDocumentsExtImpl implements EditorsAndDocumentsExt { private readonly documents = new Map(); private readonly editors = new Map(); - constructor(private readonly rpc: RPCProtocol) { - } - async $acceptEditorsAndDocumentsDelta(delta: EditorsAndDocumentsDelta): Promise { const removedDocuments = new Array(); const addedDocuments = new Array(); diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 8fe8494a49c47..3d283bedb9561 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -14,13 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import * as theia from '@theia/plugin'; import { RPCProtocol } from '../common/rpc-protocol'; import { EnvMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { QueryParameters } from '../common/env'; import { v4 } from 'uuid'; +@injectable() export abstract class EnvExtImpl { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + private proxy: EnvMain; private queryParameters: QueryParameters; private lang: string; @@ -31,13 +36,17 @@ export abstract class EnvExtImpl { private host: string; private _remoteName: string | undefined; - constructor(rpc: RPCProtocol) { - this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.ENV_MAIN); + constructor() { this.envSessionId = v4(); this.envMachineId = v4(); this._remoteName = undefined; } + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.ENV_MAIN); + } + getEnvVariable(envVarName: string): Promise { return this.proxy.$getEnvVariable(envVarName).then(x => { if (x === null) { diff --git a/packages/plugin-ext/src/plugin/localization-ext.ts b/packages/plugin-ext/src/plugin/localization-ext.ts index 0e93969a1ec95..1a70927108b70 100644 --- a/packages/plugin-ext/src/plugin/localization-ext.ts +++ b/packages/plugin-ext/src/plugin/localization-ext.ts @@ -16,6 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { nls } from '@theia/core'; import { Localization } from '@theia/core/lib/common/i18n/localization'; import { LocalizationExt, LocalizationMain, Plugin, PLUGIN_RPC_CONTEXT, StringDetails } from '../common'; @@ -23,15 +24,19 @@ import { LanguagePackBundle } from '../common/language-pack-service'; import { RPCProtocol } from '../common/rpc-protocol'; import { URI } from './types-impl'; +@injectable() export class LocalizationExtImpl implements LocalizationExt { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; - private readonly _proxy: LocalizationMain; + private _proxy: LocalizationMain; private currentLanguage?: string; private isDefaultLanguage = true; private readonly bundleCache = new Map(); - constructor(rpc: RPCProtocol) { - this._proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN); + @postConstruct() + initialize(): void { + this._proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN); } translateMessage(pluginId: string, details: StringDetails): string { diff --git a/packages/plugin-ext/src/plugin/message-registry.ts b/packages/plugin-ext/src/plugin/message-registry.ts index 016f518dec9c3..c5fb851c7f6fc 100644 --- a/packages/plugin-ext/src/plugin/message-registry.ts +++ b/packages/plugin-ext/src/plugin/message-registry.ts @@ -13,18 +13,23 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { PLUGIN_RPC_CONTEXT as Ext, MessageRegistryMain, MainMessageOptions, MainMessageType } from '../common/plugin-api-rpc'; import { RPCProtocol } from '../common/rpc-protocol'; import { MessageItem, MessageOptions } from '@theia/plugin'; +@injectable() export class MessageRegistryExt { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; private proxy: MessageRegistryMain; - constructor(rpc: RPCProtocol) { - this.proxy = rpc.getProxy(Ext.MESSAGE_REGISTRY_MAIN); + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(Ext.MESSAGE_REGISTRY_MAIN); } async showMessage(type: MainMessageType, message: string, diff --git a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts index 16b2c330ac252..25d28c1be2097 100644 --- a/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts +++ b/packages/plugin-ext/src/plugin/node/debug/debug.spec.ts @@ -13,6 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 ********************************************************************************/ +import { Container } from '@theia/core/shared/inversify'; import { DebugSession } from '@theia/plugin'; import * as chai from 'chai'; import { ProxyIdentifier, RPCProtocol } from '../../../common/rpc-protocol'; @@ -37,7 +38,10 @@ describe('Debug API', () => { } }; - const debug = new DebugExtImpl(mockRPCProtocol); + const container = new Container(); + container.bind(RPCProtocol).toConstantValue(mockRPCProtocol); + container.bind(DebugExtImpl).toSelf().inSingletonScope(); + const debug = container.get(DebugExtImpl); it('should use sourceReference, path and sessionId', () => { const source = { diff --git a/packages/plugin-ext/src/plugin/node/env-node-ext.ts b/packages/plugin-ext/src/plugin/node/env-node-ext.ts index 65924f11b95e6..2ad86064f13cf 100644 --- a/packages/plugin-ext/src/plugin/node/env-node-ext.ts +++ b/packages/plugin-ext/src/plugin/node/env-node-ext.ts @@ -14,9 +14,9 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { injectable } from '@theia/core/shared/inversify'; import * as mac from 'macaddress'; import { EnvExtImpl } from '../env'; -import { RPCProtocol } from '../../common/rpc-protocol'; import { createHash } from 'crypto'; import { v4 } from 'uuid'; import fs = require('fs'); @@ -25,13 +25,15 @@ import fs = require('fs'); * Provides machineId using mac address. It's only possible on node side * Extending the common class */ +@injectable() export class EnvNodeExtImpl extends EnvExtImpl { private macMachineId: string; private _isNewAppInstall: boolean; - constructor(rpc: RPCProtocol) { - super(rpc); + constructor() { + super(); + mac.one((err, macAddress) => { if (err) { this.macMachineId = v4(); diff --git a/packages/plugin-ext/src/plugin/node/plugin-container-module.ts b/packages/plugin-ext/src/plugin/node/plugin-container-module.ts new file mode 100644 index 0000000000000..e23285d1da569 --- /dev/null +++ b/packages/plugin-ext/src/plugin/node/plugin-container-module.ts @@ -0,0 +1,165 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces, ContainerModule } from '@theia/core/shared/inversify'; +import { Plugin, PluginManager, emptyPlugin } from '../../common'; + +export type ApiFactory = (plugin: Plugin) => T; + +/** + * Bind a service identifier for the factory function creating API objects of + * type `T` for a client plugin to a class providing a `call()` method that + * implements that factory function. + * + * @template T the API object type that the factory creates + * @param serviceIdentifier the injection key identifying the API factory function + * @param factoryClass the class implementing the API factory function via its `call()` method + */ +export type BindApiFactory = ( + apiModuleName: string, + serviceIdentifier: interfaces.ServiceIdentifier>, + factoryClass: new () => { createApi: ApiFactory}) => void; + +/** + * An analogue of the callback function in the constructor of the Inversify + * `ContainerModule` providing a registry that, in addition to the standard + * binding-related functions, includes a custom function for binding an + * API factory. + */ +export type PluginContainerModuleCallBack = (registry: { + bind: interfaces.Bind; + unbind: interfaces.Unbind; + isBound: interfaces.IsBound; + rebind: interfaces.Rebind; + bindApiFactory: BindApiFactory; +}) => void; + +/** + * Factory for an Inversify `ContainerModule` that supports registration of the plugin's + * API factory. Use the `PluginContainerModule`'s `create()` method to create the container + * module; its `callback` function provides a `registry` of Inversify binding functions that + * includes a `bindApiFactory` function for binding the API factory. + */ +export const PluginContainerModule: symbol & { create(callback: PluginContainerModuleCallBack): ContainerModule } = Object.assign(Symbol('PluginContainerModule'), { + create(callback: PluginContainerModuleCallBack): ContainerModule { + const result: InternalPluginContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { + const bindApiFactory: BindApiFactory = (apiModuleName, serviceIdentifier, factoryClass) => { + result.initializeApi = container => { + const apiCache = new PluginApiCache(apiModuleName, serviceIdentifier); + apiCache.initializeApi(container); + return apiCache; + }; + bind(factoryClass).toSelf().inSingletonScope(); + bind(serviceIdentifier).toDynamicValue(({ container }) => { + const factory = container.get(factoryClass); + return factory.createApi.bind(factory); + }).inSingletonScope(); + }; + callback({ bind, unbind, isBound, rebind, bindApiFactory }); + }); + return result; + } +}); + +/** + * Definition of additional API provided by the `ContainerModule` created by the + * {@link PluginContainerModule} factory function that is for internal use by Theia. + */ +export type InternalPluginContainerModule = ContainerModule & { + /** Use my API factory binding to initialize the plugin API in some `container`. */ + initializeApi?: (container: interfaces.Container) => PluginApiCache; +}; + +/** + * An object that creates and caches the instance of the plugin API created by the + * factory binding in a {@link PluginContainerModule} in some plugin host. + * + * @template T the custom API object's type + */ +export class PluginApiCache { + + private apiFactory: ApiFactory; + private pluginManager: PluginManager; + private defaultApi: T; + private pluginsApiImpl = new Map(); + private hookedModuleLoader = false; + + /** + * Initializes me with the module name by which plugins import the API + * and the service identifier to look up in the Inversify `Container` to + * obtain the {@link ApiFactory} that will instantiate it. + */ + constructor(private readonly apiModuleName: string, + private readonly serviceIdentifier: interfaces.ServiceIdentifier>) {} + + // Called by Theia to do any prep work needed for dishing out the API object + // when it's requested. The key part of that is hooking into the node module + // loader. This is called every time a plugin-host process is forked. + initializeApi(container: interfaces.Container): void { + this.apiFactory = container.get(this.serviceIdentifier); + this.pluginManager = container.get(PluginManager); + + if (!this.hookedModuleLoader) { + this.hookedModuleLoader = true; + this.overrideInternalLoad(); + } + } + + /** + * Hook into the override chain of JavaScript's `module` loading function + * to implement ourselves, using the API provider's registered factory, + * the construction of its default exports object. + */ + private overrideInternalLoad(): void { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require('module'); + + const internalLoad = module._load; + const self = this; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + module._load = function (request: string, parent: any, isMain: any): any { + if (request !== self.apiModuleName) { + // Pass the request to the next implementation down the chain + return internalLoad.call(this, request, parent, isMain); + } + + const plugin = self.findPlugin(parent.filename); + if (plugin) { + let apiImpl = self.pluginsApiImpl.get(plugin.model.id); + if (!apiImpl) { + apiImpl = self.apiFactory(plugin); + self.pluginsApiImpl.set(plugin.model.id, apiImpl); + } + return apiImpl; + } + + console.warn( + `Extension module ${parent.filename} did an import of '${self.apiModuleName}' but our cache ` + + ' has no knowledge of that extension. Returning a generic API object; some functionality might not work correctly.' + ); + if (!self.defaultApi) { + self.defaultApi = self.apiFactory(emptyPlugin); + } + return self.defaultApi; + }; + } + + // Search all loaded plugins to see which one has the given file (absolute path) + protected findPlugin(filePath: string): Plugin | undefined { + return this.pluginManager.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder)); + } +} diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 6486441dfda89..66e1809848db4 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -14,8 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { PLUGIN_RPC_CONTEXT, + AbstractPluginManagerExt, NotificationMain, MainMessageType, MessageRegistryMain, @@ -35,13 +37,13 @@ import * as types from './types-impl'; import { join } from './path'; import { EnvExtImpl } from './env'; import { PreferenceRegistryExtImpl } from './preference-registry'; -import { Memento, KeyValueStorageProxy, GlobalState } from './plugin-storage'; +import { InternalStorageExt, Memento, GlobalState } from './plugin-storage'; import { ExtPluginApi } from '../common/plugin-ext-api-contribution'; import { RPCProtocol } from '../common/rpc-protocol'; import { Emitter } from '@theia/core/lib/common/event'; import { WebviewsExtImpl } from './webviews'; import { URI as Uri } from './types-impl'; -import { SecretsExtImpl, SecretStorageExt } from '../plugin/secrets-ext'; +import { InternalSecretsExt, SecretStorageExt } from '../plugin/secrets-ext'; import { PluginExt } from './plugin-context'; import { Deferred } from '@theia/core/lib/common/promise-util'; @@ -77,7 +79,34 @@ class ActivatedPlugin { } } -export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { +export const MinimalTerminalServiceExt = Symbol('MinimalTerminalServiceExt'); +export type MinimalTerminalServiceExt = Pick; + +@injectable() +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export abstract class AbstractPluginManagerExtImpl

> implements AbstractPluginManagerExt

, PluginManager { + + @inject(EnvExtImpl) + protected readonly envExt: EnvExtImpl; + + @inject(MinimalTerminalServiceExt) + protected readonly terminalService: MinimalTerminalServiceExt; + + @inject(InternalStorageExt) + protected readonly storage: InternalStorageExt; + + @inject(InternalSecretsExt) + protected readonly secrets: InternalSecretsExt; + + @inject(LocalizationExt) + protected readonly localization: LocalizationExt; + + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + + // Cannot be Inversify-injected because it induces a dependency cycle + protected host: PluginHost; private configStorage: ConfigStorage | undefined; private readonly registry = new Map(); @@ -90,29 +119,22 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { private onDidChangeEmitter = new Emitter(); private messageRegistryProxy: MessageRegistryMain; private notificationMain: NotificationMain; - private supportedActivationEvents: Set; - protected fireOnDidChange(): void { - this.onDidChangeEmitter.fire(undefined); - } protected jsonValidation: PluginJsonValidationContribution[] = []; protected ready = new Deferred(); - constructor( - private readonly host: PluginHost, - private readonly envExt: EnvExtImpl, - private readonly terminalService: TerminalServiceExt, - private readonly storageProxy: KeyValueStorageProxy, - private readonly secrets: SecretsExtImpl, - private readonly preferencesManager: PreferenceRegistryExtImpl, - private readonly webview: WebviewsExtImpl, - private readonly localization: LocalizationExt, - private readonly rpc: RPCProtocol - ) { + @postConstruct() + initialize(): void { this.messageRegistryProxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.MESSAGE_REGISTRY_MAIN); this.notificationMain = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTIFICATION_MAIN); } + setPluginHost(pluginHost: PluginHost): void { + this.host = pluginHost; + } + + abstract $init(params: P): Promise; + async $stop(pluginId?: string): Promise { if (!pluginId) { return this.stopAll(); @@ -179,28 +201,6 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { } } - async $init(params: PluginManagerInitializeParams): Promise { - this.storageProxy.init(params.globalState, params.workspaceState); - - this.envExt.setQueryParameters(params.env.queryParams); - this.envExt.setLanguage(params.env.language); - this.terminalService.$setShell(params.env.shell); - this.envExt.setUIKind(params.env.uiKind); - this.envExt.setApplicationName(params.env.appName); - this.envExt.setAppHost(params.env.appHost); - - this.preferencesManager.init(params.preferences); - - if (params.extApi) { - this.host.initExtApi(params.extApi); - } - - this.webview.init(params.webview); - this.jsonValidation = params.jsonValidation; - - this.supportedActivationEvents = new Set(params.supportedActivationEvents ?? []); - } - async $start(params: PluginManagerStartParams): Promise { this.configStorage = params.configStorage; @@ -239,15 +239,16 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { contributes.jsonValidation = (contributes.jsonValidation || []).concat(this.jsonValidation); } this.registry.set(plugin.model.id, plugin); - if (plugin.pluginPath && Array.isArray(plugin.rawModel.activationEvents)) { + const activationEvents = this.getActivationEvents(plugin); + if (plugin.pluginPath && activationEvents) { const activation = () => this.$activatePlugin(plugin.model.id); // an internal activation event is a subject to change this.setActivation(`onPlugin:${plugin.model.id}`, activation); - const unsupportedActivationEvents = plugin.rawModel.activationEvents.filter(e => !this.supportedActivationEvents.has(e.split(':')[0])); + const unsupportedActivationEvents = activationEvents.filter(e => !this.isSupportedActivationEvent(e)); if (unsupportedActivationEvents.length) { console.warn(`Unsupported activation events: ${unsupportedActivationEvents.join(', ')}, please open an issue: https://github.com/eclipse-theia/theia/issues/new`); } - for (let activationEvent of plugin.rawModel.activationEvents) { + for (let activationEvent of activationEvents) { if (activationEvent === 'onUri') { activationEvent = `onUri:theia://${plugin.model.id}`; } @@ -255,6 +256,14 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { } } } + + protected getActivationEvents(plugin: Plugin): string[] | undefined { + const result = plugin.rawModel.activationEvents; + return Array.isArray(result) ? result : undefined; + } + + protected abstract isSupportedActivationEvent(activationEvent: string): boolean; + protected setActivation(activationEvent: string, activation: () => Promise): void { const activations = this.activations.get(activationEvent) || []; activations.push(activation); @@ -363,11 +372,12 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { const globalStoragePath = join(configStorage.hostGlobalStoragePath, plugin.model.id); const extension = new PluginExt(this, plugin); const extensionModeValue = plugin.isUnderDevelopment ? types.ExtensionMode.Development : types.ExtensionMode.Production; + const pluginContext: theia.PluginContext = { extensionPath: extension.extensionPath, extensionUri: extension.extensionUri, - globalState: new GlobalState(plugin.model.id, true, this.storageProxy), - workspaceState: new Memento(plugin.model.id, false, this.storageProxy), + globalState: new GlobalState(plugin.model.id, true, this.storage), + workspaceState: new Memento(plugin.model.id, false, this.storage), subscriptions: subscriptions, asAbsolutePath: asAbsolutePath, logPath: logPath, @@ -431,6 +441,49 @@ export class PluginManagerExtImpl implements PluginManagerExt, PluginManager { return this.onDidChangeEmitter.event; } + protected fireOnDidChange(): void { + this.onDidChangeEmitter.fire(undefined); + } + +} + +@injectable() +export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl implements PluginManagerExt { + + @inject(PreferenceRegistryExtImpl) + protected readonly preferencesManager: PreferenceRegistryExtImpl; + + @inject(WebviewsExtImpl) + protected readonly webview: WebviewsExtImpl; + + private supportedActivationEvents: Set; + + async $init(params: PluginManagerInitializeParams): Promise { + this.storage.init(params.globalState, params.workspaceState); + + this.envExt.setQueryParameters(params.env.queryParams); + this.envExt.setUIKind(params.env.uiKind); + this.envExt.setLanguage(params.env.language); + this.terminalService.$setShell(params.env.shell); + this.envExt.setApplicationName(params.env.appName); + this.envExt.setAppHost(params.env.appHost); + + this.preferencesManager.init(params.preferences); + + if (params.extApi) { + this.host.initExtApi(params.extApi); + } + + this.webview.init(params.webview); + this.jsonValidation = params.jsonValidation; + + this.supportedActivationEvents = new Set(params.supportedActivationEvents ?? []); + } + + protected isSupportedActivationEvent(activationEvent: string): boolean { + return this.supportedActivationEvents.has(activationEvent.split(':')[0]); + } + } // for electron diff --git a/packages/plugin-ext/src/plugin/plugin-storage.ts b/packages/plugin-ext/src/plugin/plugin-storage.ts index 9a9449ea44d76..eb2820c8d847e 100644 --- a/packages/plugin-ext/src/plugin/plugin-storage.ts +++ b/packages/plugin-ext/src/plugin/plugin-storage.ts @@ -15,7 +15,8 @@ // ***************************************************************************** import * as theia from '@theia/plugin'; -import { Event, Emitter } from '@theia/core/lib/common/event'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { Disposable, DisposableGroup, Event, Emitter } from '@theia/core'; import { PLUGIN_RPC_CONTEXT, StorageMain, StorageExt } from '../common/plugin-api-rpc'; import { KeysToAnyValues, KeysToKeysToAnyValue } from '../common/types'; import { RPCProtocol } from '../common/rpc-protocol'; @@ -27,7 +28,7 @@ export class Memento implements theia.Memento { constructor( private readonly pluginId: string, private readonly isPluginGlobalData: boolean, - private readonly storage: KeyValueStorageProxy + private readonly storage: InternalStorageExt ) { this.cache = storage.getPerPluginData(pluginId, isPluginGlobalData); @@ -69,11 +70,28 @@ export class GlobalState extends Memento { setKeysForSync(keys: readonly string[]): void { } } +export const InternalStorageExt = Symbol('InternalStorageExt'); +export interface InternalStorageExt extends StorageExt { + + init(initGlobalData: KeysToKeysToAnyValue, initWorkspaceData: KeysToKeysToAnyValue): void; + + getPerPluginData(key: string, isGlobal: boolean): KeysToAnyValues; + + setPerPluginData(key: string, value: KeysToAnyValues, isGlobal: boolean): Promise; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + storageDataChangedEvent(listener: (e: KeysToKeysToAnyValue) => any, thisArgs?: any, disposables?: DisposableGroup): Disposable; + + $updatePluginsWorkspaceData(workspaceData: KeysToKeysToAnyValue): void; + +} + /** * Singleton. * Is used to proxy storage requests to main side. */ -export class KeyValueStorageProxy implements StorageExt { +@injectable() +export class KeyValueStorageProxy implements InternalStorageExt { private storageDataChangedEmitter = new Emitter(); public readonly storageDataChangedEvent: Event = this.storageDataChangedEmitter.event; @@ -83,7 +101,7 @@ export class KeyValueStorageProxy implements StorageExt { private globalDataCache: KeysToKeysToAnyValue; private workspaceDataCache: KeysToKeysToAnyValue; - constructor(rpc: RPCProtocol) { + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.STORAGE_MAIN); } diff --git a/packages/plugin-ext/src/plugin/preference-registry.spec.ts b/packages/plugin-ext/src/plugin/preference-registry.spec.ts index 8874ccfff41fd..aaba6b4c6f97f 100644 --- a/packages/plugin-ext/src/plugin/preference-registry.spec.ts +++ b/packages/plugin-ext/src/plugin/preference-registry.spec.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { Container } from '@theia/core/shared/inversify'; import { PreferenceRegistryExtImpl, PreferenceScope } from './preference-registry'; import * as chai from 'chai'; import { WorkspaceExtImpl } from '../plugin/workspace'; @@ -38,7 +39,11 @@ describe('PreferenceRegistryExtImpl:', () => { const mockWorkspace: WorkspaceExtImpl = { workspaceFolders: [{ uri: workspaceRoot, name: 'workspace-root', index: 0 }] } as WorkspaceExtImpl; beforeEach(() => { - preferenceRegistryExtImpl = new PreferenceRegistryExtImpl(mockRPC, mockWorkspace); + const container = new Container(); + container.bind(RPCProtocol).toConstantValue(mockRPC); + container.bind(WorkspaceExtImpl).toConstantValue(mockWorkspace); + container.bind(PreferenceRegistryExtImpl).toSelf().inSingletonScope(); + preferenceRegistryExtImpl = container.get(PreferenceRegistryExtImpl); }); describe('Prototype pollution', () => { diff --git a/packages/plugin-ext/src/plugin/preference-registry.ts b/packages/plugin-ext/src/plugin/preference-registry.ts index 465c7122ec4af..7d579ce1c5ef7 100644 --- a/packages/plugin-ext/src/plugin/preference-registry.ts +++ b/packages/plugin-ext/src/plugin/preference-registry.ts @@ -16,6 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { Emitter, Event } from '@theia/core/lib/common/event'; import { isOSX, isWindows } from '@theia/core/lib/common/os'; import { URI } from '@theia/core/shared/vscode-uri'; @@ -78,18 +79,23 @@ export class TheiaWorkspace extends Workspace { } } +@injectable() export class PreferenceRegistryExtImpl implements PreferenceRegistryExt { + @inject(RPCProtocol) + protected rpc: RPCProtocol; + + @inject(WorkspaceExtImpl) + protected readonly workspace: WorkspaceExtImpl; + private proxy: PreferenceRegistryMain; private _preferences: Configuration; private readonly _onDidChangeConfiguration = new Emitter(); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - constructor( - rpc: RPCProtocol, - private readonly workspace: WorkspaceExtImpl - ) { - this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN); + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN); } init(data: PreferenceData): void { diff --git a/packages/plugin-ext/src/plugin/secrets-ext.ts b/packages/plugin-ext/src/plugin/secrets-ext.ts index 5bd9a18bbabcf..acfaba814b07c 100644 --- a/packages/plugin-ext/src/plugin/secrets-ext.ts +++ b/packages/plugin-ext/src/plugin/secrets-ext.ts @@ -20,17 +20,37 @@ *--------------------------------------------------------------------------------------------*/ // code copied and modified from https://github.com/microsoft/vscode/blob/1.55.2/src/vs/workbench/api/common/extHostSecrets.ts +import { inject, injectable } from '@theia/core/shared/inversify'; import { Plugin, PLUGIN_RPC_CONTEXT, SecretsExt, SecretsMain } from '../common/plugin-api-rpc'; import { RPCProtocol } from '../common/rpc-protocol'; import { Event, Emitter } from '@theia/core/lib/common/event'; +import { Disposable, DisposableGroup } from '@theia/core'; import * as theia from '@theia/plugin'; -export class SecretsExtImpl implements SecretsExt { +export interface PasswordChange { + extensionId: string; + key: string; +} + +export const InternalSecretsExt = Symbol('InternalSecretsExt'); +export interface InternalSecretsExt extends SecretsExt { + get(extensionId: string, key: string): Promise; + + store(extensionId: string, key: string, value: string): Promise; + + delete(extensionId: string, key: string): Promise; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onDidChangePassword(listener: (e: PasswordChange) => any, thisArgs?: any, disposables?: DisposableGroup): Disposable; +} + +@injectable() +export class SecretsExtImpl implements InternalSecretsExt { private proxy: SecretsMain; - private onDidChangePasswordEmitter = new Emitter<{ extensionId: string, key: string }>(); + private onDidChangePasswordEmitter = new Emitter(); readonly onDidChangePassword = this.onDidChangePasswordEmitter.event; - constructor(rpc: RPCProtocol) { + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.SECRETS_MAIN); } @@ -54,12 +74,12 @@ export class SecretsExtImpl implements SecretsExt { export class SecretStorageExt implements theia.SecretStorage { protected readonly id: string; - readonly secretState: SecretsExtImpl; + readonly secretState: InternalSecretsExt; private onDidChangeEmitter = new Emitter(); readonly onDidChange: Event = this.onDidChangeEmitter.event; - constructor(pluginDescription: Plugin, secretState: SecretsExtImpl) { + constructor(pluginDescription: Plugin, secretState: InternalSecretsExt) { this.id = pluginDescription.model.id.toLowerCase(); this.secretState = secretState; diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index 627c322b4ec4b..79219e4103157 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { UUID } from '@theia/core/shared/@phosphor/coreutils'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { Terminal, TerminalOptions, PseudoTerminalOptions, ExtensionTerminalOptions, TerminalState } from '@theia/plugin'; import { TerminalServiceExt, TerminalServiceMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { RPCProtocol } from '../common/rpc-protocol'; @@ -46,6 +47,7 @@ export function getIconClass(options: theia.TerminalOptions | theia.ExtensionTer * Provides high level terminal plugin api to use in the Theia plugins. * This service allow(with help proxy) create and use terminal emulator. */ + @injectable() export class TerminalServiceExtImpl implements TerminalServiceExt { private readonly proxy: TerminalServiceMain; @@ -75,7 +77,7 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { private readonly onDidChangeShellEmitter = new Emitter(); readonly onDidChangeShell: theia.Event = this.onDidChangeShellEmitter.event; - constructor(rpc: RPCProtocol) { + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TERMINAL_MAIN); } diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index 8a1fe9da7f6a0..bb86b4903e43a 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -15,6 +15,7 @@ // ***************************************************************************** import { v4 } from 'uuid'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { Plugin, WebviewsExt, WebviewPanelViewState, WebviewsMain, PLUGIN_RPC_CONTEXT, WebviewInitData, /* WebviewsMain, PLUGIN_RPC_CONTEXT */ } from '../common/plugin-api-rpc'; import * as theia from '@theia/plugin'; import { RPCProtocol } from '../common/rpc-protocol'; @@ -25,8 +26,15 @@ import { WorkspaceExtImpl } from './workspace'; import { PluginIconPath } from './plugin-icon-path'; import { hashValue } from '@theia/core/lib/common/uuid'; +@injectable() export class WebviewsExtImpl implements WebviewsExt { - private readonly proxy: WebviewsMain; + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + + @inject(WorkspaceExtImpl) + protected readonly workspace: WorkspaceExtImpl; + + private proxy: WebviewsMain; private readonly webviewPanels = new Map(); private readonly webviews = new Map(); private readonly serializers = new Map(); readonly onDidDispose: Event = this.onDidDisposeEmitter.event; - constructor( - rpc: RPCProtocol, - private readonly workspace: WorkspaceExtImpl, - ) { - this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.WEBVIEWS_MAIN); + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(PLUGIN_RPC_CONTEXT.WEBVIEWS_MAIN); } init(initData: WebviewInitData): void { diff --git a/packages/plugin-ext/src/plugin/workspace.ts b/packages/plugin-ext/src/plugin/workspace.ts index ea30c844dc6ea..9f8a1d812193d 100644 --- a/packages/plugin-ext/src/plugin/workspace.ts +++ b/packages/plugin-ext/src/plugin/workspace.ts @@ -21,6 +21,7 @@ import * as paths from 'path'; import * as theia from '@theia/plugin'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; import { @@ -44,8 +45,18 @@ import * as Converter from './type-converters'; import { FileStat } from '@theia/filesystem/lib/common/files'; import { isUndefinedOrNull, isUndefined } from '../common/types'; +@injectable() export class WorkspaceExtImpl implements WorkspaceExt { + @inject(RPCProtocol) + protected readonly rpc: RPCProtocol; + + @inject(EditorsAndDocumentsExtImpl) + protected editorsAndDocuments: EditorsAndDocumentsExtImpl; + + @inject(MessageRegistryExt) + protected messageService: MessageRegistryExt; + private proxy: WorkspaceMain; private workspaceFoldersChangedEmitter = new Emitter(); @@ -63,10 +74,9 @@ export class WorkspaceExtImpl implements WorkspaceExt { private canonicalUriProviders = new Map(); - constructor(rpc: RPCProtocol, - private editorsAndDocuments: EditorsAndDocumentsExtImpl, - private messageService: MessageRegistryExt) { - this.proxy = rpc.getProxy(Ext.WORKSPACE_MAIN); + @postConstruct() + initialize(): void { + this.proxy = this.rpc.getProxy(Ext.WORKSPACE_MAIN); } get rootPath(): string | undefined { diff --git a/sample-plugins/sample-namespace/plugin-gotd/.gitignore b/sample-plugins/sample-namespace/plugin-gotd/.gitignore new file mode 100644 index 0000000000000..aa1ec1ea06181 --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/.gitignore @@ -0,0 +1 @@ +*.tgz diff --git a/sample-plugins/sample-namespace/plugin-gotd/LICENSE b/sample-plugins/sample-namespace/plugin-gotd/LICENSE new file mode 100644 index 0000000000000..e48e0963459bf --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/LICENSE @@ -0,0 +1,277 @@ +Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + +"Contributor" means any person or entity that Distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions Distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement +or any Secondary License (as applicable), including Contributors. + +"Derivative Works" shall mean any work, whether in Source Code or other +form, that is based on (or derived from) the Program and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. + +"Modified Works" shall mean any work in Source Code or other form that +results from an addition to, deletion from, or modification of the +contents of the Program, including, for purposes of clarity any new file +in Source Code form that contains any contents of the Program. Modified +Works shall not include works that contain only declarations, +interfaces, types, classes, structures, or files of the Program solely +in each case in order to link to, bind by name, or subclass the Program +or Modified Works thereof. + +"Distribute" means the acts of a) distributing or b) making available +in any manner that enables the transfer of a copy. + +"Source Code" means the form of a Program preferred for making +modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Secondary License" means either the GNU General Public License, +Version 2.0, or any later versions of that license, including any +exceptions or additional permissions as identified by the initial +Contributor. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + +3. REQUIREMENTS + +3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + +3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + +3.3 Contributors may not remove or alter any copyright, patent, +trademark, attribution notices, disclaimers of warranty, or limitations +of liability ("notices") contained within the Program from any copy of +the Program which they Distribute, provided that Contributors may add +their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, +the Contributor who includes the Program in a commercial product +offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes +the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and indemnify every +other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits +and other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program +in a commercial product offering. The obligations in this section do not +apply to any claims or Losses relating to any actual or alleged +intellectual property infringement. In order to qualify, an Indemnified +Contributor must: a) promptly notify the Commercial Contributor in +writing of such claim, and b) allow the Commercial Contributor to control, +and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those performance +claims and warranties, and if a court requires any other Contributor to +pay any damages as a result, the Commercial Contributor must pay +those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF +TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs +or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS +SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software +or hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and +may only be modified in the following manner. The Agreement Steward +reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement +Steward has the right to modify this Agreement. The Eclipse Foundation +is the initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +Distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to Distribute the Program (including its +Contributions) under the new version. + +Except as expressly stated in Sections 2(a) and 2(b) above, Recipient +receives no rights or licenses to the intellectual property of any +Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted +under this Agreement are reserved. Nothing in this Agreement is intended +to be enforceable by any entity that is not a Contributor or Recipient. +No third-party beneficiary rights are created under this Agreement. + +Exhibit A - Form of Secondary Licenses Notice + +"This Source Code may also be made available under the following +Secondary Licenses when the conditions for such availability set forth +in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), +version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. diff --git a/sample-plugins/sample-namespace/plugin-gotd/README.md b/sample-plugins/sample-namespace/plugin-gotd/README.md new file mode 100644 index 0000000000000..f16aeb4a4ca6a --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/README.md @@ -0,0 +1,44 @@ +

+ +
+ +theia-ext-logo + +

ECLIPSE THEIA - EXAMPLE HEADLESS PLUGIN USING THE API PROVIDER SAMPLE

+ +
+ +
+ +## Description + +An example demonstrating three Theia concepts: + +- "headless plugins", being plugins loaded in a single plugin host Node process outside of the context of any frontend connection +- client of a custom "Greeting of the Day" API provided by the `@theia/api-provider-sample` extension +- "backend plugins", being plugins loaded in the backend plugin host process for a frontend connection + +Thus this plug-in demonstrates the capability of a VS Code-compatible plugin to provide two distinct backend entry-points for the two different backend contexts. +As declared in the `package.json` manifest: +- in the headless plugin host, the entry-point script is `headless.js` via the Theia-specific the `"theiaPlugin"` object +- in the backend plugin host for a frontend connection, the entry-point script is `backend.js` via the VS Code standard `"main"` property + +The plugin is for reference and test purposes only and is not published on `npm` (`private: true`). + +### Greeting of the Day + +The sample uses the custom `gotd` API to log a greeting upon activation. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/sample-plugins/sample-namespace/plugin-gotd/extension.js b/sample-plugins/sample-namespace/plugin-gotd/extension.js new file mode 100644 index 0000000000000..c902426005a8a --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/extension.js @@ -0,0 +1,41 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +const vscode = require('vscode'); + +function extensionKind(kind) { + switch (kind) { + case vscode.ExtensionKind.UI: + return 'UI'; + case vscode.ExtensionKind.Workspace: + return 'Workspace'; + default: + return 'unknown'; + } +} + +async function activate () { + console.log('[GOTD-BE]', `Running version ${vscode.version} of the VS Code Extension API.`); + console.log('[GOTD-BE]', `It looks like your shell is ${vscode.env.shell}.`); + const myself = vscode.extensions.getExtension('.plugin-gotd'); + if (myself) { + console.log('[GOTD-BE]', `And I am a(n) ${extensionKind(myself.extensionKind)} plugin installed at ${myself.extensionPath}.`); + } +} + +module.exports = { + activate +}; diff --git a/sample-plugins/sample-namespace/plugin-gotd/headless.js b/sample-plugins/sample-namespace/plugin-gotd/headless.js new file mode 100644 index 0000000000000..40a6435927e93 --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/headless.js @@ -0,0 +1,88 @@ +// ***************************************************************************** +// Copyright (C) 2023 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +const gotd = require('@theia/api-provider-sample'); + +const GreetingKind = gotd.greeting.GreetingKind; + +const toDispose = []; + +function greetingKindsToString(greetingKinds) { + return greetingKinds.map(kind => { + switch (kind) { + case GreetingKind.DIRECT: + return 'DIRECT'; + case GreetingKind.QUIRKY: + return 'QUIRKY'; + case GreetingKind.SNARKY: + return 'SNARKY'; + default: + return ''; + } + }).join(', '); +} + +async function greet(greeter) { + const message = await greeter.getMessage(); + console.log('[GOTD]', message); +} + +let busy = false; +const pending = []; +function later(_fn) { + const task = (fn) => () => { + fn(); + const next = pending.shift(); + if (next) { + setTimeout(task(next), 1000); + } else { + busy = false; + } + } + + if (busy) { + pending.push(_fn); + } else { + busy = true; + setTimeout(task(_fn), 1000); + } +} + +async function activate () { + const greeter = await gotd.greeting.createGreeter(); + toDispose.push(greeter.onGreetingKindsChanged( + kinds => { + console.log('[GOTD]', + `Now supporting these kinds of greeting: ${greetingKindsToString(kinds)}.`); + if (kinds.length > 0) { + greet(greeter); + } + })); + + greet(greeter); + + later(() => greeter.setGreetingKind(GreetingKind.DIRECT, false)); + later(() => greeter.setGreetingKind(GreetingKind.QUIRKY)); + later(() => greeter.setGreetingKind(GreetingKind.SNARKY)); +} + +module.exports = { + activate, + deactivate: function () { + console.log('[GOTD]', 'Cleaning up.'); + toDispose.forEach(d => d.dispose()); + } +}; diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json new file mode 100644 index 0000000000000..98413ec1b3b91 --- /dev/null +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -0,0 +1,34 @@ +{ + "private": true, + "name": "plugin-gotd", + "version": "1.45.0", + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "engines": { + "vscode": "^1.84.0" + }, + "main": "extension", + "activationEvents": [ + "*" + ], + "devDependencies": { + "@theia/api-provider-sample": "^1.45.0" + }, + "scripts": { + "prepare": "yarn -s package", + "package": "yarn pack" + }, + "theiaPlugin": { + "headless": "headless" + }, + "headless": { + "activationEvents": [ + "*" + ], + "contributes": { + } + } +} diff --git a/tsconfig.json b/tsconfig.json index ac07be3ca619e..3b0eb7ae410c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "./configs/base.tsconfig.json", - "include": [ ], + "include": [], "compilerOptions": { "composite": true, "allowJs": true @@ -36,6 +36,9 @@ { "path": "dev-packages/request" }, + { + "path": "examples/api-provider-sample" + }, { "path": "examples/api-samples" }, @@ -129,6 +132,9 @@ { "path": "packages/plugin-ext" }, + { + "path": "packages/plugin-ext-headless" + }, { "path": "packages/plugin-ext-vscode" }, From 1a56ba96fdc9b6df3a230df7b44e22e5785e3abd Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 22 Jan 2024 10:14:21 +0100 Subject: [PATCH 063/441] Fix dynamic notebook widgets resizing (#13289) * fix resizing of dynamic widgets Signed-off-by: Jonah Iden * Update output-webview-internal.ts removed forgotten debug changes --------- Signed-off-by: Jonah Iden --- .../notebooks/renderers/output-webview-internal.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 7c77c3de51284..4d1f76c91dba4 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -314,10 +314,17 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { // we need to check for all images are loaded. Otherwise we can't determine the correct height of the output const images = Array.from(document.images); if (images.length > 0) { - Promise.all(images.filter(img => !img.complete).map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))).then(() => - theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight })); + Promise.all(images.filter(img => !img.complete).map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))).then(() => { + theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight }); + new ResizeObserver(() => + theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight })) + .observe(document.body); + }); } else { theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight }); + new ResizeObserver(() => + theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight })) + .observe(document.body); } } From a8ee1570d686e67be8269256375d7d42655e91cc Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Fri, 19 Jan 2024 21:07:26 +0100 Subject: [PATCH 064/441] Support specifying the port of a remote SSH connection Contributed on behalf of STMicroelectronics. Fixes https://github.com/eclipse-theia/theia/issues/13295 Change-Id: If29f06b34749091797ae861101159195afcc5a8e --- CHANGELOG.md | 1 + .../ssh/remote-ssh-connection-provider.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 459ea9036d021..58983f80d9706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - [terminal] rename terminal.sendText() parameter from addNewLine to shouldExecute [#13236](https://github.com/eclipse-theia/theia/pull/13236) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics - [core] added preference 'workbench.tree.indent' to control the indentation in the tree widget [#13179](https://github.com/eclipse-theia/theia/pull/13179) - contributed on behalf of STMicroelectronics +- [remote] upport specifying the port of a remote SSH connection [#13296](https://github.com/eclipse-theia/theia/pull/13296) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_not_yet_released) diff --git a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts index 2f942de3c9a0f..3de1a97bea596 100644 --- a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts +++ b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts @@ -86,13 +86,14 @@ export class RemoteSSHConnectionProviderImpl implements RemoteSSHConnectionProvi const deferred = new Deferred(); const sshClient = new ssh2.Client(); const identityFiles = await this.identityFileCollector.gatherIdentityFiles(); - const sshAuthHandler = this.getAuthHandler(user, host, identityFiles); + const hostUrl = new URL(`ssh://${host}`); + const sshAuthHandler = this.getAuthHandler(user, hostUrl.hostname, identityFiles); sshClient .on('ready', async () => { const connection = new RemoteSSHConnection({ client: sshClient, id: v4(), - name: host, + name: hostUrl.hostname, type: 'SSH' }); try { @@ -102,11 +103,12 @@ export class RemoteSSHConnectionProviderImpl implements RemoteSSHConnectionProvi deferred.reject(err); } }).on('end', () => { - console.log(`Ended remote connection to host '${user}@${host}'`); + console.log(`Ended remote connection to host '${user}@${hostUrl.hostname}'`); }).on('error', err => { deferred.reject(err); }).connect({ - host: host, + host: hostUrl.hostname, + port: hostUrl.port ? parseInt(hostUrl.port, 10) : undefined, username: user, authHandler: (methodsLeft, successes, callback) => (sshAuthHandler(methodsLeft, successes, callback), undefined) }); From 5e0f0764ca5a58139f40c128c87975c13d759e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 23 Jan 2024 10:24:13 +0000 Subject: [PATCH 065/441] Upgrade built-ins to 1.83.1. (#13298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13066 Contributed on behalof of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 1 + examples/api-tests/src/typescript.spec.js | 18 +++--------------- package.json | 4 ++-- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58983f80d9706..89c38ab053d4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [plugin] stub multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics +- [builtins] update built-ins to version 1.83.1 [#13298](https://github.com/eclipse-theia/theia/pull/13298) - contributed on behalf of STMicroelectronics - [terminal] rename terminal.sendText() parameter from addNewLine to shouldExecute [#13236](https://github.com/eclipse-theia/theia/pull/13236) - contributed on behalf of STMicroelectronics - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics - [core] added preference 'workbench.tree.indent' to control the indentation in the tree widget [#13179](https://github.com/eclipse-theia/theia/pull/13179) - contributed on behalf of STMicroelectronics diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index c4fe77e9a098f..c399bdbda5f73 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -19,6 +19,7 @@ describe('TypeScript', function () { this.timeout(30_000); const { assert } = chai; + const { timeout } = require('@theia/core/lib/common/promise-util'); const Uri = require('@theia/core/lib/common/uri'); const { DisposableCollection } = require('@theia/core/lib/common/disposable'); @@ -700,9 +701,7 @@ SPAN { return !!node && node.style.visibility !== 'hidden'; }; - assert.isFalse(lightBulbVisible(), 'Failed at assert 2'); - await waitForAnimation(() => lightBulbVisible()); - + await timeout(1000); // quick fix is always available: need to wait for the error fix to become available. await commands.executeCommand('editor.action.quickFix'); const codeActionSelector = '.codeActionWidget'; assert.isFalse(!!document.querySelector(codeActionSelector), 'Failed at assert 3 - codeActionWidget should not be visible'); @@ -721,20 +720,9 @@ SPAN { assert.isTrue(lightBulbVisible(), 'Failed at assert 4'); keybindings.dispatchKeyDown('Enter'); console.log('Waiting for confirmation that QuickFix has taken effect'); - await waitForAnimation(() => { - const quickFixHasTakenEffect = !lightBulbVisible(); - if (!quickFixHasTakenEffect) { - console.log('...'); - return false; - } - return true; - }, 5000, 'Quickfix widget has not been dismissed despite attempts to accept suggestion'); - await waitForAnimation(() => currentChar() === 'd', 5000, 'Failed to detect expected selected char: "d"'); + await waitForAnimation(() => currentChar() === 'd', 10000, 'Failed to detect expected selected char: "d"'); assert.equal(currentChar(), 'd', 'Failed at assert 5'); - - await waitForAnimation(() => !lightBulbVisible()); - assert.isFalse(lightBulbVisible(), 'Failed at assert 6'); }); it('editor.action.formatDocument', async function () { diff --git a/package.json b/package.json index 250eb85206fa3..df4da5f8cce40 100644 --- a/package.json +++ b/package.json @@ -103,10 +103,10 @@ ], "theiaPluginsDir": "plugins", "theiaPlugins": { - "eclipse-theia.builtin-extension-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.79.0/file/eclipse-theia.builtin-extension-pack-1.79.0.vsix", + "eclipse-theia.builtin-extension-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.83.1/file/eclipse-theia.builtin-extension-pack-1.83.1.vsix", "EditorConfig.EditorConfig": "https://open-vsx.org/api/EditorConfig/EditorConfig/0.16.6/file/EditorConfig.EditorConfig-0.16.6.vsix", "dbaeumer.vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.4.2/file/dbaeumer.vscode-eslint-2.4.2.vsix", - "ms-vscode.js-debug": "https://open-vsx.org/api/ms-vscode/js-debug/1.78.0/file/ms-vscode.js-debug-1.78.0.vsix", + "ms-vscode.js-debug": "https://open-vsx.org/api/ms-vscode/js-debug/1.83.1/file/ms-vscode.js-debug-1.83.1.vsix", "ms-vscode.js-debug-companion": "https://open-vsx.org/api/ms-vscode/js-debug-companion/1.1.2/file/ms-vscode.js-debug-companion-1.1.2.vsix" }, "theiaPluginsExcludeIds": [ From aeee83bbf4ea21daec23c61434761666bd36a571 Mon Sep 17 00:00:00 2001 From: "Christian W. Damus" Date: Tue, 23 Jan 2024 08:54:51 -0500 Subject: [PATCH 066/441] Update plugin API docs for headless plugins and Inversify DI (#13299) Update the plugin API documentation to cover - headless plugins - Inversify DI in the plugin host - simplified ext API initialization and API factory using new PluginContainerModule capability Fixes #13290 Signed-off-by: Christian W. Damus --- doc/Plugin-API.md | 90 ++++++-- doc/images/headless-plugin-diagram.drawio | 163 ++++++++++++++ doc/images/headless-plugin-diagram.svg | 1 + doc/images/plugin-api-diagram.drawio.xml | 210 +++++++++++++++++- doc/images/plugin-api-diagram.png | Bin 42806 -> 0 bytes doc/images/plugin-api-diagram.svg | 1 + .../doc/how-to-add-new-custom-plugin-api.md | 161 +++++++------- 7 files changed, 526 insertions(+), 100 deletions(-) create mode 100644 doc/images/headless-plugin-diagram.drawio create mode 100644 doc/images/headless-plugin-diagram.svg delete mode 100644 doc/images/plugin-api-diagram.png create mode 100644 doc/images/plugin-api-diagram.svg diff --git a/doc/Plugin-API.md b/doc/Plugin-API.md index 539b67bfa3b44..ae614f70a101b 100644 --- a/doc/Plugin-API.md +++ b/doc/Plugin-API.md @@ -5,13 +5,22 @@ Therefore, it supports [three extension mechanisms: VS Code extensions, Theia ex In the following, we focus on the mechanics of Theia plugins and Theia’s compatibility with the [VS Code Extension API](https://code.visualstudio.com/api) in order to support running VS Code extensions in Theia. This documentation aims to support developers extending Theia’s plugin API to either enhance the extensibility of Theia via plugins and/or increase Theia’s coverage of the VS Code Extension API – and with that the number of VS Code extensions that can be used in Theia. -Theia plugins, as well as VS Code extensions, can be installed and removed from a Theia installation at runtime and may extend many different capabilities of Theia, such as theming, language support, debuggers, tree views, etc., via a clearly defined API. +Theia plugins, as well as VS Code extensions, can be installed and removed from a Theia installation at runtime. +There are three kinds of plugins that address different extensibility use cases: + +- VS Code plugins may extend many different user-facing capabilities of Theia, such as theming, language support, debuggers, tree views, etc., via a clearly defined API. +_In the context of VS Code itself these are called "extensions", not to be confused with build-time Theia extensions._ +- Theia plugins are a superset of VS Code plugins using a largely compatible API with some additional capabilities not applicable to VS Code +- Headless plugins address application-specific extension points. +They do not extend the user-facing capabilities of Theia as the other two categories of plugins do, but support application-specific services that, in turn, often do. +More information about headless plugins [is detailed below](#headless-plugins). + A plugin runs inside a "host process". -This is a sub-process spawned by Theia's backend to isolate the plugin from the main process. +This is a subprocess spawned by Theia's backend to isolate the plugin from the main process. This encapsulates the plugin to prevent it from arbitrarily accessing Theia services and potentially harm performance or functionality of Theia’s main functionality. -Instead, a plugin accesses Theia’s state and services via the plugin API. +Instead, a plugin accesses Theia’s state and services via the plugin API, if any, provided within its host process. -Theia’s plugin API thrives to be a super set of VS Code’s extension API to enable running VS Code extensions as Theia plugins. +Theia’s plugin API strives to be a superset of VS Code’s extension API to enable running VS Code extensions as Theia plugins. For many cases this already works well. A report on API compatibility is generated daily in the [vscode-theia-comparator repository](https://github.com/eclipse-theia/vscode-theia-comparator). Please note that the report only checks the API on an interface level – and not the compatibility of the interfaces’ implementation behaviour. @@ -23,10 +32,11 @@ The report can be found here: ## Relevant Theia source code -- [plugin](https://github.com/eclipse-theia/theia/tree/master/packages/plugin): Contains the API declaration of the theia plugin namespace -- [plugin-ext](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext): Contains both the mechanisms for running plugins and providing them with an API namespace and the implementation of the ‘theia’ plugin API +- [plugin](https://github.com/eclipse-theia/theia/tree/master/packages/plugin): Contains the API declaration of the theia plugin namespace. +- [plugin-ext](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext): Contains both the mechanisms for running plugins and providing them with an API namespace and the implementation of the ‘theia’ plugin API. - [plugin-ext-vscode](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext-vscode): Contains an implementation of the VS Code plugin API. -Since VS Code and Theia APIs are largely compatible, the initialization passes on the Theia plugin API and overrides a few members in the API object to be compatible to VS Code extensions (see [plugin-ext-vscode/src/node/plugin-vscode-init.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts)) +Since VS Code and Theia APIs are largely compatible, the initialization passes on the Theia plugin API and overrides a few members in the API object to be compatible to VS Code extensions (see [plugin-ext-vscode/src/node/plugin-vscode-init.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts)). +- [plugin-ext-headless](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext-headless): Contains the mechanism for running "headless" plugins in a dedicated backend plugin host process. ## API definition and exposure @@ -39,8 +49,8 @@ For VS Code plugins, the same API is available via the `vscode` namespace as exp Plugin containers are node processes (see [plugin-ext/src/hosted/node/plugin-host.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/hosted/node/plugin-host.ts))and web workers ([plugin-ext/src/hosted/browser/worker/worker-main.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts)). These expose the API in the following places: -- Browser: assign API object to `window['theia']` in [plugin-ext/src/hosted/browser/worker/worker-main.ts](https://github.com/eclipse-theia/theia/blob/541b300adc029ab1dd729da1ca49179ace1447b2/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts#L192) -- Back-end/Node: Override module loading for Theia plugins in [plugin-ext/src/hosted/node/scanners/backend-init-theia.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/hosted/node/scanners/backend-init-theia.ts) and for VS Code plugins in [plugin-ext-vscode/src/node/plugin-vscode-init.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts) +- Browser: assign API object to `window['theia']` in [plugin-ext/src/hosted/browser/worker/worker-main.ts](https://github.com/eclipse-theia/theia/blob/541b300adc029ab1dd729da1ca49179ace1447b2/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts#L192). +- Back-end/Node: Override module loading for Theia plugins in [plugin-ext/src/hosted/node/scanners/backend-init-theia.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/hosted/node/scanners/backend-init-theia.ts) and for VS Code plugins in [plugin-ext-vscode/src/node/plugin-vscode-init.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext-vscode/src/node/plugin-vscode-init.ts). **Note** that it is not necessary to adapt these for implementing new plugin API. @@ -48,15 +58,25 @@ These expose the API in the following places: As the plugin runs in a separate process, the plugin API cannot directly communicate with Theia. Instead, the plugin process and Theia’s main process communicate via RPC. -Therefore, the following "Main-Ext" pattern is used. -![Communication between Theia and Plugin API](./images/plugin-api-diagram.png) +For VS Code plugins and Theia plugins, the following "Main-Ext" pattern is used. +There is one instance of the plugin host process for each connected frontend. + +![Communication between Theia and Plugin API for VS Code Plugins](./images/plugin-api-diagram.svg) `Ext` refers to the code running on the plugin side inside the isolated host process. Therefore, this code cannot directly use any Theia services (e.g. via dependency injection). `Main` refers to code running inside the Theia frontend in the **browser** context. Therefore, it can access any Theia service just like a [build time Theia extension](https://theia-ide.org/docs/authoring_extensions/). +> [!NOTE] +> As the plugin hosts for VS Code plugins are scoped per frontend connection and the `Main` side resides in the browser, there is actually an indirection of the RPC communication channel via the Theia backend. +> This simply relays the messages in both directions as depicted in the diagram. + +For headless plugins, "Main-Ext" pattern is very similar, except that there is only one instance of the plugin host and the `Main` code runs in the **node** context, as there is no associated frontend context for headless plugins. + +![Communication between Theia and Plugin API for Headless Plugins](./images/headless-plugin-diagram.svg) + As the lifecycle of a plugin starts inside its process on the `Ext` side, anything that the plugin needs from Theia (e.g. state, command execution, access to services) has to be invoked over RCP via an implementation on the `Main` side. In the inverse direction, the same is true for code that runs on the `Main` side and that needs something from the plugin side (e.g. changing plugin state after a user input). It needs to be invoked over RCP via an implementation on the `Ext` side. @@ -68,7 +88,7 @@ The implementations do not have explicit dependencies to each other. ### Encoding and Decoding RPC Messages -The communication between each side of the API is goverend by proxies that use a `RpcProtocol` on a given channel to transmit RPC messages such as requests and notifications. +The communication between each side of the API is governed by proxies that use an `RpcProtocol` on a given channel to transmit RPC messages such as requests and notifications. In Theia, the encoding and decoding process of RPC messages can be customized through dedicated `RpcMessageEncoder` and `RpcMessageDecoder` classes that can be provided when a new RpcProtocol is created. The `RpcMessageEncoder` writes RPC messages to a buffer whereas the `RpcMessageDecoder` parses a binary message from a buffer into a RPC message. @@ -145,10 +165,10 @@ Further functions are just part of the implementations. Functions to be called over RCP must start with `$`, e.g. `$executeStuff`. - Define `Ext` and `Main` interfaces in [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts). -The interfaces should be suffixed with `Ext` and `Main` correspondingly (e.g. `LanguagesMain` and `LanguagesExt`) +The interfaces should be suffixed with `Ext` and `Main` correspondingly (e.g. `LanguagesMain` and `LanguagesExt`). - In [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts), add a proxy identifier for the `Ext` interface to `MAIN_RPC_CONTEXT` and one for the `Main` interface to `PLUGIN_RPC_CONTEXT` -- Create the `Ext` implementation in folder [plugin-ext/src/plugin](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext/src/plugin) -- Create the `Main` implementation in folder [plugin-ext/src/main/browser](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext/src/main/browser) +- Create the `Ext` implementation in folder [plugin-ext/src/plugin](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext/src/plugin). +- Create the `Main` implementation in folder [plugin-ext/src/main/browser](https://github.com/eclipse-theia/theia/tree/master/packages/plugin-ext/src/main/browser). - To communicate via RPC, each implementation has a proxy depending on the interface on the other side. For instance, see `LanguagesExtImpl` in [plugin-ext/src/plugin/languages.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/languages.ts) and `LanguagesMainImpl` in [plugin-ext/src/main/browser/languages-main.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/languages-main.ts). They each create the proxy to the other side in their constructors by using the proxy identifiers. @@ -167,7 +187,7 @@ The `Ext` side then gets the cached object, executes appropriate functions and r Another example to browse are the [TaskExtImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/tasks/tasks.ts) and [TaskMainImpl](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/main/browser/tasks-main.ts) classes. To [ensure correct type conversion](#encoding-and-decoding-rpc-messages) between the Theia backend and the plugin host we define an API protocol based on types and DTOs that can be transmitted safely. -The plugin API and it's types are defined in [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts) with some additional conversion on the `Ext` side being defined in [plugin-ext/src/plugin/type-converters.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/type-converters.ts). +The plugin API and its types are defined in [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts) with some additional conversion on the `Ext` side being defined in [plugin-ext/src/plugin/type-converters.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/type-converters.ts). Thus, this is also a good starting point to look for conversion utilities for existing types. ### Adding new types @@ -175,6 +195,42 @@ Thus, this is also a good starting point to look for conversion utilities for ex New classes and other types such as enums are usually implemented in [plugin-ext/src/plugin/types-impl.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/plugin/types-impl.ts). They can be added there and then we can add them to the API object created in the API factory. +## Headless Plugins + +The majority of plugin use cases are for extension of the Theia user experience via the VS Code compatible API provided by Theia. +These plugins use either the `vscode` API object if they use the `"vscode"` engine type in their package manifests or else the `theia` object if they use the `"theiaPlugin"` engine. +The lifecycle of these kinds of plugins is bound to _frontend connections_: for each connected frontend, the Theia backend spawns a plugin host host process dedicated to it in which these plugins are loaded and activated (as applicable to their declared activation events). +In the plugin host for a frontend connection these plugins have access to Theia services as discussed above, isolated from the main Theia backend process and from all of the other instances of the same plugin running in plugin hosts for all other frontend connections. + +Headless plugins, by contrast, are quite different in most respects: + +- They are encapsulated in a single plugin host process in the backend, separate from all frontend-connection plugin hosts. +This host is spun up only if there are any headless plugins to run in it. +- Theia does not export any default API object, analogous to `vscode` or `theia` for other plugins, as Theia itself defines no use cases for headless plugins. +Such use cases are entirely defined by the Theia-based application's requirements and reflected in its custom APIs defined [as described in this how-to document][custom-api-howto]. +- Theia supports neither any contribution points for headless plugins nor any non-trivial activation events (only `'*'` and `'onStartupFinished'`). +This is a corollary of the use cases being entirely application-specific: the application needs to define its own contribution points and activation events. +Currently this requires an application to enumerate the available deployed plugins via the [HostedPluginServer](https://github.com/eclipse-theia/theia/blob/1a56ba96fdc9b6df3a230df7b44e22e5785e3abd/packages/plugin-ext/src/common/plugin-protocol.ts#L1005) to parse their package manifests to extract application-specific contribution points and activation events, and to activate plugins via the [PluginManager::activatePlugin(pluginId)](https://github.com/eclipse-theia/theia/blob/1a56ba96fdc9b6df3a230df7b44e22e5785e3abd/packages/plugin-ext/src/common/plugin-api-rpc.ts#L182) API on the appropriate triggers. + +Thus, headless plugins are best suited to the contribution of third-party extensions of a Theia application's custom backend services, where those service are shared by all connected frontends or serve some kind of headless scenario like a CLI. + +A headless plugin may be restricted to only the headless deployment, in which case it may make this explicit by declaring the `"theiaHeadlessPlugin"` engine in its package manifest. +Alternatively, a VS Code or Theia plugin that extends the frontend user experience may also contribute a headless entrypoint for a headless deployment by identifying such entrypoint script in the `"headless"` property of the `"theiaPlugin"` object in its package manifest in addition to the `"main"` entrypoint (for VS Code plugins) or the `"theiaPlugin.backend"` entrypoint (for Theia plugins). + +The only API namespaces that are available to headless plugins are those custom APIs that are contributed by the application's custom build-time Theia extensions or by other headless plugins via the return results of their `activate()` functions. +For details of how to contribute custom API, see the [pertinent documentation][custom-api-howto]. + +[custom-api-howto]: https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md + +## Dependency Injection + +Both the `Main` and the `Ext` sides of the plugin API are configured using [InversifyJS](https://inversify.io) dependency injection. + +On the `Main` side, the usual mechanism is used to bind implementations of the API objects, consisting of `ContainerModule`s registered in `package.json` and loaded at start-up into Theia's Inversify `Container` by a generated script. + +On the `Ext` side, the plugin host initialization script creates and configures its Inversify `Container`. +You are encouraged to leverage this dependency injection in the definition of new API objects and the maintenance of existing ones, to promote reuse and substitutability of the various interface implementations. + ## Additional Links Talk by Thomas Maeder on writing plugin API: @@ -190,3 +246,5 @@ Theia versus VS Code API Comparator: Example of creating a custom namespace API and using in VS Code extensions: + +Example of a Theia extension defining a custom namespace API and a headless plugin that uses it: [Greeting-of-the-Day API Provider Sample](https://github.com/eclipse-theia/theia/blob/master/examples/api-provider-sample) and [Greeting-of-the-Day Client Sample Plugin](https://github.com/eclipse-theia/theia/blob/master/sample-plugins/sample-namespace/plugin-gotd) diff --git a/doc/images/headless-plugin-diagram.drawio b/doc/images/headless-plugin-diagram.drawio new file mode 100644 index 0000000000000..70518fc142f8a --- /dev/null +++ b/doc/images/headless-plugin-diagram.drawio @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/images/headless-plugin-diagram.svg b/doc/images/headless-plugin-diagram.svg new file mode 100644 index 0000000000000..139b275bc2b9a --- /dev/null +++ b/doc/images/headless-plugin-diagram.svg @@ -0,0 +1 @@ +
RPC
RPC
Main Proxy
Main Proxy
Ext API
Ext API
Registry
Registry
Plugin API
Plugin API
Backend
Backend
Headless Plugin Host
Headless Plugin Host
register(handle, DTO)
register(handle, DTO)
provideItems(handle, args)
provideItems(handle, args)
Main API
Main API
Main Impl
Main Impl
Ext Proxy
Ext Proxy
Contribution (application-defined)
Contribution (ap...
Headless Plugin
Headless P...
Ext Impl
register
register
provideItems
provideIte...
Node
Node
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/images/plugin-api-diagram.drawio.xml b/doc/images/plugin-api-diagram.drawio.xml index da096d9200eab..666f59d9f38e8 100644 --- a/doc/images/plugin-api-diagram.drawio.xml +++ b/doc/images/plugin-api-diagram.drawio.xml @@ -1 +1,209 @@ -5Vxbc5s4FP41nuk+xAMIcXmM7aTtTrrNNDuzbV92iFFsthh5hBzb/fUrgcAIYRs7XJwkL0EHSYFzvnMXGYDxYvOReMv5F+yjcGBo/mYAJgPD0IHtsF+csk0pjgZSwowEvpi0IzwEv5EgaoK6CnwUSxMpxiENljJxiqMITalE8wjBa3naEw7lv7r0ZkghPEy9UKX+E/h0Lqi65e5ufELBbC7+tGPY6Y2Fl00WbxLPPR+vCyRwMwBjgjFNrxabMQo58zK+pOtu99zNH4ygiNZZ8Pvfn2Bz+/HT2vPHP3/drCd3K/fKtMTD0W32xshnDBBDTOgcz3DkhTc76ojgVeQjvq3GRrs5dxgvGVFnxP8QpVshTW9FMSPN6SIUd9EmoN8L1z/4VkMDiuFkI7ZOBttsEFGy/Z7MhNnwR/HeblkyytalL8jfai/jBCnGKzJFB7iVAdAjM0QPzYO5fJliILxA7IHYQoJCjwbP8oN4AqGzfJ5Yek2Ity1MWOIgonFh53tOYBOEshmOwKRQNSADgl2kO2ajwqPtSAloTgCQeIlnL1yJ11IAJcNlPQ8oelh6CaPXzGjI0HgKwnCMQ0ySteD2djx23VyKz4hQtDksR5XtGX80mT+GKTRzXVBsG6a0eUGnM0ZWiqrA3tO5Z7TNPdcFoBnuuZlFPsQ9qHXJPbtd7mnaeKzxZTEl+BeS7vCfZvhqOcf5CitAqVttsdXpiK0tMM/U+mae+3rsoQ3gpdnDzMYcYh+K/Gse2LFRhCMej/hePE/4qcu8kxldNw5Q+VUEU4WBy2ineXfFfUNLxrKllbichidiVTHQO7KRUd4ojV+UjRoLCdSY4M+Hr38xyrf7sSJOhl8qi002t0LGRT0QJC8MZhEbTpn8EKOPuDYELHS/FjcWge8nAWuVjlWB40XqBA2Z7bqtqpNT5R/bUqYsRekntteGrivH9w5wzgrw9SMBPt/kHpGAcY3DoPGgX6RIR6N+aFXjo7ZdeJnptNr1PJZ1y34a8jyWzbM9yUg5UHU+RoW6tKcvhmq3vnhBxCj3BG+2F2q5CKYMXZivunKbCqssY1gST4Uxc7s0ZhmaC8K52XCnd33/+T1JxrZLkjEtVXGsTiUD2zY8I6NFwwMsFdkgh3+Rg05rHFRN9zc0C+Js7/cBbRuWjQ6o8AndQltNhe/D1SzxCZdrd5qQhcv0xJQ9QEV22K0w1NT6lmDGrsh/u4Iw9uVyxTBJ71IMQE3Rc534hGP6dmXhgIuTRY3qe6FcMg29OA6mh4okefYmZW41GzN5olZY1U1jRlSCj6ZoZrWAOyrwmDJ+TLeEi9oFHre0kVnaqOUCjwkU2JEkXkHkw9yLuEoabGdt8vfXP1Q8MnnfeY8olGFYX9cJioPf3mOyH4eRaIuxzeFoACeHdF00icXiQd6aLULugKLttQwac5aWLsnkymwEM8CSd7XkDfDTU4xakTFQ862GTIt+jmnRejMtsKZpcfo0LbZdTgrPNC0mPLJR26bFVGC3JPg58NFnihaxZF7Yk8Rvxb4YB+3LlTYEjtOMRYFyWJ91+9q3KKZTKauuytFnWZ3eTpqYNa3Oi0+avCj+VJVVEfGldIqBczyd7bZTbKiJlCg3X25poY26D3D0crG5oiTXaakh88p9dc5gwVjpNU1VbwGSXjdC6tVU6WqVWmjb58UybNRsNdkpM0DpTJ9TUXPQKnSjnBI2pxuv1+b3fzpIV2uYaRfrvXUYgS3j2uzd5DdfSjuzLFYdserQOOwILuQQhF0t9q7SYLkwphxUq11hg/1W2GBr1RcZVXbNRIgloJpmyDh2TPsYkvmoRVC6NTGp7zmY0xUobRmUZt7CPxWWdqkbZJZb0i3D0rqguFirh9z8GNoOuAwO/eIW1jWm+4Db7nckriOXsoEwPq1+RwLVMxBjzOQQPK6S2MOwQh4KPRJ2NeNXH9BwNuRvldTdfMQvp+lUTZQMiVodPCk0bSDOcUGJl2Z+yOFYBK/rcL9gXxbrqB9O5C1cvrOhsaAURXHCzIsMSZuQTKnSnZv7viJQq0ZmdZrPl87Fn1vUzNs2LAJwBnJIar+KkNTYU3joxv07TjkmBec5f+juOR11xPmfav7tUtbcyWeEVo3Ty+23GxtEp1G7KtZrcGodO0pQOzQtHW4A5cMNLYemtpq/p5WVyvKe8GKnuq4QPdFDjitmPiuIZnfJtIm5o3wTguMkzJY/hQmM52whYjuUeowjxroxxyqc8E4nHOm7cdKAXLIomkVH7PG9IAEm8mK6RjHNqjWF9mUDrtJ24dCRkxizog5pGcPKr8Va+/xz/zGUNxy2lL4yAhU1zU7DFvtwx/79SMKs+ESiW0moPnTE/wHGW1aH/LOh9k8pD/hx4+yfdaRua/cvT8DN/w== \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/images/plugin-api-diagram.png b/doc/images/plugin-api-diagram.png deleted file mode 100644 index 1c43cc297f06fe09b45dca97e0635c03aa7f24c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42806 zcmZ_02|U!@_Xj?fLS;=MWRD`lj9vEK*v2*}#EfAKW{hpdu0^)&g(xJ6%2vuUma-*l zBBdgGrLtw;{&#ww=llPDpWpxgdd80w(80 z2ndvb>r233ZbYIi2IuBUa`O$4_45b>r@(bz0tSc0d0_s!2POxDot1-~l~=TthfBb9 z6ji_X=YdqmHRMDPV?3{_QRru)N2Jab5{PZMilKsv1r8r1p_NZl9h#pvcC=*sStqicGtrMx~rO! zh$LSpH!zk!3Q~cLL#sHLm`K2MZ5@?;RKZyTFGoicl$n{nub!Kto;?ANvIzy3!3PHS zS9A!`F$r<-4N<|P%oV`h3S@ml9hCqRixBe=BYPZ52^#>0qN{@TK~W6I`Y5!Yuag16 z&|de#IRoV^oL$IFAFD(8W=0hTmW z@AgmW56mAN)AUo;8eefzawgf9A zDI^Go)|Us8nv(sj;ei;OxgDA!?*jx2^sxYcm3@8PgAENS<^+N*K_43oSJr`%gRK0> zXxcc9704=}eUhq&HB8YRoPc}T+S>#Lz_3Op_U1Zn2va>LxVMp=67V2hV?wa8wULSq z-pqg)YVYVqQ=y+>khzb6BGDXNr!|Pf=miBBxrGp%{7n=c(6%_UK%#-FVK5O~^A5tg z`FiQfIgta%7S>LViUDu~ygv-CjPNiw0*&dK(OPuZR|y0r)3x=ox0N%MN7yS_BW(RW z{dCE}I!<^m4}VWA+}Hu{iwjis^w!tcRX{r6jOD!K2}Yil2z_gO2qh4yrz>Y4jI>26 zshH@wyZPwj1JI_n$iPsCP-9&;ioS}zyP1Kar(Lk86CP=%X9jm9EBb@3ZS7V4P0TIy z&^k8yp$2wjyl?>v@h8KW^C%A66Ax%yQ6#&{(f+brM#0n zD%eUj#E@VOCOb?wGyrEDsN-Q_Msn~A^bb%CA>*v{Z50BDNLwJEwS@@=dh!mK-hZl?(h;;CQq1}9ZJb)mER(_!boj^~x6_`wlM13VYUC=k$#xl?v zfmSgN!RrGLBjQzU<%zHWB_p&v#@jl;N5#+%3D;9pHnSy=2-d+$=3b5%0tMmYhgHOR z0eb}7SlSv{+ncHqd@Rg~2HtLV25xxe5SpvP9D)_S@k*-lj^;LKeT1Teg=wH=V1Ti) zlfD9yfJ0a~g%F9hPCohuihAaj-~mURkFpOIN5+{TLMUV}64sn->!42vB3sHinW5ZC zK}1UjA9)pb^B^Y`oCCti!Bp81XvkX`?WY{9Lz{U4x)v%b_#pjI8xI8}&dD2PsYCYg z4Yn~e#28zXbXAnVu~#TEAebPhB|dj%sa`yhir z+AQ-$5QDAk6mhyHzCkt?3IU)CIU<^DXl~?A2r&jXIC!{I`~%VcWG}L9h`f`f6Wj*n zsOXCiHVVQhhY*Z0@-#2T2cpnU@(4vYvN6V<>}Q}5sG}O->qj)UHwiY-v6i!gAuM&V z^8We`zMf9@O2JA9Cm$m>5O@sbU<6CJ9wNwB$Cv1hG;_DLCt`Im!KOqXb2lS!J=nt1 zz|0}k7ia>lgCe55ROQ@|^73wQvc9>zDawGPibl%&S&)Jp5N=M!PDEXQogg`65X~=5 zynqAg`Vj*a9F09-b_j3q%SXZAM=sRc!~o{4v5zCPjWT6wJVuiEEd4wXY_2e8pj3}NVCW?4)KQK5@ zsIHT4h!5Px(c9b|Px}d;0w07i4sY$}ZQ*^xGRVS=2*~rl+ z$lMmGXsi>8BkE$Dyp)yj6r`DvyP3YCkDY~52nl6wA!24PSdpUXPdV5ny zCLmYn>KK!OSDW}-c)F83+z4K7p~jX3vIh=<^4IYVbik<+yxbi~HVTMPB_zs-tmNcx zB<~+!?civJq(w~}-b@bXW@?BCGysWHm*PYu0kQBt7$pM-6_r3`#}H2y64}Sd)4|lu zFT_$2Yv)D|wkNt{>`|6hgh2TqZz9prH;7^zZ0=`kuVjZ+B3b$qaSCRk!G(sP6BdwS?l zyePV8TPL$%H!pmEp^BTGCyeA~XzQc^LlWHG5pX3Fe=CH%eTYc_5=C&d_oR4x!`-bM zo$zp9f~Aj{g0Cqaj`0lj!FXa2_DB@o0Z3(t^9uqd4*n^Sa*R6L1F)EjXkjxl4_75uqwqP*Z{OgRK&W&VG1h7iZB$$2NmF;;!8w> zyF&<`4o?0kZ$GpS!r$0Po@i-n4{#vJlS(9#g$+to4ryl@YA**o+}hZah(cMr=H|+(zMuzV13z~Q1lbIO!XqpQ zHdf#f9WpK$W(`BgE4aH^DcPwgn0r{L(1HvJW`Y7~(U9iO9-fxL;7|uiLxHppO{4!H z$Ukrr{Qet_!wmuz44y+E!VnXE9b2-?kK0USTf32G`3lU9>&d^%t@ZV_wdpuH8Ev)6 zRl@J361a;`3mY@x&J=L-JA7?4Ua-6MTexw=DxEqqigD-Sf(6X}fO}%^1nlmNcViZ6 zM(RdH`ZOBMv~6u|xyfk|mel|Kc!_anFAa9wj4_F3H2&|8HfI5ONk^ED>wiDJj8ZPQ z=-}u6-HsJ?5|FRuMRA8x27 z!@o|67vw{pq9=>_AA``h4JWZU{CoHfHY2*GiFb ztiHIhII*$V%8oWXsq80kT*jebtG!9wCH4qksv_Q=k&*F%(C5Z59tp(FwvDAJB$e#n zlP%9+o~vX~jp?%G-?Vw;Ab&(ag`);PVjm&vLXEql5$68xkz*}o@){weP5h5aWQOg3K57nljr0G{NMMf!i!4ORA_jXn+hmG10TRW>Gu4Ruen8c6N1=E~HFYm~va!QvZaRzWrH@$ePaXFeY;ai6xX6?Z|EU(m@_ z^3Cko{r%RGB_ZpG?8}BTeOkCTuK4Z&yqKGkx?w(%s zA@MV*sQX8h3?krT={(AGih=Kw)x-&WA z;=PXR{6!b)NZxCRHfb1)YxVoEUQ_sHW}t-Plmr}uL?UYg=8|u1a=pp;AiX)h4Rtf#QQ3=w+Z_DP^rAndpQQi_gDX!bS+1KcHmO5gDk1h{akU)od`c$Y$dRJ z(O%q@;YW@(@fmSh%!6^9Vkf^W?X2_{k!D_#=Wd5&lH>Jns|FaJbqjc#bQh{PPq*|Kzf^Cejx6VCSG1;=^OY^d(Q0o--@B8L)yEgyF2aycMs%x z;8as~ONWauo#D(hMknpQP~--kM(A%VCe$mD6QueAnQs{q;n&!OVr2{-Sr;d$A+0Rb zGFuhd1l6vr{ro7+nW5pH+^UG_==Sfj*3MZ?_ehMt?ah0hipjYvL&(&su8=8gRdior zVP{8m3wYqr6)ugbvd(Hgz;~*5kQP4SQ`eOPCow+%3AYds_YTqm3r9+{ar~>eSD6B_ z@DhYdiQZDVK#cj1x zJ+=QgQGAsI@vuKz>|e2cLls2pd@Tqy;y(iab+8M9`xajS7{00&Cn`aN_zp}?p9A=Ny`|xxK_^oMC^Z4-Xa}nL`X`M z5c5w6_$zV!_x+1v^cTFp8nw?p1|bS`wjl_F7{8IHYzBlt(Vx4}T7JXLnhMOYJ;f z?<))IvjXP&CN}5tJD>DxK6D8|QT4wcIxh2IQYGLUS4GL4P}9beGMkcQIIfbFr4}HP z85nF%U0lLtnWY~AZM<^zFpvA%j}LKiaoTfzxBaI&SQTyFJtw%XCik+Rxgc`QxL{2^B8gsUi&G_o=9ya}<~W z+-3QsWZ}9x?C`N|k|A|kPb+fI`f~Us;J0Y0+US;|oWH0&eiZH%ey>?(HdV*$cddg@wE@ z4D(pzw(o`JNI{U3IxCv@63XlzmYojSob~`Au9h^Dz(s=}rAk^miwqqpLMe*)cMnbn zbta8~)ICsUC;iB&#?&NhN7v`wBR))rc}JS2ddM%^w=2G*Pm#?&ntK~)ZYeA6cFN9D z=Mq3}laHT|S(7@7V^0XN#h!{`WL_k)Y90r3Bv_oom6|N5eqU215@fOAZD!C%Ya8O` z($Dc`A+5a)F~2cty!UWfp;-=$Jr3o*5W>Y(G2NBX`9ebIqSpGS0MoLq)%g;8hTc3? zn;A)6sfBLwl-D<;r$BQ59+Xpc^mImK-ct3{+qv_qNUFj+cqt#0vjTYagKw)I-8V~J zUCuxAiSIIgXW1<&yL`A1q(0Mv>2$uzn`h6vFN`(%w;?nmPAK_gZ$0hszpW8w&+?vT z*yjB`{)|)R)>`R&avy}&ULJ-P7^R%#z-55Dx#ZOYW{&{vKY1}Ap2|T7fc0Szgw(k> zmeSXSVS3!hjum6CV?GpyKj05bp^aAa(~b4hqAH}WN9okLc&(`$@3(JXX`6QoqhfBF zjiI`>1?r|-@f6F|h35UThzr&A#y7d&Jw#cW6kOnjs%u6x-8kBgKx^FQgl@JOC=~&K z$_vbBV?9#~;NKPCc2ixF`TB{+&ko6C>rn-wQZ%Mk!F+qzTTy*wz+X@+Qh>5NjcgWR zKY1<=%%AqSter-k%Llz*JKv;JDYq@PE_F4_TN>OT$|d!MGQi(p01 zeal`-=R51zE++K*bxq(m>zb>&W9U~e+?Un+s-jHUW>eSCEwxKmt*e(Opl+P88``E> z>|uHJq|um-GnI_&Dhii6yV*5?fdz~_@w80H5){%D>W-f*t|};x8v#%cdC!5M zYEW>-{?UVXy4_B*ZIPUh9jfRB)dH=WhcKqjO7*X;JMJ1JmO08gj?3g*ch%2cX~TbM ziSh0*zdz&g-Fb_;{95{g_k*Rni(Y?cn8j1{eF<5{*L!@rwpll=>VoFqdEYk~D|*!w zv^ieQvkL+uc{ay$XpO}&{wM%Sj*}8XhTUVq6jup&5Qu#?3%(f3ZGHkZ2CNIp-trkh z1jBwB^h|5qnBw3hlyL^EveBY=5Xpcl&iWx&;_W1C_iWfp49ms0_sxeTb>nwM)6wHI zHiC828K*XsW+5ARD0NL_jp)l#bt%Z?d)wYOo2&ly*)6)OLk0K0ZRuwCp1kxO~#Q=e4+Do z7PR7~GPWlh+1Pwb9(@qQ-t;Pq%9GB}GrgP9^SX-;D|(%3+~Na-e-aNnNlS9P;w{Co z2VYe210?*DD?9wO9>6SljUyPT=F_V z)t4Ek<>Wh0_l5lWq;vg*;*b)vjh9? z|7895@t`A%;fu2WLKK=NAT?xo%$Wacss7*hN16lg-L2oDQvUJ*AsV<^qF-49iHUj* z!alNi2b8S-_7CC>(2_XDNK*T8kI+RW`zWp~u`mg(2xAbr#M z^J9F#Z?ZsHgyQ%jTlR1CR;w+nvy{HSu_6i_MkoegyKRZ~Br`GKY4;1CdI;}5AHM0~ zW=(6V;HtLn!UwKn6%Tr+YQ$d@h!uJ-1M6gX4IEJPDo`Y^akbBg%J*{=tSs&ZLr zfY^cisLfq+4)a@VVV`SLvP_Ly=jKBAGc(+qiQ!vc7oE%>jYV=S{LYX4-25AsxjC!R zxoEmhATFjrFZ^nr|I#eAV(J>c87US%_`NQB4}R0n;BEQQR2gyOMkot2Bw?mMIYvmq z@TY0}1bKn0hC=N|huSbDWGUNqhmnfOluG+#SChw1{6kYU6f;3gy?KL$j}cEcZU+mt!6A8|!#|mWhZ7j#|~!m*uR0HsvP% ze5;ihzqy+iL?#TRsA$e1F4RRLBD~C&NPMd5sSsfCaOTNG6b8e6(9-z|l&YYPPE4xnSeWPU7f(0&<-@nQ7!Mv4#m6e}4$mCJbfq5L{2tPpW8f-cmoV-1 zG3v~kpvLtZM>VZOP#eYla(J&Wvz+I20ro%jjfos2jK#IC#`9tUTSMQq=moFsTzaVN zr!xi#+;(-QURbGO>|{4TKB?p0aqXR~ONUKBvt)aBL~N2a{Yh~UQ-#BO^(4hzVAD?) z*o;1(DYh!q-{S>Ess@Tm#Ka-P5Q?3LL{!#ZtGaUg%b=*Kq6zrWb`0z)2|e?U;d}Ga zasa@AUrr}hE-e&s@HGUk-rpkZW;XezO5?Yfel3so5~s^nSd_xYkC|)cL|cwOx`w** zIQ0R!i#;6`$uln*Vi2ov`n1cfeE!&VT6krD_fEv+n{|nmy$Ylvetwns7Yd+_qWjiHBKY>55Xp!t;R2xe?9pS>%%08mne+K5pM3 zn+Q<}1bcFRQ(Tr7UrL?Wm>!VX-+fPewe;qWZiSVoP?@F^Z*lJGi$`t?cQG&Wz|+O{ z4042NrrN+&!@MOygs4Vb&*H#n`@Ab;dE$!*WCrpZk~&PRFAus$uIosAY|?^dlsQ_# z07-BNUs>Mn>dd;Qk>nM=l+Ny;{6q7S+Coap-3s+DzZ&Dj+aHdcUqwEcC~-JT^o$wW*(aD0JMd zWAN|4hQdp=g)DVwqQcb$-nn@~+!f;zXTR8dSt0R5Py3I;m z;^fSW^iv8`63shK>vxhO;MQ%QKY~a54iu18 zJG&Ck`89HBFMs9Xw%~OjyK=~fjUPjO|oPiw}wr<6KJ|%4adLUbQru{IcGkV>{dV#;T zv-@)t_nn0WjtWHUYmtc8!Z!w*nyC?5caqh8+ zTW9&AW)g6VdF&I+|0~2f3DH76Z0|Tmm863vto8v zdI~OB*dR^Yys=TW7;o z>kd6YHc7e6FA2V!{hXyy}gSy+K54B(+&2;Y%6|hBjK$g|g?!5sDmvE_CC$}5&Z4<9Y z9-S-Qb#jd&MU@uz$aS53GcOf_v=WZ<;0^ z+O%np8{t{8nD~98fp_=&t}FL)$Tux2?xf7l_8HAv{`AYdH@a?fNwZ&QeVvj}mamSb z9)zl0zHUU&#{3xUWxm3A;G$_R{i_>$FCI$^^`DFJzWTNxl5g|MD`jXx7SsH#RC2Ft z13xrE;rO=anjJm&dFtNi7Q540Ufgx9KpO;W3G{uOteEHI*B^A=n-|+hn6%&dTJBP> zb5)fQBClo^b`ifk@2~P$kf$zfvOB0R9bFy$>UtABQLm9bw!m6*#m%epl$XnMF1Fa) z2Ael`hb8gV7v$IeIJi6FXu;tM6x52UiZ>U_6WWAk~V8WZ{)f~j13Vty>KSBQ9tMkCQlrgl~S;&BYy^YGbFts$fx%jc# z*XxsqKyu%&lz^BR%{(ie{z=!)0NZ?!1xO`PC{qJ+Z3 zo~J-vs@n*yY5Wi1lR`4zt$0Jk?xu5BxU2@;9Vlt2s?~ElcT-v7zHe>Z@uMkUjQ0oG z<3={@<|?0fvM6S}o82yYl_tx$dtx>eGFasLqrKiTK~!OSkBPpCjHI4(u)9X?is2=7 zy?MOMGUQ3nOu1jv{ZPYYsof}2K4pu-Ct7rO=moEsR;WQQwoCEiV0h4($q(&%LoV{F zwnfP6M)HdcIgxaj4xf(n$Hp0FY`e00uH2By*1P_e^8Sc~Z}lEEEVrKg*rJ`Wb6r^W zdF*|1-PM%a8fIXLp>YAy+j{<#%LrW$)1rFx$2SPJd7~(=--6Mu3ZJCfyKw;c9#r1{ zt?n}}6ifX8ziVAe9~YNoeGM-&P8AZows&`r|11;9?0hAiTR?MLl?~67KzUjw`F2Af zE9K5Y8MfP0Lv(&>zhSy{RMM2kcFLOiuOLwcOoSY7Bm`jRwF@6*d;2z{;J92<%hvAhTyNCe3N`nhLlD0 zmlDI*=9;rNlL=9`x!Mt}QJ%bmk&I6hQ3jA+_s{D|UzHV;v()gImweFcM7~>$zmHa$ zQSTfSW*SjEAl&@Ko#2wSAUXt)c%-Zg_MC<&CZaj#A+~AyvXg3j?HH4oKk0>V4IwG( zBz@D4hD-ce#lY00h+4yDO36^<-bm3ph=x8#chO-vPDa^dMcyM3VrP#r)HAK zc6?8To{UE@^4WA@DdUZQEq@$k=ttY7v)AO$UXGS>sXxizF${{GL9W#Dt40NzAx4!NKaMYB&0MY z3g1@@+>tm^ZDhtao&9Wr{$-QQWi-!1o9YFz(@SbWGvz1Hj*<_X7j#$jGPTJGS*ty& zcbS+SWL=&^dgvC#q7Osty{Wi_EW_G0=H6DtA4n!=M~71`PasJL2k2U4A^ecv2dxZ8 zZqtEP!jj(7=tW9+MKv!tg~=7e2ZjM&Zll4GGDJKfA|4 zV3jCg{%qt&Mt0Rqx|*oV-Vxt)CcDEW7WG9!&%xe<|I+TacQuJw;h{25&Q}K-DOGfv zxV6&N&2h^A!v*N6iH5az)4#eScnM6!ZrAm%!0Z)}4!YFK3Ty^ZB`8-&vBH>0J3{P2 z>^o33D9$L~@3>FiB>dd{-dB@|qCkS^!Xd&4g;5?z&oc;|)|S8}#^0R4#itco=^z3n ziqOQphCXOm3He(Xk=Icgtn}=uk^Fg+GtZ0HEBHrbU8=qjgV6#wb$hst2X>ddse6pVZo)Mn>ymur z86F|m|Y{GG1LXZ9AhRDbZI*Kf^=Fc7{6hA(M6 z$nE48Z}k)ZqDm_eRISgXopx!u;5>Hz2?)jV@3uU=KzQz3M^=Rx z(Qh2q4!Yje0g`&rV-dn;8=FX>HP!NK@B?y+w!VX@e!2ZM;?mEyM(CC?8{mYDO%lb87P4e7sGGrEzVoRi8`Kkzq%5Z>_>LN;UQk=$A&DkHPoXph{ z?1Xsi25Tl>u_|oO$|e7nzAlp3e{rWQ;Qfaui~x<^?Oivzfoa^d#oC^%6_Px7(;j}_ zwMYHxp-tZM_?`oP&~$Rp!8YICky9<#HLYLQ0|g&2Vw%-Jyi6^3GU) z#iNVImQ1}%3*+pH>9z-!(4MG@6IyF zl~q&0kG@{uPF%ZSu27z%xTY_YkR|ybz^t1E2w6e5L8o1L^||pQB3(erq5!yKPB5!VUEIv zP_4wPVdd^?4OiXU<`*~By;Ol;1UEDf5w|)Lzrt1+li8$hdtJEp<=dl%U+=l0pRe$Z z9|x;qC<5G*ZniyM0W99k{oPcc$M=p$_DR^F?|XYXEtcn*^Ny!aRCNeQsB+i7cF z`z>3#=B*g1z@Qnbl&dCC6qw&E-(;X^dnWae-NBzI=e6~-@m+Z6gAb3RArBnTF5QWp z%lB;&hmYnieP8Nq&aPU84PH4|ra5(MPA^`d(4TE2btbmx=4}IvZKfI-JuOW^v;8!d zDet>=o-=pSYUPah_?AN(KZmwVWS6E#X&Lc z<*XH!Yw^GmEQ1N118TS)Y(|DvsUrQhH;_ za!KUxED0z??#{3EC-RmH?I)Upa6iViPa zUivzf8>9o0ttdXX_JX%puC!TOViw16g@v*N6H#9$=q8MMirlvTm$u=P7*<5_zKCW zpo_=*0QW<+KjCa+Hb^WINx$fr4-${HXhB{Z?mP2p|Y+FuC?_t{#ClebC5JH&|u_%^Or9)BWZU z(!Fp&$Tyiuouz8#r*p9r)_VO_&)qTIZS$;&|C93|sns z=!^F67%h=1u9=fi20Jv*fA$EDfx)1>sveJURoYT57#uh>RwUXm-`u3HfBoMIHR$EO zIEm@gRPn>7Ne};KfhykyCJDTE#LI{_@lQZD*(x?6$qcDBN9O6Iuym{mSqD;88CkxH zfr33izGe_vaT2@Z>SzAiAu9l$@6%$25|DuKkatBQ&x4jQa1WEgnp{nvCnF> zFX%2Pu*E+%Zn;leJ=#IuLKG|+Th3g+ND~jHK#Lg9X*)yb+p{Y~fD{kixNy5}QoeI) zNNV2Hbo*nlBaxUmr~iGly*pmusbV1+r`O#A7^s#p|MOh8mHjEuR}SM~lN-={1RiaO z1tC}ik_-8iutz_`3~_@@XH%Sc+7UdC9S7PI*<;pe{kryMSJ7xc34glyBjf_+VM@|%ye`V&Ta`2ekU%n$%X-(%>eSP*rNxS=I;IY1)H<<_-1 z0Sq1iX=dPMEMSSYg6z^Iy$wWznSYVbi58#)$oycf@Z2tG^8}zb!LD4t4R+0BgfHfh zYYxisMGHvhAJ7(r>@&!jOqr89Xe*R|DH%j-W`1?B$LXLAW=trPHX2fT*RyHtzyM1N z;RGT5Pxi9`oZ|n}k&iB@O!!(eo8!RTuWp{_fjH9zGFGy+Y(q8K}_HfM3%%C}QMgjF{4nUOoDS6(!_xMhD(n9tEfIj=>&B|jM?cMwjJMx?X z3z)>m2X-o09R4^2-3j)FdOQ{RR|p35-Y>pD%VYNPv-mjRd>l%_wEe&6=-{O?h%iI6 z6J$`5s7Jt4P5%EMNHF>`));1-uc=Y)+I&YU_xX4vKVU=YmS3pLo8>un>?*eR)&yYJ zzKEt{01T$!z^?-@zU}oX_`JD$0B||t)PlYXNA2yXMD0=GpC_Mwcuo)k6rmWv%Pkwx z)72e#fD{YgoXL8XE|KUimNadtug{n+X<0tno3ABg_t3sQTOKXv-o~5<=pZ$Ok@DO1 zmYgYXODw0RQnWfUPm`waL^*@mBpzJ{IZh`9(N4Zm;r;@qcUlZ=FP_9EF5H~-kSDUu zDFHwD$jsNm58Q%UXgZcQHPLzvS$Xk=e#Tk*N4z&>952!6SP^seWG z+&9mSZHm|ywJWnF|9)dMd?cCqD!Ky?s9+mkF0%_iu^TjW-vKPvukRl7C-6Yar}2RN z`h5Y$ATFxp_L5P;ZYdx&=K)~ohyZ?$DOLX$8{nqclvtEH76R^d+0uB#y7RFWpdYF8 z&mM8{eUnt}mdWfnG1HriVzDF=<uA71IpD{)P97|=X$%( z&7Uj*Kbh-sLl3$AI20%49{@<#K&_=8C9Af)mT_3{qWkzLP3aFz{{NV$xt76P-za;Vb2RGMqw~+%E+1rl znIE-Rs&8xL$q6=un!$*?hbbOZ5NA5)c{>}}nu720ZO%D|;)w;yu zIxyaY9N~BoxJLD~-`4MMrc?pdS3N0hTYx@(jKB%_9$Sd`kJtH?GsW+mqi{8ZEZ8Gf z#gmP#X`Bmv5jHt_T2|O z?wgEuleD`me~A=IgCcqGpyfS_n}VmrT^fRaT7&nOpnG=Z7gUb`+IRoCm830XU!H1J z#CC9dkE<-mn5UwTRj(aw+54*Dx(eBudpRoxxPSeboM>A3llxax>XlTF|9ylHrf>45 zBtJ3%(635&4M|t8T`O{F3^h&K+n8dP5Fr6_*U=|{XD{YbeGYKxevtun5ZN3Vv1&A? zt=@(ZF9xh7z&BGG*6vF0G41lP_#vR*VVEpXdap;sB?snVy?uN7jMG!8<7b_&gl{fS zw8Su&zM9_O+nTR1br>v0l&qPnZZ&Vt=9<3NKT}-MZKhlGT=>1oUdhgnV`9j3ZWRS;}1!Av6=I&ppGnljBuHYauelI)(*uX&vt%4!?(0|@Qvp|x4~~$y7A!9ALRzS9v&JsFLlrX{-APd| zkr*4Z81hvf}xT6JIv4(?xV$G;f`+Q3dk-H~d;6)4_izZ}Uw1{KS zPulN#WagwtYm_KAl>hkE(>b6rRQnr7T75jKG-E&RcN8DlT>Q_j={|C>GiM!0Rk85v#>*VBS z&TEcj58KXc6Z!~6FHXYak{AbznO`nqDOO4$hd$NwmyVWek_Gv!4;I<>S?L?nWt^Y8 zhVDR&Hfw&9N_MGbKvtf)YK%y20#JJ(uG%xw(?|OS zJ(-&6gb5dhtLWX|ALstVF~7(F01`jv*nf3(URvb=xfAs zmus>cFUI$h!M5IPZ*Hf|;Y_kG@sN7HKj$-?*y0KR$b`2GPgz z&5SljHQ%brxU9DG4NS;8SHRL(JR)D{U(;g^oKweKQVKAA+@>36Sl?fx2ywqG=I4yNZMppAiswN1o=Ek^ z!P9>THFp3xeECQSLD25zr|Ty*oZ_>LV_roY-#|BKepm8Om>{d6L3Uw5pX&Z+`B|b^p*T@x&DNqfp-+6Ox!;bFev2`f!1Fx>&?MaD|IEp z+co!})Yk$@us`qJ>c9@daL5g1w;1_dw7Ru3n%VB zYL5O!IIdqKT8hR}l>{5(N1D_xReF8MkhgHIOEfFJBw~GBUFbab5PLeC2DfRwA8O3LRk{U=Vc`l>jLE;DSNB#d&?C*y7vG*gAPhg>e?%jCQdL*JbT}%E z8u>Ap@8e6qvXSXf%8r|@Z1$ns-X3~Z4QFdrp5~2O+JAlfnRis2{TAwV++|NfQTTqN z!@7z5g}P%pOUo@bW=qSEKsKBPT@)wu8nKH@KBtEB+X=ag5Y3%Mz4^ib{kP13@Gm(# zZSeLIEY+8JBa!UfDDqNKsI>%Y_k)3fLGJ724s!E{-3qP&eWUAU>(5`G9h48@TBOi7$-1CW$+cRoiV>&pLO#`nE$l6n1eW?^mr(2T{Kb3YQ7Bv6 zyyl1bT|C?duIu~1zMfdoa$l_zTKvW~mA3m3`DtpVXg>iULFSQYlf!ZmFAP^FrrvyD zNR^(CV%o30^ulL}knKk2{w z>)hrkZ(*Oka2foV09{PXE9*k~?}kxyq3ks@zURAe5g^K{?a9mTdDzh zBOyml+;d?+!j`TL1MG$4Uq=-R&gI9^9AoAk`1*iR=G3v`G&)ks+HO!&{w{gPEoN=Rypzk2vaVAp;y^NZULQN!a!qM}1W z*R(HaMV{-MXPc<4M0ZA27#Vgjzb*wY9X##d{S@#$GNu1^;w$wK4nDzg5$)xz$8b;g zjgyl-bFX!8uYHcvfWZtar&piMk90l_Va}n-E`Rk};83CL4uB=(^fZSiC8Ga* zRaxV?ilEuP>wF<$`+H>(%SZSosZE989hA^f`UBwb`_Jcgc|9o_$LCfh%x^tdtGh!v zHM?3#S*cts&dPi=6F$o~bp0fX3GyXr@x|pyw8P^z#Yzy=Js@_+xt4Q&%h*5*p>9@CU zk`gQaANJlnp6a%H8?H1cnUa)wj53v>LP(|zDG8ajWS(a>LQ2LEVjDt2WMgNZHY)Qh z(>BXSGPlh`c-OwJ`?}uy{@u_0+|Tp*ynnubluxw3&hz}PbDe7)>sZHGk; zo=uWZ3*x8CY?G~imYd&gY&t8*^l*`;$FQzdM&Aq@pl0~|FwG2l{aI=MtA4H1?&jDc zu7=Mas9t9TENi!gmZROD>@9JWxpC;%-%Vl{JNIgzO0z}k36c>isUmNSnIw^f3pYiABDE9nev_?7SkBFVm_w{Gx2+E91^{u~* zkAa;bk&xwU}hJDawU zhkR*R_RKKkT>boST?%h`Cytfc>RTSuu_Rd=TYEic!=RZY1Qj`R@uSpPOgd@&mU9Ry z_=>@4d4d!5`NZK6*gHenj32mV2|`Sks<+(h=lcf>Z`7EQt@P%i1(g)8seU)Yo8GW+ zQ4E2K_NX~e>OS%P&;%<%jmZt0%7aKY?u;De{#+|MVs0@*dREZ8&rut@X4=qX1|y7@ z`L=!@{I0mLBIUm<3!vclguZNx$O6-mvo99&kj*G= z`<(^hWUDNUoSi(8AJyq#PVDTs+1GVL97p_R=4M*(x^*}&1RnjXB!Mv zeIP||H}LKckN?l12EnIIAW4b#YdAHNhfLC`JU1`BLn;Uu&j0+h zI`&neb2+e;O-(+yg55z|;!R>xljL94RmqFU)Xx1nI*;?nPOUyuP8*M(J||V1bO{mg z4QQf{E_uWq+tHT83=J}Ogu=OrJrDQpm)fa@prrbjLQvIh=Li369j{=1f&aszJCGO5 zXPm&WTANho!Dh+1NIaf_U66F~GXi8w2?!F4o`f8fIX;{^D>yj<(;O@xi6-_s_K4rd zs>(LsVrGTTh`-;)C@qF2^qdjy*96fTf=5CYZ1H{O9ZIE3IDsUuPN|n;f>WPV#OCg)eXFb4f(-ONlEY)9I>24! zi#dO?|9bT1b?ew_&I^NA4%?r_Kx;?sW^4G5GxuA!E+Ky0F4hnULCs`C(8}l)z90og zIY6^TLqWk$M$FSD4_RW=x^^Ym5<)Xg!Hcp-iOMOC=R>oab^*TT#P+ib#tB z15%N*B+Ge7rr!ib#uQ10PSupWzm17W;t1piTgLx1C}JCi8dRT`Pl8+9j~A!qJ|`d- zc1dIH@11T8Zem%Q1cui4=g{JzX9hq0(fl(Q{I9*u8|6>MvhO7?eAN41MhOWaesY51 za0jwnwv%k<0rsXgCoJ25v&rU3a0|Pq7a9G85j)w&a#(h|afSOUQxb{zvATq2@ z!wI2n|K}2&VTE!z9D823Uxnb7B02K&^{$UpdT4NQdsT-N*@b0KHq5HTM*dZ;sAGq{ zCAEKp_=LxQJpoKQGx=~Do@?7Dg^k3{u$nR2bM@1e^p>Pyjnc1kxFW9E=X>rULd1;J=I6!Oj(?+(CMsr{S(vSfcPx*Rrn?iYts zY+j!I>p(fFa_eIyZS%5(Zn+eE#>t!%{|w|dG8MKmFO+1R7sQ01C9J+xgE$L~soV=F zyeYZ4A=69zG&&}%TJT?t>>s|uq2d2=C45uy+;>zRi?&I)E;v9y#UbU%-g0so*1HY0TS z2|L?=^+L+vREb@8C#m4vWgWT!=Wati;dw6)0&$5ql`%f8x?qTa-^H>?P9^F zv9p3?M$SAv#MPg@V@vxQym5=nzfVU}WObkrVO26;oN)e^4+l$?p4Zs9`qpGuBdrc< zXrt?JT!N&kU@_$+!bHa8p!$i}A`TZPJ!lJIwQP4QEN5xD-N~Dx%9RDYG$EzMb zi4IgJR5+NN5TfOocYDI^p8Gl0!~56QxoNw`C(lkmpt<$1j3}-{A(^-tql017phIh% zE-!7}d7_TZG4G&mCpKRG$S*R6L#8_WJS`?={L=F&OQBS`Q8(n&#}A~+W(&0s;Acb1 z5O^c1tHIi)YiVP3FbC`Luav5Eol9Cth%V@mcp$U@JAftx&{_lNFVG6g$xlq>O%D`4 zzNvPwM|Vooi@ZL?I@F~+r0mU!`c^J|f$I<&zIw&<1hya7%8jh+?N}RmR;yK{-gNXs zA!0H@n1MFXWU#|-#j#Jwdga-1CKUoM>$>CK)ALR!bl7EEMK;^;+iF~QiFQ5#c3%5) zfO_-Zn=lAlGJt(;C-d-OFNyJSIj&#X%3UP(Wz%<8hfi`^Ub;kaPWJKhz+@74Whh(+ zGVgjO5J7^9Bc6@m04A*;VwXm+&tLF&76Hd2-akvb;Y>l|n*H_F56-WZR=f*-X5jO&uyeU~y zf~c@iWYyPlrBgPLA{B&FpyoSAa`l@1*eh*>$FUQX%ylS3pJ0%YdNNYxs20JsDh3h` zby$y8Dth`5Pz^C2ul8oaumzGx&C%RY=9ZDgyDpDO`uqET@6Xj!#!@iec=zPk zi4)v9<_6@%jMktt_M;;V1n(k!o_L^LivR=9!#I8*d%L~ zH&kL9dWK8+J3ym1+?N=kY6#_RrBO&}UZP zBC+K4t&?0qiLj~6jv0w_ey0aVWsTLhw0($;^;y6CjAzPcO~*g|4ui1Cm&<~cLN}pp ze8Q6;qiQPtV#bo$Vb$5BuS{3IT-axZ9I^TDQBzY# z`{xnWJNEui@7RXwZT~PLbinbIh0+ih936qV+L-pq{ zKq4@z3!?=8bxXU@?h_2 zUk2abHila8h}wGu!`8^8`k;bRTYMR%lM5Uj^no6TceH^v3%}@_7kno6F>UNhpozV9Z}&8}yFla1G>OPdzVUH&fPzQXbcgcnJyAqOWk4d*I^6)y?1(bOr`OZ| zLMpB^9ZbcWAcCZ3#qvfUt(E&g8fE&_$wEGYS&~)dgy}gxuzT4n*$+Ry-|;=z+oYt$ zWT>FBOrg!{i7WCR90Tlr>^Y0_3iG43-Uq1_l@J8T> zb85Ld;Z%(i#SEpb{8D3b?T^{2-h;eePgbF+OA}vze*YLC@Xe_TI!7U~CqOS0KzY3T zlEk50>_86Yu&M?g)rU;nDGbG%8y13kYZaF!r)k2gbphR26?(>Xc;e^IxO&aFlXgHR zvRtlpy@7}><)5hMZ8nFt6(#n9qQmvA4@Q(I6$r`+k*HE&%?S!VJ~IoDULOv2P5;DI z5&CLchKuep1#f15S@bY(_2a_lf^&j{$aFEkbafFG0BZST!Ty<3g^^3+{! zp+j_Z2d9FRziY;@ZX$JbStTeGz!Wq=af4_I0MM+)4_#sh002;Z9A-IIZZcBhmtJEd zVa;ot)p#sw&i6W#*8!=WIiwqf>92~@kQXK!5StJ;o$)wxV-iktcH(LN!V6Y{k{oPH zK&S*AoVEL0(XRy**+jOSXw9BJqS#Kc2wK6cJ6_TWL8+AK+X0XPthhSx zZ7Z*lqc2}M)AdUB9cM9fLP%4<1wXbpA zNsS$_T$yQ{tn!i=oiQS>IZ2_=+LKA0AXa#ZaE9zQp*sY({{1b%DJdScPFzslxX@Yf zm`8uyc82s-R$s#T=*Pk-`CE^vI#@=Yx4qOii~<}d_0jQ!p}W`sKB*U_Xfp+91Ub5V zG2hhexSfIP_G+hezHuYP+QMLu1?lj5G{@ZZBQG%@#3qnZ-y!dIrWgim?oGEw<~bO} zG*>ZPzmlderZ0)SqC}|hCa|+Fjd*0et*?;`K`r?{IH*VIXq1(Rj8{yJFhRO1AhW@J zI085=v$f0F$i2cRmsd1xo*6TkbHzQJnB&)126pWk0TjdVUF-vFDLHkd`cHich6-H! z{co6xQC($66)BwVYs+nq}%8^H=cSj7Be|5gO z1T~ryZWI^Ja3B(;cTXFUonvLc$+-EoR^4>!&eTUeIymAkX5Nc9vp6(?&od|kY*-n) zN}*i6IY!cn1UA;bc|S(mu~tz3R!EMH<_Hbhx$oU$RXoEPWFb|A0Q#A6(^e{~^I7{% znzr4X{$?#lJCI!a*8X@?oMC=jC=uYvSiS%b=-gUvMD}8%QD2<<8P#mHPB8&4l|K)J z#fu1z*kpO!_yIH&oIP^LB`2PIbbgQfG4R%N{L8#H1**63Wc9W{k^4E2&jF&2*&70& zRzwckUn?Z4Kn)0rklG=I5QRtM!)dKN{g3-oohj1l`^3{8KIFLeQ(csS%h2g}&pm0$ zF2(95ohR^f7B)6E<5rxA?xtz?C*04TWIb zA{pJMA1^5HyN}&G%5^WCB%0f}bBkb6W@o@iNQe^`O=i4yx_-=)3>U#v+bJLVzV9U& zsankC4yFb=jO1j4j1#F+%qi(}d_hOT=qG~NWF3rl-P$nPl>BPR4=3+bqTH9{99rHC zbZ1i9`M>@+d%xr)INr!55Tih%ZWf%dJO&jaz$bkPVeDncj}Jed zf$s%^(o}rwyyW`WDpolrQ$^`yEzv|%4r}J(#Y2>wF`r9 zKmPpeu;T+35sTL$lAxv6)TRl63t`9_GXa#qFQAHb)%l0&`i{gh_63l!jNl;Tx&Q7E zkq8P>xQydIyZmFG^Za*`qoC%S`^l>BGM9l+AudNpc)emL$?_}g%umQF*JWlisU4o| zaHh~VZJN{U2!&uWO8C{XtLHvfOzx?3aURN0{${=4~F_ zw(anzPckGTlw@g0$tjl91j5P$Z)%#7_m9BjIN5_;<2$;7+Fl`j@p;<;MM zHW!C8{VhRxD+^K~#^$JuAdohESb#4x>r4*Y-`h0?*5p+T9JeQdH0qIsR5G^YSOM}c z8>Q4@$C zRE}gn2HeIMSB)EZzO@3SPc;izh?ftyMsr78qC8d4OZ`f#9T|*VWE4M(1Kq98K@pP3tbHryoUOwauXBowEY!$juJ=;VLl8se`yr~+&&|st_l^<8 z$1nofBbWsmpiD?02}_mB@8yEeSSied;hvZDba^|}ht{TSPrR<}J0_0M8?rz{alt_IJ8H?Lnt)VSvb?d-*}af%e5 z@)7)f+-x(;FW?2vxWOyq+dt>?3&4|aL=P_?uz$^O{*GYeBDL1Pu}GHnf5W{l)f{1W{fS%2+ngelvmbWfw7>D(!HJ~yA3Z|}Jxk3fB;LmUZN08A8JO&>DEsYD) zR=_mZRpZ$$CyHar<$;Ff7Kr%u-B=NhZ;!rE1Ub&fx6|eY_f#PbbCMSp0DZDLl?QQ# zr2@}y%MuorYtXM4@w6Vh*J4F%vp)oLERV_OC5YxPC)#)6*DM#dcjn_JrzSfc;w2|t z*CM1JQWPA$s`B!bm8Hp@1QDWRy-*W){sq+8o`B(be+~=5-LoZ28!X!+B`xy}(tK!g zbV?;=n(U3a8}e*e;J6`uOs_maIpnpMbZH_|2TEy-+0@L;%+6&2ZBJa6Ms$KB`39ro zIpdE-^LlM8SRK4+yTNZ*tnWLSBg>P#XoMD(UyrVYj||l>g*q2!G89t`tMxJjek@DNm`EWkP+(na&JkT|#SLUVBFdsZj?nmh6Y24MN48X%E|B z9Mqj5!Be(Q!xulbP?1v@k^h!(Aba-xCtrg5omg>_6;0$}2{Q`07k=m@WvKQMaUif8 zEuRR~!bCO*9uIjzQ3>vku2EuR)iVJ1+;=aQ>~&Kb>Ct7q5Lt=!Wj1AS{ql|6m(_S* zsOfd4zlPEu_5ZTS(K_Cb3|B#2;1!q3=G41W)R)XbG}+S#XfRgIDZe8xT++$=89@g- zWSX{9(){n({zqnQt?1u5)0NAJsBhCWyABgg6lb%3pUF!W_h0h}seB9i6{aQvuD{$N zdO61+ed`(Ik}Dy;&@Yx46O1=4d>)qqg~6vE+(Pa^B2S)%ceUv291zKnVCUfI`h|Ag z$}n)e*=Kk9lxAg(dBY%#g71>?A`fxaIAWw1BW z_c6=tZF@QA=pzQg(U}xajA{-_-I>2@ot-&2c#uIy7D7v{{*X^bUjEqH@V%zLxB&I% zkuyLROf`Qm?EN=ik@xxUzMYrgQGY?+dxx6$8Y!#8L8rtf^Nrq0 zua5Z9L1(Gen^F!&8%gd`%VYD~R|*TgUAlrcpKygD>%Y=g3gc-RF3?R;-@;O1RG-SS zBK%@b5>Dl?rjz&~k5XZfSp1s6Bdpr;`|9hja2z$mL&2!xzRny`6aZPtRaQ z6pxcs8@|)`I=(fx>m%y z-}@SA>?vk(t<;1NjodA7YRpz!*^{e>wXj+a*I5xp-ih#6pKOr!<71b+#cf3SBzs=7 zgR(-ldKS`Kc$+_r$>+71_jQ9X-JHCatG?e5bW(Yu*Tw|23WBux^z$V+5PeMo*k897 zE9Uc-s&sG&uUxO~PgSM7YXD*iqvqk;m2%k!JGdH`mP?ryUqN#8M^-zc`sr*2$_muE z!(C3|Un1oz)Vr7bkr@Vl84ZfqBfNKJdnO#hx`oLvvE!#P^e;nDrY4FlzPqzWHTfUC z>$^6jKu#|jn#2Q(tCK@Ug|X6yz^G85?E8_$VjN-z&L0u`v74JhQ(eO#lba4|ooJuE z9WH7NsPR8iWO99j@SM%KqoD{d<<52Gp(M~o;e{2|I=no~iWsBL6hDNYru1dQulFh1 zXUR;OsbJCbjTcgWPOE2HEF0ZxP(DWpdCBWG<8&4SlIAiHf?=h07lgf6+pp_-ulI>U zf)7!JTFDugo!Lxlh!dW8rFJa$th9D!JMwoUM&XE*Z9yz~P*H0aO_E?dL0n7MVFXKD1k7 z`RQ_zQMyNM3I=`~XXpx(}=erZ#+LzwZ;0+cvqLO5LY~G(ntqYW%U3>0cx7=ID z;h?!@S!<$0Y=#kcP|zyij5qP{hF2rLFda!<~J#OYICcH)9Rok}%Le--!bhWJ`+l=iIkzd(I7D%9FB>qaiREZns!l}))JTUi~XZDW!zz@tbII&_i# z;#FuhaO0KSSmJMvVupV z(Z()a=v4?D-Hpl4J~tO$jkc%~s4Cihz1#cFarJqW9QM@LbKCD>Pg>`dD{PLsr_fLi z<3Y^!{2svhY8A&R7=Lnd;#BQ&?g3g#Cpj5IgMrLo9=qD%{2;~}&R|V|?}~cVBxbv0 zDP7Tb`*ZH?1h9Ee!?c z9jffMnsY6@7?)wn6z>fk9Cs<+6mQy@F*vT{1yrU9$C^;3B_ed}5c zy(dF(Re`w~GMjA&GaTPZ@W1Y3<(6c789 z_G#&rzqr;A&NOc_|4O*S0WF2jDG2e}Z?0=fZ`=3ls4gK9a|*N`h?N@NH;1pQ_hwRj ztD}=Mw9B+g@WluFyAlLpu>#uwShU1zqb@zBb=FWB65m}YG)OLCau~KPbq(|$U_0N> zcRjk$vgbqj3z?j07H)g*#Tuw@+-NQ|A5g`<`qGsuJKB=c{v}zjaHf=RX{D}}qVJQ~ z@U_MvdN}a<+#-$=HiNXOOo@|x(dj}ZnBvkC5r*7G%fMb=efMF=B!a`>_Oi5hqWIpA z0j{Wdx2NkR*;(X-5asMoKGQsimyy4m`$GKNIxXrIi!~*OqnaF78UQ%P$PQ5~AG$u!~9rBBzxrO~b$_Cn) zC2VpE*xOmR-JEY>3-l`Q#B(4buXxP*BR#$-clnck>bt_$b&321a+@RqxoymF+oX=y z|FX&T6Lq|m#i4J+@U$nRQEEXbx}2kTgAN5+a~>7XFrpIl{SK#$jcm{>}qaygLbT*qI|q-B(}?zy3cR$IC-Um2Vv)Ts~Y!>kqTcCub8P zo`VI6B>0%oA^8?YLWqB}3U^&Anbz~$hmLTuK10SFg5ZYo$>&IF%(KvFzg{ZL>9Zf> z&QfEZ{G@f~$7?c5=c%C%>5=zLFMGMvwXzs#{g>xnlzUby`hPJ!R>Vm^HOuqxXe9g@ zrefznusvj}6k|mU<{G58QDg4JVO?%xv5&bdZXX5bL-q4bfDHDxpW=TR$KUhApSlhH zXdr;<0^wBZsloQcgciF+c2l4%eVxjn!A~FKl>xIjLv;(Qe2AK`a^mXurXcOl_06}e zg0zo^JX8${(!RPDQhAPWIh;j1Q{`E1!0gJVq&R*$;tKD7c`gn6=L!1%i|J8e{BLE7 zoQ4;26CQ6%3NM8!`}HUz6=tDd;K7d|ZBA8$I2!`->5K^55gSoeQWxb~nA>NL|1yrh z=jV8g4J`D3Yn@cjZk;4$MQ}YUJ44vnpTDqwJcz2?wv>5j)9@guD_X!2jzP!ZZ_+7; z@R~n1!LSg1u)hX`oe1!D9^I^c%|SdSd)Cvbt%QG@<&LHW*2o0Cr9WK9E4pTS^$K3| zoT)y6lj`p+8OjZ71YSYx2<%|-|Gpjme|y2YtXxJ=-kIb7SdQwormV<)2w}LFw5Lhz zA$-hWx4bfOn}2+#7NjjRAmmJqkBNV9EFQ8H6CGvLS-hrrtRsg9w6Aei@mSI+?Qz;N zU$11}TiI5yNB(-z}H2MFNtu&*NIt_)Kdj{R(ocuY|pQ^Y-SA>$d zUa%uh-ZCR4-GY#D+c@+;yUnrs)`Dam4#PC-5~d-Xxysyxsd6ZMI{7dS^AR6nq_qY4 z-+$P&(b@#hyWMF~2vkvE`U_6iHzJzd-=dN?gt+Qjh5KKBE$q9JZuLfrt`5DX>P!s! z|AWE9B-UeG-lcn^oIf|*QQ}6%pTSRGu)Z?^ePDk-06wlN8v@?^Vc5d-bEGN8EMG_g z`()y>r8y8eg3~utGIj-(Bv3A=cqtmRYGDIkKaG!J-sMzK!2VV@tN)Ka4PIhRtcYAU zBFZPyB{V%hDtS#P#+0~%eropTOXxo|N(D#4Z-4W@oxt5#m*jMQPH&!%N$Jj<#lM7T z>v-96PLueh!zSLp^QUJJ3&p!aM|$A1a7IoY{%i$Tr8n|PlZ}F#qiEOc*i{8B^MFYO zu7t;8aUxR zy4oZAS%JdiXuvu`LY+BC`*|5P=BN>LRcjLp`f?+<3@**qMCHY}#HGv9BnaYTsD7>m z7@>_k7oyK!Agn0DpT=wcO*%9fsyN!jT45UBUa3;mZkkNDuH;0n;^NGKoZ50`g7T+x z@aRv`XE7>PW4OLDv-`Uza#y)VW4fMF6P@Pu(}C9M#09Gg#idxq*E=$sTS})q7`gNU z>5*f5D3uyB75Zr}hUI5_r4EY>28l@l@gZ~o6d|7!~PyI@IQh)E{()zOOB>yeW5*GL z9(#kmJo7}8sOBvmu#jW$KK-kD@57v~DwV3tUHaH^ElEXOt9$=sI~_rp)Wx&bp-h2j ze1nz9ML@Z+@dGV5GNm@vGepyDVLVPy&RZg}J9xOs0@qG;HOsb#2A&w%4_=b~;O!J_ z@|3fV_sbF3qhHk?U>CN7gQF0+kiK)-Mt|2Q+P(1EqssRn0+Z~~mkW>HB${@Rwzy^! z9mYkdX(J^4%8Y!>JVjmpo{6xh=es~#Q{30E_;#763+tkBw_*5LG zAp|8@P&fUsws+}p9ok_nNPA-8iD57(PiqyBVeLd2Ih)5`6sk9OhtZJ|LK=CWEGM~- z>0BQ}rj?Ak4Rht)SnHDQ%e=?Jg#7Nu`>F_Vv010wa(3eOQ#yi9;hsjytPF@MXTrf_*0GUt(V6O%UPcO`ceTm{Ph)> zw%)Ey_epXm6x8}?^*?1k3h0@SZ;bPi9At)u0gQO*Uq>8*yQRdUR4}8Q_#Q%jLW=5wRo{dN<%TX-+F;3?yBkg7inKrpG@!T#A8YJ_{f{u?~R09C>-*DzI!lLIEZa`XHr7^!OMi z*Lv=`^CGT~IJ{UYpJC?LFS`#dEAEDR3nL*o^Azt7s~r;e0Nrw0SqG_^N(}#XntbWK z*?Vel+y;zJ*h2CLxJJYpjB5U^bH}$Alu}usWOG0Bty75FO@dtMdr?sN>BAU~*H`W? zRgNy(hJ)4)wqM`*E;l0O=}PcLBA>o`>#KqLH+j19aTeEJ_3#&a`*FtMj=pvad^7NK z3s}YQQSZ&s8N7gf$2Kphg^nLErj?F)wzeMt4B>2?la7|~Q*}Pn= zIoQKM$>aNvCky=247FP23D<1r%Jix?)`_qe7+U)%iH^<3AXL@X-eIMCe6GI~J|1R= z%uE@3^9a0~Qj&jrH)D14qDmWw&3Kg(QGBCFKPo{c$2w{?UELy!2~I1xa;S@lAOQ_= zKIPzdX34mZ7x&HnyqbMaZ@|#zhKkYAY_NI<+nt_(f2{*+t#Z*1{^k2-*vjJeV|H_1 z-irc6d~tuPQM#h<6TDMh;yp$*w9(?x+`FQ_Xd}CN;-!~!{ke(zgb3d(C|Ff&j(M%a zHJ%1Qv#X)O>}`&ZPa^>|p8KZA(gznSrDCdgTBfB;Th8X@H_)qP1E#QabSgbs?EzGJ zT|XZ_QT8-99y$2b76C-Q2cQK)q>>G0$E}O17xY~n7O=QV6wqPv*_wcxHi&gywVj2b z>2@(gEHEmbh+1@gKAE#VZi+G((YL8?wJu>Ve$RNEoPLV4$A502uroGaDE?Z!kQotU zE3meK!P;}@T+}IFOxCid`T2rEoM&uPjkSt~Mo_}FkK%BJVbtD2!KMcjhox5pE0th` z!^tpYhBGBA;-#}9_LMo@u5umkwSp*v@ABBC7#CE!TGUwdnuLKz3F@WF#W0Z>e+=h0Vh~*XO3|>^$(M1Y6Da26%>CNw6n@|cz5d-125H;tNky8ZP;%d?(C;=> z{&Rizmk;&c6 z#Myf{3H~%OZ;?6+Vb{k7a)oq+#jYN{DrVK26)AC}b_l%1_oJO!>9O31lFP1rn%d>G zg4QLb-<%5j1Zf0mny`hEZenafT@rX zK#uaoW;WZrVN@-$S^rkosjG&yiRm!;R>G8UV<;pexIi@27H@haw*H3!cHvlyo^k=r zBlk!Xu7Yvtns+^&tEk6S}M<^xukcYnNCn*wzH0>w69C+m5peV)+5zk8~N7s*?Kxd7Z+!| zX`s!3@Ew5R-vT)!H@9S{rT$Z&$ncUm`cBE+POZl_stgi_SR3p2!C}FEx%dIAx6Pfj z+WM*PV;+h?ThG(Ps#@9T)37I|oeWSy*?ue3Eb;~4$xACZ9ANJJ$vjK-_kuRKo?$)| zH=@DF!?}Y_aQKNur(t{>lwx|O&Cy!r8k9utGgq~*?>gF0zU52F*#w)RSQVPP!&a7h zd|~MJVXZ0RmBO2RkMgugj-PhmwY}EKcWoI2hIX{tW4~5$4R+>91!T)A;4J2>(s8?_f5;iCZlnx zF?qDWJlSU23Lr+6AXM(1!HwI|+s=CZg)2~*XZ$%{1xBd-kKFy^S0&vZBinhmtPm7Fo1)G3Tn(3=$=EY#JNmYAyb zzglOecWH6Eh<19$0DHPcJIcmr6%%#tP6F?2XVh=TlJc_C$=X=8in+r{$nSY`BSza8 z>+i=}*nAY<2yoY-UHB&DZe#IXx&?x8SXB;avm^2&<*VO0mTD9|{&JgKYz;^6**n%s z8h4%GDK02K?0Mtj@_f1!Nddmt|E65EWlMURtprg-?!};)IlBor`;U^t4fQsi?Sto( zhFdJ&TcMrZJ_qM&319~2P`O34;)s#or)KTZ(HDms>XS9}bU&Y=#^~cO8kA&N55I53 z<@C0s(2YqF-qDI+TEj@g(&zYCrOWvzi@BD3Nytx~*4v1c`Pcdc9gSZV!-Qwbyji4Ky zBj;P|bd~mZ_l%+-SNzB?x$h$eiQiFsGC=4S5}A=|GvQn4a{@V0l6Jm3&ywjq9}0@v z>sB54A&-rHidia%W|*I-E^wWBB>f=lo6alNEM+n2x& z43*&TE;&22@on12Cp+6|n9O!*pLl)Nc&9H120@Q&o@5!T0gHy2%xF3o7v2B?t6*+%nkcV-^?vG8C(OLyzX9sZnb zC%p%B0uR^EbPuBq#F_Q;u20y;SjLDm3h*!Qf_uvj=spyR%Z=#_cqcya$rS zS4%Sc%(mKfD+3pgWyUZZh8m}ye5)_GgXf}Si(B(+U$-L8Fh-`*_hK^l+(sWn+&pm8 z@%@zA4G7-`=>VDx3{zTirHR9IZ}p>Hl6ba8Y4`}nb;Tza%~gZ2lm2KonvvI9|8aX& zuzFjjaNnFVm*1{rr_XDqW9N6KAz+f;)^Z)^2_dgS!uodn;uZa@dFGX6Gya>%n1rLthn)1~m&rY@kN%LfeG|knERy`Hv z0#enb6x;0^Hlk|^Qm;NwUqnpqOX$^9Ez;brun9FiWVkU!xAS9WyMpkYE5%s9!niVZ zd2Weg=%ZrekBVRUvqQzk!h01O>#<6@J7BMC*0+vxu|DoZCSAv4bYFWlpv^*4apqX% z*X~LH33Gk9Lq)p94nH5PY*=^}_ArG|q1bJt(%wBLjjYDRYuk&iT|#J&Y`YtHHquy) zeDc$>9eh|DKA$U7<@QK~Et#GgNN9j8?zo!$;Odx$h zC&#+3LB1TdR+!Gswce8We6gcY+N=7VrF>;m8nS)Yv|87VC8tmKTAwNR`Zwy`r7~53 z`ig{1i?!+Tu8YEkbK8`@4@SkE4stUoK9>;e34~N z&en0J%a*AU-`VrAwod8b7yZ6*ug~aAkL)=%!F;u+mgEv#9mUKY*4 z?{dW6lzHx0PW3Kkj~-n{iz&@)>z}2(+ZS?9w1w^$(6Xzy#;=KqZ(@3i z)X>K(rc387OYbJkzpuHAi(cOQx#Oyfj#OYQNY=8|HOxJJuZvKbm+E^jDR*}K;Jt>M zbs9=)Un#}+`aQC9%EoH5@3ijDS^iAx5&4nCGNM0JV)I?ApHEQN1yTtYt+mr}pIv=@ zv>KUS|MJ2jFdi%&>row#=4#unC>^Mhp8peb%GZqIQ6$Smv2kEt-I`m5lWg)pW3_?| zZbZ!+oXN`~N+bdpkeYS(PI^`GG5X{yLw|*AYSx-sV^h?m$ez-_JKNoe? z(4xhC)n(U+A?sVS{cOdH{vOB5oV=77frpt!2j2|CtuY4I>^@MMwMFLMs`6a=kRa}y zti~WIzf3XQroP}geWC)}Ib_wBzRyl5uzkMV8J89apGo<_*QHh~?U2cb_-4-JT5a-v z-)%)<=;>mE-EOME!IULaQFB7rIVfR@wiyyCwzn}N`!$8GGZPG1h*9y1o!JYX_B)KD z&L$GvuW)O6+T3o2vhGzYxoRHYo$ppZCClx5@3-^YvB1;S?xUf*RUT8IZ`Q|J3AE3s zctLK&ux;1F8Y5Bue)l7IS~9w%Lgmk`jw_|vJAPXcT$(j=X%&5FkNA~m{uXS1LX>q- z)>nGM0%e;aQq$e#o--KB@y;G{(N5jLxgHXn zN?j`lFD1+M&T9wcgq6-4)6`I=ayopIG9?bVj&dnZADMSYe7ffxmArmKrmmGk|8^!9 zLZc?7^ylkPoODvgeyR8S&b+L1n2)ukF2l>o-Uo_Fn>b7Q>n{1obtbVvCQrwqC0mz1 z4dIcJYEAlHb#%kJiw~xxMOweM&idz=^~cWsKD;UK!;RnCHMY9epP5p`zqJ+%`fSzP zy4pOd={QwSTp!EN8|b;8cIe7h8{V|smSRgpm3%kF+9z588o}ZBajg>Eh?%A0*oCBy zYvVIgTk4xt`$-gocA5sgmHgMAoQAD-`jJb!z+phWy_9C|wCqjxxG2%xsSsvE5HZFJ z$B+1sa$ZFEMvL6;eyp^tT(H9w;&*U^i58*74Au=1H5P$rDF^wd(+hV^1-Xb}^w+Ka znZos^lZ&GEJ~0)ByQvkf79UhL_4JqokoRybMajqA3^b#w6VSF8q#-7`@S}LKffL#& zVqU8Id$R3YY*mtjIX`oM7RmzzroP7I|h4myTi5` zwL_ClC)Hl59U_Eugd|_M_+1k#kUeO3LqDqOL;Jq`V95pMNUAFUx_6x>IV^qj42_`5 z!8AQVSF_tfsjqW#*P2+;lGD<~umAL4*5oRmL6=zLa3AOvbyj{~r%xLO7v#5R6k2~fTLgWPH0Oyc2sD{gLojjcs zha4>V85tEGEmHpDf~H=+Az|=reJ3;FaxxpW)*V)ULc-d*ei&n!RE*y(8?YH06aCln zM-cU`TICC8WxX;d9x5>?iR(Rh(D{}fp@Zczo^G3<&v!6|#2fGWDV^8aN8tjOgu+<$ zj|-T#QDIK~yN3vPwI*Wy(3+NG{5dI-7Q-Cn!##UQX_a%7vzb}f_x-zvlZO7tdh^(P zz{p?G@|jfx2cxP=t!Fn2d@g<5(Y)dWT2JasocTKxFwH5ak*|1X7gYq|MT7LxT~I4HG2;+Mzvhe zt|P4Z$+s2?Y0phS->|6Nz?DUxaqrf(?Vn4P>l35y*5h;whPxs5_ZIR4#giPK53wL0 z6TX}YdfgDBeWd3}kkCb_!8%j99$UP_iNfuZPJ#-o{JPEcrLaHyZ$=S$2}--+tU!)nPO6pBhY0SF80YZG_nrX2<1cRy8SyI30Gq2y4}iwHqRkMtj%2^o^io_B-~R#_yghwbJh-EtcCH%b#kO+)&} z%~{qNZ57o8?6&di9mTl0Rve<|xtO!a%&>i5*bGPYS%a_hVcR+dumR*YlIqe>ayDfwV2!h(?`A7 zOKgb!IfewJWYxjGRoBzzXw)^aIi5x2-nv=^^L8JmsJ$z7XHyhsk+|BS5$H!k>ssIH zZ-sA)oV#xHoE~W9Cx%LK0p7b-fk4nJrMdWXxyp04Yqlo|sZ&ET?xDDzDDrJfMZob% z>HK0T2V#31m4n>gZO9=~0{)-Qu00;gbdQfQF>WAN2 zTFREP?QE;dxQ?g|SxZ|kjp~p~Dq(F{!}b}<$;nz>&}=qsT1KjkI(xpanQAX~^Ev)_ zKfKTT{O-TsbNjyDY5MgUCl3jHy)U$GD>E)zyLaK0JYAewUn)r9M+NHCe=;8Bj9th@ zCJQ`o4^=k_Q_O-K=C1L1>NFJ7^&;xtu?zhdSu|*?#HA5=@dr=K3&DXoum_*(lsM< zPDFP4lgg?{`Hit#J1^`V>S+~qwXZuBdFmhAVqRR@{off_#LPDZEL2tUPygp68=;t7oX~QYmM`=K%I4V!Y+g-|M9&C zjEjvXr;-I6z4E+e<87t89zMFYGk1G4BeL$t+z)*t$F%GQIve9}>wQynv!h+WVU$1h zNb{<{*weu(4SbMz;*fWI_?7DqKM$mpFKrNIf19VpFgB7G_sRGj*S{8ZhP zjJ@5j#J?ubnim~WAbZ?>;Dk-6ZuEZ_c>CPuGXmR|Q~_sP?-ZZdsa_g1 zdeu_JPR7vGnwZ9`Q#5Czek9m-aG1_NFs$xSmUKG=J+ltg(7qo<#oJ~&wnAr|-z^nd zZ1u+?q~a&nMREd5WKw?SF;0YiwdDby+sYUZ_l7oG8_nIVSXy$1(vWfX0gfHJC_o?9 zZKDwII>o_n+W|h+*EHD?#2n8L9Q`MR3SYTGCgOMZs*1Af@!H_4bMm{iWPq@f_4YWd zB*~>)?|ZJN2kNwrFCvmzUvbbXnv@vL@>BAgXc?2kaI!VR4|xsK5gN;|@RR+!a9cQz z>~m~qrkv}n7#vc8@F}lPt*7k}xncBlOTCn10XS4ujQQ0&Mq&G~?fhR}+G~iwVK-x_ zW*jhj#RkRxT#;F{q+}|z{F*B3)q!fUfOTIYo-Q+|gT{2*v2X<@BHfW9Jx)NxB(DJ` zd>@KLS|B^0FA^>AdQ?C#- zqzLI(!LwSI6^D~H-LQE#xi{1wA)zXdZ7h0ETV6cswEtPMCrPe(met*^A(!se$#JEi zBgHdD1Abv_jo@V%$b9K(6rl?j0c6q4tuP{s^e?hG2rE`$TbL6UyBbjuwBf=<8G_hH%rK-_%Yb8^fn z_FSaIrf$#LHgF0H>N|2WSx`hd{(dH6(xUa~H6DTND(W{3z<_)Hz~LDb!m17GAgk^h zqP7BBl$o2q9S@+NWPes;8L(lg9#SGBSmX3l_O23Z#-MiY0>~F3OXKq(gjGb6zLs3G ze9k76@YGKyAr+_-+8_<)ORVELX8o0Tn<8sZ%DNn&5j1zHtK7TxEKfB|W$=+bl?^ zFB)ZGN7*+$?CG)yNvl(c8dV6Fu^E>Oyr5jc-OA{hvaEwk(!;_K1FVuB?iD?}Ots(# zF%Q59#`1W5E|>yS^nAzP%H~2Mw-N*0Mi*0}+zJ+2C8yEKcbuNR^K=&2ThCsCC#=9? z;*{K91E##$^k=V3#gx_-gnkxRhC`SL@`N;mM)ZtdaDA4~YwJTOHD)CwgurGYZ}bXC zAuuz-1!rmH!;x$cbgEX5#WmK;B78d%uaC!OW1me2;s&bx|EXDE08pKQs-N0d_zND{j`W_6O`v`+&W~Q!Fuk99%zhHZ8#4f+bTBLEM72tu zfw6N&u|)e2Y_Xo}X{mN)k>K zS2E=rxG|BRx#O~Bl_0B0oTEyS9&`bQux542Gnf1js-wKnl|+UMIW+&C%~eZ$V>3!b zG6ycz#WWVMQ|)?>O;i;s@{(qG1UYKCxC~W_7!#mRNPYtG2T6rWaYxNOZ6GGAP<2bs9CP?HV@dh&Iot==FM81#8+R@)W@O%E#knybi%d2#MCzmHUWX;>> zL5NjB=vQN?RHGl$GFI;mdk0uD6catO&}n8H6so!?H&_Te^kpL5Cg-iE8(hMCS5={8 zek@_gz`$@dg*TM8p&;Kh#Wb_?7EO;@g*rk-{tRfML&IDY z$zg8x_sXUiu(&_6Ifjrjl}J8qWy1(EJ!@N3jv>YL@O(r?n}4IHzQPVPlnaKc-Ug-Z zUfS$!$i+%9>QXvFX%m_Vfqy_HwTyTymL6PE@LsJXN#SKN?8VDoe!>m@@h5QQ)stJ2 zSoNpHDA~=2l=FkHF(AwS_aNBl6dH zZ~gMpR}e;%*gjz&-13`S#IF*Jqs{v{d<9*F`yg{7GDNeWW#KDp|2flM_Ykt-iA1j- hj9%ZG5M@5~`M&z1@ImUk(ho@Z;d0ivm$8H6{s&0qp1=SA diff --git a/doc/images/plugin-api-diagram.svg b/doc/images/plugin-api-diagram.svg new file mode 100644 index 0000000000000..a919537e3497f --- /dev/null +++ b/doc/images/plugin-api-diagram.svg @@ -0,0 +1 @@ +
RPC Relay
(per frontend connection)
RPC Relay...
Backend
Backend
register(handle, DTO)
register(handle, DTO)
RPC
RPC
Frontend
Frontend
Main API
Main API
Main Impl
Main Impl
Ext Proxy
Ext Proxy
Browser
Browser
Main Proxy
Main Proxy
Ext API
Ext API
Registry
Registry
Plugin API
Plugin API
Plugin Host
Plugin Host
Contribution
(e.g. a code action provider)
Contribution...
Plugin / Extension
Plugin / E...
Ext Impl
register
register
provideItems
provideIte...
RPC
RPC
provideItems(handle, args)
provideItems(handle, args)
Text is not SVG - cannot display
\ No newline at end of file diff --git a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md index 2563edc2a15ad..0038de11eb539 100644 --- a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md +++ b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md @@ -5,20 +5,28 @@ That will require API that goes beyond what's in the VS Code Extension API and t You can do that by implementing a Theia extension that creates and exposes an API object within the plugin host. The API object can be imported by your plugins and exposes one or more API namespaces. -Depending on the plugin host we can either provide a frontend or backend plugin API: +Depending on the plugin host we can either provide a frontend or backend plugin API, or an API for headless plugins that extend or otherwise access backend services: - In the backend plugin host that runs in the Node environment in a separate process, we adapt the module loading to return a custom API object instead of loading a module with a particular name. +There is a distinct plugin host for each connected Theia frontend. - In the frontend plugin host that runs in the browser environment via a web worker, we import the API scripts and put it in the global context. +There is a distinct plugin host for each connected Theia frontend. +- In the headless plugin host that also runs in the Node environment in a separate process, we similarly adapt the module loading mechanism. +When the first headless plugin is deployed, whether at start-up or upon later installation during run-time, then the one and only headless plugin host process is started. In this document we focus on the implementation of a custom backend plugin API. -However, both APIs can be provided by implementing and binding an `ExtPluginApiProvider` which should be packaged as a Theia extension. +Headless plugin APIs are similar, and the same API can be contributed to both backend and headless plugin hosts. +All three APIs — backend, frontend, and headless — can be provided by implementing and binding an `ExtPluginApiProvider` which should be packaged as a Theia extension. ## Declare your plugin API provider The plugin API provider is executed on the respective plugin host to add your custom API object and namespaces. -Add `@theia/plugin-ext` as a dependency in your `package.json` +Add `@theia/plugin-ext` as a dependency in your `package.json`. +If your plugin is contributing API to headless plugins, then you also need to add the `@theia/plugin-ext-headless` package as a dependency. -Example Foo Plugin API provider: +Example Foo Plugin API provider. +Here we see that it provides the same API initialized by the same script to both backend plugins that are frontend-connection-scoped and to headless plugins. +Any combination of these API initialization scripts may be provided, offering the same or differing capabilities in each respective plugin host, although of course it would be odd to provide API to none of them. ```typescript @injectable() @@ -30,7 +38,9 @@ export class FooExtPluginApiProvider implements ExtPluginApiProvider { initFunction: 'fooInitializationFunction', initVariable: 'foo_global_variable' }, - backendInitPath: path.join(__dirname, 'foo-init') + backendInitPath: path.join(__dirname, 'foo-init'), + // Provide the same API to headless plugins, too (or a different/subset API) + headlessInitPath: path.join(__dirname, 'foo-init') }; } } @@ -62,94 +72,69 @@ declare module '@bar/foo' { ## Implement your plugin API provider In our example, we aim to provide a new API object for the backend. -Theia expects that the `backendInitPath` that we specified in our API provider is a function called `provideApi` that follows the `ExtPluginApiBackendInitializationFn` signature. +Theia expects that the `backendInitPath` or `headlessInitPath` that we specified in our API provider exports an [InversifyJS](https://inversify.io) `ContainerModule` under the name `containerModule`. +This container-module configures the Inversify `Container` in the plugin host for creation of our API object. +It also implements for us the customization of Node's module loading system to hook our API factory into the import of the module name that we choose. Example `node/foo-init.ts`: ```typescript -import * as fooBarAPI from '@bar/foo'; - -// Factory to create an API object for each plugin. -let apiFactory: (plugin: Plugin) => typeof fooBarAPI; - -// Map key is the plugin ID. Map value is the FooBar API object. -const pluginsApiImpl = new Map(); - -// Singleton API object to use as a last resort. -let defaultApi: typeof fooBarAPI; - -// Have we hooked into the module loader yet? -let hookedModuleLoader = false; - -let plugins: PluginManager; - -// Theia expects an exported 'provideApi' function -export const provideApi: ExtPluginApiBackendInitializationFn = (rpc: RPCProtocol, manager: PluginManager) => { - apiFactory = createAPIFactory(rpc); - plugins = manager; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { Plugin } from '@theia/plugin-ext/lib/common/plugin-api-rpc'; +import { ApiFactory, PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; +import { FooExt } from '../common/foo-api-rpc'; +import { FooExtImpl } from './foo-ext-impl'; - if (!hookedModuleLoader) { - overrideInternalLoad(); - hookedModuleLoader = true; - } -}; +import * as fooBarAPI from '@bar/foo'; -function overrideInternalLoad(): void { - const module = require('module'); - const internalLoad = module._load; - - module._load = function (request: string, parent: any, isMain: {}) { - if (request !== '@bar/foo') { - // Pass the request to the next implementation down the chain - return internalLoad.apply(this, arguments); - } - - // create custom API object and return that as a result of loading '@bar/foo' - const plugin = findPlugin(parent.filename); - if (plugin) { - let apiImpl = pluginsApiImpl.get(plugin.model.id); - if (!apiImpl) { - apiImpl = apiFactory(plugin); - pluginsApiImpl.set(plugin.model.id, apiImpl); - } - return apiImpl; - } +type FooBarApi = typeof fooBarAPI; +type Foo = FooBarApi['Foo']; - if (!defaultApi) { - console.warn(`Could not identify plugin for '@bar/foo' require call from ${parent.filename}`); - defaultApi = apiFactory(emptyPlugin); - } +const FooBarApiFactory = Symbol('FooBarApiFactory'); +type FooBarApiFactory = ApiFactory; - return defaultApi; - }; -} +// Retrieved by Theia to configure the Inversify DI container when the plugin is initialized. +// This is called when the plugin-host process is forked. +export const containerModule = PluginContainerModule.create(({ bind, bindApiFactory }) => { + // Bind the implementations of our Ext API interfaces (here just one) + bind(FooExt).to(FooExtImpl).inSingletonScope(); -function findPlugin(filePath: string): Plugin | undefined { - return plugins.getAllPlugins().find(plugin => filePath.startsWith(plugin.pluginFolder)); -} + // Bind our API factory to the module name by which plugins will import it + bindApiFactory('@bar/foo', FooBarApiFactory, FooBarApiFactoryImpl); +}); ``` ## Implement your API object We create a dedicated API object for each individual plugin as part of the module loading process. Each API object is returned as part of the module loading process if a script imports `@bar/foo` and should therefore match the API definition that we provided in the `*.d.ts` file. -Multiple imports will not lead to the creation of multiple API objects as we cache it in our custom `overrideInternalLoad` function. +Multiple imports will not lead to the creation of multiple API objects as the `PluginContainerModule` automatically caches the API implementation for us. Example `node/foo-init.ts` (continued): ```typescript -export function createAPIFactory(rpc: RPCProtocol): ApiFactory { - const fooExtImpl = new FooExtImpl(rpc); - return function (plugin: Plugin): typeof fooBarAPI { - const FooBar: typeof fooBarAPI.fooBar = { - getFoo(): Promise { - return fooExtImpl.getFooImpl(); +// Creates the @foo/bar API object +@injectable() +class FooBarApiFactoryImpl { + @inject(RPCProtocol) protected readonly rpc: RPCProtocol; + @inject(FooExt) protected readonly fooExt: FooExt; + + @postConstruct() + initialize(): void { + this.rpc.set(FOO_MAIN_RPC_CONTEXT.FOO_EXT, this.fooExt); + } + + // The plugin host expects our API factory to export a `createApi()` method + createApi(plugin: Plugin): FooBarApi { + return { + fooBar: { + getFoo(): Promise { + return fooExt.getFooImpl(); + } } - } - return { - fooBar : FooBar }; - } + }; } ``` @@ -169,10 +154,12 @@ Due to the asynchronous nature of the communication over RPC, the result should Example `common/foo-api-rpc.ts`: ```typescript +export const FooMain = Symbol('FooMain'); export interface FooMain { $getFooImpl(): Promise; } +export const FooExt = Symbol('FooExt'); export interface FooExt { // placeholder for callbacks for the main application to the extension } @@ -193,13 +180,18 @@ On the plugin host side we can register our implementation and retrieve the prox Example `plugin/foo-ext.ts`: ```typescript +import { inject, injectable } from '@theia/core/shared/inversify'; +import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; +import { FooExt, FooMain, FOO_PLUGIN_RPC_CONTEXT } from '../common/foo-api-rpc'; + +@injectable() export class FooExtImpl implements FooExt { // Main application RCP counterpart private proxy: FooMain; - constructor(rpc: RPCProtocol) { - rpc.set(FOO_MAIN_RPC_CONTEXT.FOO_EXT, this); // register ourselves - this.proxy = rpc.getProxy(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN); // retrieve proxy + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { + // Retrieve a proxy for the main side + this.proxy = rpc.getProxy(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN); } getFooImpl(): Promise { @@ -208,7 +200,12 @@ export class FooExtImpl implements FooExt { } ``` -On the main side we need to implement the counterpart of the ExtPluginApiProvider, the `MainPluginApiProvider`, and expose it in a browser frontend module: +On the main side we need to implement the counterpart of the ExtPluginApiProvider, the `MainPluginApiProvider`, and expose it in a browser frontend module. + +> [!NOTE] +> If the same API is also published to headless plugins, then the Main side is actually in the Node backend, not the browser frontend, so the implementation might +then be in the `common/` tree and registered in both the frontend and backend container modules. +> Alternatively, if the API is _only_ published to headless plugins, then it can be implemented in the `node/` tree and can take advantage of capabilities only available in the Node backend. Example `main/browser/foo-main.ts`: @@ -218,7 +215,7 @@ export class FooMainImpl implements FooMain { @inject(MessageService) protected messageService: MessageService; protected proxy: FooExt; - init(rpc: RPCProtocol) { + constructor(@inject(RPCProtocol) rpc: RPCProtocol) { // We would use this if we had a need to call back into the plugin-host/plugin this.proxy = rpc.getProxy(FOO_MAIN_RPC_CONTEXT.FOO_EXT); } @@ -232,19 +229,17 @@ export class FooMainImpl implements FooMain { @injectable() export class FooMainPluginApiProvider implements MainPluginApiProvider { @inject(MessageService) protected messageService: MessageService; + @inject(FooMain) protected fooMain: FooMain; - initialize(rpc: RPCProtocol, container: interfaces.Container): void { + initialize(rpc: RPCProtocol): void { this.messageService.info('Initialize RPC communication for FooMain!'); - // create a new FooMainImpl as it is not bound as singleton - const fooMainImpl = container.get(FooMainImpl); - fooMainImpl.init(rpc); - rpc.set(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN, fooMainImpl); + rpc.set(FOO_PLUGIN_RPC_CONTEXT.FOO_MAIN, this.fooMain); } } export default new ContainerModule(bind => { - bind(FooMainImpl).toSelf(); bind(MainPluginApiProvider).to(FooMainPluginApiProvider).inSingletonScope(); + bind(FooMain).to(FooMainImpl).inSingletonScope(); }); ``` From 725a7d2eea256e983438fd280966ffed9929cf9b Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 24 Jan 2024 16:26:42 +0100 Subject: [PATCH 067/441] Fix `vscode.env.appRoot` path (#13285) --- .../browser-only/frontend-only-application-module.ts | 1 + packages/core/src/common/application-protocol.ts | 1 + packages/core/src/node/application-server.ts | 9 ++++++++- packages/plugin-ext/src/common/plugin-api-rpc.ts | 3 ++- .../plugin-ext/src/hosted/browser/hosted-plugin.ts | 12 ++++++++++-- .../src/hosted/browser/worker/worker-env-ext.ts | 2 +- packages/plugin-ext/src/plugin/env.ts | 9 ++++++++- packages/plugin-ext/src/plugin/node/env-node-ext.ts | 7 ------- packages/plugin-ext/src/plugin/plugin-manager.ts | 1 + 9 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/core/src/browser-only/frontend-only-application-module.ts b/packages/core/src/browser-only/frontend-only-application-module.ts index 680e7d227c83a..1820adde62504 100644 --- a/packages/core/src/browser-only/frontend-only-application-module.ts +++ b/packages/core/src/browser-only/frontend-only-application-module.ts @@ -55,6 +55,7 @@ export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind, const mockedApplicationServer: ApplicationServer = { getExtensionsInfos: async (): Promise => [], getApplicationInfo: async (): Promise => undefined, + getApplicationRoot: async (): Promise => '', getBackendOS: async (): Promise => OS.Type.Linux }; if (isBound(ApplicationServer)) { diff --git a/packages/core/src/common/application-protocol.ts b/packages/core/src/common/application-protocol.ts index d84822c908d0a..d4b4a848d961f 100644 --- a/packages/core/src/common/application-protocol.ts +++ b/packages/core/src/common/application-protocol.ts @@ -23,6 +23,7 @@ export const ApplicationServer = Symbol('ApplicationServer'); export interface ApplicationServer { getExtensionsInfos(): Promise; getApplicationInfo(): Promise; + getApplicationRoot(): Promise; /** * @deprecated since 1.25.0. Use `OS.backend.type()` instead. */ diff --git a/packages/core/src/node/application-server.ts b/packages/core/src/node/application-server.ts index 011f90db35e60..e80212248293d 100644 --- a/packages/core/src/node/application-server.ts +++ b/packages/core/src/node/application-server.ts @@ -37,11 +37,18 @@ export class ApplicationServerImpl implements ApplicationServer { const name = pck.name; const version = pck.version; - return Promise.resolve({ name, version }); + return Promise.resolve({ + name, + version + }); } return Promise.resolve(undefined); } + getApplicationRoot(): Promise { + return Promise.resolve(this.applicationPackage.projectPath); + } + async getBackendOS(): Promise { return OS.type(); } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 32140ec1c0625..0364b384cf4a0 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -163,9 +163,10 @@ export interface EnvInit { queryParams: QueryParameters; language: string; shell: string; - uiKind: UIKind, + uiKind: UIKind; appName: string; appHost: string; + appRoot: string; } export interface PluginAPI { diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index d9e957a986af5..c2016725f7aa0 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -66,6 +66,7 @@ import { LanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/ import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; import { BasicChannel } from '@theia/core/lib/common/message-rpc/channel'; import { NotebookTypeRegistry, NotebookService, NotebookRendererMessagingService } from '@theia/notebook/lib/browser'; +import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import { AbstractHostedPluginSupport, PluginContributions, PluginHost, ALL_ACTIVATION_EVENT, isConnectionScopedBackendPlugin @@ -174,6 +175,9 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport supportedActivationEvents.push(event)); } @@ -317,7 +324,8 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport { return this.proxy.$getClientOperatingSystem(); } @@ -92,7 +97,9 @@ export abstract class EnvExtImpl { return this.applicationName; } - abstract get appRoot(): string; + get appRoot(): string { + return this.applicationRoot; + } abstract get isNewAppInstall(): boolean; diff --git a/packages/plugin-ext/src/plugin/node/env-node-ext.ts b/packages/plugin-ext/src/plugin/node/env-node-ext.ts index 2ad86064f13cf..18ac72b3038d0 100644 --- a/packages/plugin-ext/src/plugin/node/env-node-ext.ts +++ b/packages/plugin-ext/src/plugin/node/env-node-ext.ts @@ -51,13 +51,6 @@ export class EnvNodeExtImpl extends EnvExtImpl { return this.macMachineId; } - /** - * Provides application root. - */ - get appRoot(): string { - return __dirname; - } - get isNewAppInstall(): boolean { return this._isNewAppInstall; } diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 66e1809848db4..bffd73d1de30a 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -467,6 +467,7 @@ export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl Date: Thu, 25 Jan 2024 09:50:05 +0100 Subject: [PATCH 068/441] Add document URI as context to getDefaultFormatter (#13280) This way the default formatter will be resolved correctly in multi-root workspaces. Beforehand only the workspace settings where taken into account. Now the behavior is as expected, see #13108 for an example. Fixes #13108. Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 1 + packages/monaco/src/browser/monaco-formatting-conflicts.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89c38ab053d4f..919029e59c84e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [terminal] update terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics - [core] added preference 'workbench.tree.indent' to control the indentation in the tree widget [#13179](https://github.com/eclipse-theia/theia/pull/13179) - contributed on behalf of STMicroelectronics - [remote] upport specifying the port of a remote SSH connection [#13296](https://github.com/eclipse-theia/theia/pull/13296) - contributed on behalf of STMicroelectronics +- [monaco] add document URI as context to getDefaultFormatter [#13280](https://github.com/eclipse-theia/theia/pull/13280) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_not_yet_released) diff --git a/packages/monaco/src/browser/monaco-formatting-conflicts.ts b/packages/monaco/src/browser/monaco-formatting-conflicts.ts index 361db7e5350cd..b3eb98faf823f 100644 --- a/packages/monaco/src/browser/monaco-formatting-conflicts.ts +++ b/packages/monaco/src/browser/monaco-formatting-conflicts.ts @@ -59,13 +59,13 @@ export class MonacoFormattingConflictsContribution implements FrontendApplicatio await this.preferenceService.set(name, formatter); } - private getDefaultFormatter(language: string): string | undefined { + private getDefaultFormatter(language: string, resourceURI: string): string | undefined { const name = this.preferenceSchema.overridePreferenceName({ preferenceName: PREFERENCE_NAME, overrideIdentifier: language }); - return this.preferenceService.get(name); + return this.preferenceService.get(name, undefined, resourceURI); } private async selectFormatter( @@ -85,7 +85,7 @@ export class MonacoFormattingConflictsContribution implements FrontendApplicatio } const languageId = currentEditor.editor.document.languageId; - const defaultFormatterId = this.getDefaultFormatter(languageId); + const defaultFormatterId = this.getDefaultFormatter(languageId, document.uri.toString()); if (defaultFormatterId) { const formatter = formatters.find(f => f.extensionId && f.extensionId.value === defaultFormatterId); From 90250f58ea5285cf320fd5416236f63ccfb7326c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:28:19 +0100 Subject: [PATCH 069/441] Translation update for version 1.46.0 (#13315) --- packages/core/i18n/nls.cs.json | 4 ++++ packages/core/i18n/nls.de.json | 4 ++++ packages/core/i18n/nls.es.json | 4 ++++ packages/core/i18n/nls.fr.json | 4 ++++ packages/core/i18n/nls.hu.json | 4 ++++ packages/core/i18n/nls.it.json | 4 ++++ packages/core/i18n/nls.ja.json | 4 ++++ packages/core/i18n/nls.json | 4 ++++ packages/core/i18n/nls.pl.json | 4 ++++ packages/core/i18n/nls.pt-br.json | 4 ++++ packages/core/i18n/nls.pt-pt.json | 4 ++++ packages/core/i18n/nls.ru.json | 4 ++++ packages/core/i18n/nls.zh-cn.json | 4 ++++ 13 files changed, 52 insertions(+) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 49be2b4124272..4d0f5508d290d 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Hierarchie nadtypů" }, "vsx-registry": { + "confirmDialogMessage": "Rozšíření \"{0}\" není ověřeno a může představovat bezpečnostní riziko.", + "confirmDialogTitle": "Jste si jisti, že chcete pokračovat v instalaci ?", "downloadCount": "Počet stažení: {0}", "errorFetching": "Chyba při načítání rozšíření.", "errorFetchingConfigurationHint": "To může být způsobeno problémy s konfigurací sítě.", "failedInstallingVSIX": "Nepodařilo se nainstalovat {0} z VSIX.", "invalidVSIX": "Vybraný soubor není platný zásuvný modul \"*.vsix\".", "license": "Licence: {0}", + "onlyShowVerifiedExtensionsDescription": "Díky tomu se na stránkách {0} zobrazují pouze ověřená rozšíření.", + "onlyShowVerifiedExtensionsTitle": "Zobrazit pouze ověřená rozšíření", "recommendedExtensions": "Seznam názvů rozšíření doporučených pro použití v tomto pracovním prostoru.", "searchPlaceholder": "Hledat rozšíření v {0}", "showInstalled": "Zobrazit nainstalovaná rozšíření", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index 638c34d1298c4..b4764e8190151 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Supertyp-Hierarchie" }, "vsx-registry": { + "confirmDialogMessage": "Die Erweiterung \"{0}\" ist ungeprüft und könnte ein Sicherheitsrisiko darstellen.", + "confirmDialogTitle": "Sind Sie sicher, dass Sie mit der Installation fortfahren wollen?", "downloadCount": "Anzahl der Downloads: {0}", "errorFetching": "Fehler beim Abrufen von Erweiterungen.", "errorFetchingConfigurationHint": "Dies könnte auf Probleme bei der Netzwerkkonfiguration zurückzuführen sein.", "failedInstallingVSIX": "Die Installation von {0} aus VSIX ist fehlgeschlagen.", "invalidVSIX": "Die ausgewählte Datei ist kein gültiges \"*.vsix\"-Plugin.", "license": "Lizenz: {0}", + "onlyShowVerifiedExtensionsDescription": "Auf diese Weise kann {0} nur verifizierte Durchwahlen anzeigen.", + "onlyShowVerifiedExtensionsTitle": "Nur geprüfte Erweiterungen anzeigen", "recommendedExtensions": "Eine Liste mit den Namen der Erweiterungen, die für die Verwendung in diesem Arbeitsbereich empfohlen werden.", "searchPlaceholder": "Erweiterungen suchen in {0}", "showInstalled": "Installierte Erweiterungen anzeigen", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 0ec7fd0e738c8..76fd534ccb756 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Jerarquía de supertipos" }, "vsx-registry": { + "confirmDialogMessage": "La extensión \"{0}\" no está verificada y puede suponer un riesgo para la seguridad.", + "confirmDialogTitle": "¿Está seguro de que desea continuar con la instalación?", "downloadCount": "Descargue el recuento: {0}", "errorFetching": "Error en la búsqueda de extensiones.", "errorFetchingConfigurationHint": "Esto podría deberse a problemas de configuración de la red.", "failedInstallingVSIX": "Fallo en la instalación de {0} desde VSIX.", "invalidVSIX": "El archivo seleccionado no es un plugin válido \"*.vsix\".", "license": "License: {0}", + "onlyShowVerifiedExtensionsDescription": "Esto permite que {0} sólo muestre extensiones verificadas.", + "onlyShowVerifiedExtensionsTitle": "Mostrar sólo extensiones verificadas", "recommendedExtensions": "Una lista de los nombres de las extensiones recomendadas para su uso en este espacio de trabajo.", "searchPlaceholder": "Buscar extensiones en {0}", "showInstalled": "Mostrar extensiones instaladas", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index f69223826e88c..20cdbb68d413a 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Hiérarchie des supertypes" }, "vsx-registry": { + "confirmDialogMessage": "L'extension \"{0}\" n'est pas vérifiée et peut présenter un risque pour la sécurité.", + "confirmDialogTitle": "Êtes-vous sûr de vouloir procéder à l'installation ?", "downloadCount": "Compte de téléchargement : {0}", "errorFetching": "Erreur de récupération des extensions.", "errorFetchingConfigurationHint": "Cela peut être dû à des problèmes de configuration du réseau.", "failedInstallingVSIX": "Échec de l'installation de {0} à partir de VSIX.", "invalidVSIX": "Le fichier sélectionné n'est pas un plugin \"*.vsix\" valide.", "license": "Licence : {0}", + "onlyShowVerifiedExtensionsDescription": "Cela permet à {0} de n'afficher que les extensions vérifiées.", + "onlyShowVerifiedExtensionsTitle": "Afficher uniquement les extensions vérifiées", "recommendedExtensions": "Une liste des noms des extensions dont l'utilisation est recommandée dans cet espace de travail.", "searchPlaceholder": "Rechercher les extensions dans {0}", "showInstalled": "Afficher les extensions installées", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index 7d15f2570f673..48bbfd8d5d988 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Szupertípus hierarchia" }, "vsx-registry": { + "confirmDialogMessage": "A \"{0}\" kiterjesztés nem ellenőrzött, és biztonsági kockázatot jelenthet.", + "confirmDialogTitle": "Biztos, hogy folytatni akarja a telepítést ?", "downloadCount": "Letöltési szám: {0}", "errorFetching": "Hiba a kiterjesztések lekérdezésében.", "errorFetchingConfigurationHint": "Ezt hálózati konfigurációs problémák okozhatják.", "failedInstallingVSIX": "Nem sikerült telepíteni a {0} oldalt a VSIX-ből.", "invalidVSIX": "A kiválasztott fájl nem érvényes \"*.vsix\" bővítmény.", "license": "Engedély: {0}", + "onlyShowVerifiedExtensionsDescription": "Ez lehetővé teszi, hogy a {0} csak ellenőrzött kiterjesztéseket jelenítsen meg.", + "onlyShowVerifiedExtensionsTitle": "Csak ellenőrzött kiterjesztések megjelenítése", "recommendedExtensions": "A munkaterületre ajánlott kiterjesztések neveinek listája.", "searchPlaceholder": "Keresés kiterjesztések {0}", "showInstalled": "Telepített bővítmények megjelenítése", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 60c0ced3cb709..26779a74beec3 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Gerarchia dei supertipi" }, "vsx-registry": { + "confirmDialogMessage": "L'estensione \"{0}\" non è verificata e potrebbe rappresentare un rischio per la sicurezza.", + "confirmDialogTitle": "Siete sicuri di voler procedere con l'installazione?", "downloadCount": "Conteggio dei download: {0}", "errorFetching": "Errore nel recupero delle estensioni.", "errorFetchingConfigurationHint": "Questo potrebbe essere causato da problemi di configurazione della rete.", "failedInstallingVSIX": "Impossibile installare {0} da VSIX.", "invalidVSIX": "Il file selezionato non è un plugin \"*.vsix\" valido.", "license": "Licenza: {0}", + "onlyShowVerifiedExtensionsDescription": "Ciò consente a {0} di mostrare solo le estensioni verificate.", + "onlyShowVerifiedExtensionsTitle": "Mostra solo le estensioni verificate", "recommendedExtensions": "Una lista dei nomi delle estensioni raccomandate per l'uso in questo spazio di lavoro.", "searchPlaceholder": "Estensioni di ricerca in {0}", "showInstalled": "Mostra le estensioni installate", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index bc31ae4a46c77..6a1baf1c0901e 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "スーパータイプヒエラルキー" }, "vsx-registry": { + "confirmDialogMessage": "拡張子 \"{0}\" は未検証であり、セキュリティリスクをもたらす可能性があります。", + "confirmDialogTitle": "本当にインストールを続行しますか?", "downloadCount": "ダウンロード回数{0}", "errorFetching": "拡張機能の取得にエラーが発生しました。", "errorFetchingConfigurationHint": "これは、ネットワーク設定の問題が原因である可能性がある。", "failedInstallingVSIX": "VSIXから{0} のインストールに失敗しました。", "invalidVSIX": "選択されたファイルは、有効な \"*.vsix \"プラグインではありません。", "license": "ライセンス{0}", + "onlyShowVerifiedExtensionsDescription": "これにより、{0} 、検証済みの拡張子のみを表示することができる。", + "onlyShowVerifiedExtensionsTitle": "検証済みのエクステンションのみ表示", "recommendedExtensions": "このワークスペースでの使用が推奨される拡張機能の名前のリストです。", "searchPlaceholder": "{0}で拡張子を検索", "showInstalled": "インストールされている拡張機能を表示する", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index f094b4eeec4d2..f1d0cce1d2309 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Supertype Hierarchy" }, "vsx-registry": { + "confirmDialogMessage": "The extension \"{0}\" is unverified and might pose a security risk.", + "confirmDialogTitle": "Are you sure you want to proceed with the installation ?", "downloadCount": "Download count: {0}", "errorFetching": "Error fetching extensions.", "errorFetchingConfigurationHint": "This could be caused by network configuration issues.", "failedInstallingVSIX": "Failed to install {0} from VSIX.", "invalidVSIX": "The selected file is not a valid \"*.vsix\" plugin.", "license": "License: {0}", + "onlyShowVerifiedExtensionsDescription": "This allows the {0} to only show verified extensions.", + "onlyShowVerifiedExtensionsTitle": "Only Show Verified Extensions", "recommendedExtensions": "Do you want to install the recommended extensions for this repository?", "searchPlaceholder": "Search Extensions in {0}", "showInstalled": "Show Installed Extensions", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 11d774d51e38c..d0d141e9161ad 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Hierarchia nadtypów" }, "vsx-registry": { + "confirmDialogMessage": "Rozszerzenie \"{0}\" jest niezweryfikowane i może stanowić zagrożenie dla bezpieczeństwa.", + "confirmDialogTitle": "Czy na pewno chcesz kontynuować instalację?", "downloadCount": "Pobierz licz: {0}", "errorFetching": "Błąd pobierania rozszerzeń.", "errorFetchingConfigurationHint": "Może to być spowodowane problemami z konfiguracją sieci.", "failedInstallingVSIX": "Nie udało się zainstalować {0} z VSIX.", "invalidVSIX": "Wybrany plik nie jest prawidłowym pluginem \"*.vsix\".", "license": "Licencja: {0}", + "onlyShowVerifiedExtensionsDescription": "Dzięki temu strona {0} będzie wyświetlać tylko zweryfikowane rozszerzenia.", + "onlyShowVerifiedExtensionsTitle": "Pokaż tylko zweryfikowane rozszerzenia", "recommendedExtensions": "Lista nazw rozszerzeń zalecanych do użycia w tym obszarze roboczym.", "searchPlaceholder": "Wyszukaj rozszerzenia w {0}.", "showInstalled": "Pokaż zainstalowane rozszerzenia", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index b37bd60a787b3..d352d7578cb96 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Supertipo Hierarquia" }, "vsx-registry": { + "confirmDialogMessage": "A extensão \"{0}\" não foi verificada e pode representar um risco à segurança.", + "confirmDialogTitle": "Tem certeza de que deseja prosseguir com a instalação?", "downloadCount": "Contagem de downloads: {0}", "errorFetching": "Extensões de erro de busca.", "errorFetchingConfigurationHint": "Isso pode ser causado por problemas de configuração de rede.", "failedInstallingVSIX": "Falha na instalação {0} da VSIX.", "invalidVSIX": "O arquivo selecionado não é um plugin \"*.vsix\" válido.", "license": "Licença: {0}", + "onlyShowVerifiedExtensionsDescription": "Isso permite que o site {0} mostre apenas as extensões verificadas.", + "onlyShowVerifiedExtensionsTitle": "Mostrar apenas as extensões verificadas", "recommendedExtensions": "Uma lista dos nomes das extensões recomendadas para uso neste espaço de trabalho.", "searchPlaceholder": "Pesquisar extensões em {0}", "showInstalled": "Mostrar extensões instaladas", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 2f52b0b10b0a3..126d26704ff93 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Hierarquia de Supertipo" }, "vsx-registry": { + "confirmDialogMessage": "A extensão \"{0}\" não foi verificada e pode representar um risco de segurança.", + "confirmDialogTitle": "Tem a certeza de que pretende prosseguir com a instalação?", "downloadCount": "Contagem de downloads: {0}", "errorFetching": "Extensões de erro de busca.", "errorFetchingConfigurationHint": "Isto pode ser causado por problemas de configuração da rede.", "failedInstallingVSIX": "Falha na instalação {0} da VSIX.", "invalidVSIX": "O ficheiro seleccionado não é um plugin \"*.vsix\" válido.", "license": "Licença: {0}", + "onlyShowVerifiedExtensionsDescription": "Isto permite que o {0} mostre apenas as extensões verificadas.", + "onlyShowVerifiedExtensionsTitle": "Mostrar apenas extensões verificadas", "recommendedExtensions": "Uma lista dos nomes das extensões recomendadas para utilização neste espaço de trabalho.", "searchPlaceholder": "Pesquisar extensões em {0}", "showInstalled": "Mostrar extensões instaladas", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index e27fa1d750c96..f4d50dac8f0bb 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "Иерархия супертипов" }, "vsx-registry": { + "confirmDialogMessage": "Расширение \"{0}\" является непроверенным и может представлять угрозу безопасности.", + "confirmDialogTitle": "Вы уверены, что хотите продолжить установку?", "downloadCount": "Скачать граф: {0}", "errorFetching": "Ошибка при получении расширений.", "errorFetchingConfigurationHint": "Это может быть вызвано проблемами конфигурации сети.", "failedInstallingVSIX": "Не удалось установить {0} из VSIX.", "invalidVSIX": "Выбранный файл не является действительным плагином \"*.vsix\".", "license": "Лицензия: {0}", + "onlyShowVerifiedExtensionsDescription": "Это позволяет сайту {0} показывать только проверенные расширения.", + "onlyShowVerifiedExtensionsTitle": "Показывать только проверенные расширения", "recommendedExtensions": "Список имен расширений, рекомендуемых для использования в данной рабочей области.", "searchPlaceholder": "Поисковые расширения в {0}", "showInstalled": "Показать установленные расширения", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 1db0707465509..caa0cc64dd203 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -468,12 +468,16 @@ "supertypeHierarchy": "超类型层次结构" }, "vsx-registry": { + "confirmDialogMessage": "扩展名 \"{0}\" 未经验证,可能存在安全风险。", + "confirmDialogTitle": "您确定要继续安装吗?", "downloadCount": "下载次数: {0}", "errorFetching": "取出扩展程序时出错。", "errorFetchingConfigurationHint": "这可能是网络配置问题造成的。", "failedInstallingVSIX": "从VSIX安装{0} ,失败了。", "invalidVSIX": "所选文件不是有效的 \"*.vsix \"插件。", "license": "许可证: {0}", + "onlyShowVerifiedExtensionsDescription": "这样,{0} 只显示经过验证的扩展名。", + "onlyShowVerifiedExtensionsTitle": "只显示已验证的扩展名", "recommendedExtensions": "建议在该工作区使用的扩展名称的列表。", "searchPlaceholder": "在{0}中搜索扩展", "showInstalled": "显示已安装的扩展程序", From f754fe2606c97a5c2cf3c547915b43b88c7f5c1f Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 25 Jan 2024 12:21:06 +0100 Subject: [PATCH 070/441] docs: updated changelog for 1.46.0 Signed-off-by: Johannes Faltermeier --- CHANGELOG.md | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 919029e59c84e..05661bfd5be99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,57 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## not yet released + + +## v1.46.0 - 01/25/2024 + +- [application-manager] updated message for missing Electron main entries [#13242](https://github.com/eclipse-theia/theia/pull/13242) +- [application-package] bumped the default supported API from `1.84.2` to `1.85.1` [#13276](https://github.com/eclipse-theia/theia/pull/13276) - contributed on behalf of STMicroelectronics +- [browser-only] added support for 'browser-only' Theia [#12853](https://github.com/eclipse-theia/theia/pull/12853) +- [builtins] update built-ins to version 1.83.1 [#13298](https://github.com/eclipse-theia/theia/pull/13298) - contributed on behalf of STMicroelectronics +- [core] added keybindings to toggle the tree checkbox [#13271](https://github.com/eclipse-theia/theia/pull/13271) +- [core] added logic to dispose cancellation event listeners [#13254](https://github.com/eclipse-theia/theia/pull/13254) +- [core] added preference 'workbench.tree.indent' to control the indentation in the tree widget [#13179](https://github.com/eclipse-theia/theia/pull/13179) - contributed on behalf of STMicroelectronics +- [core] fixed copy/paste from a menu in electron [#13220](https://github.com/eclipse-theia/theia/pull/13220) - contributed on behalf of STMicroelectronics +- [core] fixed file explorer progress bar issue [#13268](https://github.com/eclipse-theia/theia/pull/13268) +- [core] fixed issue with cyclic menu contributions [#13264](https://github.com/eclipse-theia/theia/pull/13264) +- [core] fixed leak when reconnecting to back end without reload [#13250](https://github.com/eclipse-theia/theia/pull/13250) - contributed on behalf of STMicroelectronics +- [core] fixed SelectComponent to render dropdown correctly in dialog [#13261](https://github.com/eclipse-theia/theia/pull/13261) +- [core] removed error logs from RpcProxyFactory [#13191](https://github.com/eclipse-theia/theia/pull/13191) +- [documentation] improved documentation about 'ContributionProvider' use [#13278](https://github.com/eclipse-theia/theia/pull/13278) +- [docuemtnation] improved documentation on passing objects across RPC [#13238](https://github.com/eclipse-theia/theia/pull/13238) +- [documentation] updated plugin API docs for headless plugins and Inversify DI [#13299](https://github.com/eclipse-theia/theia/pull/13299) +- [filesystem] updated logic to only read unbuffered when we read the whole file [#13197](https://github.com/eclipse-theia/theia/pull/13197) +- [headless-plugin] added support for "headless plugins" in a new plugin host [#13138](https://github.com/eclipse-theia/theia/pull/13138) +- [monaco] updated logic to add document URI as context to getDefaultFormatter [#13280](https://github.com/eclipse-theia/theia/pull/13280) - contributed on behalf of STMicroelectronics +- [notebook] fixed dynamic notebook widgets resizing [#13289](https://github.com/eclipse-theia/theia/pull/13289) +- [notebook] fixed multiple problems with the notebook output rendering [#13239](https://github.com/eclipse-theia/theia/pull/13239) +- [notebook] improved notebook error logging [#13256](https://github.com/eclipse-theia/theia/pull/13256) +- [plugin] added logic to synchronize messages sent via different proxies [#13180](https://github.com/eclipse-theia/theia/pull/13180) +- [remote] added support for specifying the port of a remote SSH connection [#13296](https://github.com/eclipse-theia/theia/pull/13296) - contributed on behalf of STMicroelectronics +- [plugin] fixed inputbox onTriggerButton() event [#13207](https://github.com/eclipse-theia/theia/pull/13207) - contributed on behalf of STMicroelectronics +- [plugin] fixed localization for the removeSession method [#13257](https://github.com/eclipse-theia/theia/pull/13257) +- [plugin] fixed `vscode.env.appRoot` path [#13285](https://github.com/eclipse-theia/theia/pull/13285) +- [plugin] stubbed multiDocumentHighlightProvider proposed API [#13248](https://github.com/eclipse-theia/theia/pull/13248) - contributed on behalf of STMicroelectronics +- [plugin] updated logic to handle activeCustomEditorId [#13267](https://github.com/eclipse-theia/theia/pull/13267) +- [plugin] updated logic to pass context to webview context menu action [#13228](https://github.com/eclipse-theia/theia/pull/13228) +- [plugin] updated logic to use more stable hostname for webviews [#13092](https://github.com/eclipse-theia/theia/pull/13225) [#13258](https://github.com/eclipse-theia/theia/pull/13265) +- [terminal] fixed wording in error message [#13245](https://github.com/eclipse-theia/theia/pull/13245) - contributed on behalf of STMicroelectronics +- [terminal] renamed terminal.sendText() parameter from addNewLine to shouldExecute [#13236](https://github.com/eclipse-theia/theia/pull/13236) - contributed on behalf of STMicroelectronics +- [terminal] updated logic to resize terminal [#13281](https://github.com/eclipse-theia/theia/pull/13281) +- [terminal] updated terminalQuickFixProvider proposed API according to vscode 1.85 version [#13240](https://github.com/eclipse-theia/theia/pull/13240) - contributed on behalf of STMicroelectronics +- [vsx-registry] implemented verified extension filtering [#12995](https://github.com/eclipse-theia/theia/pull/12995) + +[Breaking Changes:](#breaking_changes_1.46.0) + +- [core] moved `FileUri` from `node` package to `common` [#12853](https://github.com/eclipse-theia/theia/pull/12853) +- [plugin] introduced new common interfaces/classes for reuse by different plugin hosts [#13138](https://github.com/eclipse-theia/theia/pull/13138) ## v1.45.0 - 12/21/2023 From 8a97810f44ab4bc462ab07c3f548912072ca2b20 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 25 Jan 2024 15:26:47 +0100 Subject: [PATCH 071/441] v1.46.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 90 ++++++++-------- examples/browser/package.json | 100 +++++++++--------- examples/electron/package.json | 98 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 28 ++--- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 12 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 28 ++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 10 +- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 16 +-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 18 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 7 +- 71 files changed, 504 insertions(+), 505 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 7ad70e5831baf..1c643941f7e55 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.45.0", - "@theia/ffmpeg": "1.45.0", - "@theia/native-webpack-plugin": "1.45.0", + "@theia/application-package": "1.46.0", + "@theia/ffmpeg": "1.46.0", + "@theia/native-webpack-plugin": "1.46.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index c5fbae2d10987..9bab3e6f6d5c7 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.45.0", + "@theia/request": "1.46.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -42,7 +42,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 7b70f54e5d527..9ad1d24de52dd 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -30,12 +30,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.45.0", - "@theia/application-package": "1.45.0", - "@theia/ffmpeg": "1.45.0", - "@theia/localization-manager": "1.45.0", - "@theia/ovsx-client": "1.45.0", - "@theia/request": "1.45.0", + "@theia/application-manager": "1.46.0", + "@theia/application-package": "1.46.0", + "@theia/ffmpeg": "1.46.0", + "@theia/localization-manager": "1.46.0", + "@theia/ovsx-client": "1.46.0", + "@theia/request": "1.46.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index b0397338f4d1c..7a4041fbf2f53 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index c470dde42c975..dc5940cf17721 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "typescript": "~4.5.5" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index b0b088767f6e5..1e0cd31552c28 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.45.0", + "version": "1.46.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index e6670246ba8b3..a99fb06dfc5aa 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.45.0", + "@theia/request": "1.46.0", "semver": "^7.5.4" } } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index 61b33c6ce3c0b..3a22c543a0efa 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.45.0", + "version": "1.46.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.45.0", - "@theia/ext-scripts": "1.45.0", - "@theia/re-exports": "1.45.0", + "@theia/core": "1.46.0", + "@theia/ext-scripts": "1.46.0", + "@theia/re-exports": "1.46.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index bd748c367e710..6f0785a2512d1 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.45.0", + "version": "1.46.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index 5f27e40dbc82d..74075dad2f119 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index 408239cb86252..7529428ebe6b6 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index 7f3f6c9bbfd50..f16424804b1f3 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.45.0", - "@theia/plugin-ext-headless": "1.45.0", - "@theia/plugin-ext": "1.45.0" + "@theia/core": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/plugin-ext-headless": "1.46.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 28f584a2356e9..a59f303f4b455 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/output": "1.45.0", - "@theia/ovsx-client": "1.45.0", - "@theia/search-in-workspace": "1.45.0", - "@theia/test": "1.45.0", - "@theia/toolbar": "1.45.0", - "@theia/vsx-registry": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/output": "1.46.0", + "@theia/ovsx-client": "1.46.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/test": "1.46.0", + "@theia/toolbar": "1.46.0", + "@theia/vsx-registry": "1.46.0", + "@theia/workspace": "1.46.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 393dbf6eab7cf..555e1cf428dca 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.45.0" + "@theia/core": "1.46.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 453fb7dae24a4..bee95a93f7ff6 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.45.0", + "version": "1.46.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,49 +15,49 @@ } }, "dependencies": { - "@theia/api-samples": "1.45.0", - "@theia/bulk-edit": "1.45.0", - "@theia/callhierarchy": "1.45.0", - "@theia/console": "1.45.0", - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/editor-preview": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/getting-started": "1.45.0", - "@theia/git": "1.45.0", - "@theia/keymaps": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/memory-inspector": "1.45.0", - "@theia/messages": "1.45.0", - "@theia/metrics": "1.45.0", - "@theia/mini-browser": "1.45.0", - "@theia/monaco": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/outline-view": "1.45.0", - "@theia/output": "1.45.0", - "@theia/plugin-dev": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/plugin-ext-vscode": "1.45.0", - "@theia/plugin-metrics": "1.45.0", - "@theia/preferences": "1.45.0", - "@theia/preview": "1.45.0", - "@theia/process": "1.45.0", - "@theia/property-view": "1.45.0", - "@theia/scm": "1.45.0", - "@theia/scm-extra": "1.45.0", - "@theia/search-in-workspace": "1.45.0", - "@theia/secondary-window": "1.45.0", - "@theia/task": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/timeline": "1.45.0", - "@theia/toolbar": "1.45.0", - "@theia/typehierarchy": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/vsx-registry": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/api-samples": "1.46.0", + "@theia/bulk-edit": "1.46.0", + "@theia/callhierarchy": "1.46.0", + "@theia/console": "1.46.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/editor-preview": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/getting-started": "1.46.0", + "@theia/git": "1.46.0", + "@theia/keymaps": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/memory-inspector": "1.46.0", + "@theia/messages": "1.46.0", + "@theia/metrics": "1.46.0", + "@theia/mini-browser": "1.46.0", + "@theia/monaco": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/outline-view": "1.46.0", + "@theia/output": "1.46.0", + "@theia/plugin-dev": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/plugin-ext-vscode": "1.46.0", + "@theia/plugin-metrics": "1.46.0", + "@theia/preferences": "1.46.0", + "@theia/preview": "1.46.0", + "@theia/process": "1.46.0", + "@theia/property-view": "1.46.0", + "@theia/scm": "1.46.0", + "@theia/scm-extra": "1.46.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/secondary-window": "1.46.0", + "@theia/task": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/timeline": "1.46.0", + "@theia/toolbar": "1.46.0", + "@theia/typehierarchy": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/vsx-registry": "1.46.0", + "@theia/workspace": "1.46.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -73,6 +73,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.45.0" + "@theia/cli": "1.46.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index 125daa00f26b3..da2dc95adfb07 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.45.0", + "version": "1.46.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,54 +20,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.45.0", - "@theia/api-samples": "1.45.0", - "@theia/plugin-ext-headless": "1.45.0", - "@theia/bulk-edit": "1.45.0", - "@theia/callhierarchy": "1.45.0", - "@theia/console": "1.45.0", - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/editor-preview": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/getting-started": "1.45.0", - "@theia/git": "1.45.0", - "@theia/keymaps": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/memory-inspector": "1.45.0", - "@theia/messages": "1.45.0", - "@theia/metrics": "1.45.0", - "@theia/mini-browser": "1.45.0", - "@theia/monaco": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/notebook": "1.45.0", - "@theia/outline-view": "1.45.0", - "@theia/output": "1.45.0", - "@theia/plugin-dev": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/plugin-ext-vscode": "1.45.0", - "@theia/plugin-metrics": "1.45.0", - "@theia/preferences": "1.45.0", - "@theia/preview": "1.45.0", - "@theia/process": "1.45.0", - "@theia/property-view": "1.45.0", - "@theia/remote": "1.45.0", - "@theia/scm": "1.45.0", - "@theia/scm-extra": "1.45.0", - "@theia/search-in-workspace": "1.45.0", - "@theia/secondary-window": "1.45.0", - "@theia/task": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/test": "1.45.0", - "@theia/timeline": "1.45.0", - "@theia/toolbar": "1.45.0", - "@theia/typehierarchy": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/vsx-registry": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/api-provider-sample": "1.46.0", + "@theia/api-samples": "1.46.0", + "@theia/bulk-edit": "1.46.0", + "@theia/callhierarchy": "1.46.0", + "@theia/console": "1.46.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/editor-preview": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/getting-started": "1.46.0", + "@theia/git": "1.46.0", + "@theia/keymaps": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/memory-inspector": "1.46.0", + "@theia/messages": "1.46.0", + "@theia/metrics": "1.46.0", + "@theia/mini-browser": "1.46.0", + "@theia/monaco": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/notebook": "1.46.0", + "@theia/outline-view": "1.46.0", + "@theia/output": "1.46.0", + "@theia/plugin-dev": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/plugin-ext-headless": "1.46.0", + "@theia/plugin-ext-vscode": "1.46.0", + "@theia/plugin-metrics": "1.46.0", + "@theia/preferences": "1.46.0", + "@theia/preview": "1.46.0", + "@theia/process": "1.46.0", + "@theia/property-view": "1.46.0", + "@theia/remote": "1.46.0", + "@theia/scm": "1.46.0", + "@theia/scm-extra": "1.46.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/secondary-window": "1.46.0", + "@theia/task": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/test": "1.46.0", + "@theia/timeline": "1.46.0", + "@theia/toolbar": "1.46.0", + "@theia/typehierarchy": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/vsx-registry": "1.46.0", + "@theia/workspace": "1.46.0" }, "scripts": { "clean": "theia clean", @@ -90,6 +90,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.45.0" + "@theia/cli": "1.46.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 49e3f629d2422..63e6b146207f4 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.45.0", + "version": "1.46.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -20,53 +20,53 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.45.0", - "@theia/api-samples": "1.45.0", - "@theia/plugin-ext-headless": "1.45.0", - "@theia/bulk-edit": "1.45.0", - "@theia/callhierarchy": "1.45.0", - "@theia/console": "1.45.0", - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/editor-preview": "1.45.0", - "@theia/electron": "1.45.0", - "@theia/external-terminal": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/getting-started": "1.45.0", - "@theia/git": "1.45.0", - "@theia/keymaps": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/memory-inspector": "1.45.0", - "@theia/messages": "1.45.0", - "@theia/metrics": "1.45.0", - "@theia/mini-browser": "1.45.0", - "@theia/monaco": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/outline-view": "1.45.0", - "@theia/output": "1.45.0", - "@theia/plugin-dev": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/plugin-ext-vscode": "1.45.0", - "@theia/preferences": "1.45.0", - "@theia/preview": "1.45.0", - "@theia/process": "1.45.0", - "@theia/property-view": "1.45.0", - "@theia/remote": "1.45.0", - "@theia/scm": "1.45.0", - "@theia/scm-extra": "1.45.0", - "@theia/search-in-workspace": "1.45.0", - "@theia/secondary-window": "1.45.0", - "@theia/task": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/timeline": "1.45.0", - "@theia/toolbar": "1.45.0", - "@theia/typehierarchy": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/vsx-registry": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/api-provider-sample": "1.46.0", + "@theia/api-samples": "1.46.0", + "@theia/bulk-edit": "1.46.0", + "@theia/callhierarchy": "1.46.0", + "@theia/console": "1.46.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/editor-preview": "1.46.0", + "@theia/electron": "1.46.0", + "@theia/external-terminal": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/getting-started": "1.46.0", + "@theia/git": "1.46.0", + "@theia/keymaps": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/memory-inspector": "1.46.0", + "@theia/messages": "1.46.0", + "@theia/metrics": "1.46.0", + "@theia/mini-browser": "1.46.0", + "@theia/monaco": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/outline-view": "1.46.0", + "@theia/output": "1.46.0", + "@theia/plugin-dev": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/plugin-ext-headless": "1.46.0", + "@theia/plugin-ext-vscode": "1.46.0", + "@theia/preferences": "1.46.0", + "@theia/preview": "1.46.0", + "@theia/process": "1.46.0", + "@theia/property-view": "1.46.0", + "@theia/remote": "1.46.0", + "@theia/scm": "1.46.0", + "@theia/scm-extra": "1.46.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/secondary-window": "1.46.0", + "@theia/task": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/timeline": "1.46.0", + "@theia/toolbar": "1.46.0", + "@theia/typehierarchy": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/vsx-registry": "1.46.0", + "@theia/workspace": "1.46.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -84,7 +84,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.45.0", + "@theia/cli": "1.46.0", "electron": "^23.2.4" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index bfb70db2bfecb..f6dc6852a5708 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.45.0", + "version": "1.46.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index 8121b8e9bc681..760a87b68884e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.45.0", + "version": "1.46.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 34f24159ef41a..e3fd0d5e14400 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/workspace": "1.45.0" + "@theia/workspace": "1.46.0" }, "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index bff3ebd1683b0..793c3dc4028a7 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", "ts-md5": "^1.2.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 2ac8242ca0e9a..ab7c5abd02a1c 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", "anser": "^2.0.1" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index babe4599f4599..e169e74cd67b0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.45.0", - "@theia/request": "1.45.0", + "@theia/application-package": "1.46.0", + "@theia/request": "1.46.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -204,8 +204,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", - "@theia/re-exports": "1.45.0", + "@theia/ext-scripts": "1.46.0", + "@theia/re-exports": "1.46.0", "minimist": "^1.2.0" }, "nyc": { diff --git a/packages/debug/package.json b/packages/debug/package.json index 488b7fa24f32a..fb0dfc1a1b135 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,21 +1,21 @@ { "name": "@theia/debug", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.45.0", - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/console": "1.46.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/output": "1.45.0", - "@theia/process": "1.45.0", - "@theia/task": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/output": "1.46.0", + "@theia/process": "1.46.0", + "@theia/task": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/workspace": "1.46.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -56,7 +56,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index 108c7975208e0..477487d689504 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/navigator": "1.45.0" + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/navigator": "1.46.0" }, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index 205287d5bc19c..5f256b2ae122b 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/variable-resolver": "1.45.0" + "@theia/core": "1.46.0", + "@theia/variable-resolver": "1.46.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index e7ed637bf66b2..200671208e5ae 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", - "@theia/re-exports": "1.45.0" + "@theia/ext-scripts": "1.46.0", + "@theia/re-exports": "1.46.0" }, "peerDependencies": { "electron": "^23.2.4" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index d2e08d16bc33d..a16dddbfd399e 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/workspace": "1.46.0" }, "publishConfig": { "access": "public" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index f6f0fa5614cd4..64914d81544a5 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/process": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/process": "1.46.0", + "@theia/workspace": "1.46.0", "@vscode/ripgrep": "^1.14.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 997fca2738f1f..fa832e1cfca3e 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.45.0", + "@theia/core": "1.46.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -74,7 +74,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 2dbdafdef4f1a..c66ca3f9cbc30 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/keymaps": "1.45.0", - "@theia/preview": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/keymaps": "1.46.0", + "@theia/preview": "1.46.0", + "@theia/workspace": "1.46.0" }, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 340c9d6b568f8..b787f26e88726 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.45.0", - "@theia/scm": "1.45.0", - "@theia/scm-extra": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/navigator": "1.46.0", + "@theia/scm": "1.46.0", + "@theia/scm-extra": "1.46.0", + "@theia/workspace": "1.46.0", "@types/diff": "^3.2.2", "@types/p-queue": "^2.3.1", "diff": "^3.4.0", @@ -66,7 +66,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index 9f4672a743e99..c878bab03c9c3 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,17 +1,17 @@ { "name": "@theia/keymaps", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/preferences": "1.45.0", - "@theia/userstorage": "1.45.0", + "@theia/preferences": "1.46.0", + "@theia/userstorage": "1.46.0", "jsonc-parser": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index 4822f6b584fa2..7f290b2e149ca 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/workspace": "1.45.0" + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/workspace": "1.46.0" }, "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index f297627427049..879bc07d48fbc 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0" }, diff --git a/packages/messages/package.json b/packages/messages/package.json index 182c0d62e8fa4..9a525b88f22b2 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.45.0", + "@theia/core": "1.46.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2" }, @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index eb37f0e96cd41..8a2e8927f1a24 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.45.0", + "@theia/core": "1.46.0", "prom-client": "^10.2.0" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 3dae7d419dca5..8f6ee15eb0961 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 39474e39a2da1..8937cb1aa6dbb 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/markers": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/markers": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/outline-view": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/outline-view": "1.46.0", + "@theia/workspace": "1.46.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index da5e4e31b047a..7b84d98af529d 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/workspace": "1.46.0", "minimatch": "^5.1.0" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 5578169830a78..243a82fb92647 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,12 +1,12 @@ { "name": "@theia/notebook", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "uuid": "^8.3.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/vscode-notebook-renderer": "^1.72.0" }, "nyc": { diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index ae0473e38cb62..52efc5961b221 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.45.0" + "@theia/core": "1.46.0" }, "publishConfig": { "access": "public" @@ -38,7 +38,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index 1f78b4733b9f1..fa994e74ac7f5 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index 2677963d91308..34ea4621c01fe 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/output": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/output": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/workspace": "1.46.0", "ps-tree": "^1.2.0" }, "publishConfig": { @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index 913519140f197..da640a995521e 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/terminal": "1.45.0" + "@theia/core": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/terminal": "1.46.0" }, "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index aa26a3341769a..8958523bb232d 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,21 +1,21 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.45.0", - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/callhierarchy": "1.46.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.45.0", - "@theia/plugin": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/typehierarchy": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/navigator": "1.46.0", + "@theia/plugin": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/typehierarchy": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/workspace": "1.46.0", "decompress": "^4.2.1", "filenamify": "^4.1.0" }, @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 2a069d8489e48..bdaa99e2df2d5 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.45.0", - "@theia/callhierarchy": "1.45.0", - "@theia/console": "1.45.0", - "@theia/core": "1.45.0", - "@theia/debug": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/editor-preview": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/messages": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/bulk-edit": "1.46.0", + "@theia/callhierarchy": "1.46.0", + "@theia/console": "1.46.0", + "@theia/core": "1.46.0", + "@theia/debug": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/editor-preview": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/messages": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/navigator": "1.45.0", - "@theia/notebook": "1.45.0", - "@theia/output": "1.45.0", - "@theia/plugin": "1.45.0", - "@theia/preferences": "1.45.0", - "@theia/scm": "1.45.0", - "@theia/search-in-workspace": "1.45.0", - "@theia/task": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/timeline": "1.45.0", - "@theia/typehierarchy": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/workspace": "1.45.0", - "@theia/test": "1.45.0", + "@theia/navigator": "1.46.0", + "@theia/notebook": "1.46.0", + "@theia/output": "1.46.0", + "@theia/plugin": "1.46.0", + "@theia/preferences": "1.46.0", + "@theia/scm": "1.46.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/task": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/timeline": "1.46.0", + "@theia/typehierarchy": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/workspace": "1.46.0", + "@theia/test": "1.46.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index d3c8dfdd48beb..1f63607b7cd5b 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.45.0", - "@theia/metrics": "1.45.0", + "@theia/core": "1.46.0", + "@theia/metrics": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/plugin": "1.45.0", - "@theia/plugin-ext": "1.45.0" + "@theia/plugin": "1.46.0", + "@theia/plugin-ext": "1.46.0" }, "publishConfig": { "access": "public" @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 260f69211e174..0752bcfd445e7 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 48061689a0d53..32d66867944f6 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/userstorage": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/userstorage": "1.46.0", + "@theia/workspace": "1.46.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index 5704c932e77fa..db28a79cbac0b 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/mini-browser": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/mini-browser": "1.46.0", + "@theia/monaco": "1.46.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index 799f0d70490b1..cdfbc83ca3af5 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.45.0", + "@theia/core": "1.46.0", "node-pty": "0.11.0-beta17", "string-argv": "^0.1.1" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index 192ad48314610..063bd8ee23c47 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0" + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 5262012b76e1d..770098e651bcd 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index f62cadb415786..88c1e514a266f 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/scm": "1.45.0" + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/scm": "1.46.0" }, "publishConfig": { "access": "public" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index 23dfd44e7cbd2..733506ee4fd52 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,11 +1,11 @@ { "name": "@theia/scm", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", "@types/diff": "^3.2.2", "diff": "^3.4.0", "p-debounce": "^2.1.0", @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 53556d2ba3308..8eaf145a03624 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/process": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/process": "1.46.0", + "@theia/workspace": "1.46.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0" @@ -47,6 +47,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index acdaa23befb8a..c1af7f82cb4c0 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.45.0" + "@theia/core": "1.46.0" }, "publishConfig": { "access": "public" @@ -38,6 +38,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index 64b2817055bad..e86a3ecfa0a33 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/markers": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/markers": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/process": "1.45.0", - "@theia/terminal": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/process": "1.46.0", + "@theia/terminal": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/workspace": "1.46.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0" @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index ba1069c93d9a5..b802d27cbcd24 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,14 +1,14 @@ { "name": "@theia/terminal", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/process": "1.45.0", - "@theia/variable-resolver": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/process": "1.46.0", + "@theia/variable-resolver": "1.46.0", + "@theia/workspace": "1.46.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0", "xterm-addon-search": "^0.8.2" @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index 5be5bb957e437..e910aac210461 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/navigator": "1.45.0", - "@theia/terminal": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/navigator": "1.46.0", + "@theia/terminal": "1.46.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index a30fea1a14403..c0152c9b14f28 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/navigator": "1.45.0" + "@theia/core": "1.46.0", + "@theia/navigator": "1.46.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 67c585779a1d9..03ae12987d1a7 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", - "@theia/file-search": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/monaco": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", + "@theia/file-search": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/monaco": "1.46.0", "@theia/monaco-editor-core": "1.72.3", - "@theia/search-in-workspace": "1.45.0", - "@theia/userstorage": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/search-in-workspace": "1.46.0", + "@theia/userstorage": "1.46.0", + "@theia/workspace": "1.46.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0" diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 6c3d008f01805..5df8530b51a38 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/editor": "1.45.0", + "@theia/core": "1.46.0", + "@theia/editor": "1.46.0", "@types/uuid": "^7.0.3", "uuid": "^8.0.0" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 32fe52e0014c2..68124ee6dd487 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0" + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0" }, "publishConfig": { "access": "public" @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index e92a79e18db93..d12c0933bf556 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.45.0" + "@theia/core": "1.46.0" }, "publishConfig": { "access": "public" @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 056e3447357b4..65f6aa0e4ffc8 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,15 +1,15 @@ { "name": "@theia/vsx-registry", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/ovsx-client": "1.45.0", - "@theia/plugin-ext": "1.45.0", - "@theia/plugin-ext-vscode": "1.45.0", - "@theia/preferences": "1.45.0", - "@theia/workspace": "1.45.0", + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/ovsx-client": "1.46.0", + "@theia/plugin-ext": "1.46.0", + "@theia/plugin-ext-vscode": "1.46.0", + "@theia/preferences": "1.46.0", + "@theia/workspace": "1.46.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0", + "@theia/ext-scripts": "1.46.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 542c9a1b56205..24b3510fc394a 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.45.0", + "version": "1.46.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.45.0", - "@theia/filesystem": "1.45.0", - "@theia/variable-resolver": "1.45.0", + "@theia/core": "1.46.0", + "@theia/filesystem": "1.46.0", + "@theia/variable-resolver": "1.46.0", "jsonc-parser": "^2.2.0", "valid-filename": "^2.0.1" }, @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.45.0" + "@theia/ext-scripts": "1.46.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 64c9d35ed75d7..fd1bb9288f0d9 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.45.0", + "version": "1.46.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index d47151177379a..ae3009f72ad7e 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.45.0", + "version": "1.46.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index 98413ec1b3b91..9ffe255d601b3 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.45.0", + "version": "1.46.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "^1.45.0" + "@theia/api-provider-sample": "1.46.0" }, "scripts": { "prepare": "yarn -s package", @@ -28,7 +28,6 @@ "activationEvents": [ "*" ], - "contributes": { - } + "contributes": {} } } From 69d7cd017c7509ee9867bd778ee180db20b513f4 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 25 Jan 2024 15:30:47 +0100 Subject: [PATCH 072/441] core: update re-exports for 1.46.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 4d0a824e94fea..5b4bfd6e91ccb 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.45.0`](https://www.npmjs.com/package/@theia/application-package/v/1.45.0)) - - `@theia/request` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.45.0`](https://www.npmjs.com/package/@theia/request/v/1.45.0)) + - `@theia/application-package` (from [`@theia/application-package@1.46.0`](https://www.npmjs.com/package/@theia/application-package/v/1.46.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.46.0`](https://www.npmjs.com/package/@theia/application-package/v/1.46.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.46.0`](https://www.npmjs.com/package/@theia/application-package/v/1.46.0)) + - `@theia/request` (from [`@theia/request@1.46.0`](https://www.npmjs.com/package/@theia/request/v/1.46.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.46.0`](https://www.npmjs.com/package/@theia/request/v/1.46.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.46.0`](https://www.npmjs.com/package/@theia/request/v/1.46.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From b15b639fa3b64a9518efdf3e82ea9098915ba145 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 30 Jan 2024 17:01:15 +0100 Subject: [PATCH 073/441] Fix core localizations for electron builds (#13331) --- .../messaging/electron-messaging-frontend-module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts b/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts index 046fb542b7c73..7eef88d505e35 100644 --- a/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts +++ b/packages/core/src/electron-browser/messaging/electron-messaging-frontend-module.ts @@ -23,7 +23,7 @@ import { ElectronFrontendIdProvider } from './electron-frontend-id-provider'; import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider'; import { ConnectionSource } from '../../browser/messaging/connection-source'; import { LocalConnectionProvider, RemoteConnectionProvider, ServiceConnectionProvider } from '../../browser/messaging/service-connection-provider'; -import { WebSocketConnectionProvider } from '../../browser'; +import { WebSocketConnectionProvider } from '../../browser/messaging/ws-connection-provider'; import { ConnectionCloseService, connectionCloseServicePath } from '../../common/messaging/connection-management'; import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source'; From 9a51317fe81a179e9e7c6c9071e9a00218bba299 Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Thu, 1 Feb 2024 14:30:29 +0100 Subject: [PATCH 074/441] fix: regressions from headless plugins refactoring (#13337) - fix gaps and errors in Inversify DI configuration in the web worker for frontend plugins - restore the onDidDeploy() forwarding lost in the refactoring for headless plugins Fixes #13320 Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich Co-authored-by: Christian W. Damus --- .../src/hosted/browser/hosted-plugin.ts | 1 + .../src/hosted/browser/worker/worker-main.ts | 4 ++-- .../browser/worker/worker-plugin-module.ts | 19 +++++++++++++------ .../plugin-ext/src/plugin/plugin-manager.ts | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index c2016725f7aa0..64e35536a529c 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -240,6 +240,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport this.load()); this.server.onDidOpenConnection(() => this.load()); } diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts index f24f83223e6d2..56045b5f839fe 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts @@ -20,7 +20,7 @@ import * as theia from '@theia/plugin'; import { emptyPlugin, MAIN_RPC_CONTEXT, Plugin } from '../../../common/plugin-api-rpc'; import { ExtPluginApi } from '../../../common/plugin-ext-api-contribution'; import { getPluginId, PluginMetadata } from '../../../common/plugin-protocol'; -import { RPCProtocol, RPCProtocolImpl } from '../../../common/rpc-protocol'; +import { RPCProtocol } from '../../../common/rpc-protocol'; import { ClipboardExt } from '../../../plugin/clipboard-ext'; import { EditorsAndDocumentsExtImpl } from '../../../plugin/editors-and-documents'; import { MessageRegistryExt } from '../../../plugin/message-registry'; @@ -56,7 +56,7 @@ function initialize(contextPath: string, pluginMetadata: PluginMetadata): void { const container = new Container(); container.load(pluginHostModule); -const rpc: RPCProtocol = container.get(RPCProtocolImpl); +const rpc: RPCProtocol = container.get(RPCProtocol); const pluginManager = container.get(PluginManagerExtImpl); pluginManager.setPluginHost({ // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts index ebace3685d069..155575aaff93a 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-plugin-module.ts @@ -23,10 +23,10 @@ import { RPCProtocol, RPCProtocolImpl } from '../../../common/rpc-protocol'; import { ClipboardExt } from '../../../plugin/clipboard-ext'; import { EditorsAndDocumentsExtImpl } from '../../../plugin/editors-and-documents'; import { MessageRegistryExt } from '../../../plugin/message-registry'; -import { PluginManagerExtImpl } from '../../../plugin/plugin-manager'; -import { KeyValueStorageProxy } from '../../../plugin/plugin-storage'; +import { MinimalTerminalServiceExt, PluginManagerExtImpl } from '../../../plugin/plugin-manager'; +import { InternalStorageExt, KeyValueStorageProxy } from '../../../plugin/plugin-storage'; import { PreferenceRegistryExtImpl } from '../../../plugin/preference-registry'; -import { SecretsExtImpl } from '../../../plugin/secrets-ext'; +import { InternalSecretsExt, SecretsExtImpl } from '../../../plugin/secrets-ext'; import { TerminalServiceExtImpl } from '../../../plugin/terminal-ext'; import { WebviewsExtImpl } from '../../../plugin/webviews'; import { WorkspaceExtImpl } from '../../../plugin/workspace'; @@ -58,16 +58,23 @@ export default new ContainerModule(bind => { bind(PluginManagerExtImpl).toSelf().inSingletonScope(); bind(EnvExtImpl).to(WorkerEnvExtImpl).inSingletonScope(); - bind(LocalizationExt).to(LocalizationExtImpl).inSingletonScope(); + bind(LocalizationExtImpl).toSelf().inSingletonScope(); + bind(LocalizationExt).toService(LocalizationExtImpl); bind(KeyValueStorageProxy).toSelf().inSingletonScope(); + bind(InternalStorageExt).toService(KeyValueStorageProxy); bind(SecretsExtImpl).toSelf().inSingletonScope(); + bind(InternalSecretsExt).toService(SecretsExtImpl); bind(PreferenceRegistryExtImpl).toSelf().inSingletonScope(); - bind(DebugExtImpl).toDynamicValue(({ container }) => createDebugExtStub(container)) - .inSingletonScope(); + bind(DebugExtImpl).toDynamicValue(({ container }) => { + const child = container.createChild(); + child.bind(DebugExtImpl).toSelf(); + return createDebugExtStub(child); + }).inSingletonScope(); bind(EditorsAndDocumentsExtImpl).toSelf().inSingletonScope(); bind(WorkspaceExtImpl).toSelf().inSingletonScope(); bind(MessageRegistryExt).toSelf().inSingletonScope(); bind(ClipboardExt).toSelf().inSingletonScope(); bind(WebviewsExtImpl).toSelf().inSingletonScope(); bind(TerminalServiceExtImpl).toSelf().inSingletonScope(); + bind(MinimalTerminalServiceExt).toService(TerminalServiceExtImpl); }); diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index bffd73d1de30a..c9ea874c98537 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -81,7 +81,7 @@ class ActivatedPlugin { export const MinimalTerminalServiceExt = Symbol('MinimalTerminalServiceExt'); export type MinimalTerminalServiceExt = Pick; + 'getEnvironmentVariableCollection' | '$initEnvironmentVariableCollections' | '$setShell'>; @injectable() // eslint-disable-next-line @typescript-eslint/no-explicit-any From 466b16dc89e9739ff37b043271f63de583e405fd Mon Sep 17 00:00:00 2001 From: John Cortell <109228283+jcortell68@users.noreply.github.com> Date: Mon, 5 Feb 2024 03:14:03 -0600 Subject: [PATCH 075/441] Add comment-documentation for several key Theia utility classes (#13324) - Add JavaDoc comments -- DisposableCollection -- AbstractReferenceCollection -- ReferenceCollection -- SyncReferenceCollection - Extend some test cases --- packages/core/src/common/disposable.spec.ts | 67 ++++++++++++++++++++- packages/core/src/common/disposable.ts | 28 +++++++++ packages/core/src/common/reference.ts | 55 +++++++++++++++++ 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/packages/core/src/common/disposable.spec.ts b/packages/core/src/common/disposable.spec.ts index 0149c7adc27c4..e80d83bd1c294 100644 --- a/packages/core/src/common/disposable.spec.ts +++ b/packages/core/src/common/disposable.spec.ts @@ -14,8 +14,11 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { expect } from 'chai'; +import { expect, spy, use } from 'chai'; import { DisposableCollection, Disposable } from './disposable'; +import * as spies from 'chai-spies'; + +use(spies); describe('Disposables', () => { it('Is safe to use Disposable.NULL', () => { @@ -26,4 +29,66 @@ describe('Disposables', () => { expect(collectionA.disposed, 'A should be disposed after being disposed.').to.be.true; expect(collectionB.disposed, 'B should not be disposed because A was disposed.').to.be.false; }); + + it('Collection is auto-pruned when an element is disposed', () => { + const onDispose = spy(() => { }); + const elementDispose = () => { }; + + const collection = new DisposableCollection(); + collection.onDispose(onDispose); + + const disposable1 = Disposable.create(elementDispose); + collection.push(disposable1); + expect(collection['disposables']).to.have.lengthOf(1); + + const disposable2 = Disposable.create(elementDispose); + collection.push(disposable2); + expect(collection['disposables']).to.have.lengthOf(2); + + disposable1.dispose(); + expect(collection['disposables']).to.have.lengthOf(1); + expect(onDispose).to.have.not.been.called(); + expect(collection.disposed).is.false; + + // Test that calling dispose on an already disposed element doesn't + // alter the collection state + disposable1.dispose(); + expect(collection['disposables']).to.have.lengthOf(1); + expect(onDispose).to.have.not.been.called(); + expect(collection.disposed).is.false; + + disposable2.dispose(); + expect(collection['disposables']).to.be.empty; + expect(collection.disposed).is.true; + expect(onDispose).to.have.been.called.once; + }); + + it('onDispose is only called once on actual disposal of elements', () => { + const onDispose = spy(() => { }); + const elementDispose = spy(() => { }); + + const collection = new DisposableCollection(); + collection.onDispose(onDispose); + + // if the collection is empty 'onDispose' is not called + collection.dispose(); + expect(onDispose).to.not.have.been.called(); + + // 'onDispose' is called because we actually dispose an element + collection.push(Disposable.create(elementDispose)); + collection.dispose(); + expect(elementDispose).to.have.been.called.once; + expect(onDispose).to.have.been.called.once; + + // if the collection is empty 'onDispose' is not called and no further element is disposed + collection.dispose(); + expect(elementDispose).to.have.been.called.once; + expect(onDispose).to.have.been.called.once; + + // 'onDispose' is not called again even if we actually dispose an element + collection.push(Disposable.create(elementDispose)); + collection.dispose(); + expect(elementDispose).to.have.been.called.twice; + expect(onDispose).to.have.been.called.once; + }); }); diff --git a/packages/core/src/common/disposable.ts b/packages/core/src/common/disposable.ts index cfb07b03166b0..3eb776598fd48 100644 --- a/packages/core/src/common/disposable.ts +++ b/packages/core/src/common/disposable.ts @@ -47,6 +47,34 @@ Object.defineProperty(Disposable, 'NULL', { } }); +/** + * Utility for tracking a collection of Disposable objects. + * + * This utility provides a number of benefits over just using an array of + * Disposables: + * + * - the collection is auto-pruned when an element it contains is disposed by + * any code that has a reference to it + * - you can register to be notified when all elements in the collection have + * been disposed [1] + * - you can conveniently dispose all elements by calling dispose() + * on the collection + * + * Unlike an array, however, this utility does not give you direct access to + * its elements. + * + * Being notified when all elements are disposed is simple: + * ``` + * const dc = new DisposableCollection(myDisposables); + * dc.onDispose(() => { + * console.log('All elements in the collection have been disposed'); + * }); + * ``` + * + * [1] The collection will notify only once. It will continue to function in so + * far as accepting new Disposables and pruning them when they are disposed, but + * such activity will never result in another notification. + */ export class DisposableCollection implements Disposable { protected readonly disposables: Disposable[] = []; diff --git a/packages/core/src/common/reference.ts b/packages/core/src/common/reference.ts index f8a77ec4f0b3a..bf6fe4a888e24 100644 --- a/packages/core/src/common/reference.ts +++ b/packages/core/src/common/reference.ts @@ -22,6 +22,50 @@ export interface Reference extends Disposable { readonly object: T } +/** + * Abstract class for a map of reference-counted disposable objects, with the + * following features: + * + * - values are not inserted explicitly; instead, acquire() is used to + * create the value for a given key, or return the previously created + * value for it. How the value is created for a given key is + * implementation specific. + * + * - any subsquent acquire() with the same key will bump the reference + * count on that value. acquire() returns not the value directly but + * a reference object that holds the value. Calling dispose() on the + * reference decreases the value's effective reference count. + * + * - a contained value will have its dispose() function called when its + * reference count reaches zero. The key/value pair will be purged + * from the collection. + * + * - calling dispose() on the value directly, instead of calling it on + * the reference returned by acquire(), will automatically dispose + * all outstanding references to that value and the key/value pair + * will be purged from the collection. + * + * - supports synchronous and asynchronous implementations. acquire() will + * return a Promise if the value cannot be created immediately + * + * - functions has|keys|values|get are always synchronous and the result + * excludes asynchronous additions in flight. + * + * - functions values|get return the value directly and not a reference + * to the value. Use these functions to obtain a value without bumping + * its reference count. + * + * - clients can register to be notified when values are added and removed; + * notification for asynchronous additions happen when the creation + * completes, not when it's requested. + * + * - keys can be any value/object that can be successfully stringified using + * JSON.stringify(), sans arguments + * + * - calling dispose() on the collection will dispose all outstanding + * references to all contained values, which results in the disposal of + * the values themselves. + */ export abstract class AbstractReferenceCollection implements Disposable { protected readonly _keys = new Map(); @@ -108,6 +152,12 @@ export abstract class AbstractReferenceCollection imple } +/** + * Asynchronous implementation of AbstractReferenceCollection that requires + * the client to provide a value factory, used to service the acquire() + * function. That factory may return a Promise if the value cannot be + * created immediately. + */ export class ReferenceCollection extends AbstractReferenceCollection { constructor(protected readonly factory: (key: K) => MaybePromise) { @@ -148,6 +198,11 @@ export class ReferenceCollection extends AbstractRefere } +/** + * Synchronous implementation of AbstractReferenceCollection that requires + * the client to provide a value factory, used to service the acquire() + * function. + */ export class SyncReferenceCollection extends AbstractReferenceCollection { constructor(protected readonly factory: (key: K) => V) { From 3e7f6eec297c4029ef01b2ef2c8f20361360c48c Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Mon, 5 Feb 2024 10:52:21 +0100 Subject: [PATCH 076/441] Replace Discourse with Github Discussions (#13322) fixed #13321 --- README.md | 3 +-- doc/Publishing.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d925522f2df2b..d656aeab01a14 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-curved)](https://github.com/eclipse-theia/theia/labels/help%20wanted) - [![Discourse status](https://img.shields.io/discourse/status?label=Chat&server=https%3A%2F%2Fcommunity.theia-ide.org%2F)](https://community.theia-ide.org/) [![Build Status](https://github.com/eclipse-theia/theia/workflows/Build/badge.svg?branch=master)](https://github.com/eclipse-theia/theia/actions?query=branch%3Amaster+event%3Apush+event%3Aschedule) [![Publish VS Code Built-in Extensions](https://github.com/eclipse-theia/vscode-builtin-extensions/actions/workflows/publish-vsx-latest.yml/badge.svg?branch=master)](https://github.com/eclipse-theia/vscode-builtin-extensions/actions/workflows/publish-vsx-latest.yml) [![Open questions](https://img.shields.io/badge/Open-questions-blue.svg?style=flat-curved)](https://github.com/eclipse-theia/theia/discussions/categories/q-a) @@ -88,7 +87,7 @@ Read below to learn how to take part in improving Theia: Read below how to engage with Theia community: -- Join the discussion on [Discourse](https://community.theia-ide.org/). +- Join the discussion on [GitHub](https://github.com/eclipse-theia/theia/discussions). - Ask a question, request a new feature and file a bug with [GitHub issues](https://github.com/eclipse-theia/theia/issues/new/choose). - Vote on existing GitHub issues by reacting with a 👍. We regularly check issues with votes! - Star the repository to show your support. diff --git a/doc/Publishing.md b/doc/Publishing.md index 6ca655a3a7d5a..16072f28a12a3 100644 --- a/doc/Publishing.md +++ b/doc/Publishing.md @@ -40,7 +40,7 @@ In order to successfully perform a `yarn upgrade` one must: ### Announce Release It is a good idea to give a heads-up to developers and the community some hours before a release. -At the time of writing this is [Discourse](https://community.theia-ide.org/). Here is an [example](https://community.theia-ide.org/t/eclipse-theia-v1-40-0-release/3112/5). +At the time of writing this is [GitHub Discussions](https://github.com/eclipse-theia/theia/discussions). Here is an [example](https://github.com/eclipse-theia/theia/discussions/13314). ### Localization From 9530193862783ca5197eb614f173a360061134d2 Mon Sep 17 00:00:00 2001 From: John Cortell <109228283+jcortell68@users.noreply.github.com> Date: Tue, 6 Feb 2024 11:04:12 -0500 Subject: [PATCH 077/441] Minor improvements/fixes to custom plugin API doc (#13358) --- examples/api-provider-sample/src/plugin/gotd-api-init.ts | 3 +-- packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/api-provider-sample/src/plugin/gotd-api-init.ts b/examples/api-provider-sample/src/plugin/gotd-api-init.ts index 118410785db71..cae7b4c0a3e7f 100644 --- a/examples/api-provider-sample/src/plugin/gotd-api-init.ts +++ b/examples/api-provider-sample/src/plugin/gotd-api-init.ts @@ -21,7 +21,7 @@ import type * as gotd from '../gotd'; import { GreetingKind, GreetingExt, MAIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { GreetingExtImpl } from './greeting-ext-impl'; import { Disposable, DisposableCollection } from '@theia/core'; -import { ApiFactory, PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; +import { PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; // This script is responsible for creating and returning the extension's // custom API object when a plugin's module imports it. Keep in mind that @@ -31,7 +31,6 @@ import { ApiFactory, PluginContainerModule } from '@theia/plugin-ext/lib/plugin/ type Gotd = typeof gotd; const GotdApiFactory = Symbol('GotdApiFactory'); -type GotdApiFactory = ApiFactory; // Retrieved by Theia to configure the Inversify DI container when the plugin is initialized. // This is called when the plugin-host process is forked. diff --git a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md index 0038de11eb539..c19c68bbefb1e 100644 --- a/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md +++ b/packages/plugin-ext/doc/how-to-add-new-custom-plugin-api.md @@ -82,7 +82,7 @@ Example `node/foo-init.ts`: import { inject, injectable } from '@theia/core/shared/inversify'; import { RPCProtocol } from '@theia/plugin-ext/lib/common/rpc-protocol'; import { Plugin } from '@theia/plugin-ext/lib/common/plugin-api-rpc'; -import { ApiFactory, PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; +import { PluginContainerModule } from '@theia/plugin-ext/lib/plugin/node/plugin-container-module'; import { FooExt } from '../common/foo-api-rpc'; import { FooExtImpl } from './foo-ext-impl'; @@ -92,7 +92,6 @@ type FooBarApi = typeof fooBarAPI; type Foo = FooBarApi['Foo']; const FooBarApiFactory = Symbol('FooBarApiFactory'); -type FooBarApiFactory = ApiFactory; // Retrieved by Theia to configure the Inversify DI container when the plugin is initialized. // This is called when the plugin-host process is forked. @@ -127,10 +126,11 @@ class FooBarApiFactoryImpl { // The plugin host expects our API factory to export a `createApi()` method createApi(plugin: Plugin): FooBarApi { + const self = this; return { fooBar: { getFoo(): Promise { - return fooExt.getFooImpl(); + return self.fooExt.getFooImpl(); } } }; From ba9cf88544e2621e2c090f66211b2558dddc536a Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Wed, 31 Jan 2024 10:35:22 +0000 Subject: [PATCH 078/441] Improves style and titles of toolbar items in notebooks. Signed-off-by: Jan Bicker --- .../contributions/notebook-actions-contribution.ts | 12 +++++++----- .../src/browser/view/notebook-main-toolbar.tsx | 6 ++++-- packages/notebook/src/common/notebook-common.ts | 3 +-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 9d73b6a766323..fcd1c94e4c141 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -19,7 +19,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from '../service/notebook-service'; -import { CellEditType, CellKind } from '../../common'; +import { CellEditType, CellKind, NotebookCommand } from '../../common'; import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @@ -32,13 +32,15 @@ export namespace NotebookCommands { export const ADD_NEW_MARKDOWN_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.add-new-markdown-cell', - iconClass: codicon('add') - }); + iconClass: codicon('add'), + tooltip: nls.localizeByDefault('Add Markdown Cell') + } as NotebookCommand); export const ADD_NEW_CODE_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.add-new-code-cell', - iconClass: codicon('add') - }); + iconClass: codicon('add'), + tooltip: nls.localizeByDefault('Add Code Cell') + } as NotebookCommand); export const SELECT_KERNEL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.selectKernel', diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index 80e21e1974d34..a183583ae83e2 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -21,6 +21,7 @@ import { NotebookModel } from '../view-model/notebook-model'; import { NotebookKernelService } from '../service/notebook-kernel-service'; import { inject, injectable } from '@theia/core/shared/inversify'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { NotebookCommand } from '../../common'; export interface NotebookMainToolbarProps { notebookModel: NotebookModel @@ -85,7 +86,7 @@ export class NotebookMainToolbar extends React.Component {this.getMenuItems().map(item => this.renderMenuItem(item))}
-
this.props.commandRegistry.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, this.props.notebookModel)}> @@ -103,7 +104,8 @@ export class NotebookMainToolbar extends React.Component 0 && } ; } else if (!item.when || this.props.contextKeyService.match(item.when)) { - return
{ if (item.command) { this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel); diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index 0812027f0ca5a..af1b1b4a2905a 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -19,8 +19,7 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdo import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { UriComponents } from '@theia/core/lib/common/uri'; -export interface NotebookCommand { - id: string; +export interface NotebookCommand extends Command { title?: string; tooltip?: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any From c4fb1cd7d9030dd6acc964cef8c8299ede421efd Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 8 Feb 2024 12:17:54 +0100 Subject: [PATCH 079/441] Fix race condition in notebook kernel association (#13364) --- .../service/notebook-kernel-service.ts | 34 +++++++------------ .../notebook-documents-and-editors-main.ts | 13 +++---- .../src/plugin/notebook/notebook-kernels.ts | 8 ++--- .../src/plugin/notebook/notebooks.ts | 20 +++++++++++ .../plugin-ext/src/plugin/plugin-context.ts | 3 +- 5 files changed, 44 insertions(+), 34 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index f5e412bd52c4d..85309f3ebae64 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -144,7 +144,7 @@ export class SourceCommand implements Disposable { const NOTEBOOK_KERNEL_BINDING_STORAGE_KEY = 'notebook.kernel.bindings'; @injectable() -export class NotebookKernelService implements Disposable { +export class NotebookKernelService { @inject(NotebookService) protected notebookService: NotebookService; @@ -152,33 +152,34 @@ export class NotebookKernelService implements Disposable { @inject(StorageService) protected storageService: StorageService; - private readonly kernels = new Map(); + protected readonly kernels = new Map(); - private notebookBindings: { [key: string]: string } = {}; + protected notebookBindings: Record = {}; - private readonly kernelDetectionTasks = new Map(); - private readonly onDidChangeKernelDetectionTasksEmitter = new Emitter(); + protected readonly kernelDetectionTasks = new Map(); + protected readonly onDidChangeKernelDetectionTasksEmitter = new Emitter(); readonly onDidChangeKernelDetectionTasks = this.onDidChangeKernelDetectionTasksEmitter.event; - private readonly onDidChangeSourceActionsEmitter = new Emitter(); - private readonly kernelSourceActionProviders = new Map(); + protected readonly onDidChangeSourceActionsEmitter = new Emitter(); + protected readonly kernelSourceActionProviders = new Map(); readonly onDidChangeSourceActions: Event = this.onDidChangeSourceActionsEmitter.event; - private readonly onDidAddKernelEmitter = new Emitter(); + protected readonly onDidAddKernelEmitter = new Emitter(); readonly onDidAddKernel: Event = this.onDidAddKernelEmitter.event; - private readonly onDidRemoveKernelEmitter = new Emitter(); + protected readonly onDidRemoveKernelEmitter = new Emitter(); readonly onDidRemoveKernel: Event = this.onDidRemoveKernelEmitter.event; - private readonly onDidChangeSelectedNotebookKernelBindingEmitter = new Emitter(); + protected readonly onDidChangeSelectedNotebookKernelBindingEmitter = new Emitter(); readonly onDidChangeSelectedKernel: Event = this.onDidChangeSelectedNotebookKernelBindingEmitter.event; - private readonly onDidChangeNotebookAffinityEmitter = new Emitter(); + protected readonly onDidChangeNotebookAffinityEmitter = new Emitter(); readonly onDidChangeNotebookAffinity: Event = this.onDidChangeNotebookAffinityEmitter.event; @postConstruct() init(): void { - this.storageService.getData(NOTEBOOK_KERNEL_BINDING_STORAGE_KEY).then((value: { [key: string]: string } | undefined) => { + this.notebookService.onDidAddNotebookDocument(model => this.tryAutoBindNotebook(model)); + this.storageService.getData(NOTEBOOK_KERNEL_BINDING_STORAGE_KEY).then((value: Record | undefined) => { if (value) { this.notebookBindings = value; } @@ -344,13 +345,4 @@ export class NotebookKernelService implements Disposable { const allActions = await Promise.all(promises); return allActions.flat(); } - - dispose(): void { - this.onDidChangeKernelDetectionTasksEmitter.dispose(); - this.onDidChangeSourceActionsEmitter.dispose(); - this.onDidAddKernelEmitter.dispose(); - this.onDidRemoveKernelEmitter.dispose(); - this.onDidChangeSelectedNotebookKernelBindingEmitter.dispose(); - this.onDidChangeNotebookAffinityEmitter.dispose(); - } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index baad9444acdfa..b41159f8e2f0f 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -31,7 +31,6 @@ import { NotebookEditorsMainImpl } from './notebook-editors-main'; import { NotebookDocumentsMainImpl } from './notebook-documents-main'; import { diffMaps, diffSets } from '../../../common/collections'; import { Mutex } from 'async-mutex'; -import throttle = require('@theia/core/shared/lodash.throttle'); interface NotebookAndEditorDelta { removedDocuments: UriComponents[]; @@ -107,12 +106,12 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain this.notebookEditorService = container.get(NotebookEditorWidgetService); this.WidgetManager = container.get(WidgetManager); - this.notebookService.onDidAddNotebookDocument(async () => this.throttleStateUpdate(), this, this.disposables); - this.notebookService.onDidRemoveNotebookDocument(async () => this.throttleStateUpdate(), this, this.disposables); + this.notebookService.onDidAddNotebookDocument(async () => this.updateState(), this, this.disposables); + this.notebookService.onDidRemoveNotebookDocument(async () => this.updateState(), this, this.disposables); // this.WidgetManager.onActiveEditorChanged(() => this.updateState(), this, this.disposables); this.notebookEditorService.onDidAddNotebookEditor(async editor => this.handleEditorAdd(editor), this, this.disposables); this.notebookEditorService.onDidRemoveNotebookEditor(async editor => this.handleEditorRemove(editor), this, this.disposables); - this.notebookEditorService.onDidChangeFocusedEditor(async editor => this.throttleStateUpdate(editor), this, this.disposables); + this.notebookEditorService.onDidChangeFocusedEditor(async editor => this.updateState(editor), this, this.disposables); } dispose(): void { @@ -130,18 +129,16 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain } else { this.editorListeners.set(editor.id, [disposable]); } - await this.throttleStateUpdate(); + await this.updateState(); } private handleEditorRemove(editor: NotebookEditorWidget): void { const listeners = this.editorListeners.get(editor.id); listeners?.forEach(listener => listener.dispose()); this.editorListeners.delete(editor.id); - this.throttleStateUpdate(); + this.updateState(); } - private throttleStateUpdate = throttle((focusedEditor?: NotebookEditorWidget) => this.updateState(focusedEditor), 100); - private async updateState(focusedEditor?: NotebookEditorWidget): Promise { await this.updateMutex.runExclusive(async () => this.doUpdateState(focusedEditor)); } diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts index 9dddde9768293..c089d53abb571 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts @@ -294,11 +294,11 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { }; } - $acceptNotebookAssociation(handle: number, uri: UriComponents, value: boolean): void { + async $acceptNotebookAssociation(handle: number, uri: UriComponents, value: boolean): Promise { const obj = this.kernelData.get(handle); if (obj) { // update data structure - const notebook = this.notebooks.getNotebookDocument(URI.from(uri))!; + const notebook = await this.notebooks.waitForNotebookDocument(URI.from(uri)); if (value) { obj.associatedNotebooks.set(notebook.uri.toString(), true); } else { @@ -320,7 +320,7 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { // extension can dispose kernels in the meantime return Promise.resolve(); } - const document = this.notebooks.getNotebookDocument(URI.from(uri)); + const document = await this.notebooks.waitForNotebookDocument(URI.from(uri)); const cells: theia.NotebookCell[] = []; for (const cellHandle of handles) { const cell = document.getCell(cellHandle); @@ -347,7 +347,7 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { // cancel or interrupt depends on the controller. When an interrupt handler is used we // don't trigger the cancelation token of executions.N - const document = this.notebooks.getNotebookDocument(URI.from(uri)); + const document = await this.notebooks.waitForNotebookDocument(URI.from(uri)); if (obj.controller.interruptHandler) { await obj.controller.interruptHandler.call(obj.controller, document.apiNotebook); diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 58aa248cb4396..01034fd711fe8 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -312,6 +312,26 @@ export class NotebooksExtImpl implements NotebooksExt { return result; } + waitForNotebookDocument(uri: TheiaURI, duration = 2000): Promise { + const existing = this.getNotebookDocument(uri, true); + if (existing) { + return Promise.resolve(existing); + } + return new Promise((resolve, reject) => { + const listener = this.onDidOpenNotebookDocument(event => { + if (event.uri.toString() === uri.toString()) { + clearTimeout(timeout); + listener.dispose(); + resolve(this.getNotebookDocument(uri)); + } + }); + const timeout = setTimeout(() => { + listener.dispose(); + reject(new Error(`Notebook document did NOT open in ${duration}ms: ${uri}`)); + }, duration); + }); + } + private createExtHostEditor(document: NotebookDocument, editorId: string, data: NotebookEditorAddData): void { if (this.editors.has(editorId)) { diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index c90338531dd4e..bc4ed5e144977 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -709,7 +709,8 @@ export function createAPIFactory( } else { throw new Error('Invalid arguments'); } - return notebooksExt.getNotebookDocument(uri).apiNotebook; + const result = await notebooksExt.waitForNotebookDocument(uri); + return result.apiNotebook; }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): theia.FileSystemWatcher => From 913eb3e878ded6370d5a1b1e1b32afec9eacfe29 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 8 Feb 2024 13:33:29 +0100 Subject: [PATCH 080/441] Update notebook execution timer (#13366) --- .../browser/view/notebook-code-cell-view.tsx | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index d219addef2bf2..4528b16d3a47d 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -71,21 +71,40 @@ export class NotebookCodeCellRenderer implements CellRenderer { export interface NotebookCodeCellStatusProps { cell: NotebookCellModel; - executionStateService: NotebookExecutionStateService + executionStateService: NotebookExecutionStateService; } -export class NotebookCodeCellStatus extends React.Component { +export interface NotebookCodeCellStatusState { + currentExecution?: CellExecution; + executionTime: number; +} + +export class NotebookCodeCellStatus extends React.Component { protected toDispose = new DisposableCollection(); constructor(props: NotebookCodeCellStatusProps) { super(props); - this.state = {}; + this.state = { + executionTime: 0 + }; + let currentInterval: NodeJS.Timeout | undefined; this.toDispose.push(props.executionStateService.onDidChangeExecution(event => { if (event.affectsCell(this.props.cell.uri)) { - this.setState({ currentExecution: event.changed }); + this.setState({ currentExecution: event.changed, executionTime: 0 }); + clearInterval(currentInterval); + if (event.changed?.state === NotebookCellExecutionState.Executing) { + const startTime = Date.now(); + // The resolution of the time display is only a single digit after the decimal point. + // Therefore, we only need to update the display every 100ms. + currentInterval = setInterval(() => { + this.setState({ + executionTime: Date.now() - startTime + }); + }, 100); + } } })); } @@ -126,17 +145,22 @@ export class NotebookCodeCellStatus extends React.Component -
{this.getExecutionTime()}
+
{this.renderTime(this.getExecutionTime())}
} ; } - private getExecutionTime(): string { + private getExecutionTime(): number { const { runStartTime, runEndTime } = this.props.cell.internalMetadata; - if (runStartTime && runEndTime) { - return `${((runEndTime - runStartTime) / 1000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1 })}s`; + const { executionTime } = this.state; + if (runStartTime !== undefined && runEndTime !== undefined) { + return runEndTime - runStartTime; } - return '0.0s'; + return executionTime; + } + + private renderTime(ms: number): string { + return `${(ms / 1000).toLocaleString(undefined, { maximumFractionDigits: 1, minimumFractionDigits: 1 })}s`; } } From b7ed5e98da98dfd6b6875e39464039a2b8d2a0e1 Mon Sep 17 00:00:00 2001 From: Vlad Arama <86936229+vladarama@users.noreply.github.com> Date: Thu, 8 Feb 2024 07:52:11 -0500 Subject: [PATCH 081/441] search-in-workspace: focus on next and previous search results (#12703) --- .../tree/test/mock-selectable-tree-model.ts | 109 +++++++++++++ packages/core/src/browser/tree/tree-model.ts | 74 +++++++++ .../src/browser/tree/tree-selectable.spec.ts | 152 ++++++++++++++++++ ...arch-in-workspace-frontend-contribution.ts | 36 +++++ ...search-in-workspace-result-tree-widget.tsx | 73 +++++++++ 5 files changed, 444 insertions(+) create mode 100644 packages/core/src/browser/tree/test/mock-selectable-tree-model.ts create mode 100644 packages/core/src/browser/tree/tree-selectable.spec.ts diff --git a/packages/core/src/browser/tree/test/mock-selectable-tree-model.ts b/packages/core/src/browser/tree/test/mock-selectable-tree-model.ts new file mode 100644 index 0000000000000..7a6c263c14096 --- /dev/null +++ b/packages/core/src/browser/tree/test/mock-selectable-tree-model.ts @@ -0,0 +1,109 @@ +// ***************************************************************************** +// Copyright (C) 2023 Ericsson and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CompositeTreeNode } from '../tree'; +import { SelectableTreeNode } from '../tree-selection'; +import { ExpandableTreeNode } from '../tree-expansion'; + +export namespace MockSelectableTreeModel { + + export interface SelectableNode { + readonly id: string; + readonly selected: boolean; + readonly focused?: boolean; + readonly children?: SelectableNode[]; + } + + export namespace SelectableNode { + export function toTreeNode(root: SelectableNode, parent?: SelectableTreeNode & CompositeTreeNode): SelectableTreeNode { + const { id } = root; + const name = id; + const selected = false; + const focus = false; + const expanded = true; + const node: CompositeTreeNode & SelectableTreeNode = { + id, + name, + selected, + focus, + parent: parent, + children: [] + }; + const children = (root.children || []).map(child => SelectableNode.toTreeNode(child, node)); + if (children.length === 0) { + return node; + } else { + node.children = children; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (node as any).expanded = expanded; + return node as CompositeTreeNode & SelectableTreeNode & ExpandableTreeNode; + } + } + } + + export const HIERARCHICAL_MOCK_ROOT = () => SelectableNode.toTreeNode({ + 'id': '1', + 'selected': false, + 'children': [ + { + 'id': '1.1', + 'selected': false, + 'children': [ + { + 'id': '1.1.1', + 'selected': false, + }, + { + 'id': '1.1.2', + 'selected': false, + } + ] + }, + { + 'id': '1.2', + 'selected': false, + 'children': [ + { + 'id': '1.2.1', + 'selected': false, + 'children': [ + { + 'id': '1.2.1.1', + 'selected': false, + }, + { + 'id': '1.2.1.2', + 'selected': false, + } + ] + }, + { + 'id': '1.2.2', + 'selected': false, + }, + { + 'id': '1.2.3', + 'selected': false, + } + ] + }, + { + 'id': '1.3', + 'selected': false, + } + ] + }); +} diff --git a/packages/core/src/browser/tree/tree-model.ts b/packages/core/src/browser/tree/tree-model.ts index 97526a3da4c0d..d4a6dda0c167c 100644 --- a/packages/core/src/browser/tree/tree-model.ts +++ b/packages/core/src/browser/tree/tree-model.ts @@ -99,21 +99,41 @@ export interface TreeModel extends Tree, TreeSelectionService, TreeExpansionServ */ navigateBackward(): Promise; + /** + * Selects the previous tree node, regardless of its selection or visibility state. + */ + selectPrev(): void; + /** * Selects the previous node relatively to the currently selected one. This method takes the expansion state of the tree into consideration. */ selectPrevNode(type?: TreeSelection.SelectionType): void; + /** + * Returns the previous tree node, regardless of its selection or visibility state. + */ + getPrevNode(node?: TreeNode): TreeNode | undefined; + /** * Returns the previous selectable tree node. */ getPrevSelectableNode(node?: TreeNode): SelectableTreeNode | undefined; + /** + * Selects the next tree node, regardless of its selection or visibility state. + */ + selectNext(): void; + /** * Selects the next node relatively to the currently selected one. This method takes the expansion state of the tree into consideration. */ selectNextNode(type?: TreeSelection.SelectionType): void; + /** + * Returns the next tree node, regardless of its selection or visibility state. + */ + getNextNode(node?: TreeNode): TreeNode | undefined; + /** * Returns the next selectable tree node. */ @@ -294,6 +314,11 @@ export class TreeModelImpl implements TreeModel, SelectionProvider(iterator: TreeIterator, criterion: (node: TreeNode) => node is T): T | undefined { // Skip the first item. // TODO: clean this up, and skip the first item in a different way without loading everything. iterator.next(); @@ -338,6 +390,17 @@ export class TreeModelImpl implements TreeModel, SelectionProvider { + let model: TreeModel; + function assertNodeRetrieval(method: () => TreeNode | undefined, sequence: string[]): void { + for (const expectedNodeId of sequence) { + const actualNode = method(); + const expectedNode = retrieveNode(expectedNodeId); + expect(actualNode?.id).to.be.equal(expectedNode.id); + model.addSelection(expectedNode); + } + } + function assertNodeSelection(method: () => void, sequence: string[]): void { + for (const expectedNodeId of sequence) { + method(); + const node = retrieveNode(expectedNodeId); + expect(node.selected).to.be.true; + } + } + describe('Get and Set Next Nodes Methods', () => { + const uncollapsedSelectionOrder = ['1.1', '1.1.1', '1.1.2', '1.2', '1.2.1', '1.2.1.1', '1.2.1.2', '1.2.2', '1.2.3', '1.3']; + const collapsedSelectionOrder = ['1.1', '1.2', '1.2.1', '1.2.2', '1.2.3', '1.3']; + beforeEach(() => { + model = createTreeModel(); + model.root = MockSelectableTreeModel.HIERARCHICAL_MOCK_ROOT(); + model.addSelection(retrieveNode('1')); + + }); + it('`getNextNode()` should select each node in sequence (uncollapsed)', done => { + assertNodeRetrieval(model.getNextNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getNextNode()` should select each node in sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeRetrieval(model.getNextNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getNextSelectableNode()` should select each node in sequence (uncollapsed)', done => { + assertNodeRetrieval(model.getNextSelectableNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getNextSelectableNode()` should select each node in sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeRetrieval(model.getNextSelectableNode.bind(model), collapsedSelectionOrder); + done(); + }); + it('`selectNext()` should select each node in sequence (uncollapsed)', done => { + assertNodeSelection(model.selectNext.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectNext()` should select each node in sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeSelection(model.selectNext.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectNextNode()` should select each node in sequence (uncollapsed)', done => { + assertNodeSelection(model.selectNextNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectNextNode()` should select each node in sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeSelection(model.selectNextNode.bind(model), collapsedSelectionOrder); + done(); + }); + }); + + describe('Get and Set Previous Nodes Methods', () => { + const uncollapsedSelectionOrder = ['1.2.3', '1.2.2', '1.2.1.2', '1.2.1.1', '1.2.1', '1.2', '1.1.2', '1.1.1', '1.1']; + const collapsedSelectionOrder = ['1.2.3', '1.2.2', '1.2.1', '1.2', '1.1']; + beforeEach(() => { + model = createTreeModel(); + model.root = MockSelectableTreeModel.HIERARCHICAL_MOCK_ROOT(); + model.addSelection(retrieveNode('1.3')); + }); + it('`getPrevNode()` should select each node in reverse sequence (uncollapsed)', done => { + assertNodeRetrieval(model.getPrevNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getPrevNode()` should select each node in reverse sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeRetrieval(model.getPrevNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getPrevSelectableNode()` should select each node in reverse sequence (uncollapsed)', done => { + assertNodeRetrieval(model.getPrevSelectableNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`getPrevSelectableNode()` should select each node in reverse sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeRetrieval(model.getPrevSelectableNode.bind(model), collapsedSelectionOrder); + done(); + }); + it('`selectPrev()` should select each node in reverse sequence (uncollapsed)', done => { + assertNodeSelection(model.selectPrev.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectPrev()` should select each node in reverse sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeSelection(model.selectPrev.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectPrevNode()` should select each node in reverse sequence (uncollapsed)', done => { + assertNodeSelection(model.selectPrevNode.bind(model), uncollapsedSelectionOrder); + done(); + }); + it('`selectPrevNode()` should select each node in reverse sequence (collapsed)', done => { + collapseNode('1.1', '1.2.1'); + assertNodeSelection(model.selectPrevNode.bind(model), collapsedSelectionOrder); + done(); + }); + }); + + const findNode = (id: string) => model.getNode(id); + function createTreeModel(): TreeModel { + const container = createTreeTestContainer(); + return container.get(TreeModel); + } + function retrieveNode(id: string): Readonly { + const readonlyNode: Readonly = model.getNode(id) as T; + return readonlyNode; + } + function collapseNode(...ids: string[]): void { + ids.map(findNode).filter(notEmpty).filter(ExpandableTreeNode.is).forEach(node => { + model.collapseNode(node); + expect(node).to.have.property('expanded', false); + }); + } + +}); diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts index 754c2020e434f..19f041953eb22 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts +++ b/packages/search-in-workspace/src/browser/search-in-workspace-frontend-contribution.ts @@ -57,6 +57,16 @@ export namespace SearchInWorkspaceCommands { category: SEARCH_CATEGORY, label: 'Find in Folder...' }); + export const FOCUS_NEXT_RESULT = Command.toDefaultLocalizedCommand({ + id: 'search.action.focusNextSearchResult', + category: SEARCH_CATEGORY, + label: 'Focus Next Search Result' + }); + export const FOCUS_PREV_RESULT = Command.toDefaultLocalizedCommand({ + id: 'search.action.focusPreviousSearchResult', + category: SEARCH_CATEGORY, + label: 'Focus Previous Search Result' + }); export const REFRESH_RESULTS = Command.toDefaultLocalizedCommand({ id: 'search-in-workspace.refresh', category: SEARCH_CATEGORY, @@ -169,6 +179,22 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut } }); + commands.registerCommand(SearchInWorkspaceCommands.FOCUS_NEXT_RESULT, { + isEnabled: () => this.withWidget(undefined, widget => widget.hasResultList()), + execute: async () => { + const widget = await this.openView({ reveal: true }); + widget.resultTreeWidget.selectNextResult(); + } + }); + + commands.registerCommand(SearchInWorkspaceCommands.FOCUS_PREV_RESULT, { + isEnabled: () => this.withWidget(undefined, widget => widget.hasResultList()), + execute: async () => { + const widget = await this.openView({ reveal: true }); + widget.resultTreeWidget.selectPreviousResult(); + } + }); + commands.registerCommand(SearchInWorkspaceCommands.FIND_IN_FOLDER, this.newMultiUriAwareCommandHandler({ execute: async uris => { const resources: string[] = []; @@ -343,6 +369,16 @@ export class SearchInWorkspaceFrontendContribution extends AbstractViewContribut keybinding: 'shift+alt+f', when: 'explorerResourceIsFolder' }); + keybindings.registerKeybinding({ + command: SearchInWorkspaceCommands.FOCUS_NEXT_RESULT.id, + keybinding: 'f4', + when: 'hasSearchResult' + }); + keybindings.registerKeybinding({ + command: SearchInWorkspaceCommands.FOCUS_PREV_RESULT.id, + keybinding: 'shift+f4', + when: 'hasSearchResult' + }); keybindings.registerKeybinding({ command: SearchInWorkspaceCommands.DISMISS_RESULT.id, keybinding: isOSX ? 'cmd+backspace' : 'del', diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx index 2b6922ba3f7c7..1abf85b4a678f 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx +++ b/packages/search-in-workspace/src/browser/search-in-workspace-result-tree-widget.tsx @@ -281,6 +281,79 @@ export class SearchInWorkspaceResultTreeWidget extends TreeWidget { return true; } + selectNextResult(): void { + if (!this.model.getFocusedNode()) { + return this.selectFirstResult(); + } + let foundNextResult = false; + while (!foundNextResult) { + const nextNode = this.model.getNextNode(); + if (!nextNode) { + return this.selectFirstResult(); + } else if (SearchInWorkspaceResultLineNode.is(nextNode)) { + foundNextResult = true; + this.selectExpandOpenResultNode(nextNode); + } else { + this.model.selectNext(); + } + } + } + + selectPreviousResult(): void { + if (!this.model.getFocusedNode()) { + return this.selectLastResult(); + } + let foundSelectedNode = false; + while (!foundSelectedNode) { + const prevNode = this.model.getPrevNode(); + if (!prevNode) { + return this.selectLastResult(); + } else if (SearchInWorkspaceResultLineNode.is(prevNode)) { + foundSelectedNode = true; + this.selectExpandOpenResultNode(prevNode); + } else if (prevNode.id === 'ResultTree') { + return this.selectLastResult(); + } else { + this.model.selectPrev(); + } + } + } + + protected selectExpandOpenResultNode(node: SearchInWorkspaceResultLineNode): void { + this.model.expandNode(node.parent.parent); + this.model.expandNode(node.parent); + this.model.selectNode(node); + this.model.openNode(node); + } + + protected selectFirstResult(): void { + for (const rootFolder of this.resultTree.values()) { + for (const file of rootFolder.children) { + for (const result of file.children) { + if (SelectableTreeNode.is(result)) { + return this.selectExpandOpenResultNode(result); + } + } + } + } + } + + protected selectLastResult(): void { + const rootFolders = Array.from(this.resultTree.values()); + for (let i = rootFolders.length - 1; i >= 0; i--) { + const rootFolder = rootFolders[i]; + for (let j = rootFolder.children.length - 1; j >= 0; j--) { + const file = rootFolder.children[j]; + for (let k = file.children.length - 1; k >= 0; k--) { + const result = file.children[k]; + if (SelectableTreeNode.is(result)) { + return this.selectExpandOpenResultNode(result); + } + } + } + } + } + /** * Find matches for the given editor. * @param searchTerm the search term. From 3034c0a0b4dd1082589ee5ec78a066a8532705fa Mon Sep 17 00:00:00 2001 From: Daniel Friederich Date: Tue, 6 Feb 2024 20:42:13 -0600 Subject: [PATCH 082/441] Fix file system delete of folder Deleting a folder without recursive enabled failed as the used nodejs API unlink does not support folder deletion. Fixing it by first checking if it is a folder, and if so using rmdir instead. Added a simple unit test, which also exposed that the folder deletion also fails when using trash support, but that is a different issue. Signed-off-by: Daniel Friederich --- .../node/disk-file-system-provider.spec.ts | 35 ++++++++++++++++++- .../src/node/disk-file-system-provider.ts | 7 +++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/filesystem/src/node/disk-file-system-provider.spec.ts b/packages/filesystem/src/node/disk-file-system-provider.spec.ts index f5f434fb7b0fd..641d8342d2fe3 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.spec.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.spec.ts @@ -26,7 +26,7 @@ import { promises as fs } from 'fs'; import { join } from 'path'; import * as temp from 'temp'; import { v4 } from 'uuid'; -import { FilePermission, FileSystemProviderError, FileSystemProviderErrorCode } from '../common/files'; +import { FilePermission, FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode } from '../common/files'; import { DiskFileSystemProvider } from './disk-file-system-provider'; import { bindFileSystemWatcherServer } from './filesystem-backend-module'; @@ -93,6 +93,39 @@ describe('disk-file-system-provider', () => { }); }); + describe('delete', () => { + it('delete is able to delete folder', async () => { + const tempDirPath = tracked.mkdirSync(); + const testFolder = join(tempDirPath, 'test'); + const folderUri = FileUri.create(testFolder); + for (const recursive of [true, false]) { + // Note: useTrash = true fails on Linux + const useTrash = false; + if ((fsProvider.capabilities & FileSystemProviderCapabilities.Access) === 0 && useTrash) { + continue; + } + await fsProvider.mkdir(folderUri); + if (recursive) { + await fsProvider.writeFile(FileUri.create(join(testFolder, 'test.file')), Buffer.from('test'), { overwrite: false, create: true }); + await fsProvider.mkdir(FileUri.create(join(testFolder, 'subFolder'))); + } + await fsProvider.delete(folderUri, { recursive, useTrash }); + } + }); + + it('delete is able to delete file', async () => { + const tempDirPath = tracked.mkdirSync(); + const testFile = join(tempDirPath, 'test.file'); + const testFileUri = FileUri.create(testFile); + for (const recursive of [true, false]) { + for (const useTrash of [true, false]) { + await fsProvider.writeFile(testFileUri, Buffer.from('test'), { overwrite: false, create: true }); + await fsProvider.delete(testFileUri, { recursive, useTrash }); + } + } + }); + }); + function createContainer(): Container { const container = new Container({ defaultScope: 'Singleton' }); const module = new ContainerModule(bind => { diff --git a/packages/filesystem/src/node/disk-file-system-provider.ts b/packages/filesystem/src/node/disk-file-system-provider.ts index e1e1e64e532a3..de2132e8a7886 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.ts @@ -509,7 +509,12 @@ export class DiskFileSystemProvider implements Disposable, if (opts.recursive) { await this.rimraf(filePath); } else { - await promisify(unlink)(filePath); + const stat = await promisify(lstat)(filePath); + if (stat.isDirectory() && !stat.isSymbolicLink()) { + await promisify(rmdir)(filePath); + } else { + await promisify(unlink)(filePath); + } } } else { await trash(filePath); From 4b8ec2de6576702d1470d520e8ab195149046508 Mon Sep 17 00:00:00 2001 From: noneghost Date: Mon, 12 Feb 2024 18:05:22 +0800 Subject: [PATCH 083/441] fix update debug createFrameDecorations bug when debug thread is not in stopped state (#12543) --- packages/debug/src/browser/debug-session-manager.ts | 2 +- packages/debug/src/browser/editor/debug-editor-model.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/debug/src/browser/debug-session-manager.ts b/packages/debug/src/browser/debug-session-manager.ts index adae46254f038..583a0d8af86f6 100644 --- a/packages/debug/src/browser/debug-session-manager.ts +++ b/packages/debug/src/browser/debug-session-manager.ts @@ -526,7 +526,7 @@ export class DebugSessionManager { } open(): void { const { currentFrame } = this; - if (currentFrame) { + if (currentFrame && currentFrame.thread.stopped) { currentFrame.open(); } } diff --git a/packages/debug/src/browser/editor/debug-editor-model.ts b/packages/debug/src/browser/editor/debug-editor-model.ts index fd04ef9c95105..935558f39975b 100644 --- a/packages/debug/src/browser/editor/debug-editor-model.ts +++ b/packages/debug/src/browser/editor/debug-editor-model.ts @@ -193,6 +193,10 @@ export class DebugEditorModel implements Disposable { return []; } + if (!currentFrame.thread.stopped) { + return []; + } + if (!this.sessions.isCurrentEditorFrame(this.uri)) { return []; } From fba46c6c74073ebc23da146b8c7c35e5cbeab2a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 13 Feb 2024 09:56:28 +0100 Subject: [PATCH 084/441] Add prefix to contributed view container ids (#13362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13249 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 2 +- .../main/browser/view/plugin-view-registry.ts | 21 ++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05661bfd5be99..d861a1fd945b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ - [component] add here --> ## v1.46.0 - 01/25/2024 - +- [plugin] Add prefix to contributed view container ids [#13362](https://github.com/eclipse-theia/theia/pull/13362) - contributed on behalf of STMicroelectronics - [application-manager] updated message for missing Electron main entries [#13242](https://github.com/eclipse-theia/theia/pull/13242) - [application-package] bumped the default supported API from `1.84.2` to `1.85.1` [#13276](https://github.com/eclipse-theia/theia/pull/13276) - contributed on behalf of STMicroelectronics - [browser-only] added support for 'browser-only' Theia [#12853](https://github.com/eclipse-theia/theia/pull/12853) diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 03312edd5803f..9698c259428f8 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -109,6 +109,16 @@ export class PluginViewRegistry implements FrontendApplicationContribution { private readonly webviewViewRevivals = new Map }>(); + private nextViewContainerId = 0; + + private static readonly BUILTIN_VIEW_CONTAINERS = new Set([ + 'explorer', + 'scm', + 'search', + 'test', + 'debug' + ]); + private static readonly ID_MAPPINGS: Map = new Map([ // VS Code Viewlets [EXPLORER_VIEW_CONTAINER_ID, 'workbench.view.explorer'], @@ -266,14 +276,15 @@ export class PluginViewRegistry implements FrontendApplicationContribution { } registerViewContainer(location: string, viewContainer: ViewContainer): Disposable { - if (this.viewContainers.has(viewContainer.id)) { + const containerId = `workbench.view.extension.${viewContainer.id}`; + if (this.viewContainers.has(containerId)) { console.warn('view container such id already registered: ', JSON.stringify(viewContainer)); return Disposable.NULL; } const toDispose = new DisposableCollection(); const containerClass = 'theia-plugin-view-container'; let themeIconClass = ''; - const iconClass = 'plugin-view-container-icon-' + viewContainer.id; + const iconClass = 'plugin-view-container-icon-' + this.nextViewContainerId++; // having dots in class would not work for css, so we need to generate an id. if (viewContainer.themeIcon) { const icon = ThemeIcon.fromString(viewContainer.themeIcon); @@ -290,7 +301,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { `)); } - toDispose.push(this.doRegisterViewContainer(viewContainer.id, location, { + toDispose.push(this.doRegisterViewContainer(containerId, location, { label: viewContainer.title, // The container class automatically sets a mask; if we're using a theme icon, we don't want one. iconClass: (themeIconClass || containerClass) + ' ' + iconClass, @@ -349,6 +360,10 @@ export class PluginViewRegistry implements FrontendApplicationContribution { } registerView(viewContainerId: string, view: View): Disposable { + if (!PluginViewRegistry.BUILTIN_VIEW_CONTAINERS.has(viewContainerId)) { + // if it's not a built-in view container, it must be a contributed view container, see https://github.com/eclipse-theia/theia/issues/13249 + viewContainerId = `workbench.view.extension.${viewContainerId}`; + } if (this.views.has(view.id)) { console.warn('view with such id already registered: ', JSON.stringify(view)); return Disposable.NULL; From ef486bda56e3adc29f5960e08baf220c34bfc8cb Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Tue, 13 Feb 2024 15:19:15 +0100 Subject: [PATCH 085/441] WebView Extensions asset load 403 Forbidden #13373 (#13382) * fix regression where webview based editors were using the new more stable origin URL from regular web views * add doc --- .../plugin-ext/src/plugin/custom-editors.ts | 2 +- packages/plugin-ext/src/plugin/webviews.ts | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/plugin-ext/src/plugin/custom-editors.ts b/packages/plugin-ext/src/plugin/custom-editors.ts index 901d05dc7f8fd..63f0c21888593 100644 --- a/packages/plugin-ext/src/plugin/custom-editors.ts +++ b/packages/plugin-ext/src/plugin/custom-editors.ts @@ -129,7 +129,7 @@ export class CustomEditorsExtImpl implements CustomEditorsExt { if (!entry) { throw new Error(`No provider found for '${viewType}'`); } - const panel = this.webviewExt.createWebviewPanel(viewType, title, {}, options, entry.plugin, handler); + const panel = this.webviewExt.createWebviewPanel(viewType, title, {}, options, entry.plugin, handler, false); const webviewOptions = WebviewImpl.toWebviewOptions(options, this.workspace, entry.plugin); await this.proxy.$createCustomEditorPanel(handler, title, widgetOpenerOptions, webviewOptions); diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index bb86b4903e43a..72a2e903fb656 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -126,19 +126,33 @@ export class WebviewsExtImpl implements WebviewsExt { return panel; } + /** + * Creates a new webview panel. + * + * @param viewType Identifies the type of the webview panel. + * @param title Title of the panel. + * @param showOptions Where webview panel will be reside. + * @param options Settings for the new panel. + * @param plugin The plugin contributing the webview. + * @param viewId The identifier of the webview instance. + * @param originBasedOnType true if a stable origin based on the viewType shall be used, false if the viewId should be used. + * @returns The new webview panel. + */ createWebviewPanel( viewType: string, title: string, showOptions: theia.ViewColumn | theia.WebviewPanelShowOptions, options: theia.WebviewPanelOptions & theia.WebviewOptions, plugin: Plugin, - viewId: string + viewId: string, + originBasedOnType = true ): WebviewPanelImpl { if (!this.initData) { throw new Error('Webviews are not initialized'); } const webviewShowOptions = toWebviewPanelShowOptions(showOptions); - const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin, hashValue(viewType)); + const origin = originBasedOnType ? hashValue(viewType) : undefined; + const webview = new WebviewImpl(viewId, this.proxy, options, this.initData, this.workspace, plugin, origin); const panel = new WebviewPanelImpl(viewId, this.proxy, viewType, title, webviewShowOptions, options, webview); this.webviewPanels.set(viewId, panel); return panel; From 84a54f4a30811b20d6c353822f3a5942d85cefbc Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Tue, 13 Feb 2024 17:08:08 +0100 Subject: [PATCH 086/441] fix: configure tasks from open task quick pick (#13367) The gear icon in the task quick pick should open the task configuration. Fixes #13086 Contributed on behalf of STMicroelectronics Signed-off-by: Alexandra Buzila --- packages/core/src/common/quick-pick-service.ts | 2 +- packages/task/src/browser/quick-open-task.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/src/common/quick-pick-service.ts b/packages/core/src/common/quick-pick-service.ts index 6762f47cb554a..ef832dbd6611f 100644 --- a/packages/core/src/common/quick-pick-service.ts +++ b/packages/core/src/common/quick-pick-service.ts @@ -191,7 +191,7 @@ export interface QuickPick extends QuickInpu readonly onDidAccept: Event<{ inBackground: boolean } | undefined>; readonly onDidChangeValue: Event; readonly onDidTriggerButton: Event; - readonly onDidTriggerItemButton: Event>; + readonly onDidTriggerItemButton: Event>; readonly onDidChangeActive: Event; readonly onDidChangeSelection: Event; } diff --git a/packages/task/src/browser/quick-open-task.ts b/packages/task/src/browser/quick-open-task.ts index a16e00dead071..1ff0f329e672a 100644 --- a/packages/task/src/browser/quick-open-task.ts +++ b/packages/task/src/browser/quick-open-task.ts @@ -182,6 +182,7 @@ export class QuickOpenTask implements QuickAccessProvider { picker.matchOnDescription = true; picker.ignoreFocusOut = false; picker.items = this.items; + picker.onDidTriggerItemButton(({ item }) => this.onDidTriggerGearIcon(item)); const firstLevelTask = await this.doPickerFirstLevel(picker); @@ -225,7 +226,10 @@ export class QuickOpenTask implements QuickAccessProvider { execute: () => this.showMultiLevelQuickPick(true) })); - this.quickInputService?.showQuickPick(providedTasksItems, { placeholder: CHOOSE_TASK }); + this.quickInputService?.showQuickPick(providedTasksItems, { + placeholder: CHOOSE_TASK, + onDidTriggerItemButton: ({ item }) => this.onDidTriggerGearIcon(item) + }); } attach(): void { From 5bc2c2e54be0f919ec2e1e925ee5ea1b3a3035a0 Mon Sep 17 00:00:00 2001 From: Vincent Fugnitto Date: Wed, 14 Feb 2024 10:26:26 -0500 Subject: [PATCH 087/441] terminal: bump `xterm` and addons (#12691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps `xterm` and related addons to their latest versions to benefit from additional improvements and bug fixes. Signed-off-by: vince-fugnitto Signed-off-by: Thomas Mäder Co-authored-by: Thomas Mäder --- CHANGELOG.md | 2 + packages/terminal/package.json | 8 +- .../browser/terminal-frontend-contribution.ts | 22 ++++ .../src/browser/terminal-preferences.ts | 3 +- .../src/browser/terminal-theme-service.ts | 8 +- .../src/browser/terminal-widget-impl.ts | 123 +++++++++--------- yarn.lock | 18 ++- 7 files changed, 113 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d861a1fd945b8..4d25f0597802e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -232,6 +232,8 @@ - [deps] bumped supported Node.js version from 16.x to >=18, you may need to update your environments [#12711](https://github.com/eclipse-theia/theia/pull/12711) - [preferences] removed the `welcome.alwaysShowWelcomePage` preference in favor of `workbench.startupEditor` [#12813](https://github.com/eclipse-theia/theia/pull/12813) +- [terminal] deprecated `terminal.integrated.rendererType` preference [#12691](https://github.com/eclipse-theia/theia/pull/12691) +- [terminal] removed protected method `TerminalWidgetImpl.getTerminalRendererType` [#12691](https://github.com/eclipse-theia/theia/pull/12691) ## v1.40.0 - 07/27/2023 diff --git a/packages/terminal/package.json b/packages/terminal/package.json index b802d27cbcd24..cb72c4a33591b 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -9,9 +9,9 @@ "@theia/process": "1.46.0", "@theia/variable-resolver": "1.46.0", "@theia/workspace": "1.46.0", - "xterm": "^4.16.0", - "xterm-addon-fit": "^0.5.0", - "xterm-addon-search": "^0.8.2" + "xterm": "^5.3.0", + "xterm-addon-fit": "^0.8.0", + "xterm-addon-search": "^0.13.0" }, "publishConfig": { "access": "public" @@ -53,4 +53,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index 723db33c3e477..81c322f371a5a 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -55,6 +55,7 @@ import { nls } from '@theia/core/lib/common/nls'; import { Profiles, TerminalPreferences } from './terminal-preferences'; import { ShellTerminalProfile } from './shell-terminal-profile'; import { VariableResolverService } from '@theia/variable-resolver/lib/browser'; +import { Color } from '@theia/core/lib/common/color'; export namespace TerminalMenus { export const TERMINAL = [...MAIN_MENU_BAR, '7_terminal']; @@ -1074,6 +1075,27 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu }, description: 'The selection background color of the terminal.' }); + colors.register({ + id: 'terminal.inactiveSelectionBackground', + defaults: { + light: Color.transparent('terminal.selectionBackground', 0.5), + dark: Color.transparent('terminal.selectionBackground', 0.5), + hcDark: Color.transparent('terminal.selectionBackground', 0.7), + hcLight: Color.transparent('terminal.selectionBackground', 0.5), + }, + description: 'The selection background color of the terminal when it does not have focus.' + }); + colors.register({ + id: 'terminal.selectionForeground', + defaults: { + light: undefined, + dark: undefined, + hcDark: '#000000', + hcLight: '#ffffff' + }, + // eslint-disable-next-line max-len + description: 'The selection foreground color of the terminal. When this is null the selection foreground will be retained and have the minimum contrast ratio feature applied.' + }); colors.register({ id: 'terminal.border', defaults: { diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts index ec1fe0592af0e..39c8dc8f4044d 100644 --- a/packages/terminal/src/browser/terminal-preferences.ts +++ b/packages/terminal/src/browser/terminal-preferences.ts @@ -134,7 +134,8 @@ export const TerminalConfigSchema: PreferenceSchema = { description: nls.localize('theia/terminal/rendererType', 'Controls how the terminal is rendered.'), type: 'string', enum: ['canvas', 'dom'], - default: 'canvas' + default: 'canvas', + deprecationMessage: nls.localize('theia/terminal/rendererTypeDeprecationMessage', 'The renderer type is no longer supported as an option.') }, 'terminal.integrated.copyOnSelection': { description: nls.localizeByDefault('Controls whether text selected in the terminal will be copied to the clipboard.'), diff --git a/packages/terminal/src/browser/terminal-theme-service.ts b/packages/terminal/src/browser/terminal-theme-service.ts index 3603f8f30f643..dc74e80765106 100644 --- a/packages/terminal/src/browser/terminal-theme-service.ts +++ b/packages/terminal/src/browser/terminal-theme-service.ts @@ -187,14 +187,18 @@ export class TerminalThemeService { const backgroundColor = this.colorRegistry.getCurrentColor('terminal.background') || this.colorRegistry.getCurrentColor('panel.background'); const cursorColor = this.colorRegistry.getCurrentColor('terminalCursor.foreground') || foregroundColor; const cursorAccentColor = this.colorRegistry.getCurrentColor('terminalCursor.background') || backgroundColor; - const selectionColor = this.colorRegistry.getCurrentColor('terminal.selectionBackground'); + const selectionBackgroundColor = this.colorRegistry.getCurrentColor('terminal.selectionBackground'); + const selectionInactiveBackground = this.colorRegistry.getCurrentColor('terminal.inactiveSelectionBackground'); + const selectionForegroundColor = this.colorRegistry.getCurrentColor('terminal.selectionForeground'); const theme: ITheme = { background: backgroundColor, foreground: foregroundColor, cursor: cursorColor, cursorAccent: cursorAccentColor, - selection: selectionColor + selectionBackground: selectionBackgroundColor, + selectionInactiveBackground: selectionInactiveBackground, + selectionForeground: selectionForegroundColor }; // eslint-disable-next-line guard-for-in for (const id in terminalAnsiColorMap) { diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index ba7f8935a7000..17992cbdb5f85 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Terminal, RendererType } from 'xterm'; +import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core'; @@ -32,7 +32,7 @@ import { TerminalLocation } from './base/terminal-widget'; import { Deferred } from '@theia/core/lib/common/promise-util'; -import { TerminalPreferences, TerminalRendererType, isTerminalRendererType, DEFAULT_TERMINAL_RENDERER_TYPE, CursorStyle } from './terminal-preferences'; +import { TerminalPreferences } from './terminal-preferences'; import URI from '@theia/core/lib/common/uri'; import { TerminalService } from './base/terminal-service'; import { TerminalSearchWidgetFactory, TerminalSearchWidget } from './search/terminal-search-widget'; @@ -161,7 +161,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.term = new Terminal({ cursorBlink: this.preferences['terminal.integrated.cursorBlinking'], - cursorStyle: this.getCursorStyle(), + cursorStyle: this.preferences['terminal.integrated.cursorStyle'] === 'line' ? 'bar' : this.preferences['terminal.integrated.cursorStyle'], cursorWidth: this.preferences['terminal.integrated.cursorWidth'], fontFamily: this.preferences['terminal.integrated.fontFamily'], fontSize: this.preferences['terminal.integrated.fontSize'], @@ -172,7 +172,6 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget lineHeight: this.preferences['terminal.integrated.lineHeight'], scrollback: this.preferences['terminal.integrated.scrollback'], fastScrollSensitivity: this.preferences['terminal.integrated.fastScrollSensitivity'], - rendererType: this.getTerminalRendererType(this.preferences['terminal.integrated.rendererType']), theme: this.themeService.theme }); @@ -182,34 +181,12 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.initializeLinkHover(); this.toDispose.push(this.preferences.onPreferenceChanged(change => { - const lastSeparator = change.preferenceName.lastIndexOf('.'); - if (lastSeparator > 0) { - let preferenceName = change.preferenceName.substring(lastSeparator + 1); - let preferenceValue = change.newValue; - - if (preferenceName === 'rendererType') { - const newRendererType = preferenceValue as string; - if (newRendererType !== this.getTerminalRendererType(newRendererType)) { - // Given terminal renderer type is not supported or invalid - preferenceValue = DEFAULT_TERMINAL_RENDERER_TYPE; - } - } else if (preferenceName === 'cursorBlinking') { - // Convert the terminal preference into a valid `xterm` option - preferenceName = 'cursorBlink'; - } else if (preferenceName === 'cursorStyle') { - preferenceValue = this.getCursorStyle(); - } - try { - this.term.setOption(preferenceName, preferenceValue); - } catch (e) { - console.debug(`xterm configuration: '${preferenceName}' with value '${preferenceValue}' is not valid.`); - } - this.needsResize = true; - this.update(); - } + this.updateConfig(); + this.needsResize = true; + this.update(); })); - this.toDispose.push(this.themeService.onDidChange(() => this.term.setOption('theme', this.themeService.theme))); + this.toDispose.push(this.themeService.onDidChange(() => this.term.options.theme = this.themeService.theme)); this.attachCustomKeyEventHandler(); const titleChangeListenerDispose = this.term.onTitleChange((title: string) => { if (this.options.useServerTitle) { @@ -314,25 +291,38 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget return this.terminalKind; } - /** - * Get the cursor style compatible with `xterm`. - * @returns CursorStyle - */ - private getCursorStyle(): CursorStyle { - const value = this.preferences['terminal.integrated.cursorStyle']; - return value === 'line' ? 'bar' : value; + updateConfig(): void { + this.setCursorBlink(this.preferences.get('terminal.integrated.cursorBlinking')); + this.setCursorStyle(this.preferences.get('terminal.integrated.cursorStyle')); + this.setCursorWidth(this.preferences.get('terminal.integrated.cursorWidth')); + this.term.options.fontFamily = this.preferences.get('terminal.integrated.fontFamily'); + this.term.options.fontSize = this.preferences.get('terminal.integrated.fontSize'); + this.term.options.fontWeight = this.preferences.get('terminal.integrated.fontWeight'); + this.term.options.fontWeightBold = this.preferences.get('terminal.integrated.fontWeightBold'); + this.term.options.drawBoldTextInBrightColors = this.preferences.get('terminal.integrated.drawBoldTextInBrightColors'); + this.term.options.letterSpacing = this.preferences.get('terminal.integrated.letterSpacing'); + this.term.options.lineHeight = this.preferences.get('terminal.integrated.lineHeight'); + this.term.options.scrollback = this.preferences.get('terminal.integrated.scrollback'); + this.term.options.fastScrollSensitivity = this.preferences.get('terminal.integrated.fastScrollSensitivity'); } - /** - * Returns given renderer type if it is valid and supported or default renderer otherwise. - * - * @param terminalRendererType desired terminal renderer type - */ - private getTerminalRendererType(terminalRendererType?: string | TerminalRendererType): RendererType { - if (terminalRendererType && isTerminalRendererType(terminalRendererType)) { - return terminalRendererType; + private setCursorBlink(blink: boolean): void { + if (this.term.options.cursorBlink !== blink) { + this.term.options.cursorBlink = blink; + this.term.refresh(0, this.term.rows - 1); + } + } + + private setCursorStyle(style: 'block' | 'underline' | 'bar' | 'line'): void { + if (this.term.options.cursorStyle !== style) { + this.term.options.cursorStyle = (style === 'line') ? 'bar' : style; + } + } + + private setCursorWidth(width: number): void { + if (this.term.options.cursorWidth !== width) { + this.term.options.cursorWidth = width; } - return DEFAULT_TERMINAL_RENDERER_TYPE; } protected initializeLinkHover(): void { @@ -670,16 +660,34 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget } this.term.open(this.node); + interface ViewportType { + register(d: Disposable): void; + _refreshAnimationFrame: number | null; + _coreBrowserService: { + window: Window; + } + } + + // Workaround for https://github.com/xtermjs/xterm.js/issues/4775. Can be removed for releases > 5.3.0 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const viewPort: ViewportType = (this.term as any)._core.viewport; + viewPort.register(Disposable.create(() => { + if (typeof viewPort._refreshAnimationFrame === 'number') { + viewPort._coreBrowserService.window.cancelAnimationFrame(viewPort._refreshAnimationFrame); + } + })); + if (isFirefox) { // monkey patching intersection observer handling for secondary window support // eslint-disable-next-line @typescript-eslint/no-explicit-any const renderService: any = (this.term as any)._core._renderService; - const originalFunc: (entry: IntersectionObserverEntry) => void = renderService._onIntersectionChange.bind(renderService); + + const originalFunc: (entry: IntersectionObserverEntry) => void = renderService._handleIntersectionChange.bind(renderService); const replacement = function (entry: IntersectionObserverEntry): void { if (entry.target.ownerDocument !== document) { // in Firefox, the intersection observer always reports the widget as non-intersecting if the dom element // is in a different document from when the IntersectionObserver started observing. Since we know - // that the widget is always "visible" when in a secondary window, so we mark the entry as "intersecting" + // that the widget is always "visible" when in a secondary window, so we refresh the rows ourselves const patchedEvent: IntersectionObserverEntry = { ...entry, isIntersecting: true, @@ -690,7 +698,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget } }; - renderService._onIntersectionChange = replacement; + renderService._handleIntersectionChange = replacement.bind(renderService); } if (this.initialData) { @@ -698,13 +706,6 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget } this.termOpened = true; this.initialData = ''; - - if (isFirefox) { - // The software scrollbars don't work with xterm.js, so we disable the scrollbar if we are on firefox. - if (this.term.element) { - (this.term.element.children.item(0) as HTMLElement).style.overflow = 'hidden'; - } - } } write(data: string): void { @@ -792,11 +793,13 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget return; } const geo = this.fitAddon.proposeDimensions(); - const cols = geo.cols; - const rows = geo.rows - 1; // subtract one row for margin - this.term.resize(cols, rows); + if (geo) { + const cols = geo.cols; + const rows = geo.rows - 1; // subtract one row for margin + this.term.resize(cols, rows); - this.resizeTerminalProcess(); + this.resizeTerminalProcess(); + } } protected resizeTerminalProcess(): void { diff --git a/yarn.lock b/yarn.lock index 011b396fd23e3..43bec804e016e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12134,16 +12134,26 @@ xterm-addon-fit@^0.5.0: resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596" integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ== -xterm-addon-search@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.2.tgz#be7aa74d5ff12c901707c6ff674229f214318032" - integrity sha512-I1863mjn8P6uVrqm/X+btalVsqjAKLhnhpbP7SavAOpEkI1jJhbHU2UTp7NjeRtcKTks6UWk/ycgds5snDSejg== +xterm-addon-fit@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz#48ca99015385141918f955ca7819e85f3691d35f" + integrity sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw== + +xterm-addon-search@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.13.0.tgz#21286f4db48aa949fbefce34bb8bc0c9d3cec627" + integrity sha512-sDUwG4CnqxUjSEFh676DlS3gsh3XYCzAvBPSvJ5OPgF3MRL3iHLPfsb06doRicLC2xXNpeG2cWk8x1qpESWJMA== xterm@^4.16.0: version "4.19.0" resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0.tgz#c0f9d09cd61de1d658f43ca75f992197add9ef6d" integrity sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ== +xterm@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" + integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" From f332cadcb5f05ddeabe5d87a3abc76da73ffdd1f Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 14 Feb 2024 23:34:05 +0100 Subject: [PATCH 088/441] Add GitHub action for next publishing (#13385) --- .github/workflows/publish-next.yml | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/publish-next.yml diff --git a/.github/workflows/publish-next.yml b/.github/workflows/publish-next.yml new file mode 100644 index 0000000000000..edb8a987e32cf --- /dev/null +++ b/.github/workflows/publish-next.yml @@ -0,0 +1,39 @@ +name: Publish Next + +on: workflow_dispatch + +jobs: + publish: + name: Perform Publishing + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + registry-url: 'https://registry.npmjs.org' + + - name: Use Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install + shell: bash + run: | + yarn global add node-gyp + yarn --skip-integrity-check --network-timeout 100000 + env: + NODE_OPTIONS: --max_old_space_size=4096 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # https://github.com/microsoft/vscode-ripgrep/issues/9 + + - name: Publish NPM + shell: bash + run: | + yarn publish:next + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} From e53924d4f544a3cd7301b3f10b256fc6e55af644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 15 Feb 2024 11:12:16 +0100 Subject: [PATCH 089/441] Upgrade Monaco dependency to 1.83.1 (#13217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #12679 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 11 +- .../src/generator/frontend-generator.ts | 10 +- dev-packages/cli/src/test-page.ts | 3 +- examples/api-samples/package.json | 4 +- examples/api-tests/src/monaco-api.spec.js | 60 +- examples/api-tests/src/typescript.spec.js | 79 +- packages/bulk-edit/package.json | 4 +- .../bulk-edit-tree/bulk-edit-tree.spec.ts | 2 +- packages/console/package.json | 4 +- .../console/src/browser/console-widget.ts | 1 - .../core/src/browser/context-key-service.ts | 3 +- .../core/src/browser/context-menu-renderer.ts | 4 - packages/core/src/browser/icon-registry.ts | 11 +- .../markdown-rendering/markdown-renderer.ts | 2 +- packages/core/src/browser/style/index.css | 301 +++---- .../markdown-rendering/markdown-string.ts | 9 +- .../core/src/common/quick-pick-service.ts | 42 +- packages/core/src/common/theme.ts | 32 + packages/core/src/common/uri.ts | 6 +- packages/debug/package.json | 4 +- .../src/browser/debug-frontend-module.ts | 5 +- .../src/browser/debug-session-manager.ts | 2 +- .../disassembly-view-instruction-renderer.ts | 16 +- .../disassembly-view-widget.ts | 2 +- .../src/browser/editor/debug-editor-model.ts | 7 +- .../editor/debug-inline-value-decorator.ts | 8 +- packages/editor/src/browser/diff-navigator.ts | 1 - .../editor-generated-preference-schema.ts | 734 +++++++++++------- .../browser/editor-linenumber-contribution.ts | 3 +- .../src/browser/file-upload-service.ts | 10 +- packages/git/package.json | 4 +- packages/keymaps/package.json | 4 +- packages/monaco/package.json | 4 +- .../monaco-markdown-renderer.ts | 28 +- .../src/browser/monaco-bulk-edit-service.ts | 4 +- .../src/browser/monaco-command-registry.ts | 5 +- .../src/browser/monaco-command-service.ts | 41 +- packages/monaco/src/browser/monaco-command.ts | 20 +- .../src/browser/monaco-context-key-service.ts | 13 +- .../monaco/src/browser/monaco-context-menu.ts | 16 +- .../monaco/src/browser/monaco-diff-editor.ts | 17 +- .../browser/monaco-diff-navigator-factory.ts | 34 +- .../src/browser/monaco-editor-provider.ts | 72 +- .../src/browser/monaco-editor-service.ts | 15 +- packages/monaco/src/browser/monaco-editor.ts | 13 +- ...onaco-frontend-application-contribution.ts | 35 - .../src/browser/monaco-frontend-module.ts | 42 +- .../src/browser/monaco-icon-registry.ts | 10 +- packages/monaco/src/browser/monaco-init.ts | 114 +++ .../monaco/src/browser/monaco-keybinding.ts | 6 +- .../src/browser/monaco-quick-input-service.ts | 187 ++--- .../src/browser/monaco-resolved-keybinding.ts | 46 +- .../src/browser/monaco-text-model-service.ts | 2 +- .../monaco/src/browser/monaco-workspace.ts | 20 +- .../src/browser/simple-monaco-editor.ts | 2 +- .../textmate/monaco-textmate-service.ts | 2 +- .../service/notebook-cell-context-manager.ts | 1 - packages/output/package.json | 4 +- .../src/browser/output-editor-factory.ts | 12 +- packages/plugin-ext-vscode/package.json | 4 +- packages/plugin-ext/package.json | 4 +- .../src/common/plugin-api-rpc-model.ts | 2 +- .../plugin-ext/src/common/plugin-api-rpc.ts | 45 +- .../src/hosted/browser/hosted-plugin.ts | 4 +- .../browser/comments/comments-contribution.ts | 10 +- .../data-transfer-type-converters.ts | 10 +- .../src/main/browser/languages-main.ts | 14 +- .../src/main/browser/main-context.ts | 4 +- .../menus/menus-contribution-handler.ts | 2 +- .../browser/plugin-contribution-handler.ts | 2 +- .../src/main/browser/plugin-icon-service.ts | 80 +- .../src/main/browser/quick-open-main.ts | 105 ++- .../plugin-ext/src/main/browser/scm-main.ts | 2 +- .../src/main/browser/text-editors-main.ts | 14 +- .../src/main/browser/theme-icon-override.ts | 32 +- .../plugin-tree-view-node-label-provider.ts | 2 +- .../main/browser/view/plugin-view-registry.ts | 2 +- .../main/browser/view/plugin-view-widget.ts | 1 - .../main/browser/view/tree-view-widget.tsx | 5 +- .../plugin-ext/src/plugin/known-commands.ts | 1 + .../plugin-ext/src/plugin/markdown-string.ts | 6 +- packages/plugin-ext/src/plugin/quick-open.ts | 34 +- .../plugin-ext/src/plugin/terminal-ext.ts | 2 +- .../plugin-ext/src/plugin/type-converters.ts | 51 +- packages/plugin-metrics/package.json | 4 +- packages/plugin/src/theia.d.ts | 8 +- packages/preferences/package.json | 4 +- packages/task/package.json | 4 +- packages/toolbar/package.json | 4 +- yarn.lock | 8 +- 90 files changed, 1408 insertions(+), 1215 deletions(-) create mode 100644 packages/monaco/src/browser/monaco-init.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d25f0597802e..fe038a8dfe136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,18 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) - +- [monaco] Upgrade Monaco dependency to 1.83.1 [#13217](https://github.com/eclipse-theia/theia/pull/13217)- contributed on behalf of STMicroelectronics\ + There are a couple of breaking changes that come with this monaco update + - Moved `ThemaIcon` and `ThemeColor` to the common folder + - Minor typing adjustments in QuickPickService: in parti + - FileUploadService: moved id field from data transfer item to the corresponding file info + - The way we instantiate monaco services has changed completely: if you touch monaco services in your code, please read the description in the + file comment in `monaco-init.ts`. ## v1.46.0 - 01/25/2024 - [plugin] Add prefix to contributed view container ids [#13362](https://github.com/eclipse-theia/theia/pull/13362) - contributed on behalf of STMicroelectronics diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index 672b11cbcb43e..ee304fec409a7 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -108,7 +108,7 @@ ${Array.from(frontendPreloadModules.values(), jsModulePath => `\ } module.exports = (async () => { - const { messagingFrontendModule } = require('@theia/core/lib/${!this.pck.isElectron() + const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowser() ? 'browser/messaging/messaging-frontend-module' : 'electron-browser/messaging/electron-messaging-frontend-module'}'); const container = new Container(); @@ -117,6 +117,11 @@ module.exports = (async () => { container.load(messagingFrontendOnlyModule);`)} await preload(container); + + ${this.ifMonaco(() => ` + const { MonacoInit } = require('@theia/monaco/lib/browser/monaco-init'); + `)}; + const { FrontendApplication } = require('@theia/core/lib/browser'); const { frontendApplicationModule } = require('@theia/core/lib/browser/frontend-application-module'); const { loggerFrontendModule } = require('@theia/core/lib/browser/logger-frontend-module'); @@ -132,6 +137,9 @@ module.exports = (async () => { try { ${Array.from(frontendModules.values(), jsModulePath => `\ await load(container, ${this.importOrRequire()}('${jsModulePath}'));`).join(EOL)} + ${this.ifMonaco(() => ` + MonacoInit.init(container); + `)}; await start(); } catch (reason) { console.error('Failed to start the frontend application.'); diff --git a/dev-packages/cli/src/test-page.ts b/dev-packages/cli/src/test-page.ts index fe158c48f4882..95d5ba7df5d0e 100644 --- a/dev-packages/cli/src/test-page.ts +++ b/dev-packages/cli/src/test-page.ts @@ -113,7 +113,8 @@ export default async function newTestPage(options: TestPageOptions): Promise { - const simpleKeybinding = new SimpleKeybinding(true, true, true, true, 41 /* KeyCode.KeyK */); - const chordKeybinding = simpleKeybinding.toChord(); - assert.equal(chordKeybinding.parts.length, 1); - assert.equal(chordKeybinding.parts[0], simpleKeybinding); + const chord = new KeyCodeChord(true, true, true, true, 41 /* KeyCode.KeyK */); + const chordKeybinding = chord.toKeybinding(); + assert.equal(chordKeybinding.chords.length, 1); + assert.equal(chordKeybinding.chords[0], chord); const resolvedKeybindings = StandaloneServices.get(IKeybindingService).resolveKeybinding(chordKeybinding); assert.equal(resolvedKeybindings.length, 1); @@ -74,9 +74,8 @@ describe('Monaco API', async function () { const electronAccelerator = resolvedKeybinding.getElectronAccelerator(); const userSettingsLabel = resolvedKeybinding.getUserSettingsLabel(); const WYSIWYG = resolvedKeybinding.isWYSIWYG(); - const chord = resolvedKeybinding.isChord(); - const parts = resolvedKeybinding.getParts(); - const dispatchParts = resolvedKeybinding.getDispatchParts(); + const parts = resolvedKeybinding.getChords(); + const dispatchParts = resolvedKeybinding.getDispatchChords().map(str => str === null ? '' : str); const platform = window.navigator.platform; let expected; @@ -89,14 +88,14 @@ describe('Monaco API', async function () { userSettingsLabel: 'ctrl+shift+alt+cmd+K', WYSIWYG: true, chord: false, - parts: [{ - altKey: true, - ctrlKey: true, - keyAriaLabel: 'K', - keyLabel: 'K', - metaKey: true, - shiftKey: true - }], + parts: [new ResolvedChord( + true, + true, + true, + true, + 'K', + 'K', + )], dispatchParts: [ 'ctrl+shift+alt+meta+K' ] @@ -108,15 +107,14 @@ describe('Monaco API', async function () { electronAccelerator: 'Ctrl+Shift+Alt+K', userSettingsLabel: 'ctrl+shift+alt+K', WYSIWYG: true, - chord: false, - parts: [{ - altKey: true, - ctrlKey: true, - keyAriaLabel: 'K', - keyLabel: 'K', - metaKey: false, - shiftKey: true - }], + parts: [new ResolvedChord( + true, + true, + true, + false, + 'K', + 'K' + )], dispatchParts: [ 'ctrl+shift+alt+K' ] @@ -124,7 +122,7 @@ describe('Monaco API', async function () { } assert.deepStrictEqual({ - label, ariaLabel, electronAccelerator, userSettingsLabel, WYSIWYG, chord, parts, dispatchParts + label, ariaLabel, electronAccelerator, userSettingsLabel, WYSIWYG, parts, dispatchParts }, expected); } else { assert.fail(`resolvedKeybinding must be of ${MonacoResolvedKeybinding.name} type`); @@ -136,7 +134,7 @@ describe('Monaco API', async function () { const didChangeColorMap = new Promise(resolve => { const toDispose = TokenizationRegistry.onDidChange(() => { toDispose.dispose(); - resolve(); + resolve(undefined); }); }); textmateService['themeService'].setCurrentTheme('light'); @@ -175,15 +173,15 @@ describe('Monaco API', async function () { }); it('Supports setting contexts using the command registry', async () => { - const setContext = 'setContext'; + const setContext = '_setContext'; const key = 'monaco-api-test-context'; const firstValue = 'first setting'; const secondValue = 'second setting'; - assert.isFalse(contextKeys.match(`${key} == ${firstValue}`)); + assert.isFalse(contextKeys.match(`${key} == '${firstValue}'`)); await commands.executeCommand(setContext, key, firstValue); - assert.isTrue(contextKeys.match(`${key} == ${firstValue}`)); + assert.isTrue(contextKeys.match(`${key} == '${firstValue}'`)); await commands.executeCommand(setContext, key, secondValue); - assert.isTrue(contextKeys.match(`${key} == ${secondValue}`)); + assert.isTrue(contextKeys.match(`${key} == '${secondValue}'`)); }); it('Supports context key: inQuickOpen', async () => { diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index c399bdbda5f73..4b2cda473bf99 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -119,19 +119,28 @@ describe('TypeScript', function () { */ function waitForAnimation(condition, timeout, message) { const success = new Promise(async (resolve, reject) => { + if (timeout === undefined) { + timeout = 100000; + } + + let timedOut = false; + const handle = setTimeout(() => { + console.log(message); + timedOut = true; + }, timeout); + toTearDown.push({ dispose: () => reject(message ?? 'Test terminated before resolution.') }); do { await animationFrame(); - } while (!condition()); - resolve(); + } while (!timedOut && !condition()); + if (timedOut) { + reject(new Error(message ?? 'Wait for animation timed out.')); + } else { + clearTimeout(handle); + resolve(undefined); + } + }); - if (timeout !== undefined) { - const timedOut = new Promise((_, fail) => { - const toClear = setTimeout(() => fail(new Error(message ?? 'Wait for animation timed out.')), timeout); - toTearDown.push({ dispose: () => (fail(new Error(message ?? 'Wait for animation timed out.')), clearTimeout(toClear)) }); - }); - return Promise.race([success, timedOut]); - } return success; } @@ -689,11 +698,10 @@ SPAN { editor.getControl().revealPosition({ lineNumber, column }); assert.equal(currentChar(), ';', 'Failed at assert 1'); - /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */ + /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionController').CodeActionController} */ const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController'); const lightBulbNode = () => { - const ui = codeActionController['_ui'].rawValue; - const lightBulb = ui && ui['_lightBulbWidget'].rawValue; + const lightBulb = codeActionController['_lightBulbWidget'].rawValue; return lightBulb && lightBulb['_domNode']; }; const lightBulbVisible = () => { @@ -703,14 +711,14 @@ SPAN { await timeout(1000); // quick fix is always available: need to wait for the error fix to become available. await commands.executeCommand('editor.action.quickFix'); - const codeActionSelector = '.codeActionWidget'; + const codeActionSelector = '.action-widget'; assert.isFalse(!!document.querySelector(codeActionSelector), 'Failed at assert 3 - codeActionWidget should not be visible'); console.log('Waiting for Quick Fix widget to be visible'); await waitForAnimation(() => { const quickFixWidgetVisible = !!document.querySelector(codeActionSelector); if (!quickFixWidgetVisible) { - console.log('...'); + // console.log('...'); return false; } return true; @@ -768,27 +776,13 @@ SPAN { assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength); }); - for (const referenceViewCommand of ['references-view.find', 'references-view.findImplementations']) { - it(referenceViewCommand, async function () { - let steps = 0; - const editor = await openEditor(demoFileUri); - editor.getControl().setPosition({ lineNumber: 24, column: 11 }); - assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance'); - await commands.executeCommand(referenceViewCommand); - const view = await pluginViewRegistry.openView('references-view.tree', { reveal: true }); - const expectedMessage = referenceViewCommand === 'references-view.find' ? '2 results in 1 file' : '1 result in 1 file'; - const getResultText = () => view.node.getElementsByClassName('theia-TreeViewInfo').item(0)?.textContent; - await waitForAnimation(() => getResultText() === expectedMessage, 5000); - assert.equal(getResultText(), expectedMessage); - }); - } it('Can execute code actions', async function () { const editor = await openEditor(demoFileUri); - /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */ + /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionController').CodeActionController} */ const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController'); const isActionAvailable = () => { - const lightbulbVisibility = codeActionController['_ui'].rawValue?.['_lightBulbWidget'].rawValue?.['_domNode'].style.visibility; + const lightbulbVisibility = codeActionController['_lightBulbWidget'].rawValue?.['_domNode'].style.visibility; return lightbulbVisibility !== undefined && lightbulbVisibility !== 'hidden'; } assert.isFalse(isActionAvailable()); @@ -798,8 +792,16 @@ SPAN { await waitForAnimation(() => isActionAvailable(), 5000, 'No code action available. (1)'); assert.isTrue(isActionAvailable()); + try { // for some reason, we need to wait a second here, otherwise, we run into some cancellation. + await waitForAnimation(() => false, 1000); + } catch (e) { + } + await commands.executeCommand('editor.action.quickFix'); - await waitForAnimation(() => Boolean(document.querySelector('.context-view-pointerBlock')), 5000, 'No context menu appeared. (1)'); + await waitForAnimation(() => { + const elements = document.querySelector('.action-widget'); + return !!elements; + }, 5000, 'No context menu appeared. (1)'); await animationFrame(); keybindings.dispatchKeyDown('Enter'); @@ -841,4 +843,19 @@ SPAN { assert.isNotNull(editor.getControl().getModel()); await waitForAnimation(() => editor.getControl().getModel().getLineContent(30) === 'import { DefinedInterface } from "./demo-definitions-file";', 5000, 'The named import did not take effect.'); }); + + for (const referenceViewCommand of ['references-view.find', 'references-view.findImplementations']) { + it(referenceViewCommand, async function () { + let steps = 0; + const editor = await openEditor(demoFileUri); + editor.getControl().setPosition({ lineNumber: 24, column: 11 }); + assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance'); + await commands.executeCommand(referenceViewCommand); + const view = await pluginViewRegistry.openView('references-view.tree', { reveal: true }); + const expectedMessage = referenceViewCommand === 'references-view.find' ? '2 results in 1 file' : '1 result in 1 file'; + const getResultText = () => view.node.getElementsByClassName('theia-TreeViewInfo').item(0)?.textContent; + await waitForAnimation(() => getResultText() === expectedMessage, 5000); + assert.equal(getResultText(), expectedMessage); + }); + } }); diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index e3fd0d5e14400..19fd04253ff7b 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -7,7 +7,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/workspace": "1.46.0" }, "publishConfig": { @@ -48,4 +48,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree.spec.ts b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree.spec.ts index 49f38561ec75c..3f9e315579e7f 100644 --- a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree.spec.ts +++ b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-tree.spec.ts @@ -17,7 +17,7 @@ import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; import * as chai from 'chai'; import { ResourceTextEdit } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; -import { URI as Uri } from 'vscode-uri'; +import { URI as Uri } from '@theia/core/shared/vscode-uri'; let disableJSDOM = enableJSDOM(); diff --git a/packages/console/package.json b/packages/console/package.json index ab7c5abd02a1c..4105ba64b8c84 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -5,7 +5,7 @@ "dependencies": { "@theia/core": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1" }, "publishConfig": { @@ -46,4 +46,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/console/src/browser/console-widget.ts b/packages/console/src/browser/console-widget.ts index 0f6065fefde09..8948c9b57141b 100644 --- a/packages/console/src/browser/console-widget.ts +++ b/packages/console/src/browser/console-widget.ts @@ -139,7 +139,6 @@ export class ConsoleWidget extends BaseWidget implements StatefulWidget { input.getControl().createContextKey('consoleInputFocus', true); const contentContext = this.contextKeyService.createScoped(this.content.node); contentContext.setContext('consoleContentFocus', true); - this.toDispose.push(contentContext); } protected createInput(node: HTMLElement): Promise { diff --git a/packages/core/src/browser/context-key-service.ts b/packages/core/src/browser/context-key-service.ts index 9ed540beb5394..0bb30ad313df4 100644 --- a/packages/core/src/browser/context-key-service.ts +++ b/packages/core/src/browser/context-key-service.ts @@ -15,7 +15,6 @@ // ***************************************************************************** import { injectable } from 'inversify'; -import { Disposable } from '../common'; import { Emitter, Event } from '../common/event'; export type ContextKeyValue = null | undefined | boolean | number | string @@ -43,7 +42,7 @@ export interface ContextKeyChangeEvent { export const ContextKeyService = Symbol('ContextKeyService'); -export interface ContextMatcher extends Disposable { +export interface ContextMatcher { /** * Whether the expression is satisfied. If `context` provided, the service will attempt to retrieve a context object associated with that element. */ diff --git a/packages/core/src/browser/context-menu-renderer.ts b/packages/core/src/browser/context-menu-renderer.ts index e8f88586f7c35..dd9bad8463373 100644 --- a/packages/core/src/browser/context-menu-renderer.ts +++ b/packages/core/src/browser/context-menu-renderer.ts @@ -26,10 +26,6 @@ export const Coordinate = Symbol('Coordinate'); export type Anchor = MouseEvent | Coordinate; -export function toAnchor(anchor: HTMLElement | Coordinate): Anchor { - return anchor instanceof HTMLElement ? { x: anchor.offsetLeft, y: anchor.offsetTop } : anchor; -} - export function coordinateFromAnchor(anchor: Anchor): Coordinate { const { x, y } = anchor instanceof MouseEvent ? { x: anchor.clientX, y: anchor.clientY } : anchor; return { x, y }; diff --git a/packages/core/src/browser/icon-registry.ts b/packages/core/src/browser/icon-registry.ts index 90c1088012f37..f41728d13046f 100644 --- a/packages/core/src/browser/icon-registry.ts +++ b/packages/core/src/browser/icon-registry.ts @@ -19,6 +19,7 @@ *--------------------------------------------------------------------------------------------*/ // code copied and modified from https://github.com/Microsoft/vscode/blob/main/src/vs/platform/theme/common/iconRegistry.ts +import { ThemeIcon } from '../common/theme'; import { URI } from 'vscode-uri'; export interface IconDefinition { @@ -48,16 +49,6 @@ export interface IconFontSource { readonly location: URI; readonly format: string; } - -export interface ThemeIcon { - readonly id: string; - readonly color?: ThemeColor; -} - -export interface ThemeColor { - id: string; -} - export const IconRegistry = Symbol('IconRegistry'); export interface IconRegistry { /** diff --git a/packages/core/src/browser/markdown-rendering/markdown-renderer.ts b/packages/core/src/browser/markdown-rendering/markdown-renderer.ts index fa9eb5bcf031f..f3a9d93b611f4 100644 --- a/packages/core/src/browser/markdown-rendering/markdown-renderer.ts +++ b/packages/core/src/browser/markdown-rendering/markdown-renderer.ts @@ -24,7 +24,7 @@ import { codicon } from '../widgets'; // #region Copied from Copied from https://github.com/microsoft/vscode/blob/7d9b1c37f8e5ae3772782ba3b09d827eb3fdd833/src/vs/base/browser/formattedTextRenderer.ts export interface ContentActionHandler { - callback: (content: string, event?: MouseEvent) => void; + callback: (content: string, event?: MouseEvent | KeyboardEvent) => void; readonly disposables: DisposableGroup; } diff --git a/packages/core/src/browser/style/index.css b/packages/core/src/browser/style/index.css index bc3e99abe5ffe..e2ba596cb6491 100644 --- a/packages/core/src/browser/style/index.css +++ b/packages/core/src/browser/style/index.css @@ -22,18 +22,18 @@ |----------------------------------------------------------------------------*/ :root { - /* Borders: Width and color (dark to bright) */ + /* Borders: Width and color (dark to bright) */ - --theia-border-width: 1px; - --theia-panel-border-width: 1px; + --theia-border-width: 1px; + --theia-panel-border-width: 1px; - /* UI fonts: Family, size and color (bright to dark) + /* UI fonts: Family, size and color (bright to dark) --------------------------------------------------- The UI font CSS variables are used for the typography all of the Theia user interface elements that are not directly user-generated content. */ - --theia-ui-font-scale-factor: 1.2; + --theia-ui-font-scale-factor: 1.2; --theia-ui-font-size0: calc( var(--theia-ui-font-size1) / var(--theia-ui-font-scale-factor) ); @@ -45,181 +45,186 @@ var(--theia-ui-font-size2) * var(--theia-ui-font-scale-factor) ); --theia-ui-icon-font-size: 14px; /* Ensures px perfect FontAwesome icons */ - --theia-ui-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + --theia-ui-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - /* Content fonts: Family, size and color (bright to dark) + /* Content fonts: Family, size and color (bright to dark) Content font variables are used for typography of user-generated content. */ - --theia-content-font-size: 13px; - --theia-content-line-height: 22px; + --theia-content-font-size: 13px; + --theia-content-line-height: 22px; - --theia-code-font-size: 13px; - --theia-code-line-height: 17px; - --theia-code-padding: 5px; - --theia-code-font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", - "Courier New", monospace, "Droid Sans Fallback"; - --theia-monospace-font-family: monospace; - --theia-ui-padding: 6px; + --theia-code-font-size: 13px; + --theia-code-line-height: 17px; + --theia-code-padding: 5px; + --theia-code-font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", + "Courier New", monospace, "Droid Sans Fallback"; + --theia-monospace-font-family: monospace; + --theia-ui-padding: 6px; - /* Icons */ - --theia-icon-size: 16px; + /* Icons */ + --theia-icon-size: 16px; - /* Scrollbars */ - --theia-scrollbar-width: 10px; - --theia-scrollbar-rail-width: 10px; + /* Scrollbars */ + --theia-scrollbar-width: 10px; + --theia-scrollbar-rail-width: 10px; - /* Statusbar */ - --theia-statusBar-font-size: 12px; + /* Statusbar */ + --theia-statusBar-font-size: 12px; - /* Opacity for disabled mod */ - --theia-mod-disabled-opacity: 0.4; + /* Opacity for disabled mod */ + --theia-mod-disabled-opacity: 0.4; } +html, body { - margin: 0; - padding: 0; - overflow: hidden; - font-family: var(--theia-ui-font-family); - background: var(--theia-editor-background); - color: var(--theia-foreground); - border: 1px solid var(--theia-window-activeBorder); + height: 100vh; +} + +body { + margin: 0; + padding: 0; + overflow: hidden; + font-family: var(--theia-ui-font-family); + background: var(--theia-editor-background); + color: var(--theia-foreground); + border: 1px solid var(--theia-window-activeBorder); } body:window-inactive, body:-moz-window-inactive { - border-color: var(--theia-window-inactiveBorder); + border-color: var(--theia-window-inactiveBorder); } a { - color: var(--theia-textLink-foreground); + color: var(--theia-textLink-foreground); } a:active, a:hover { - color: var(--theia-textLink-activeForeground); + color: var(--theia-textLink-activeForeground); } code { - color: var(--theia-textPreformat-foreground); + color: var(--theia-textPreformat-foreground); } blockquote { - margin: 0 7px 0 5px; - padding: 0px 16px 0px 10px; - background: var(--theia-textBlockQuote-background); - border-left: 5px solid var(--theia-textBlockQuote-border); + margin: 0 7px 0 5px; + padding: 0px 16px 0px 10px; + background: var(--theia-textBlockQuote-background); + border-left: 5px solid var(--theia-textBlockQuote-border); } .theia-input { - background: var(--theia-input-background); - color: var(--theia-input-foreground); - border: var(--theia-border-width) solid var(--theia-input-border); - font-family: var(--theia-ui-font-family); - font-size: var(--theia-ui-font-size1); - line-height: var(--theia-content-line-height); - padding-left: 5px; - border-radius: 2px; + background: var(--theia-input-background); + color: var(--theia-input-foreground); + border: var(--theia-border-width) solid var(--theia-input-border); + font-family: var(--theia-ui-font-family); + font-size: var(--theia-ui-font-size1); + line-height: var(--theia-content-line-height); + padding-left: 5px; + border-radius: 2px; } .theia-input[type="text"] { - text-overflow: ellipsis; - white-space: nowrap; + text-overflow: ellipsis; + white-space: nowrap; } .theia-input::placeholder { - color: var(--theia-input-placeholderForeground); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + color: var(--theia-input-placeholderForeground); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .theia-maximized { - position: fixed !important; - top: 0 !important; - bottom: 0 !important; - left: 0 !important; - right: 0 !important; - width: auto !important; - height: auto !important; - z-index: 255 !important; - background: var(--theia-editor-background); + position: fixed !important; + top: 0 !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + width: auto !important; + height: auto !important; + z-index: 255 !important; + background: var(--theia-editor-background); } .theia-visible-menu-maximized { - top: var(--theia-private-menubar-height) !important; + top: var(--theia-private-menubar-height) !important; } .theia-ApplicationShell { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: var(--theia-editor-background); + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: var(--theia-editor-background); } .theia-preload { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 50000; - background: var(--theia-editor-background); - background-size: 60px 60px; - background-repeat: no-repeat; - background-attachment: fixed; - background-position: center; - transition: opacity 0.8s; - display: flex; - justify-content: center; - align-items: center; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 50000; + background: var(--theia-editor-background); + background-size: 60px 60px; + background-repeat: no-repeat; + background-attachment: fixed; + background-position: center; + transition: opacity 0.8s; + display: flex; + justify-content: center; + align-items: center; } .theia-preload::after { - animation: 1s theia-preload-rotate infinite; + animation: 1s theia-preload-rotate infinite; color: #777; /* color works on both light and dark themes */ content: "\eb19"; /* codicon-load */ - font: normal normal normal 72px/1 codicon; + font: normal normal normal 72px/1 codicon; } @keyframes theia-preload-rotate { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } } .theia-preload.theia-hidden { - opacity: 0; + opacity: 0; } .theia-icon { - width: 32px; - height: 18px; - margin: 5px; - margin-left: 8px; + width: 32px; + height: 18px; + margin: 5px; + margin-left: 8px; } .theia-mod-disabled, .theia-mod-disabled:focus { - opacity: var(--theia-mod-disabled-opacity) !important; + opacity: var(--theia-mod-disabled-opacity) !important; } .theia-header { - text-transform: uppercase; - font-size: var(--theia-ui-font-size0); - font-weight: 700; + text-transform: uppercase; + font-size: var(--theia-ui-font-size0); + font-weight: 700; } .p-Widget { - font-size: var(--theia-ui-font-size1); + font-size: var(--theia-ui-font-size1); } .p-Widget.p-mod-hidden { - display: none !important; + display: none !important; } .noselect, @@ -231,91 +236,91 @@ blockquote { -ms-user-select: none; /* Internet Explorer/Edge */ user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */ - -o-user-select: none; + -o-user-select: none; } :focus { - outline-width: 1px; - outline-style: solid; - outline-offset: -1px; - opacity: 1; - outline-color: var(--theia-focusBorder); + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + opacity: 1; + outline-color: var(--theia-focusBorder); } ::selection { - background: var(--theia-selection-background); + background: var(--theia-selection-background); } .action-label { - padding: 2px; - border-radius: 5px; - cursor: pointer; + padding: 2px; + border-radius: 5px; + cursor: pointer; } .action-label:hover { - background-color: var(--theia-toolbar-hoverBackground); + background-color: var(--theia-toolbar-hoverBackground); } .theia-button { - border: none; - color: var(--theia-button-foreground); - min-width: 65px; - outline: none; - cursor: pointer; - padding: 4px 9px; - margin-left: calc(var(--theia-ui-padding) * 2); - border-radius: 2px; + border: none; + color: var(--theia-button-foreground); + min-width: 65px; + outline: none; + cursor: pointer; + padding: 4px 9px; + margin-left: calc(var(--theia-ui-padding) * 2); + border-radius: 2px; } .theia-button:focus { - outline: 1px solid var(--theia-focusBorder); - outline-offset: 1px; + outline: 1px solid var(--theia-focusBorder); + outline-offset: 1px; } .theia-button.secondary { - color: var(--theia-secondaryButton-foreground); + color: var(--theia-secondaryButton-foreground); } .theia-button[disabled] { - opacity: 0.6; - color: var(--theia-button-disabledForeground); - background-color: var(--theia-button-disabledBackground); - cursor: default; + opacity: 0.6; + color: var(--theia-button-disabledForeground); + background-color: var(--theia-button-disabledBackground); + cursor: default; } button.secondary[disabled], .theia-button.secondary[disabled] { - color: var(--theia-secondaryButton-disabledForeground); - background-color: var(--theia-secondaryButton-disabledBackground); + color: var(--theia-secondaryButton-disabledForeground); + background-color: var(--theia-secondaryButton-disabledBackground); } .theia-select { - color: var(--dropdown-foreground); - font-size: var(--theia-ui-font-size1); - border-radius: 2px; - border: 1px solid var(--theia-dropdown-border); - background: var(--theia-dropdown-background); - outline: none; - cursor: pointer; + color: var(--dropdown-foreground); + font-size: var(--theia-ui-font-size1); + border-radius: 2px; + border: 1px solid var(--theia-dropdown-border); + background: var(--theia-dropdown-background); + outline: none; + cursor: pointer; } .theia-select option { - background: var(--theia-dropdown-listBackground); + background: var(--theia-dropdown-listBackground); } .theia-transparent-overlay { - background-color: transparent; - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - z-index: 999; + background-color: transparent; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 999; } .theia-cursor-no-drop, .theia-cursor-no-drop:active { - cursor: no-drop; + cursor: no-drop; } /*----------------------------------------------------------------------------- diff --git a/packages/core/src/common/markdown-rendering/markdown-string.ts b/packages/core/src/common/markdown-rendering/markdown-string.ts index 9f857181685a1..19d93e94132a1 100644 --- a/packages/core/src/common/markdown-rendering/markdown-string.ts +++ b/packages/core/src/common/markdown-rendering/markdown-string.ts @@ -19,9 +19,13 @@ import { UriComponents } from '../uri'; import { escapeIcons } from './icon-utilities'; import { isObject, isString } from '../types'; +export interface MarkdownStringTrustedOptions { + readonly enabledCommands: readonly string[]; +} + export interface MarkdownString { readonly value: string; - readonly isTrusted?: boolean; + readonly isTrusted?: boolean | MarkdownStringTrustedOptions; readonly supportThemeIcons?: boolean; readonly supportHtml?: boolean; readonly baseUri?: UriComponents; @@ -45,9 +49,8 @@ export namespace MarkdownString { // Copied from https://github.com/microsoft/vscode/blob/7d9b1c37f8e5ae3772782ba3b09d827eb3fdd833/src/vs/base/common/htmlContent.ts export class MarkdownStringImpl implements MarkdownString { - public value: string; - public isTrusted?: boolean; + public isTrusted?: boolean | MarkdownStringTrustedOptions; public supportThemeIcons?: boolean; public supportHtml?: boolean; public baseUri?: UriComponents; diff --git a/packages/core/src/common/quick-pick-service.ts b/packages/core/src/common/quick-pick-service.ts index ef832dbd6611f..935b6a3c80963 100644 --- a/packages/core/src/common/quick-pick-service.ts +++ b/packages/core/src/common/quick-pick-service.ts @@ -14,7 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import URI from './uri'; import * as fuzzy from 'fuzzy'; import { Event } from './event'; import { KeySequence } from './keys'; @@ -54,11 +53,11 @@ export interface QuickPickItem { description?: string; detail?: string; keySequence?: KeySequence; - iconPath?: URI | Uri | { light?: URI | Uri; dark: URI | Uri } | { id: string }; + iconPath?: { light?: Uri; dark: Uri }; iconClasses?: string[]; alwaysShow?: boolean; highlights?: QuickPickItemHighlights; - buttons?: readonly QuickInputButton[]; + buttons?: QuickInputButton[]; execute?: () => void; } @@ -95,7 +94,7 @@ export interface QuickPickValue extends QuickPickItem { } export interface QuickInputButton { - iconPath?: URI | Uri | { light?: URI | Uri; dark: URI | Uri } | { id: string }; + iconPath?: { light?: Uri; dark: Uri }; iconClass?: string; tooltip?: string; /** @@ -104,32 +103,6 @@ export interface QuickInputButton { alwaysVisible?: boolean; } -export interface NormalizedQuickInputButton extends QuickInputButton { - iconPath?: { light?: Uri, dark: Uri }; -} - -export namespace QuickInputButton { - export function normalize(button: undefined): undefined; - export function normalize(button: QuickInputButton): NormalizedQuickInputButton; - export function normalize(button?: QuickInputButton): NormalizedQuickInputButton | undefined { - if (!button) { - return button; - } - let iconPath: NormalizedQuickInputButton['iconPath'] = undefined; - if (button.iconPath instanceof URI) { - iconPath = { dark: button.iconPath['codeUri'] }; - } else if (button.iconPath && 'dark' in button.iconPath) { - const dark = Uri.isUri(button.iconPath.dark) ? button.iconPath.dark : button.iconPath.dark['codeUri']; - const light = Uri.isUri(button.iconPath.light) ? button.iconPath.light : button.iconPath.light?.['codeUri']; - iconPath = { dark, light }; - } - return { - ...button, - iconPath, - }; - } -} - export interface QuickInputButtonHandle extends QuickInputButton { handle: number; // index of where the button is in buttons array if QuickInputButton or -1 if QuickInputButtons.Back } @@ -281,8 +254,13 @@ export interface QuickInputService { open(filter: string): void; createInputBox(): InputBox; input(options?: InputOptions, token?: CancellationToken): Promise; - pick>(picks: Promise[]> | QuickPickInput[], options?: O, token?: CancellationToken): - Promise<(O extends { canPickMany: true } ? T[] : T) | undefined>; + pick(picks: Promise[]> | QuickPickInput[], + options?: PickOptions & { canPickMany: true }, token?: CancellationToken): Promise; + pick(picks: Promise[]> | QuickPickInput[], + options?: PickOptions & { canPickMany: false }, token?: CancellationToken): Promise; + pick(picks: Promise[]> | QuickPickInput[], + options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; + showQuickPick(items: Array, options?: QuickPickOptions): Promise; hide(): void; /** diff --git a/packages/core/src/common/theme.ts b/packages/core/src/common/theme.ts index e6b5e737c8e16..8350011737611 100644 --- a/packages/core/src/common/theme.ts +++ b/packages/core/src/common/theme.ts @@ -14,6 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { URI } from 'vscode-uri'; + export type ThemeType = 'light' | 'dark' | 'hc' | 'hcLight'; export interface Theme { @@ -34,3 +36,33 @@ export interface ThemeChangeEvent { readonly newTheme: Theme; readonly oldTheme?: Theme; } + +export interface ThemeColor { + id: string; +} + +export interface ThemeIcon { + readonly id: string; + readonly color?: ThemeColor; +} + +export interface IconDefinition { + font?: IconFontContribution; // undefined for the default font (codicon) + fontCharacter: string; +} + +export interface IconFontContribution { + readonly id: string; + readonly definition: IconFontDefinition; +} + +export interface IconFontDefinition { + readonly weight?: string; + readonly style?: string; + readonly src: IconFontSource[]; +} + +export interface IconFontSource { + readonly location: URI; + readonly format: string; +} diff --git a/packages/core/src/common/uri.ts b/packages/core/src/common/uri.ts index fe24bc2e54339..5816c57cae48f 100644 --- a/packages/core/src/common/uri.ts +++ b/packages/core/src/common/uri.ts @@ -19,8 +19,10 @@ import { Path } from './path'; export class URI { - public static fromComponents(components: UriComponents): URI { - return new URI(Uri.revive(components)); + public static fromComponents(components: UriComponents): URI; + public static fromComponents(components: undefined): undefined; + public static fromComponents(components: UriComponents | undefined): URI | undefined { + return components ? new URI(Uri.revive(components)) : undefined; } public static fromFilePath(path: string): URI { diff --git a/packages/debug/package.json b/packages/debug/package.json index fb0dfc1a1b135..dde55cb6baa24 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -9,7 +9,7 @@ "@theia/filesystem": "1.46.0", "@theia/markers": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/output": "1.46.0", "@theia/process": "1.46.0", "@theia/task": "1.46.0", @@ -61,4 +61,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/debug/src/browser/debug-frontend-module.ts b/packages/debug/src/browser/debug-frontend-module.ts index d00eb179a8a9f..05a6e80031ed9 100644 --- a/packages/debug/src/browser/debug-frontend-module.ts +++ b/packages/debug/src/browser/debug-frontend-module.ts @@ -49,7 +49,6 @@ import { CommandContribution } from '@theia/core/lib/common/command'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { DebugWatchManager } from './debug-watch-manager'; -import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; import { DebugBreakpointWidget } from './editor/debug-breakpoint-widget'; import { DebugInlineValueDecorator } from './editor/debug-inline-value-decorator'; import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; @@ -61,6 +60,8 @@ import { DebugViewModel } from './view/debug-view-model'; import { DebugToolBar } from './view/debug-toolbar-widget'; import { DebugSessionWidget } from './view/debug-session-widget'; import { bindDisassemblyView } from './disassembly-view/disassembly-view-contribution'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; export default new ContainerModule((bind: interfaces.Bind) => { bindContributionProvider(bind, DebugContribution); @@ -78,7 +79,7 @@ export default new ContainerModule((bind: interfaces.Bind) => { DebugEditorModel.createModel(container, editor) )).inSingletonScope(); bind(DebugEditorService).toSelf().inSingletonScope().onActivation((context, service) => { - context.container.get(MonacoEditorService).registerDecorationType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, {}); + StandaloneServices.get(ICodeEditorService).registerDecorationType('Debug breakpoint placeholder', DebugBreakpointWidget.PLACEHOLDER_DECORATION, {}); return service; }); diff --git a/packages/debug/src/browser/debug-session-manager.ts b/packages/debug/src/browser/debug-session-manager.ts index 583a0d8af86f6..788db2c5c9009 100644 --- a/packages/debug/src/browser/debug-session-manager.ts +++ b/packages/debug/src/browser/debug-session-manager.ts @@ -179,7 +179,7 @@ export class DebugSessionManager { } isCurrentEditorFrame(uri: URI | string | monaco.Uri): boolean { - return this.currentFrame?.source?.uri.toString() === (uri instanceof URI ? uri : new URI(uri)).toString(); + return this.currentFrame?.source?.uri.toString() === (uri instanceof URI ? uri : new URI(uri.toString())).toString(); } protected async saveAll(): Promise { diff --git a/packages/debug/src/browser/disassembly-view/disassembly-view-instruction-renderer.ts b/packages/debug/src/browser/disassembly-view/disassembly-view-instruction-renderer.ts index 1fb92dfd9fd23..e43eea44d9a02 100644 --- a/packages/debug/src/browser/disassembly-view/disassembly-view-instruction-renderer.ts +++ b/packages/debug/src/browser/disassembly-view/disassembly-view-instruction-renderer.ts @@ -110,8 +110,8 @@ export class InstructionRenderer extends Disposable implements ITableRenderer= 1 && lineNumber <= textModel.getLineCount()) { const lineContent = textModel.getLineContent(lineNumber); - sourceSB.appendASCIIString(` ${lineNumber}: `); - sourceSB.appendASCIIString(lineContent + '\n'); + sourceSB.appendString(` ${lineNumber}: `); + sourceSB.appendString(lineContent + '\n'); if (instruction.endLine && lineNumber < instruction.endLine) { lineNumber++; @@ -129,27 +129,27 @@ export class InstructionRenderer extends Disposable implements ITableRenderer DebugEditorModel; @@ -94,9 +94,6 @@ export class DebugEditorModel implements Disposable { @inject(DebugInlineValueDecorator) readonly inlineValueDecorator: DebugInlineValueDecorator; - @inject(MonacoConfigurationService) - readonly configurationService: IConfigurationService; - @inject(DebugSessionManager) protected readonly sessionManager: DebugSessionManager; @@ -156,7 +153,7 @@ export class DebugEditorModel implements Disposable { resource: model.uri, overrideIdentifier: model.getLanguageId(), }; - const { enabled, delay, sticky } = this.configurationService.getValue('editor.hover', overrides); + const { enabled, delay, sticky } = StandaloneServices.get(IConfigurationService).getValue('editor.hover', overrides); codeEditor.updateOptions({ hover: { enabled, diff --git a/packages/debug/src/browser/editor/debug-inline-value-decorator.ts b/packages/debug/src/browser/editor/debug-inline-value-decorator.ts index 92c7a77ca58fa..00b27fd05eaa2 100644 --- a/packages/debug/src/browser/editor/debug-inline-value-decorator.ts +++ b/packages/debug/src/browser/editor/debug-inline-value-decorator.ts @@ -31,11 +31,11 @@ import { InlineValueContext } from '@theia/monaco-editor-core/esm/vs/editor/comm import { ITextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; -import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; import { DebugVariable, ExpressionContainer, ExpressionItem } from '../console/debug-console-items'; import { DebugPreferences } from '../debug-preferences'; import { DebugStackFrame } from '../model/debug-stack-frame'; import { DebugEditorModel } from './debug-editor-model'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; // https://github.com/theia-ide/vscode/blob/standalone/0.19.x/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts#L40-L43 export const INLINE_VALUE_DECORATION_KEY = 'inlinevaluedecoration'; @@ -59,10 +59,6 @@ class InlineSegment { @injectable() export class DebugInlineValueDecorator implements FrontendApplicationContribution { - - @inject(MonacoEditorService) - protected readonly editorService: MonacoEditorService; - @inject(DebugPreferences) protected readonly preferences: DebugPreferences; @@ -70,7 +66,7 @@ export class DebugInlineValueDecorator implements FrontendApplicationContributio protected wordToLineNumbersMap: Map | undefined = new Map(); onStart(): void { - this.editorService.registerDecorationType('Inline debug decorations', INLINE_VALUE_DECORATION_KEY, {}); + StandaloneServices.get(ICodeEditorService).registerDecorationType('Inline debug decorations', INLINE_VALUE_DECORATION_KEY, {}); this.enabled = !!this.preferences['debug.inlineValues']; this.preferences.onPreferenceChanged(({ preferenceName, newValue }) => { if (preferenceName === 'debug.inlineValues' && !!newValue !== this.enabled) { diff --git a/packages/editor/src/browser/diff-navigator.ts b/packages/editor/src/browser/diff-navigator.ts index 71679f9dea2bf..b9517831a8691 100644 --- a/packages/editor/src/browser/diff-navigator.ts +++ b/packages/editor/src/browser/diff-navigator.ts @@ -17,7 +17,6 @@ import { TextEditor } from './editor'; export interface DiffNavigator { - canNavigate(): boolean; hasNext(): boolean; hasPrevious(): boolean; next(): void; diff --git a/packages/editor/src/browser/editor-generated-preference-schema.ts b/packages/editor/src/browser/editor-generated-preference-schema.ts index 37d5749bee459..4f00af0a3071f 100644 --- a/packages/editor/src/browser/editor-generated-preference-schema.ts +++ b/packages/editor/src/browser/editor-generated-preference-schema.ts @@ -30,43 +30,54 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "type": "number", "default": 4, "minimum": 1, - "markdownDescription": nls.localizeByDefault('The number of spaces a tab is equal to. This setting is overridden based on the file contents when {0} is on.', '`#editor.detectIndentation#`'), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.tabSize", "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), + "restricted": false + }, + "editor.indentSize": { + "anyOf": [ + { + "type": "string", + "enum": [ + "tabSize" + ] + }, + { + "type": "number", + "minimum": 1 + } + ], + "default": "tabSize", + "markdownDescription": nls.localizeByDefault("The number of spaces used for indentation or `\"tabSize\"` to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), "restricted": false }, "editor.insertSpaces": { "type": "boolean", "default": true, - "markdownDescription": nls.localizeByDefault('Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when {0} is on.', `#editor.detectIndentation#`), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.insertSpaces", "Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), "restricted": false }, "editor.detectIndentation": { "type": "boolean", "default": true, - "markdownDescription": nls.localizeByDefault('Controls whether {0} and {1} will be automatically detected when a file is opened based on the file contents.', '`#editor.tabSize#`', '`#editor.insertSpaces#`'), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.detectIndentation", "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents."), "restricted": false }, "editor.trimAutoWhitespace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Remove trailing auto inserted whitespace."), - "scope": "language-overridable", "restricted": false }, "editor.largeFileOptimizations": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Special handling for large files to disable certain memory intensive features."), - "scope": "language-overridable", "restricted": false }, "editor.wordBasedSuggestions": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether completions should be computed based on words in the document."), - "scope": "language-overridable", "restricted": false }, "editor.wordBasedSuggestionsMode": { @@ -82,7 +93,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Suggest words from all open documents.") ], "description": nls.localizeByDefault("Controls from which documents word based completions are computed."), - "scope": "language-overridable", "restricted": false }, "editor.semanticHighlighting.enabled": { @@ -98,21 +108,42 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "configuredByTheme", "description": nls.localizeByDefault("Controls whether the semanticHighlighting is shown for the languages that support it."), - "scope": "language-overridable", "restricted": false }, "editor.stablePeek": { "type": "boolean", "default": false, - "markdownDescription": nls.localizeByDefault('Keep peek editors open even when double-clicking their content or when hitting `Escape`.'), - "scope": "language-overridable", + "markdownDescription": nls.localizeByDefault("Keep peek editors open even when double-clicking their content or when hitting `Escape`."), "restricted": false }, "editor.maxTokenizationLineLength": { "type": "integer", "default": 20000, "description": nls.localizeByDefault("Lines above this length will not be tokenized for performance reasons"), - "scope": "language-overridable", + "restricted": false + }, + "editor.experimental.asyncTokenization": { + "type": "boolean", + "default": false, + "description": nls.localizeByDefault("Controls whether the tokenization should happen asynchronously on a web worker."), + "tags": [ + "experimental" + ], + "restricted": false + }, + "editor.experimental.asyncTokenizationLogging": { + "type": "boolean", + "default": false, + "description": nls.localizeByDefault("Controls whether async tokenization should be logged. For debugging only."), + "restricted": false + }, + "editor.experimental.asyncTokenizationVerification": { + "type": "boolean", + "default": false, + "description": nls.localizeByDefault("Controls whether async tokenization should be verified against legacy background tokenization. Might slow down tokenization. For debugging only."), + "tags": [ + "experimental" + ], "restricted": false }, "editor.language.brackets": { @@ -135,7 +166,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] } ] }, - "scope": "language-overridable", "restricted": false }, "editor.language.colorizedBracketPairs": { @@ -158,56 +188,60 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] } ] }, - "scope": "language-overridable", "restricted": false }, "diffEditor.maxComputationTime": { "type": "number", "default": 5000, "description": nls.localizeByDefault("Timeout in milliseconds after which diff computation is cancelled. Use 0 for no timeout."), - "scope": "language-overridable", "restricted": false }, "diffEditor.maxFileSize": { "type": "number", "default": 50, "description": nls.localizeByDefault("Maximum file size in MB for which to compute diffs. Use 0 for no limit."), - "scope": "language-overridable", "restricted": false }, "diffEditor.renderSideBySide": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the diff editor shows the diff side by side or inline."), - "scope": "language-overridable", + "restricted": false + }, + "diffEditor.renderSideBySideInlineBreakpoint": { + "type": "number", + "default": 900, + "description": nls.localizeByDefault("If the diff editor width is smaller than this value, the inline view is used."), + "restricted": false + }, + "diffEditor.useInlineViewWhenSpaceIsLimited": { + "type": "boolean", + "default": true, + "description": nls.localizeByDefault("If enabled and the editor width is too small, the inline view is used."), "restricted": false }, "diffEditor.renderMarginRevertIcon": { "type": "boolean", "default": true, "description": nls.localizeByDefault("When enabled, the diff editor shows arrows in its glyph margin to revert changes."), - "scope": "language-overridable", "restricted": false }, "diffEditor.ignoreTrimWhitespace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("When enabled, the diff editor ignores changes in leading or trailing whitespace."), - "scope": "language-overridable", "restricted": false }, "diffEditor.renderIndicators": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the diff editor shows +/- indicators for added/removed changes."), - "scope": "language-overridable", "restricted": false }, "diffEditor.codeLens": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether the editor shows CodeLens."), - "scope": "language-overridable", "restricted": false }, "diffEditor.wordWrap": { @@ -221,30 +255,69 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "markdownEnumDescriptions": [ nls.localizeByDefault("Lines will never wrap."), nls.localizeByDefault("Lines will wrap at the viewport width."), - nls.localizeByDefault('Lines will wrap according to the {0} setting.', '`#editor.wordWrap#`') + nls.localize("theia/editor/diffEditor.wordWrap2", "Lines will wrap according to the `#editor.wordWrap#` setting.") ], - "scope": "language-overridable", "restricted": false }, "diffEditor.diffAlgorithm": { "type": "string", "enum": [ - "smart", - "experimental" + "legacy", + "advanced" ], - "default": "smart", + "default": "advanced", "markdownEnumDescriptions": [ nls.localizeByDefault("Uses the legacy diffing algorithm."), nls.localizeByDefault("Uses the advanced diffing algorithm.") ], - "scope": "language-overridable", + "tags": [ + "experimental" + ], + "restricted": false + }, + "diffEditor.hideUnchangedRegions.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": nls.localizeByDefault("Controls whether the diff editor shows unchanged regions."), + "restricted": false + }, + "diffEditor.hideUnchangedRegions.revealLineCount": { + "type": "integer", + "default": 20, + "markdownDescription": nls.localizeByDefault("Controls how many lines are used for unchanged regions."), + "minimum": 1, + "restricted": false + }, + "diffEditor.hideUnchangedRegions.minimumLineCount": { + "type": "integer", + "default": 3, + "markdownDescription": nls.localizeByDefault("Controls how many lines are used as a minimum for unchanged regions."), + "minimum": 1, + "restricted": false + }, + "diffEditor.hideUnchangedRegions.contextLineCount": { + "type": "integer", + "default": 3, + "markdownDescription": nls.localizeByDefault("Controls how many lines are used as context when comparing unchanged regions."), + "minimum": 1, + "restricted": false + }, + "diffEditor.experimental.showMoves": { + "type": "boolean", + "default": false, + "markdownDescription": nls.localizeByDefault("Controls whether the diff editor should show detected code moves."), + "restricted": false + }, + "diffEditor.experimental.showEmptyDecorations": { + "type": "boolean", + "default": true, + "description": nls.localizeByDefault("Controls whether the diff editor shows empty decorations to see where characters got inserted or deleted."), "restricted": false }, "editor.acceptSuggestionOnCommitCharacter": { - "markdownDescription": nls.localizeByDefault('Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.'), + "markdownDescription": nls.localizeByDefault("Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.acceptSuggestionOnEnter": { @@ -261,7 +334,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "off" ], "default": "on", - "scope": "language-overridable", "restricted": false }, "editor.accessibilitySupport": { @@ -272,22 +344,26 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "off" ], "enumDescriptions": [ - nls.localizeByDefault('Use platform APIs to detect when a Screen Reader is attached.'), - nls.localizeByDefault("Optimize for usage with a Screen Reader."), - nls.localizeByDefault("Assume a screen reader is not attached.") + nls.localize("theia/editor/editor.accessibilitySupport0", "Use platform APIs to detect when a Screen Reader is attached"), + nls.localize("theia/editor/editor.accessibilitySupport1", "Optimize for usage with a Screen Reader"), + nls.localize("theia/editor/editor.accessibilitySupport2", "Assume a screen reader is not attached") ], "default": "auto", + "tags": [ + "accessibility" + ], "description": nls.localizeByDefault("Controls if the UI should run in a mode where it is optimized for screen readers."), - "scope": "language-overridable", "restricted": false }, "editor.accessibilityPageSize": { "description": nls.localizeByDefault("Controls the number of lines in the editor that can be read out by a screen reader at once. When we detect a screen reader we automatically set the default to be 500. Warning: this has a performance implication for numbers larger than the default."), + "tags": [ + "accessibility" + ], "type": "integer", "default": 10, "minimum": 1, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.autoClosingBrackets": { @@ -306,7 +382,33 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", - "scope": "language-overridable", + "restricted": false + }, + "editor.autoClosingComments": { + "enumDescriptions": [ + "", + nls.localizeByDefault("Use language configurations to determine when to autoclose comments."), + nls.localizeByDefault("Autoclose comments only when the cursor is to the left of whitespace."), + "" + ], + "description": nls.localizeByDefault("Controls whether the editor should automatically close comments after the user adds an opening comment."), + "type": "string", + "enum": [ + "always", + "languageDefined", + "beforeWhitespace", + "never" + ], + "default": "languageDefined", + "restricted": false + }, + "editor.screenReaderAnnounceInlineSuggestion": { + "description": nls.localizeByDefault("Control whether inline suggestions are announced by a screen reader."), + "tags": [ + "accessibility" + ], + "type": "boolean", + "default": true, "restricted": false }, "editor.autoClosingDelete": { @@ -323,7 +425,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "auto", - "scope": "language-overridable", "restricted": false }, "editor.autoClosingOvertype": { @@ -340,7 +441,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "auto", - "scope": "language-overridable", "restricted": false }, "editor.autoClosingQuotes": { @@ -359,7 +459,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", - "scope": "language-overridable", "restricted": false }, "editor.autoIndent": { @@ -380,7 +479,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "full" ], "default": "full", - "scope": "language-overridable", "restricted": false }, "editor.autoSurround": { @@ -399,21 +497,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", - "scope": "language-overridable", "restricted": false }, "editor.bracketPairColorization.enabled": { "type": "boolean", "default": true, - "markdownDescription": nls.localizeByDefault('Controls whether bracket pair colorization is enabled or not. Use {0} to override the bracket highlight colors.', '`#workbench.colorCustomizations#`'), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.bracketPairColorization.enabled", "Controls whether bracket pair colorization is enabled or not. Use `#workbench.colorCustomizations#` to override the bracket highlight colors."), "restricted": false }, "editor.bracketPairColorization.independentColorPoolPerBracketType": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether each bracket type has its own independent color pool."), - "scope": "language-overridable", "restricted": false }, "editor.guides.bracketPairs": { @@ -433,7 +528,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": false, "description": nls.localizeByDefault("Controls whether bracket pair guides are enabled or not."), - "scope": "language-overridable", "restricted": false }, "editor.guides.bracketPairsHorizontal": { @@ -453,21 +547,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "active", "description": nls.localizeByDefault("Controls whether horizontal bracket pair guides are enabled or not."), - "scope": "language-overridable", "restricted": false }, "editor.guides.highlightActiveBracketPair": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the editor should highlight the active bracket pair."), - "scope": "language-overridable", "restricted": false }, "editor.guides.indentation": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the editor should render indent guides."), - "scope": "language-overridable", "restricted": false }, "editor.guides.highlightActiveIndentation": { @@ -487,21 +578,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": true, "description": nls.localizeByDefault("Controls whether the editor should highlight the active indent guide."), - "scope": "language-overridable", "restricted": false }, "editor.codeLens": { "description": nls.localizeByDefault("Controls whether the editor shows CodeLens."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.codeLensFontFamily": { "description": nls.localizeByDefault("Controls the font family for CodeLens."), "type": "string", "default": "", - "scope": "language-overridable", "restricted": false }, "editor.codeLensFontSize": { @@ -509,43 +597,45 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 100, - "markdownDescription": nls.localizeByDefault('Controls the font size in pixels for CodeLens. When set to 0, 90% of `#editor.fontSize#` is used.'), - "scope": "language-overridable", + "markdownDescription": nls.localizeByDefault("Controls the font size in pixels for CodeLens. When set to 0, 90% of `#editor.fontSize#` is used."), "restricted": false }, "editor.colorDecorators": { "description": nls.localizeByDefault("Controls whether the editor should render the inline color decorators and color picker."), "type": "boolean", "default": true, - "scope": "language-overridable", + "restricted": false + }, + "editor.colorDecoratorsLimit": { + "markdownDescription": nls.localizeByDefault("Controls the max number of color decorators that can be rendered in an editor at once."), + "type": "integer", + "default": 500, + "minimum": 1, + "maximum": 1000000, "restricted": false }, "editor.columnSelection": { "description": nls.localizeByDefault("Enable that the selection with the mouse and keys is doing column selection."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.comments.insertSpace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether a space character is inserted when commenting."), - "scope": "language-overridable", "restricted": false }, "editor.comments.ignoreEmptyLines": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls if empty lines should be ignored with toggle, add or remove actions for line comments."), - "scope": "language-overridable", "restricted": false }, "editor.copyWithSyntaxHighlighting": { "description": nls.localizeByDefault("Controls whether syntax highlighting should be copied into the clipboard."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.cursorBlinking": { @@ -559,14 +649,22 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "solid" ], "default": "blink", - "scope": "language-overridable", "restricted": false }, "editor.cursorSmoothCaretAnimation": { + "enumDescriptions": [ + nls.localizeByDefault("Smooth caret animation is disabled."), + nls.localizeByDefault("Smooth caret animation is enabled only when the user moves the cursor with an explicit gesture."), + nls.localizeByDefault("Smooth caret animation is always enabled.") + ], "description": nls.localizeByDefault("Controls whether the smooth caret animation should be enabled."), - "type": "boolean", - "default": false, - "scope": "language-overridable", + "type": "string", + "enum": [ + "off", + "explicit", + "on" + ], + "default": "off", "restricted": false }, "editor.cursorStyle": { @@ -581,16 +679,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "underline-thin" ], "default": "line", - "scope": "language-overridable", "restricted": false }, "editor.cursorSurroundingLines": { - "description": nls.localizeByDefault('Controls the minimal number of visible leading lines (minimum 0) and trailing lines (minimum 1) surrounding the cursor. Known as \'scrollOff\' or \'scrollOffset\' in some other editors.'), + "description": nls.localizeByDefault("Controls the minimal number of visible leading lines (minimum 0) and trailing lines (minimum 1) surrounding the cursor. Known as 'scrollOff' or 'scrollOffset' in some other editors."), "type": "integer", "default": 0, "minimum": 0, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.cursorSurroundingLinesStyle": { @@ -598,14 +694,13 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("`cursorSurroundingLines` is enforced only when triggered via the keyboard or API."), nls.localizeByDefault("`cursorSurroundingLines` is enforced always.") ], - "description": nls.localizeByDefault("Controls when `#cursorSurroundingLines#` should be enforced."), + "markdownDescription": nls.localizeByDefault("Controls when `#cursorSurroundingLines#` should be enforced."), "type": "string", "enum": [ "default", "all" ], "default": "default", - "scope": "language-overridable", "restricted": false }, "editor.cursorWidth": { @@ -614,42 +709,66 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.dragAndDrop": { "description": nls.localizeByDefault("Controls whether the editor should allow moving selections via drag and drop."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.dropIntoEditor.enabled": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor)."), - "scope": "language-overridable", + "restricted": false + }, + "editor.dropIntoEditor.showDropSelector": { + "type": "string", + "markdownDescription": nls.localizeByDefault("Controls if a widget is shown when dropping files into the editor. This widget lets you control how the file is dropped."), + "enum": [ + "afterDrop", + "never" + ], + "enumDescriptions": [ + nls.localizeByDefault("Show the drop selector widget after a file is dropped into the editor."), + nls.localizeByDefault("Never show the drop selector widget. Instead the default drop provider is always used.") + ], + "default": "afterDrop", "restricted": false }, "editor.emptySelectionClipboard": { "description": nls.localizeByDefault("Controls whether copying without a selection copies the current line."), "type": "boolean", "default": true, - "scope": "language-overridable", + "restricted": false + }, + "editor.experimentalWhitespaceRendering": { + "enumDescriptions": [ + nls.localizeByDefault("Use a new rendering method with svgs."), + nls.localizeByDefault("Use a new rendering method with font characters."), + nls.localizeByDefault("Use the stable rendering method.") + ], + "description": nls.localizeByDefault("Controls whether whitespace is rendered with a new, experimental method."), + "type": "string", + "enum": [ + "svg", + "font", + "off" + ], + "default": "svg", "restricted": false }, "editor.fastScrollSensitivity": { "markdownDescription": nls.localizeByDefault("Scrolling speed multiplier when pressing `Alt`."), "type": "number", "default": 5, - "scope": "language-overridable", "restricted": false }, "editor.find.cursorMoveOnType": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the cursor should jump to find matches while typing."), - "scope": "language-overridable", "restricted": false }, "editor.find.seedSearchStringFromSelection": { @@ -666,7 +785,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Only seed search string from the editor selection.") ], "description": nls.localizeByDefault("Controls whether the search string in the Find Widget is seeded from the editor selection."), - "scope": "language-overridable", "restricted": false }, "editor.find.autoFindInSelection": { @@ -678,33 +796,29 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "never", "enumDescriptions": [ - nls.localizeByDefault('Never turn on Find in Selection automatically (default).'), - nls.localizeByDefault('Always turn on Find in Selection automatically.'), - nls.localizeByDefault('Turn on Find in Selection automatically when multiple lines of content are selected.'), + nls.localizeByDefault("Never turn on Find in Selection automatically (default)."), + nls.localizeByDefault("Always turn on Find in Selection automatically."), + nls.localizeByDefault("Turn on Find in Selection automatically when multiple lines of content are selected.") ], - "description": nls.localizeByDefault('Controls the condition for turning on Find in Selection automatically.'), - "scope": "language-overridable", + "description": nls.localizeByDefault("Controls the condition for turning on Find in Selection automatically."), "restricted": false }, "editor.find.addExtraSpaceOnTop": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible."), - "scope": "language-overridable", "restricted": false }, "editor.find.loop": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found."), - "scope": "language-overridable", "restricted": false }, "editor.folding": { "description": nls.localizeByDefault("Controls whether the editor has code folding enabled."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.foldingStrategy": { @@ -719,21 +833,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "indentation" ], "default": "auto", - "scope": "language-overridable", "restricted": false }, "editor.foldingHighlight": { "description": nls.localizeByDefault("Controls whether the editor should highlight folded ranges."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.foldingImportsByDefault": { "description": nls.localizeByDefault("Controls whether the editor automatically collapses import ranges."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.foldingMaximumRegions": { @@ -742,21 +853,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 5000, "minimum": 10, "maximum": 65000, - "scope": "language-overridable", "restricted": false }, "editor.unfoldOnClickAfterEndOfLine": { "description": nls.localizeByDefault("Controls whether clicking on the empty content after a folded line will unfold the line."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.fontFamily": { "description": nls.localizeByDefault("Controls the font family."), "type": "string", "default": isOSX ? 'Menlo, Monaco, \'Courier New\', monospace' : isWindows ? 'Consolas, \'Courier New\', monospace' : '\'Droid Sans Mono\', \'monospace\', monospace', - "scope": "language-overridable", "restricted": false }, "editor.fontLigatures": { @@ -772,7 +880,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "description": nls.localizeByDefault("Configures font ligatures or font features. Can be either a boolean to enable/disable ligatures or a string for the value of the CSS 'font-feature-settings' property."), "default": false, - "scope": "language-overridable", "restricted": false }, "editor.fontSize": { @@ -781,7 +888,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "maximum": 100, "default": isOSX ? 12 : 14, "description": nls.localizeByDefault("Controls the font size in pixels."), - "scope": "language-overridable", "restricted": false }, "editor.fontWeight": { @@ -814,34 +920,44 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "normal", "description": nls.localizeByDefault("Controls the font weight. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."), - "scope": "language-overridable", + "restricted": false + }, + "editor.fontVariations": { + "anyOf": [ + { + "type": "boolean", + "description": nls.localizeByDefault("Enables/Disables the translation from font-weight to font-variation-settings. Change this to a string for fine-grained control of the 'font-variation-settings' CSS property.") + }, + { + "type": "string", + "description": nls.localizeByDefault("Explicit 'font-variation-settings' CSS property. A boolean can be passed instead if one only needs to translate font-weight to font-variation-settings.") + } + ], + "description": nls.localizeByDefault("Configures font variations. Can be either a boolean to enable/disable the translation from font-weight to font-variation-settings or a string for the value of the CSS 'font-variation-settings' property."), + "default": false, "restricted": false }, "editor.formatOnPaste": { "description": nls.localizeByDefault("Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.formatOnType": { "description": nls.localizeByDefault("Controls whether the editor should automatically format the line after typing."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.glyphMargin": { "description": nls.localizeByDefault("Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multiple": { "deprecationMessage": "This setting is deprecated, please use separate settings like 'editor.editor.gotoLocation.multipleDefinitions' or 'editor.editor.gotoLocation.multipleImplementations' instead.", "default": null, - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleDefinitions": { @@ -856,9 +972,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "enumDescriptions": [ nls.localizeByDefault("Show Peek view of the results (default)"), nls.localizeByDefault("Go to the primary result and show a Peek view"), - nls.localizeByDefault('Go to the primary result and enable Peek-less navigation to others') + nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleTypeDefinitions": { @@ -873,9 +988,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "enumDescriptions": [ nls.localizeByDefault("Show Peek view of the results (default)"), nls.localizeByDefault("Go to the primary result and show a Peek view"), - nls.localizeByDefault('Go to the primary result and enable Peek-less navigation to others') + nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleDeclarations": { @@ -890,9 +1004,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "enumDescriptions": [ nls.localizeByDefault("Show Peek view of the results (default)"), nls.localizeByDefault("Go to the primary result and show a Peek view"), - nls.localizeByDefault('Go to the primary result and enable Peek-less navigation to others') + nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleImplementations": { @@ -907,9 +1020,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "enumDescriptions": [ nls.localizeByDefault("Show Peek view of the results (default)"), nls.localizeByDefault("Go to the primary result and show a Peek view"), - nls.localizeByDefault('Go to the primary result and enable Peek-less navigation to others') + nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleReferences": { @@ -924,9 +1036,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "enumDescriptions": [ nls.localizeByDefault("Show Peek view of the results (default)"), nls.localizeByDefault("Go to the primary result and show a Peek view"), - nls.localizeByDefault('Go to the primary result and enable Peek-less navigation to others') + nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeDefinitionCommand": { @@ -947,7 +1058,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Definition' is the current location."), - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeTypeDefinitionCommand": { @@ -968,7 +1078,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Type Definition' is the current location."), - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeDeclarationCommand": { @@ -989,7 +1098,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Declaration' is the current location."), - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeImplementationCommand": { @@ -1010,7 +1118,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Implementation' is the current location."), - "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeReferenceCommand": { @@ -1031,21 +1138,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Reference' is the current location."), - "scope": "language-overridable", "restricted": false }, "editor.hideCursorInOverviewRuler": { "description": nls.localizeByDefault("Controls whether the cursor should be hidden in the overview ruler."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.hover.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the hover is shown."), - "scope": "language-overridable", "restricted": false }, "editor.hover.delay": { @@ -1054,49 +1158,69 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 10000, "description": nls.localizeByDefault("Controls the delay in milliseconds after which the hover is shown."), - "scope": "language-overridable", "restricted": false }, "editor.hover.sticky": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the hover should remain visible when mouse is moved over it."), - "scope": "language-overridable", + "restricted": false + }, + "editor.hover.hidingDelay": { + "type": "integer", + "minimum": 0, + "default": 300, + "description": nls.localize("theia/editor/editor.hover.hidingDelay", "Controls the delay in milliseconds after thich the hover is hidden. Requires `editor.hover.sticky` to be enabled."), "restricted": false }, "editor.hover.above": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Prefer showing hovers above the line, if there's space."), - "scope": "language-overridable", "restricted": false }, "editor.inlineSuggest.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether to automatically show inline suggestions in the editor."), - "scope": "language-overridable", + "restricted": false + }, + "editor.inlineSuggest.showToolbar": { + "type": "string", + "default": "onHover", + "enum": [ + "always", + "onHover" + ], + "enumDescriptions": [ + nls.localizeByDefault("Show the inline suggestion toolbar whenever an inline suggestion is shown."), + nls.localizeByDefault("Show the inline suggestion toolbar when hovering over an inline suggestion.") + ], + "description": nls.localizeByDefault("Controls when to show the inline suggestion toolbar."), + "restricted": false + }, + "editor.inlineSuggest.suppressSuggestions": { + "type": "boolean", + "default": false, + "description": nls.localizeByDefault("Controls how inline suggestions interact with the suggest widget. If enabled, the suggest widget is not shown automatically when inline suggestions are available."), "restricted": false }, "editor.letterSpacing": { "description": nls.localizeByDefault("Controls the letter spacing in pixels."), "type": "number", "default": 0, - "scope": "language-overridable", "restricted": false }, "editor.lightbulb.enabled": { "type": "boolean", "default": true, - "description": nls.localizeByDefault('Enables the Code Action lightbulb in the editor.'), - "scope": "language-overridable", + "description": nls.localizeByDefault("Enables the Code Action lightbulb in the editor."), "restricted": false }, "editor.lineHeight": { "markdownDescription": nls.localizeByDefault("Controls the line height. \n - Use 0 to automatically compute the line height from the font size.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values."), "type": "number", "default": 0, - "scope": "language-overridable", "restricted": false }, "editor.lineNumbers": { @@ -1115,21 +1239,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "on", "description": nls.localizeByDefault("Controls the display of line numbers."), - "scope": "language-overridable", "restricted": false }, "editor.linkedEditing": { - "description": nls.localizeByDefault('Controls whether the editor has linked editing enabled. Depending on the language, related symbols such as HTML tags, are updated while editing.'), + "description": nls.localizeByDefault("Controls whether the editor has linked editing enabled. Depending on the language, related symbols such as HTML tags, are updated while editing."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.links": { "description": nls.localizeByDefault("Controls whether the editor should detect links and make them clickable."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.matchBrackets": { @@ -1141,21 +1262,18 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "always", - "scope": "language-overridable", "restricted": false }, "editor.minimap.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the minimap is shown."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.autohide": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether the minimap is hidden automatically."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.size": { @@ -1172,7 +1290,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "proportional", "description": nls.localizeByDefault("Controls the size of the minimap."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.side": { @@ -1183,7 +1300,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "right", "description": nls.localizeByDefault("Controls the side where to render the minimap."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.showSlider": { @@ -1194,7 +1310,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "mouseover", "description": nls.localizeByDefault("Controls when the minimap slider is shown."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.scale": { @@ -1208,42 +1323,36 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] 3 ], "description": nls.localizeByDefault("Scale of content drawn in the minimap: 1, 2 or 3."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.renderCharacters": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Render the actual characters on a line as opposed to color blocks."), - "scope": "language-overridable", "restricted": false }, "editor.minimap.maxColumn": { "type": "number", "default": 120, "description": nls.localizeByDefault("Limit the width of the minimap to render at most a certain number of columns."), - "scope": "language-overridable", "restricted": false }, "editor.mouseWheelScrollSensitivity": { "markdownDescription": nls.localizeByDefault("A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events."), "type": "number", "default": 1, - "scope": "language-overridable", "restricted": false }, "editor.mouseWheelZoom": { "markdownDescription": nls.localizeByDefault("Zoom the font of the editor when using mouse wheel and holding `Ctrl`."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.multiCursorMergeOverlapping": { "description": nls.localizeByDefault("Merge multiple cursors when they are overlapping."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.multiCursorModifier": { @@ -1258,7 +1367,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "alt" ], "default": "alt", - "scope": "language-overridable", "restricted": false }, "editor.multiCursorPaste": { @@ -1273,21 +1381,26 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "full" ], "default": "spread", - "scope": "language-overridable", + "restricted": false + }, + "editor.multiCursorLimit": { + "markdownDescription": nls.localizeByDefault("Controls the max number of cursors that can be in an active editor at once."), + "type": "integer", + "default": 10000, + "minimum": 1, + "maximum": 100000, "restricted": false }, "editor.occurrencesHighlight": { "description": nls.localizeByDefault("Controls whether the editor should highlight semantic symbol occurrences."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.overviewRulerBorder": { "description": nls.localizeByDefault("Controls whether a border should be drawn around the overview ruler."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.padding.top": { @@ -1296,7 +1409,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 1000, "description": nls.localizeByDefault("Controls the amount of space between the top edge of the editor and the first line."), - "scope": "language-overridable", "restricted": false }, "editor.padding.bottom": { @@ -1305,21 +1417,38 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 1000, "description": nls.localizeByDefault("Controls the amount of space between the bottom edge of the editor and the last line."), - "scope": "language-overridable", + "restricted": false + }, + "editor.pasteAs.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": nls.localizeByDefault("Controls whether you can paste content in different ways."), + "restricted": false + }, + "editor.pasteAs.showPasteSelector": { + "type": "string", + "markdownDescription": nls.localizeByDefault("Controls if a widget is shown when pasting content in to the editor. This widget lets you control how the file is pasted."), + "enum": [ + "afterPaste", + "never" + ], + "enumDescriptions": [ + nls.localizeByDefault("Show the paste selector widget after content is pasted into the editor."), + nls.localizeByDefault("Never show the paste selector widget. Instead the default pasting behavior is always used.") + ], + "default": "afterPaste", "restricted": false }, "editor.parameterHints.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Enables a pop-up that shows parameter documentation and type information as you type."), - "scope": "language-overridable", "restricted": false }, "editor.parameterHints.cycle": { "type": "boolean", - "default": false, + "default": true, "description": nls.localizeByDefault("Controls whether the parameter hints menu cycles or closes when reaching the end of the list."), - "scope": "language-overridable", "restricted": false }, "editor.peekWidgetDefaultFocus": { @@ -1334,14 +1463,12 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor" ], "default": "tree", - "scope": "language-overridable", "restricted": false }, "editor.definitionLinkOpensInPeek": { "description": nls.localizeByDefault("Controls whether the Go to Definition mouse gesture always opens the peek widget."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.quickSuggestions": { @@ -1421,7 +1548,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "strings": "off" }, "markdownDescription": nls.localize("theia/editor/editor.quickSuggestions", "Controls whether suggestions should automatically show up while typing. This can be controlled for typing in comments, strings, and other code. Quick suggestion can be configured to show as ghost text or with the suggest widget. Also be aware of the '#editor.suggestOnTriggerCharacters#'-setting which controls if suggestions are triggered by special characters."), - "scope": "language-overridable", "restricted": false }, "editor.quickSuggestionsDelay": { @@ -1430,7 +1556,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 10, "minimum": 0, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.renameOnType": { @@ -1438,7 +1563,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "markdownDeprecationMessage": "Deprecated, use `editor.linkedEditing` instead.", "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false, "deprecationMessage": "Deprecated, use `editor.linkedEditing` instead." }, @@ -1446,14 +1570,17 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "description": nls.localizeByDefault("Controls whether the editor should render control characters."), "restricted": true, "type": "boolean", - "default": true, - "scope": "language-overridable" + "default": true }, "editor.renderFinalNewline": { "description": nls.localizeByDefault("Render last line number when the file ends with a newline."), - "type": "boolean", - "default": true, - "scope": "language-overridable", + "type": "string", + "enum": [ + "off", + "on", + "dimmed" + ], + "default": "on", "restricted": false }, "editor.renderLineHighlight": { @@ -1472,14 +1599,12 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "all" ], "default": "line", - "scope": "language-overridable", "restricted": false }, "editor.renderLineHighlightOnlyWhenFocus": { "description": nls.localizeByDefault("Controls if the editor should render the current line highlight only when the editor is focused."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.renderWhitespace": { @@ -1500,14 +1625,12 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "all" ], "default": "selection", - "scope": "language-overridable", "restricted": false }, "editor.roundedSelection": { "description": nls.localizeByDefault("Controls whether selections should have rounded corners."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.rulers": { @@ -1538,7 +1661,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] }, "default": [], "description": nls.localizeByDefault("Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty."), - "scope": "language-overridable", "restricted": false }, "editor.scrollbar.vertical": { @@ -1555,7 +1677,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "auto", "description": nls.localizeByDefault("Controls the visibility of the vertical scrollbar."), - "scope": "language-overridable", "restricted": false }, "editor.scrollbar.horizontal": { @@ -1572,28 +1693,24 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "auto", "description": nls.localizeByDefault("Controls the visibility of the horizontal scrollbar."), - "scope": "language-overridable", "restricted": false }, "editor.scrollbar.verticalScrollbarSize": { "type": "number", "default": 14, "description": nls.localizeByDefault("The width of the vertical scrollbar."), - "scope": "language-overridable", "restricted": false }, "editor.scrollbar.horizontalScrollbarSize": { "type": "number", "default": 12, "description": nls.localizeByDefault("The height of the horizontal scrollbar."), - "scope": "language-overridable", "restricted": false }, "editor.scrollbar.scrollByPage": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether clicks scroll by page or jump to click position."), - "scope": "language-overridable", "restricted": false }, "editor.scrollBeyondLastColumn": { @@ -1602,34 +1719,24 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 4, "minimum": 0, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.scrollBeyondLastLine": { "description": nls.localizeByDefault("Controls whether the editor will scroll beyond the last line."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.scrollPredominantAxis": { "description": nls.localizeByDefault("Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, - "editor.selectionClipboard": { - "type": "boolean", - "default": true, - "description": nls.localizeByDefault("Controls whether the Linux primary clipboard should be supported."), - "included": !isOSX && !isWindows - }, "editor.selectionHighlight": { "description": nls.localizeByDefault("Controls whether the editor should highlight matches similar to the selection."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.showFoldingControls": { @@ -1646,14 +1753,12 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "mouseover" ], "default": "mouseover", - "scope": "language-overridable", "restricted": false }, "editor.showUnused": { "description": nls.localizeByDefault("Controls fading out of unused code."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.snippetSuggestions": { @@ -1672,28 +1777,30 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "none" ], "default": "inline", - "scope": "language-overridable", "restricted": false }, "editor.smartSelect.selectLeadingAndTrailingWhitespace": { "description": nls.localizeByDefault("Whether leading and trailing whitespace should always be selected."), "default": true, "type": "boolean", - "scope": "language-overridable", + "restricted": false + }, + "editor.smartSelect.selectSubwords": { + "description": nls.localizeByDefault("Whether subwords (like 'foo' in 'fooBar' or 'foo_bar') should be selected."), + "default": true, + "type": "boolean", "restricted": false }, "editor.smoothScrolling": { "description": nls.localizeByDefault("Controls whether the editor will scroll using an animation."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.enabled": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Shows the nested current scopes during the scroll at the top of the editor."), - "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.maxLineCount": { @@ -1702,14 +1809,29 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 1, "maximum": 10, "description": nls.localizeByDefault("Defines the maximum number of sticky lines to show."), - "scope": "language-overridable", + "restricted": false + }, + "editor.stickyScroll.defaultModel": { + "type": "string", + "enum": [ + "outlineModel", + "foldingProviderModel", + "indentationModel" + ], + "default": "outlineModel", + "description": nls.localizeByDefault("Defines the model to use for determining which lines to stick. If the outline model does not exist, it will fall back on the folding provider model which falls back on the indentation model. This order is respected in all three cases."), + "restricted": false + }, + "editor.stickyScroll.scrollWithEditor": { + "type": "boolean", + "default": true, + "description": nls.localize("theia/editor/editor.stickyScroll.scrollWithEditor", "Enable scrolling of the sticky scroll widget with the editor's horizontal scrollbar."), "restricted": false }, "editor.stickyTabStops": { "description": nls.localizeByDefault("Emulate selection behavior of tab characters when using spaces for indentation. Selection will stick to tab stops."), "type": "boolean", "default": false, - "scope": "language-overridable", "restricted": false }, "editor.suggest.insertMode": { @@ -1724,312 +1846,286 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "insert", "description": nls.localizeByDefault("Controls whether words are overwritten when accepting completions. Note that this depends on extensions opting into this feature."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.filterGraceful": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether filtering and sorting suggestions accounts for small typos."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.localityBonus": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether sorting favors words that appear close to the cursor."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.shareSuggestSelections": { "type": "boolean", "default": false, "markdownDescription": nls.localizeByDefault("Controls whether remembered suggestion selections are shared between multiple workspaces and windows (needs `#editor.suggestSelection#`)."), - "scope": "language-overridable", + "restricted": false + }, + "editor.suggest.selectionMode": { + "type": "string", + "enum": [ + "always", + "never", + "whenTriggerCharacter", + "whenQuickSuggestion" + ], + "enumDescriptions": [ + nls.localizeByDefault("Always select a suggestion when automatically triggering IntelliSense."), + nls.localizeByDefault("Never select a suggestion when automatically triggering IntelliSense."), + nls.localizeByDefault("Select a suggestion only when triggering IntelliSense from a trigger character."), + nls.localizeByDefault("Select a suggestion only when triggering IntelliSense as you type.") + ], + "default": "always", + "markdownDescription": nls.localizeByDefault("Controls whether a suggestion is selected when the widget shows. Note that this only applies to automatically triggered suggestions (`#editor.quickSuggestions#` and `#editor.suggestOnTriggerCharacters#`) and that a suggestion is always selected when explicitly invoked, e.g via `Ctrl+Space`."), "restricted": false }, "editor.suggest.snippetsPreventQuickSuggestions": { "type": "boolean", - "default": true, + "default": false, "description": nls.localizeByDefault("Controls whether an active snippet prevents quick suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showIcons": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether to show or hide icons in suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showStatusBar": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls the visibility of the status bar at the bottom of the suggest widget."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.preview": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether to preview the suggestion outcome in the editor."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showInlineDetails": { "type": "boolean", "default": true, - "description": nls.localizeByDefault('Controls whether suggest details show inline with the label or only in the details widget.'), - "scope": "language-overridable", + "description": nls.localizeByDefault("Controls whether suggest details show inline with the label or only in the details widget."), "restricted": false }, "editor.suggest.maxVisibleSuggestions": { "type": "number", "deprecationMessage": "This setting is deprecated. The suggest widget can now be resized.", "default": 0, - "scope": "language-overridable", "restricted": false }, "editor.suggest.filteredTypes": { "type": "object", "deprecationMessage": "This setting is deprecated, please use separate settings like 'editor.suggest.showKeywords' or 'editor.suggest.showSnippets' instead.", "default": {}, - "scope": "language-overridable", "restricted": false }, "editor.suggest.showMethods": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `method`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showFunctions": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `function`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showConstructors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `constructor`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showDeprecated": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `deprecated`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.matchOnWordStartOnly": { "type": "boolean", "default": true, - "markdownDescription": nls.localize("theia/editor/editor.suggest.matchOnWordStartOnly", "When enabled IntelliSense filtering requires that the first character matches on a word start, e.g `c` on `Console` or `WebContext` but _not_ on `description`. When disabled IntelliSense will show more results but still sorts them by match quality."), - "scope": "language-overridable", + "markdownDescription": nls.localizeByDefault("When enabled IntelliSense filtering requires that the first character matches on a word start. For example, `c` on `Console` or `WebContext` but _not_ on `description`. When disabled IntelliSense will show more results but still sorts them by match quality."), "restricted": false }, "editor.suggest.showFields": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `field`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showVariables": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `variable`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showClasses": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `class`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showStructs": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `struct`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showInterfaces": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `interface`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showModules": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `module`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showProperties": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `property`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showEvents": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `event`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showOperators": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `operator`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showUnits": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `unit`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showValues": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `value`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showConstants": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `constant`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showEnums": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `enum`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showEnumMembers": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `enumMember`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showKeywords": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `keyword`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showWords": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `text`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showColors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `color`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showFiles": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `file`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showReferences": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `reference`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showCustomcolors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `customcolor`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showFolders": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `folder`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showTypeParameters": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `typeParameter`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showSnippets": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `snippet`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showUsers": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `user`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggest.showIssues": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `issues`-suggestions."), - "scope": "language-overridable", "restricted": false }, "editor.suggestFontSize": { - "markdownDescription": nls.localizeByDefault('Font size for the suggest widget. When set to {0}, the value of {1} is used.', '`0`', '`#editor.fontSize#`'), + "markdownDescription": nls.localize("theia/editor/editor.suggestFontSize", "Font size for the suggest widget. When set to `0`, the value of `#editor.fontSize#` is used."), "type": "integer", "default": 0, "minimum": 0, "maximum": 1000, - "scope": "language-overridable", "restricted": false }, "editor.suggestLineHeight": { - "markdownDescription": nls.localizeByDefault('Line height for the suggest widget. When set to {0}, the value of {1} is used. The minimum value is 8.', '`0`', '`#editor.lineHeight#`'), + "markdownDescription": nls.localize("theia/editor/editor.suggestLineHeight", "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used. The minimum value is 8."), "type": "integer", "default": 0, "minimum": 0, "maximum": 1000, - "scope": "language-overridable", "restricted": false }, "editor.suggestOnTriggerCharacters": { "description": nls.localizeByDefault("Controls whether suggestions should automatically show up when typing trigger characters."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.suggestSelection": { @@ -2046,7 +2142,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "recentlyUsedByPrefix" ], "default": "first", - "scope": "language-overridable", "restricted": false }, "editor.tabCompletion": { @@ -2063,7 +2158,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "onlySnippets" ], "default": "off", - "scope": "language-overridable", "restricted": false }, "editor.unicodeHighlight.nonBasicASCII": { @@ -2078,22 +2172,19 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": "inUntrustedWorkspace", - "description": nls.localizeByDefault("Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII."), - "scope": "language-overridable" + "description": nls.localizeByDefault("Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII.") }, "editor.unicodeHighlight.invisibleCharacters": { "restricted": true, "type": "boolean", "default": true, - "description": nls.localizeByDefault("Controls whether characters that just reserve space or have no width at all are highlighted."), - "scope": "language-overridable" + "description": nls.localizeByDefault("Controls whether characters that just reserve space or have no width at all are highlighted.") }, "editor.unicodeHighlight.ambiguousCharacters": { "restricted": true, "type": "boolean", "default": true, - "description": nls.localizeByDefault("Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale."), - "scope": "language-overridable" + "description": nls.localizeByDefault("Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale.") }, "editor.unicodeHighlight.includeComments": { "restricted": true, @@ -2107,8 +2198,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": "inUntrustedWorkspace", - "description": nls.localizeByDefault('Controls whether characters in comments should also be subject to Unicode highlighting.'), - "scope": "language-overridable" + "description": nls.localizeByDefault("Controls whether characters in comments should also be subject to Unicode highlighting.") }, "editor.unicodeHighlight.includeStrings": { "restricted": true, @@ -2122,8 +2212,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": true, - "description": nls.localizeByDefault('Controls whether characters in strings should also be subject to Unicode highlighting.'), - "scope": "language-overridable" + "description": nls.localizeByDefault("Controls whether characters in strings should also be subject to Unicode highlighting.") }, "editor.unicodeHighlight.allowedCharacters": { "restricted": true, @@ -2132,8 +2221,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "description": nls.localizeByDefault("Defines allowed characters that are not being highlighted."), "additionalProperties": { "type": "boolean" - }, - "scope": "language-overridable" + } }, "editor.unicodeHighlight.allowedLocales": { "restricted": true, @@ -2145,8 +2233,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "_os": true, "_vscode": true }, - "description": nls.localizeByDefault("Unicode characters that are common in allowed locales are not being highlighted."), - "scope": "language-overridable" + "description": nls.localizeByDefault("Unicode characters that are common in allowed locales are not being highlighted.") }, "editor.unusualLineTerminators": { "enumDescriptions": [ @@ -2162,21 +2249,32 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "prompt" ], "default": "prompt", - "scope": "language-overridable", "restricted": false }, "editor.useTabStops": { "description": nls.localizeByDefault("Inserting and deleting whitespace follows tab stops."), "type": "boolean", "default": true, - "scope": "language-overridable", + "restricted": false + }, + "editor.wordBreak": { + "markdownEnumDescriptions": [ + nls.localizeByDefault("Use the default line break rule."), + nls.localizeByDefault("Word breaks should not be used for Chinese/Japanese/Korean (CJK) text. Non-CJK text behavior is the same as for normal.") + ], + "description": nls.localizeByDefault("Controls the word break rules used for Chinese/Japanese/Korean (CJK) text."), + "type": "string", + "enum": [ + "normal", + "keepAll" + ], + "default": "normal", "restricted": false }, "editor.wordSeparators": { "description": nls.localizeByDefault("Characters that will be used as word separators when doing word related navigations or operations."), "type": "string", "default": "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?", - "scope": "language-overridable", "restricted": false }, "editor.wordWrap": { @@ -2195,7 +2293,6 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "bounded" ], "default": "off", - "scope": "language-overridable", "restricted": false }, "editor.wordWrapColumn": { @@ -2204,17 +2301,9 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 80, "minimum": 1, "maximum": 1073741824, - "scope": "language-overridable", "restricted": false }, "editor.wrappingIndent": { - "enumDescriptions": [ - nls.localizeByDefault("No indentation. Wrapped lines begin at column 1."), - nls.localizeByDefault("Wrapped lines get the same indentation as the parent."), - nls.localizeByDefault("Wrapped lines get +1 indentation toward the parent."), - nls.localizeByDefault("Wrapped lines get +2 indentation toward the parent.") - ], - "description": nls.localizeByDefault("Controls the indentation of wrapped lines."), "type": "string", "enum": [ "none", @@ -2222,8 +2311,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "indent", "deepIndent" ], + "enumDescriptions": [ + nls.localizeByDefault("No indentation. Wrapped lines begin at column 1."), + nls.localizeByDefault("Wrapped lines get the same indentation as the parent."), + nls.localizeByDefault("Wrapped lines get +1 indentation toward the parent."), + nls.localizeByDefault("Wrapped lines get +2 indentation toward the parent.") + ], + "description": nls.localizeByDefault("Controls the indentation of wrapped lines."), "default": "same", - "scope": "language-overridable", "restricted": false }, "editor.wrappingStrategy": { @@ -2231,21 +2326,19 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width."), nls.localizeByDefault("Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.") ], - "description": nls.localizeByDefault('Controls the algorithm that computes wrapping points. Note that when in accessibility mode, advanced will be used for the best experience.'), "type": "string", "enum": [ "simple", "advanced" ], "default": "simple", - "scope": "language-overridable", + "description": nls.localizeByDefault("Controls the algorithm that computes wrapping points. Note that when in accessibility mode, advanced will be used for the best experience."), "restricted": false }, "editor.showDeprecated": { "description": nls.localizeByDefault("Controls strikethrough deprecated variables."), "type": "boolean", "default": true, - "scope": "language-overridable", "restricted": false }, "editor.inlayHints.enabled": { @@ -2264,49 +2357,85 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localize("theia/editor/editor.inlayHints.enabled2", "Inlay hints are hidden by default and show when holding Ctrl+Alt"), nls.localizeByDefault("Inlay hints are disabled") ], - "scope": "language-overridable", "restricted": false }, "editor.inlayHints.fontSize": { "type": "number", "default": 0, - "markdownDescription": nls.localizeByDefault('Controls font size of inlay hints in the editor. As default the {0} is used when the configured value is less than {1} or greater than the editor font size.', '`#editor.fontSize#`'), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.inlayHints.fontSize", "Controls font size of inlay hints in the editor. As default the `#editor.fontSize#` is used when the configured value is less than `5` or greater than the editor font size."), "restricted": false }, "editor.inlayHints.fontFamily": { "type": "string", "default": "", - "markdownDescription": nls.localizeByDefault('Controls font family of inlay hints in the editor. When set to empty, the {0} is used.', '`#editor.fontFamily#`'), - "scope": "language-overridable", + "markdownDescription": nls.localize("theia/editor/editor.inlayHints.fontFamily", "Controls font family of inlay hints in the editor. When set to empty, the `#editor.fontFamily#` is used."), "restricted": false }, "editor.inlayHints.padding": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Enables the padding around the inlay hints in the editor."), - "scope": "language-overridable", + "restricted": false + }, + "editor.tabFocusMode": { + "markdownDescription": nls.localizeByDefault("Controls whether the editor receives tabs or defers them to the workbench for navigation."), + "type": "boolean", + "default": false, + "restricted": false + }, + "editor.defaultColorDecorators": { + "markdownDescription": nls.localizeByDefault("Controls whether inline color decorations should be shown using the default document color provider"), + "type": "boolean", + "default": false, + "restricted": false + }, + "editor.colorDecoratorsActivatedOn": { + "enumDescriptions": [ + nls.localizeByDefault("Make the color picker appear both on click and hover of the color decorator"), + nls.localizeByDefault("Make the color picker appear on hover of the color decorator"), + nls.localizeByDefault("Make the color picker appear on click of the color decorator") + ], + "description": nls.localizeByDefault("Controls the condition to make a color picker appear from a color decorator"), + "type": "string", + "enum": [ + "clickAndHover", + "hover", + "click" + ], + "default": "clickAndHover", + "restricted": false + }, + "editor.inlineCompletionsAccessibilityVerbose": { + "description": nls.localizeByDefault("Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown."), + "type": "boolean", + "default": false, "restricted": false }, "editor.codeActionWidget.showHeaders": { "type": "boolean", - "description": nls.localize("theia/editor/editor.codeActionWidget.showHeaders", "Enable/disable showing group headers in the code action menu."), + "description": nls.localizeByDefault("Enable/disable showing group headers in the Code Action menu."), "default": true, - "scope": "language-overridable", "restricted": false }, - "editor.experimental.pasteActions.enabled": { + "editor.codeActionWidget.includeNearbyQuickfixes": { "type": "boolean", - "description": nls.localize('theia/editor/editor.experimental.pasteActions.enabled', "Enable/disable running edits from extensions on paste."), + "description": nls.localize("theia/editor/editor.codeActionWidget.includeNearbyQuickfixes", "Enable/disable showing nearest quickfix within a line when not currently on a diagnostic."), "default": false, - "scope": "language-overridable", + "restricted": false + }, + "editor.experimental.dropIntoEditor.defaultProvider": { + "type": "object", + "description": nls.localizeByDefault("Configures the default drop provider to use for content of a given mime type."), + "default": {}, + "additionalProperties": { + "type": "string" + }, "restricted": false }, "editor.rename.enablePreview": { "description": nls.localizeByDefault("Enable/disable the ability to preview changes before renaming"), "default": true, "type": "boolean", - "scope": "language-overridable", "restricted": false }, "editor.find.globalFindClipboard": { @@ -2314,6 +2443,12 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": false, "description": nls.localizeByDefault("Controls whether the Find Widget should read or modify the shared find clipboard on macOS."), "included": isOSX + }, + "editor.selectionClipboard": { + "type": "boolean", + "default": true, + "description": nls.localizeByDefault("Controls whether the Linux primary clipboard should be supported."), + "included": !isOSX && !isWindows } }; @@ -2321,6 +2456,7 @@ type QuickSuggestionValues = boolean | 'on' | 'inline' | 'off'; export interface GeneratedEditorPreferences { 'editor.tabSize': number; + 'editor.indentSize': 'tabSize' | number; 'editor.insertSpaces': boolean; 'editor.detectIndentation': boolean; 'editor.trimAutoWhitespace': boolean; @@ -2330,22 +2466,35 @@ export interface GeneratedEditorPreferences { 'editor.semanticHighlighting.enabled': true | false | 'configuredByTheme'; 'editor.stablePeek': boolean; 'editor.maxTokenizationLineLength': number; + 'editor.experimental.asyncTokenization': boolean; + 'editor.experimental.asyncTokenizationLogging': boolean; + 'editor.experimental.asyncTokenizationVerification': boolean; 'editor.language.brackets': Array<[string, string]> | null | 'null'; 'editor.language.colorizedBracketPairs': Array<[string, string]> | null; 'diffEditor.maxComputationTime': number; 'diffEditor.maxFileSize': number; 'diffEditor.renderSideBySide': boolean; + 'diffEditor.renderSideBySideInlineBreakpoint': number; + 'diffEditor.useInlineViewWhenSpaceIsLimited': boolean; 'diffEditor.renderMarginRevertIcon': boolean; 'diffEditor.ignoreTrimWhitespace': boolean; 'diffEditor.renderIndicators': boolean; 'diffEditor.codeLens': boolean; 'diffEditor.wordWrap': 'off' | 'on' | 'inherit'; - 'diffEditor.diffAlgorithm': 'smart' | 'experimental'; + 'diffEditor.diffAlgorithm': 'legacy' | 'advanced'; + 'diffEditor.hideUnchangedRegions.enabled': boolean; + 'diffEditor.hideUnchangedRegions.revealLineCount': number; + 'diffEditor.hideUnchangedRegions.minimumLineCount': number; + 'diffEditor.hideUnchangedRegions.contextLineCount': number; + 'diffEditor.experimental.showMoves': boolean; + 'diffEditor.experimental.showEmptyDecorations': boolean; 'editor.acceptSuggestionOnCommitCharacter': boolean; 'editor.acceptSuggestionOnEnter': 'on' | 'smart' | 'off'; 'editor.accessibilitySupport': 'auto' | 'on' | 'off'; 'editor.accessibilityPageSize': number; 'editor.autoClosingBrackets': 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; + 'editor.autoClosingComments': 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; + 'editor.screenReaderAnnounceInlineSuggestion': boolean; 'editor.autoClosingDelete': 'always' | 'auto' | 'never'; 'editor.autoClosingOvertype': 'always' | 'auto' | 'never'; 'editor.autoClosingQuotes': 'always' | 'languageDefined' | 'beforeWhitespace' | 'never'; @@ -2362,19 +2511,22 @@ export interface GeneratedEditorPreferences { 'editor.codeLensFontFamily': string; 'editor.codeLensFontSize': number; 'editor.colorDecorators': boolean; + 'editor.colorDecoratorsLimit': number; 'editor.columnSelection': boolean; 'editor.comments.insertSpace': boolean; 'editor.comments.ignoreEmptyLines': boolean; 'editor.copyWithSyntaxHighlighting': boolean; 'editor.cursorBlinking': 'blink' | 'smooth' | 'phase' | 'expand' | 'solid'; - 'editor.cursorSmoothCaretAnimation': boolean; + 'editor.cursorSmoothCaretAnimation': 'off' | 'explicit' | 'on'; 'editor.cursorStyle': 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'; 'editor.cursorSurroundingLines': number; 'editor.cursorSurroundingLinesStyle': 'default' | 'all'; 'editor.cursorWidth': number; 'editor.dragAndDrop': boolean; 'editor.dropIntoEditor.enabled': boolean; + 'editor.dropIntoEditor.showDropSelector': 'afterDrop' | 'never'; 'editor.emptySelectionClipboard': boolean; + 'editor.experimentalWhitespaceRendering': 'svg' | 'font' | 'off'; 'editor.fastScrollSensitivity': number; 'editor.find.cursorMoveOnType': boolean; 'editor.find.seedSearchStringFromSelection': 'never' | 'always' | 'selection'; @@ -2409,8 +2561,11 @@ export interface GeneratedEditorPreferences { 'editor.hover.enabled': boolean; 'editor.hover.delay': number; 'editor.hover.sticky': boolean; + 'editor.hover.hidingDelay': number; 'editor.hover.above': boolean; 'editor.inlineSuggest.enabled': boolean; + 'editor.inlineSuggest.showToolbar': 'always' | 'onHover'; + 'editor.inlineSuggest.suppressSuggestions': boolean; 'editor.letterSpacing': number; 'editor.lightbulb.enabled': boolean; 'editor.lineHeight': number; @@ -2431,10 +2586,13 @@ export interface GeneratedEditorPreferences { 'editor.multiCursorMergeOverlapping': boolean; 'editor.multiCursorModifier': 'ctrlCmd' | 'alt'; 'editor.multiCursorPaste': 'spread' | 'full'; + 'editor.multiCursorLimit': number; 'editor.occurrencesHighlight': boolean; 'editor.overviewRulerBorder': boolean; 'editor.padding.top': number; 'editor.padding.bottom': number; + 'editor.pasteAs.enabled': boolean; + 'editor.pasteAs.showPasteSelector': 'afterPaste' | 'never'; 'editor.parameterHints.enabled': boolean; 'editor.parameterHints.cycle': boolean; 'editor.peekWidgetDefaultFocus': 'tree' | 'editor'; @@ -2443,7 +2601,7 @@ export interface GeneratedEditorPreferences { 'editor.quickSuggestionsDelay': number; 'editor.renameOnType': boolean; 'editor.renderControlCharacters': boolean; - 'editor.renderFinalNewline': boolean; + 'editor.renderFinalNewline': 'off' | 'on' | 'dimmed'; 'editor.renderLineHighlight': 'none' | 'gutter' | 'line' | 'all'; 'editor.renderLineHighlightOnlyWhenFocus': boolean; 'editor.renderWhitespace': 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; @@ -2457,20 +2615,23 @@ export interface GeneratedEditorPreferences { 'editor.scrollBeyondLastColumn': number; 'editor.scrollBeyondLastLine': boolean; 'editor.scrollPredominantAxis': boolean; - 'editor.selectionClipboard': boolean; 'editor.selectionHighlight': boolean; 'editor.showFoldingControls': 'always' | 'never' | 'mouseover'; 'editor.showUnused': boolean; 'editor.snippetSuggestions': 'top' | 'bottom' | 'inline' | 'none'; 'editor.smartSelect.selectLeadingAndTrailingWhitespace': boolean; + 'editor.smartSelect.selectSubwords': boolean; 'editor.smoothScrolling': boolean; 'editor.stickyScroll.enabled': boolean; 'editor.stickyScroll.maxLineCount': number; + 'editor.stickyScroll.defaultModel': 'outlineModel' | 'foldingProviderModel' | 'indentationModel'; + 'editor.stickyScroll.scrollWithEditor': boolean; 'editor.stickyTabStops': boolean; 'editor.suggest.insertMode': 'insert' | 'replace'; 'editor.suggest.filterGraceful': boolean; 'editor.suggest.localityBonus': boolean; 'editor.suggest.shareSuggestSelections': boolean; + 'editor.suggest.selectionMode': 'always' | 'never' | 'whenTriggerCharacter' | 'whenQuickSuggestion'; 'editor.suggest.snippetsPreventQuickSuggestions': boolean; 'editor.suggest.showIcons': boolean; 'editor.suggest.showStatusBar': boolean; @@ -2522,6 +2683,7 @@ export interface GeneratedEditorPreferences { 'editor.unicodeHighlight.allowedLocales': Record; 'editor.unusualLineTerminators': 'auto' | 'off' | 'prompt'; 'editor.useTabStops': boolean; + 'editor.wordBreak': 'normal' | 'keepAll'; 'editor.wordSeparators': string; 'editor.wordWrap': 'off' | 'on' | 'wordWrapColumn' | 'bounded'; 'editor.wordWrapColumn': number; @@ -2532,8 +2694,14 @@ export interface GeneratedEditorPreferences { 'editor.inlayHints.fontSize': number; 'editor.inlayHints.fontFamily': string; 'editor.inlayHints.padding': boolean; + 'editor.tabFocusMode': boolean; + 'editor.defaultColorDecorators': boolean; + 'editor.colorDecoratorsActivatedOn': 'clickAndHover' | 'hover' | 'click'; + 'editor.inlineCompletionsAccessibilityVerbose': boolean; 'editor.codeActionWidget.showHeaders': boolean; - 'editor.experimental.pasteActions.enabled': boolean; + 'editor.codeActionWidget.includeNearbyQuickfixes': boolean; + 'editor.experimental.dropIntoEditor.defaultProvider': null; 'editor.rename.enablePreview': boolean; 'editor.find.globalFindClipboard': boolean; + 'editor.selectionClipboard': boolean; } diff --git a/packages/editor/src/browser/editor-linenumber-contribution.ts b/packages/editor/src/browser/editor-linenumber-contribution.ts index 8dae394c8de72..3f42f64aeb0d0 100644 --- a/packages/editor/src/browser/editor-linenumber-contribution.ts +++ b/packages/editor/src/browser/editor-linenumber-contribution.ts @@ -69,8 +69,7 @@ export class EditorLineNumberContribution implements FrontendApplicationContribu menuPath: EDITOR_LINENUMBER_CONTEXT_MENU, anchor: event.event, args, - contextKeyService, - onHide: () => contextKeyService.dispose() + contextKeyService }); }); } diff --git a/packages/filesystem/src/browser/file-upload-service.ts b/packages/filesystem/src/browser/file-upload-service.ts index 66fdf2838dfeb..76db7ea89927b 100644 --- a/packages/filesystem/src/browser/file-upload-service.ts +++ b/packages/filesystem/src/browser/file-upload-service.ts @@ -33,13 +33,11 @@ import { nls } from '@theia/core/lib/common/nls'; export const HTTP_UPLOAD_URL: string = new Endpoint({ path: HTTP_FILE_UPLOAD_PATH }).getRestUrl().toString(true); -export interface CustomDataTransfer { - values(): Iterable -} +export type CustomDataTransfer = Iterable; export interface CustomDataTransferItem { - readonly id: string; asFile(): { + readonly id: string; readonly name: string; data(): Promise; } | undefined @@ -420,10 +418,10 @@ export class FileUploadService { } protected async indexCustomDataTransfer(targetUri: URI, dataTransfer: CustomDataTransfer, context: FileUploadService.Context): Promise { - for (const item of dataTransfer.values()) { + for (const [_, item] of dataTransfer) { const fileInfo = item.asFile(); if (fileInfo) { - await this.indexFile(targetUri, new File([await fileInfo.data()], item.id), context); + await this.indexFile(targetUri, new File([await fileInfo.data()], fileInfo.id), context); } } } diff --git a/packages/git/package.json b/packages/git/package.json index b787f26e88726..d24200fd36d67 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -6,7 +6,7 @@ "@theia/core": "1.46.0", "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/navigator": "1.46.0", "@theia/scm": "1.46.0", "@theia/scm-extra": "1.46.0", @@ -73,4 +73,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index c878bab03c9c3..fb3bf6f6a5847 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -5,7 +5,7 @@ "dependencies": { "@theia/core": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/preferences": "1.46.0", "@theia/userstorage": "1.46.0", "jsonc-parser": "^2.2.0" @@ -48,4 +48,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 8937cb1aa6dbb..ff0fa606435e0 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -7,7 +7,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/markers": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/outline-view": "1.46.0", "@theia/workspace": "1.46.0", "fast-plist": "^0.1.2", @@ -55,4 +55,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/monaco/src/browser/markdown-renderer/monaco-markdown-renderer.ts b/packages/monaco/src/browser/markdown-renderer/monaco-markdown-renderer.ts index 5f2852d94cba6..dd46b027c06d8 100644 --- a/packages/monaco/src/browser/markdown-renderer/monaco-markdown-renderer.ts +++ b/packages/monaco/src/browser/markdown-renderer/monaco-markdown-renderer.ts @@ -16,14 +16,11 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/language'; -import { MarkdownRenderer as CodeMarkdownRenderer } from '@theia/monaco-editor-core/esm/vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; +import { MarkdownRenderer as CodeMarkdownRenderer, IMarkdownRendererOptions } from '@theia/monaco-editor-core/esm/vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; -import { MonacoCommandServiceFactory } from '../monaco-command-service'; -import { MonacoEditorService } from '../monaco-editor-service'; import * as monaco from '@theia/monaco-editor-core'; -import { OpenerService as MonacoOpenerService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/openerService'; import { OpenerService, PreferenceService, WidgetOpenerOptions, open } from '@theia/core/lib/browser'; -import { OpenExternalOptions, OpenInternalOptions } from '@theia/monaco-editor-core/esm/vs/platform/opener/common/opener'; +import { IOpenerService, OpenExternalOptions, OpenInternalOptions } from '@theia/monaco-editor-core/esm/vs/platform/opener/common/opener'; import { HttpOpenHandlerOptions } from '@theia/core/lib/browser/http-open-handler'; import { URI } from '@theia/core/lib/common/uri'; import { MarkdownRenderer, MarkdownRenderOptions, MarkdownRenderResult } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; @@ -34,8 +31,6 @@ import { DisposableCollection, DisposableGroup } from '@theia/core'; @injectable() export class MonacoMarkdownRenderer implements MarkdownRenderer { - @inject(MonacoEditorService) protected readonly codeEditorService: MonacoEditorService; - @inject(MonacoCommandServiceFactory) protected readonly commandServiceFactory: MonacoCommandServiceFactory; @inject(OpenerService) protected readonly openerService: OpenerService; @inject(PreferenceService) protected readonly preferences: PreferenceService; @@ -72,21 +67,18 @@ export class MonacoMarkdownRenderer implements MarkdownRenderer { @postConstruct() protected init(): void { const languages = StandaloneServices.get(ILanguageService); - const openerService = new MonacoOpenerService(this.codeEditorService, this.commandServiceFactory()); + const openerService = StandaloneServices.get(IOpenerService); openerService.registerOpener({ open: (u, options) => this.interceptOpen(u, options) }); - const getPreference = () => this.preferences.get('editor.fontFamily'); - const rendererOptions = new Proxy(Object.create(null), { // eslint-disable-line no-null/no-null - get(_, field): string | undefined { - if (field === 'codeBlockFontFamily') { - return getPreference(); - } else { - return undefined; - } + const that = this; + const prefs = new class implements IMarkdownRendererOptions { + get codeBlockFontFamily(): string | undefined { + return that.preferences.get('editor.fontFamily'); } - }); - this.delegate = new CodeMarkdownRenderer(rendererOptions, languages, openerService); + }; + + this.delegate = new CodeMarkdownRenderer(prefs, languages, openerService); } protected async interceptOpen(monacoUri: monaco.Uri | string, monacoOptions?: OpenInternalOptions | OpenExternalOptions): Promise { diff --git a/packages/monaco/src/browser/monaco-bulk-edit-service.ts b/packages/monaco/src/browser/monaco-bulk-edit-service.ts index f1d614c63bccf..a5f7accc1b2d3 100644 --- a/packages/monaco/src/browser/monaco-bulk-edit-service.ts +++ b/packages/monaco/src/browser/monaco-bulk-edit-service.ts @@ -31,12 +31,12 @@ export class MonacoBulkEditService implements IBulkEditService { private _previewHandler?: IBulkEditPreviewHandler; - async apply(editsIn: ResourceEdit[] | WorkspaceEdit, options?: IBulkEditOptions): Promise { + async apply(editsIn: ResourceEdit[] | WorkspaceEdit, options?: IBulkEditOptions): Promise { const edits = Array.isArray(editsIn) ? editsIn : ResourceEdit.convert(editsIn); if (this._previewHandler && (options?.showPreview || edits.some(value => value.metadata?.needsConfirmation))) { editsIn = await this._previewHandler(edits, options); - return { ariaSummary: '', success: true }; + return { ariaSummary: '', isApplied: true }; } else { return this.workspace.applyBulkEdit(edits, options); } diff --git a/packages/monaco/src/browser/monaco-command-registry.ts b/packages/monaco/src/browser/monaco-command-registry.ts index e9b76e454b373..540425850460f 100644 --- a/packages/monaco/src/browser/monaco-command-registry.ts +++ b/packages/monaco/src/browser/monaco-command-registry.ts @@ -36,7 +36,10 @@ export class MonacoCommandRegistry { @inject(SelectionService) protected readonly selectionService: SelectionService; - validate(command: string): string | undefined { + validate(command: string | undefined): string | undefined { + if (!command) { + return undefined; + } return this.commands.commandIds.indexOf(command) !== -1 ? command : undefined; } diff --git a/packages/monaco/src/browser/monaco-command-service.ts b/packages/monaco/src/browser/monaco-command-service.ts index 7334eff649435..b6da221068cf6 100644 --- a/packages/monaco/src/browser/monaco-command-service.ts +++ b/packages/monaco/src/browser/monaco-command-service.ts @@ -14,18 +14,14 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { CommandRegistry } from '@theia/core/lib/common/command'; import { Emitter } from '@theia/core/lib/common/event'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { ICommandEvent, ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; -import { StandaloneCommandService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { StandaloneCommandService, StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import * as monaco from '@theia/monaco-editor-core'; - -export const MonacoCommandServiceFactory = Symbol('MonacoCommandServiceFactory'); -export interface MonacoCommandServiceFactory { - (): MonacoCommandService; -} +import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; @injectable() export class MonacoCommandService implements ICommandService, Disposable { @@ -38,7 +34,6 @@ export class MonacoCommandService implements ICommandService, Disposable { ); protected delegate: StandaloneCommandService | undefined; - protected readonly delegateListeners = new DisposableCollection(); constructor( @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry @@ -47,6 +42,19 @@ export class MonacoCommandService implements ICommandService, Disposable { this.toDispose.push(this.commandRegistry.onDidExecuteCommand(e => this.onDidExecuteCommandEmitter.fire(e))); } + @postConstruct() + init(): void { + this.delegate = new StandaloneCommandService(StandaloneServices.get(IInstantiationService)); + if (this.delegate) { + this.toDispose.push(this.delegate.onWillExecuteCommand(event => + this.onWillExecuteCommandEmitter.fire(event) + )); + this.toDispose.push(this.delegate.onDidExecuteCommand(event => + this.onDidExecuteCommandEmitter.fire(event) + )); + } + } + dispose(): void { this.toDispose.dispose(); } @@ -59,23 +67,6 @@ export class MonacoCommandService implements ICommandService, Disposable { return this.onDidExecuteCommandEmitter.event; } - setDelegate(delegate: StandaloneCommandService | undefined): void { - if (this.toDispose.disposed) { - return; - } - this.delegateListeners.dispose(); - this.toDispose.push(this.delegateListeners); - this.delegate = delegate; - if (this.delegate) { - this.delegateListeners.push(this.delegate.onWillExecuteCommand(event => - this.onWillExecuteCommandEmitter.fire(event) - )); - this.delegateListeners.push(this.delegate.onDidExecuteCommand(event => - this.onDidExecuteCommandEmitter.fire(event) - )); - } - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any async executeCommand(commandId: any, ...args: any[]): Promise { try { diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index 323f4408a655f..2b8e13fe188e1 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -21,16 +21,15 @@ import { CommonCommands, QuickInputService, ApplicationShell } from '@theia/core import { EditorCommands, EditorManager, EditorWidget } from '@theia/editor/lib/browser'; import { MonacoEditor } from './monaco-editor'; import { MonacoCommandRegistry, MonacoEditorCommandHandler } from './monaco-command-registry'; -import { MonacoEditorService } from './monaco-editor-service'; -import { MonacoTextModelService } from './monaco-text-model-service'; import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter'; import { nls } from '@theia/core/lib/common/nls'; -import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorExtensions'; import { CommandsRegistry, ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; import * as monaco from '@theia/monaco-editor-core'; import { EndOfLineSequence } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; export namespace MonacoCommands { @@ -67,15 +66,6 @@ export class MonacoEditorCommandHandlers implements CommandContribution { @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; - @inject(MonacoEditorService) - protected readonly codeEditorService: MonacoEditorService; - - @inject(MonacoTextModelService) - protected readonly textModelService: MonacoTextModelService; - - @inject(VSCodeContextKeyService) - protected readonly contextKeyService: VSCodeContextKeyService; - @inject(ApplicationShell) protected readonly shell: ApplicationShell; @@ -135,10 +125,10 @@ export class MonacoEditorCommandHandlers implements CommandContribution { * and execute them using the instantiation service of the current editor. */ protected registerMonacoCommands(): void { - const editorActions = new Map(EditorExtensionsRegistry.getEditorActions().map(({ id, label, alias }) => [id, { label, alias }])); + const editorActions = new Map([...EditorExtensionsRegistry.getEditorActions()].map(({ id, label, alias }) => [id, { label, alias }])); - const { codeEditorService } = this; - const globalInstantiationService = StandaloneServices.initialize({}); + const codeEditorService = StandaloneServices.get(ICodeEditorService); + const globalInstantiationService = StandaloneServices.get(IInstantiationService); const monacoCommands = CommandsRegistry.getCommands(); for (const id of monacoCommands.keys()) { if (MonacoCommands.EXCLUDE_ACTIONS.has(id)) { diff --git a/packages/monaco/src/browser/monaco-context-key-service.ts b/packages/monaco/src/browser/monaco-context-key-service.ts index 6450f4240d2d1..3de29a0826c4c 100644 --- a/packages/monaco/src/browser/monaco-context-key-service.ts +++ b/packages/monaco/src/browser/monaco-context-key-service.ts @@ -14,22 +14,24 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, postConstruct } from '@theia/core/shared/inversify'; import { ContextKeyService as TheiaContextKeyService, ContextKey, ContextKeyChangeEvent, ScopedValueStore, ContextMatcher, ContextKeyValue } from '@theia/core/lib/browser/context-key-service'; import { Emitter } from '@theia/core'; -import { AbstractContextKeyService, ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; +import { AbstractContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; import { ContextKeyExpr, ContextKeyExpression, IContext, IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; @injectable() export class MonacoContextKeyService implements TheiaContextKeyService { protected readonly onDidChangeEmitter = new Emitter(); readonly onDidChange = this.onDidChangeEmitter.event; - @inject(VSCodeContextKeyService) - protected readonly contextKeyService: VSCodeContextKeyService; + get contextKeyService(): AbstractContextKeyService { + return StandaloneServices.get(IContextKeyService) as AbstractContextKeyService; + } @postConstruct() protected init(): void { @@ -125,8 +127,7 @@ export class MonacoContextKeyService implements TheiaContextKeyService { return parsed.evaluate(ctx); } return true; - }, - dispose: () => delegate.dispose(), + } }; } diff --git a/packages/monaco/src/browser/monaco-context-menu.ts b/packages/monaco/src/browser/monaco-context-menu.ts index 5049414bf83b9..681909b0a4a0b 100644 --- a/packages/monaco/src/browser/monaco-context-menu.ts +++ b/packages/monaco/src/browser/monaco-context-menu.ts @@ -17,13 +17,14 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { MenuPath } from '@theia/core/lib/common/menu'; import { EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser'; -import { ContextMenuRenderer, toAnchor } from '@theia/core/lib/browser'; +import { Anchor, ContextMenuRenderer, Coordinate } from '@theia/core/lib/browser'; import { Menu } from '@theia/core/shared/@phosphor/widgets'; import { CommandRegistry } from '@theia/core/shared/@phosphor/commands'; import { IContextMenuService } from '@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView'; import { IContextMenuDelegate } from '@theia/monaco-editor-core/esm/vs/base/browser/contextmenu'; import { MenuItemAction } from '@theia/monaco-editor-core/esm/vs/platform/actions/common/actions'; import { Event, Emitter } from '@theia/monaco-editor-core/esm/vs/base/common/event'; +import { StandardMouseEvent } from '@theia/monaco-editor-core/esm/vs/base/browser/mouseEvent'; @injectable() export class MonacoContextMenuService implements IContextMenuService { @@ -38,11 +39,20 @@ export class MonacoContextMenuService implements IContextMenuService { return this.onDidShowContextMenuEmitter.event; }; - constructor(@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer) { + @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; + + toAnchor(anchor: HTMLElement | Coordinate | StandardMouseEvent): Anchor { + if (anchor instanceof HTMLElement) { + return { x: anchor.offsetLeft, y: anchor.offsetTop }; + } else if (anchor instanceof StandardMouseEvent) { + return { x: anchor.posx, y: anchor.posy }; + } else { + return anchor; + } } showContextMenu(delegate: IContextMenuDelegate): void { - const anchor = toAnchor(delegate.getAnchor()); + const anchor = this.toAnchor(delegate.getAnchor()); const actions = delegate.getActions(); const onHide = () => { delegate.onHide?.(false); diff --git a/packages/monaco/src/browser/monaco-diff-editor.ts b/packages/monaco/src/browser/monaco-diff-editor.ts index fa001542240d4..b631ebb8f95da 100644 --- a/packages/monaco/src/browser/monaco-diff-editor.ts +++ b/packages/monaco/src/browser/monaco-diff-editor.ts @@ -23,16 +23,15 @@ import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { DiffUris } from '@theia/core/lib/browser/diff-uris'; import * as monaco from '@theia/monaco-editor-core'; import { IDiffEditorConstructionOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { IDiffNavigatorOptions } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneEditor'; -import { StandaloneDiffEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { StandaloneDiffEditor2 } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; export namespace MonacoDiffEditor { - export interface IOptions extends MonacoEditor.ICommonOptions, IDiffEditorConstructionOptions, IDiffNavigatorOptions { + export interface IOptions extends MonacoEditor.ICommonOptions, IDiffEditorConstructionOptions { } } export class MonacoDiffEditor extends MonacoEditor { - protected _diffEditor: monaco.editor.IStandaloneDiffEditor; + protected _diffEditor: StandaloneDiffEditor2; protected _diffNavigator: DiffNavigator; constructor( @@ -43,18 +42,18 @@ export class MonacoDiffEditor extends MonacoEditor { services: MonacoEditorServices, protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, options?: MonacoDiffEditor.IOptions, - override?: EditorServiceOverrides, + override?: EditorServiceOverrides ) { super(uri, modifiedModel, node, services, options, override); this.documents.add(originalModel); const original = originalModel.textEditorModel; const modified = modifiedModel.textEditorModel; - this._diffNavigator = diffNavigatorFactory.createdDiffNavigator(this._diffEditor, options); + this._diffNavigator = diffNavigatorFactory.createdDiffNavigator(this._diffEditor); this._diffEditor.setModel({ original, modified }); } get diffEditor(): monaco.editor.IStandaloneDiffEditor { - return this._diffEditor; + return this._diffEditor as unknown as monaco.editor.IStandaloneDiffEditor; } get diffNavigator(): DiffNavigator { @@ -68,8 +67,8 @@ export class MonacoDiffEditor extends MonacoEditor { * Incomparable enums prevent TypeScript from believing that public IStandaloneDiffEditor is satisfied by private StandaloneDiffEditor */ this._diffEditor = instantiator - .createInstance(StandaloneDiffEditor, this.node, { ...options, fixedOverflowWidgets: true }) as unknown as monaco.editor.IStandaloneDiffEditor; - this.editor = this._diffEditor.getModifiedEditor(); + .createInstance(StandaloneDiffEditor2, this.node, { ...options, fixedOverflowWidgets: true }); + this.editor = this._diffEditor.getModifiedEditor() as unknown as monaco.editor.IStandaloneCodeEditor; return this._diffEditor; } diff --git a/packages/monaco/src/browser/monaco-diff-navigator-factory.ts b/packages/monaco/src/browser/monaco-diff-navigator-factory.ts index 906057465ed0e..639037c304800 100644 --- a/packages/monaco/src/browser/monaco-diff-navigator-factory.ts +++ b/packages/monaco/src/browser/monaco-diff-navigator-factory.ts @@ -16,46 +16,24 @@ import { injectable } from '@theia/core/shared/inversify'; import { DiffNavigator } from '@theia/editor/lib/browser'; -import * as monaco from '@theia/monaco-editor-core'; -import { DiffNavigator as MonacoDiffNavigator } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/diffNavigator'; -import { IStandaloneDiffEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { StandaloneDiffEditor2 } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; @injectable() export class MonacoDiffNavigatorFactory { static nullNavigator = { - canNavigate: () => false, hasNext: () => false, hasPrevious: () => false, next: () => { }, previous: () => { }, }; - createdDiffNavigator(editor: IStandaloneDiffEditor | monaco.editor.IStandaloneDiffEditor, options?: monaco.editor.IDiffNavigatorOptions): DiffNavigator { - const navigator = new MonacoDiffNavigator(editor as IStandaloneDiffEditor, options); - const ensureInitialized = (fwd: boolean) => { - if (navigator['nextIdx'] < 0) { - navigator['_initIdx'](fwd); - } - }; + createdDiffNavigator(editor: StandaloneDiffEditor2): DiffNavigator { return { - canNavigate: () => navigator.canNavigate(), - hasNext: () => { - if (navigator.canNavigate()) { - ensureInitialized(true); - return navigator['nextIdx'] + 1 < navigator['ranges'].length; - } - return false; - }, - hasPrevious: () => { - if (navigator.canNavigate()) { - ensureInitialized(false); - return navigator['nextIdx'] > 0; - } - return false; - }, - next: () => navigator.next(), - previous: () => navigator.previous(), + hasNext: () => true, + hasPrevious: () => true, + next: () => editor.goToDiff('next'), + previous: () => editor.goToDiff('previous') }; } } diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index e939ee5f2cc6c..0b928dc71ddd7 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -21,16 +21,11 @@ import { DiffUris } from '@theia/core/lib/browser/diff-uris'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import { DisposableCollection, deepClone, Disposable } from '@theia/core/lib/common'; import { TextDocumentSaveReason } from '@theia/core/shared/vscode-languageserver-protocol'; -import { MonacoCommandServiceFactory } from './monaco-command-service'; -import { MonacoContextMenuService } from './monaco-context-menu'; import { MonacoDiffEditor } from './monaco-diff-editor'; import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { EditorServiceOverrides, MonacoEditor, MonacoEditorServices } from './monaco-editor'; import { MonacoEditorModel, WillSaveMonacoModelEvent } from './monaco-editor-model'; -import { MonacoEditorService } from './monaco-editor-service'; -import { MonacoTextModelService } from './monaco-text-model-service'; import { MonacoWorkspace } from './monaco-workspace'; -import { MonacoBulkEditService } from './monaco-bulk-edit-service'; import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import { ContributionProvider } from '@theia/core'; import { KeybindingRegistry, OpenerService, open, WidgetOpenerOptions, FormatType } from '@theia/core/lib/browser'; @@ -39,23 +34,16 @@ import { HttpOpenHandlerOptions } from '@theia/core/lib/browser/http-open-handle import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter'; import { FileSystemPreferences } from '@theia/filesystem/lib/browser'; -import { MonacoQuickInputImplementation } from './monaco-quick-input-service'; -import { ContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; import * as monaco from '@theia/monaco-editor-core'; -import { OpenerService as MonacoOpenerService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/openerService'; -import { StandaloneCommandService, StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { IOpenerService, OpenExternalOptions, OpenInternalOptions } from '@theia/monaco-editor-core/esm/vs/platform/opener/common/opener'; -import { SimpleKeybinding } from '@theia/monaco-editor-core/esm/vs/base/common/keybindings'; -import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; -import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from '@theia/monaco-editor-core/esm/vs/platform/keybinding/common/keybinding'; import { timeoutReject } from '@theia/core/lib/common/promise-util'; -import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService'; import { IContextMenuService } from '@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView'; -import { IBulkEditService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; +import { KeyCodeChord } from '@theia/monaco-editor-core/esm/vs/base/common/keybindings'; import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; -import { IQuickInputService } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput'; -import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; +import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService'; +import { IReference } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; export const MonacoEditorFactory = Symbol('MonacoEditorFactory'); export interface MonacoEditorFactory { @@ -70,9 +58,6 @@ export class MonacoEditorProvider { @named(MonacoEditorFactory) protected readonly factories: ContributionProvider; - @inject(MonacoBulkEditService) - protected readonly bulkEditService: MonacoBulkEditService; - @inject(MonacoEditorServices) protected readonly services: MonacoEditorServices; @@ -85,9 +70,6 @@ export class MonacoEditorProvider { @inject(FileSystemPreferences) protected readonly filePreferences: FileSystemPreferences; - @inject(MonacoQuickInputImplementation) - protected readonly quickInputService: MonacoQuickInputImplementation; - protected _current: MonacoEditor | undefined; /** * Returns the last focused MonacoEditor. @@ -99,26 +81,18 @@ export class MonacoEditorProvider { } constructor( - @inject(MonacoEditorService) protected readonly codeEditorService: MonacoEditorService, - @inject(MonacoTextModelService) protected readonly textModelService: MonacoTextModelService, - @inject(MonacoContextMenuService) protected readonly contextMenuService: MonacoContextMenuService, @inject(MonacoToProtocolConverter) protected readonly m2p: MonacoToProtocolConverter, @inject(ProtocolToMonacoConverter) protected readonly p2m: ProtocolToMonacoConverter, @inject(MonacoWorkspace) protected readonly workspace: MonacoWorkspace, - @inject(MonacoCommandServiceFactory) protected readonly commandServiceFactory: MonacoCommandServiceFactory, @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences, @inject(MonacoDiffNavigatorFactory) protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, /** @deprecated since 1.6.0 */ @inject(ApplicationServer) protected readonly applicationServer: ApplicationServer, - @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService ) { - StandaloneServices.initialize({ - [ICodeEditorService.toString()]: codeEditorService, - }); } protected async getModel(uri: URI, toDispose: DisposableCollection): Promise { - const reference = await this.textModelService.createModelReference(uri); + const reference = await StandaloneServices.get(ITextModelService).createModelReference(monaco.Uri.from(uri.toComponents())) as IReference; // if document is invalid makes sure that all events from underlying resource are processed before throwing invalid model if (!reference.object.valid) { await reference.object.sync(); @@ -139,34 +113,20 @@ export class MonacoEditorProvider { protected async doCreateEditor(uri: URI, factory: ( override: EditorServiceOverrides, toDispose: DisposableCollection) => Promise ): Promise { - const commandService = this.commandServiceFactory(); const domNode = document.createElement('div'); - const contextKeyService = this.contextKeyService.createScoped(domNode); - const { codeEditorService, textModelService, contextMenuService } = this; - const workspaceEditService = this.bulkEditService; - const toDispose = new DisposableCollection(commandService); - const openerService = new MonacoOpenerService(codeEditorService, commandService); - openerService.registerOpener({ + const contextKeyService = StandaloneServices.get(IContextKeyService).createScoped(domNode); + StandaloneServices.get(IOpenerService).registerOpener({ open: (u, options) => this.interceptOpen(u, options) }); const overrides: EditorServiceOverrides = [ - [ICodeEditorService, codeEditorService], - [ITextModelService, textModelService], - [IContextMenuService, contextMenuService], - [IBulkEditService, workspaceEditService], [IContextKeyService, contextKeyService], - [IOpenerService, openerService], - [IQuickInputService, this.quickInputService], - [ICommandService, commandService] ]; + const toDispose = new DisposableCollection(); const editor = await factory(overrides, toDispose); editor.onDispose(() => toDispose.dispose()); this.injectKeybindingResolver(editor); - const standaloneCommandService = new StandaloneCommandService(StandaloneServices.get(IInstantiationService)); - commandService.setDelegate(standaloneCommandService); - toDispose.push(editor.onFocusChanged(focused => { if (focused) { this._current = editor; @@ -212,16 +172,16 @@ export class MonacoEditorProvider { protected injectKeybindingResolver(editor: MonacoEditor): void { const keybindingService = StandaloneServices.get(IKeybindingService); - keybindingService.resolveKeybinding = keybinding => [new MonacoResolvedKeybinding(MonacoResolvedKeybinding.keySequence(keybinding), this.keybindingRegistry)]; + keybindingService.resolveKeybinding = keybinding => [new MonacoResolvedKeybinding(MonacoResolvedKeybinding.keySequence(keybinding.chords), this.keybindingRegistry)]; keybindingService.resolveKeyboardEvent = keyboardEvent => { - const keybinding = new SimpleKeybinding( + const keybinding = new KeyCodeChord( keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode - ).toChord(); - return new MonacoResolvedKeybinding(MonacoResolvedKeybinding.keySequence(keybinding), this.keybindingRegistry); + ); + return new MonacoResolvedKeybinding(MonacoResolvedKeybinding.keySequence([keybinding]), this.keybindingRegistry); }; } @@ -403,10 +363,10 @@ export class MonacoEditorProvider { const overrides = override ? Array.from(override) : []; overrides.push([IContextMenuService, { showContextMenu: () => {/** no op! */ } }]); const document = new MonacoEditorModel({ - uri, - readContents: async () => '', - dispose: () => { } - }, this.m2p, this.p2m); + uri, + readContents: async () => '', + dispose: () => { } + }, this.m2p, this.p2m); toDispose.push(document); const model = (await document.load()).textEditorModel; return new MonacoEditor( diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts index e6cee47621571..85c57658b2e1b 100644 --- a/packages/monaco/src/browser/monaco-editor-service.ts +++ b/packages/monaco/src/browser/monaco-editor-service.ts @@ -22,15 +22,20 @@ import { MonacoEditor } from './monaco-editor'; import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; import { MonacoEditorModel } from './monaco-editor-model'; import { IResourceEditorInput, ITextResourceEditorInput } from '@theia/monaco-editor-core/esm/vs/platform/editor/common/editor'; -import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; -import { IStandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/common/standaloneTheme'; import { StandaloneCodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditorService'; import { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; +import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; decorate(injectable(), StandaloneCodeEditorService); +export const VSCodeContextKeyService = Symbol('VSCodeContextKeyService'); +export const VSCodeThemeService = Symbol('VSCodeThemeService'); + +export const MonacoEditorServiceFactory = Symbol('MonacoEditorServiceFactory'); +export type MonacoEditorServiceFactoryType = (contextKeyService: IContextKeyService, themeService: IThemeService) => MonacoEditorService; + @injectable() export class MonacoEditorService extends StandaloneCodeEditorService { @@ -51,8 +56,8 @@ export class MonacoEditorService extends StandaloneCodeEditorService { @inject(PreferenceService) protected readonly preferencesService: PreferenceService; - constructor(@inject(VSCodeContextKeyService) contextKeyService: VSCodeContextKeyService) { - super(contextKeyService, StandaloneServices.get(IStandaloneThemeService)); + constructor(@inject(VSCodeContextKeyService) contextKeyService: IContextKeyService, @inject(VSCodeThemeService) themeService: IThemeService) { + super(contextKeyService, themeService); } /** diff --git a/packages/monaco/src/browser/monaco-editor.ts b/packages/monaco/src/browser/monaco-editor.ts index 3bee47b20feff..be9029fb2bde3 100644 --- a/packages/monaco/src/browser/monaco-editor.ts +++ b/packages/monaco/src/browser/monaco-editor.ts @@ -35,7 +35,8 @@ import { EditorDecoration, EditorMouseEvent, EncodingMode, - EditorDecorationOptions + EditorDecorationOptions, + MouseTargetType } from '@theia/editor/lib/browser'; import { MonacoEditorModel } from './monaco-editor-model'; import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; @@ -46,9 +47,9 @@ import * as monaco from '@theia/monaco-editor-core'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/language'; import { IInstantiationService, ServiceIdentifier } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; -import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { ServiceCollection } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/serviceCollection'; +import { ICodeEditor, IMouseTargetMargin } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; import { IStandaloneEditorConstructionOptions, StandaloneEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { ServiceCollection } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/serviceCollection'; export type ServicePair = [ServiceIdentifier, T]; @@ -151,7 +152,7 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { } protected getInstantiatorWithOverrides(override?: EditorServiceOverrides): IInstantiationService { - const instantiator = StandaloneServices.initialize({}); + const instantiator = StandaloneServices.get(IInstantiationService); if (override) { const overrideServices = new ServiceCollection(...override); return instantiator.createChild(overrideServices); @@ -185,12 +186,12 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { const { element, position, range } = e.target; this.onMouseDownEmitter.fire({ target: { - ...e.target, + type: e.target.type as unknown as MouseTargetType, element: element || undefined, mouseColumn: this.m2p.asPosition(undefined, e.target.mouseColumn).character, range: range && this.m2p.asRange(range) || undefined, position: position && this.m2p.asPosition(position.lineNumber, position.column) || undefined, - detail: (e.target as monaco.editor.IMouseTargetMargin).detail || {}, + detail: (e.target as unknown as IMouseTargetMargin).detail || {}, }, event: e.event.browserEvent }); diff --git a/packages/monaco/src/browser/monaco-frontend-application-contribution.ts b/packages/monaco/src/browser/monaco-frontend-application-contribution.ts index 4d8ccb9511129..6d9b4251d5b41 100644 --- a/packages/monaco/src/browser/monaco-frontend-application-contribution.ts +++ b/packages/monaco/src/browser/monaco-frontend-application-contribution.ts @@ -20,15 +20,7 @@ import { MonacoSnippetSuggestProvider } from './monaco-snippet-suggest-provider' import * as monaco from '@theia/monaco-editor-core'; import { setSnippetSuggestSupport } from '@theia/monaco-editor-core/esm/vs/editor/contrib/suggest/browser/suggest'; import { CompletionItemProvider } from '@theia/monaco-editor-core/esm/vs/editor/common/languages'; -import { MonacoEditorService } from './monaco-editor-service'; import { MonacoTextModelService } from './monaco-text-model-service'; -import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; -import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; -import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; -import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService'; -import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from '@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView'; -import { MonacoContextMenuService } from './monaco-context-menu'; import { MonacoThemingService } from './monaco-theming-service'; import { isHighContrast } from '@theia/core/lib/common/theme'; import { editorOptionsRegistry, IEditorOption } from '@theia/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; @@ -36,28 +28,12 @@ import { MAX_SAFE_INTEGER } from '@theia/core'; import { editorGeneratedPreferenceProperties } from '@theia/editor/lib/browser/editor-generated-preference-schema'; import { WorkspaceFileService } from '@theia/workspace/lib/common/workspace-file-service'; -let theiaDidInitialize = false; -const originalInitialize = StandaloneServices.initialize; -StandaloneServices.initialize = overrides => { - if (!theiaDidInitialize) { - console.warn('Monaco was initialized before overrides were installed by Theia\'s initialization.' - + ' Please check the lifecycle of services that use Monaco and ensure that Monaco entities are not instantiated before Theia is initialized.', new Error()); - } - return originalInitialize(overrides); -}; - @injectable() export class MonacoFrontendApplicationContribution implements FrontendApplicationContribution, StylingParticipant { - @inject(MonacoEditorService) - protected readonly codeEditorService: MonacoEditorService; - @inject(MonacoTextModelService) protected readonly textModelService: MonacoTextModelService; - @inject(VSCodeContextKeyService) - protected readonly contextKeyService: VSCodeContextKeyService; - @inject(MonacoSnippetSuggestProvider) protected readonly snippetSuggestProvider: MonacoSnippetSuggestProvider; @@ -67,9 +43,6 @@ export class MonacoFrontendApplicationContribution implements FrontendApplicatio @inject(QuickAccessRegistry) protected readonly quickAccessRegistry: QuickAccessRegistry; - @inject(MonacoContextMenuService) - protected readonly contextMenuService: MonacoContextMenuService; - @inject(MonacoThemingService) protected readonly monacoThemingService: MonacoThemingService; @inject(WorkspaceFileService) protected readonly workspaceFileService: WorkspaceFileService; @@ -77,14 +50,6 @@ export class MonacoFrontendApplicationContribution implements FrontendApplicatio @postConstruct() protected init(): void { this.addAdditionalPreferenceValidations(); - const { codeEditorService, textModelService, contextKeyService, contextMenuService } = this; - theiaDidInitialize = true; - StandaloneServices.initialize({ - [ICodeEditorService.toString()]: codeEditorService, - [ITextModelService.toString()]: textModelService, - [IContextKeyService.toString()]: contextKeyService, - [IContextMenuService.toString()]: contextMenuService, - }); // Monaco registers certain quick access providers (e.g. QuickCommandAccess) at import time, but we want to use our own. this.quickAccessRegistry.clear(); diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 6a717c3f4dab5..0bee7f59228b4 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -31,7 +31,7 @@ Object.assign(MonacoNls, { }); import '../../src/browser/style/index.css'; -import { ContainerModule, decorate, injectable, interfaces } from '@theia/core/shared/inversify'; +import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { MenuContribution, CommandContribution, quickInputServicePath } from '@theia/core/lib/common'; import { FrontendApplicationContribution, KeybindingContribution, @@ -45,12 +45,12 @@ import { MonacoEditorCommandHandlers } from './monaco-command'; import { MonacoKeybindingContribution } from './monaco-keybinding'; import { MonacoLanguages } from './monaco-languages'; import { MonacoWorkspace } from './monaco-workspace'; -import { MonacoEditorService } from './monaco-editor-service'; +import { MonacoEditorService, MonacoEditorServiceFactory, VSCodeContextKeyService, VSCodeThemeService } from './monaco-editor-service'; import { MonacoTextModelService, MonacoEditorModelFactory } from './monaco-text-model-service'; import { MonacoContextMenuService } from './monaco-context-menu'; import { MonacoOutlineContribution } from './monaco-outline-contribution'; import { MonacoStatusBarContribution } from './monaco-status-bar-contribution'; -import { MonacoCommandService, MonacoCommandServiceFactory } from './monaco-command-service'; +import { MonacoCommandService } from './monaco-command-service'; import { MonacoCommandRegistry } from './monaco-command-registry'; import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { MonacoFrontendApplicationContribution } from './monaco-frontend-application-contribution'; @@ -80,16 +80,15 @@ import { GotoLineQuickAccessContribution } from './monaco-gotoline-quick-access' import { GotoSymbolQuickAccessContribution } from './monaco-gotosymbol-quick-access'; import { QuickAccessContribution, QuickAccessRegistry } from '@theia/core/lib/browser/quick-input/quick-access'; import { MonacoQuickAccessRegistry } from './monaco-quick-access-registry'; -import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration'; -import { StandaloneConfigurationService, StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { StandaloneConfigurationService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { Configuration } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configurationModels'; import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { MonacoMarkdownRenderer } from './markdown-renderer/monaco-markdown-renderer'; import { ThemeService } from '@theia/core/lib/browser/theming'; import { ThemeServiceWithDB } from './monaco-indexed-db'; - -decorate(injectable(), VSCodeContextKeyService); +import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoThemingService).toSelf().inSingletonScope(); @@ -114,15 +113,16 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoWorkspace).toSelf().inSingletonScope(); - bind(MonacoConfigurationService).toDynamicValue(({ container }) => - createMonacoConfigurationService(container) - ).inSingletonScope(); - bind(VSCodeContextKeyService).toDynamicValue(({ container }) => - new VSCodeContextKeyService(container.get(MonacoConfigurationService)) - ).inSingletonScope(); + bind(MonacoConfigurationService).toDynamicValue(({ container }) => createMonacoConfigurationService(container)).inSingletonScope(); bind(MonacoBulkEditService).toSelf().inSingletonScope(); - bind(MonacoEditorService).toSelf().inSingletonScope(); + bind(MonacoEditorServiceFactory).toFactory((context: interfaces.Context) => (contextKeyService: IContextKeyService, themeService: IThemeService) => { + const child = context.container.createChild(); + child.bind(VSCodeContextKeyService).toConstantValue(contextKeyService); + child.bind(VSCodeThemeService).toConstantValue(themeService); + child.bind(MonacoEditorService).toSelf().inSingletonScope(); + return child.get(MonacoEditorService); + }); bind(MonacoTextModelService).toSelf().inSingletonScope(); bind(MonacoContextMenuService).toSelf().inSingletonScope(); bind(MonacoEditorServices).toSelf().inSingletonScope(); @@ -130,7 +130,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bindContributionProvider(bind, MonacoEditorFactory); bindContributionProvider(bind, MonacoEditorModelFactory); bind(MonacoCommandService).toSelf().inTransientScope(); - bind(MonacoCommandServiceFactory).toAutoFactory(MonacoCommandService); bind(TextEditorProvider).toProvider(context => uri => context.container.get(MonacoEditorProvider).get(uri) @@ -198,7 +197,7 @@ export const MonacoConfigurationService = Symbol('MonacoConfigurationService'); export function createMonacoConfigurationService(container: interfaces.Container): IConfigurationService { const preferences = container.get(PreferenceService); const preferenceSchemaProvider = container.get(PreferenceSchemaProvider); - const service = StandaloneServices.get(IConfigurationService) as StandaloneConfigurationService; + const service = new StandaloneConfigurationService(); const _configuration: Configuration = service['_configuration']; _configuration.getValue = (section, overrides) => { @@ -216,6 +215,14 @@ export function createMonacoConfigurationService(container: interfaces.Container return proxy; }; + /* + * Since we never read values from the underlying service, writing to it doesn't make sense. The standalone editor writes to the configuration when being created, + * which makes sense in the standalone case where there is no preference infrastructure in place. Those writes degrade the performance, however, so we patch the + * service to an empty implementation. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + service.updateValues = (values: [string, any][]) => Promise.resolve(); + const toTarget = (scope: PreferenceScope): ConfigurationTarget => { switch (scope) { case PreferenceScope.Default: return ConfigurationTarget.DEFAULT; @@ -246,11 +253,12 @@ export function createMonacoConfigurationService(container: interfaces.Container overrides.push([override, [...values]]); } service['_onDidChangeConfiguration'].fire({ + sourceConfig: {}, change: { keys: [...context.keys], overrides }, - affectedKeys: [...context.affectedKeys], + affectedKeys: context.affectedKeys, source, affectsConfiguration: (prefix, options) => { if (!context.affectedKeys.has(prefix)) { diff --git a/packages/monaco/src/browser/monaco-icon-registry.ts b/packages/monaco/src/browser/monaco-icon-registry.ts index 48cadadcdf118..0df38bf51aa25 100644 --- a/packages/monaco/src/browser/monaco-icon-registry.ts +++ b/packages/monaco/src/browser/monaco-icon-registry.ts @@ -15,9 +15,9 @@ // ***************************************************************************** import { injectable } from '@theia/core/shared/inversify'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; import { IconRegistry } from '@theia/core/lib/browser/icon-registry'; -import { IconDefinition, IconFontDefinition, getIconRegistry } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/iconRegistry'; +import { getIconRegistry } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/iconRegistry'; +import { IconDefinition, IconFontDefinition, ThemeIcon } from '@theia/core/lib/common/theme'; @injectable() export class MonacoIconRegistry implements IconRegistry { @@ -33,7 +33,8 @@ export class MonacoIconRegistry implements IconRegistry { } registerIconFont(id: string, definition: IconFontDefinition): IconFontDefinition { - return this.iconRegistry.registerIconFont(id, definition); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + return this.iconRegistry.registerIconFont(id, definition) as IconFontDefinition; } deregisterIconFont(id: string): void { @@ -41,7 +42,8 @@ export class MonacoIconRegistry implements IconRegistry { } getIconFont(id: string): IconFontDefinition | undefined { - return this.iconRegistry.getIconFont(id); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + return this.iconRegistry.getIconFont(id) as IconFontDefinition; } } diff --git a/packages/monaco/src/browser/monaco-init.ts b/packages/monaco/src/browser/monaco-init.ts new file mode 100644 index 0000000000000..1fa6c367b0f40 --- /dev/null +++ b/packages/monaco/src/browser/monaco-init.ts @@ -0,0 +1,114 @@ +// ***************************************************************************** +// Copyright (C) 2023 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* + * The code in this file is responsible for overriding service implementations in the Monaco editor with our own Theia-based implementations. + * Since we only get a single chance to call `StandaloneServies.initialize()` with our overrides, we need to make sure that intialize is called before the first call to + * `StandaloneServices.get()` or `StandaloneServies.initialize()`. As we do not control the mechanics of Inversify instance constructions, the approach here is to call + * `MonacoInit.init()` from the `index.js` file after all container modules are loaded, but before the first object is fetched from it. + * `StandaloneServices.initialize()` is called with service descriptors, not service instances. This lets us finish all overrides before any inversify object is constructed and + * might call `initialize()` while being constructed. + * The service descriptors require a constructor function, so we declare dummy class for each Monaco service we override. But instead of returning an instance of the dummy class, + * we fetch the implementation of the monaco service from the inversify container. + * The inversify-constructed services must not call StandaloneServices.get() or StandaloneServices.initialize() from their constructors. Calling `get`()` in postConstruct mehtods + * is allowed. + */ + +import { Container } from '@theia/core/shared/inversify'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { SyncDescriptor } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/descriptors'; +import { MonacoEditorServiceFactory, MonacoEditorServiceFactoryType } from './monaco-editor-service'; +import { IConfigurationService } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration'; +import { ITextModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService'; +import { MonacoConfigurationService } from './monaco-frontend-module'; +import { MonacoTextModelService } from './monaco-text-model-service'; +import { MonacoContextMenuService } from './monaco-context-menu'; +import { IContextMenuService } from '@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView'; +import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { MonacoBulkEditService } from './monaco-bulk-edit-service'; +import { MonacoCommandService } from './monaco-command-service'; +import { IBulkEditService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; +import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; +import { MonacoQuickInputImplementation } from './monaco-quick-input-service'; +import { IQuickInputService } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput'; + +class MonacoEditorServiceConstructor { + /** + * MonacoEditorService needs other Monaco services as constructor parameters, so we need to do use a factory for constructing the service. If we want the singleton instance, + * we need to fetch it from the `StandaloneServices` class instead of injecting it. + * @param container + * @param contextKeyService + * @param themeService + */ + constructor(container: Container, + @IContextKeyService contextKeyService: IContextKeyService, + @IThemeService themeService: IThemeService) { + + return container.get(MonacoEditorServiceFactory)(contextKeyService, themeService); + }; +} + +class MonacoConfigurationServiceConstructor { + constructor(container: Container) { + return container.get(MonacoConfigurationService); + } +} + +class MonacoTextModelServiceConstructor { + constructor(container: Container) { + return container.get(MonacoTextModelService); + } +} + +class MonacoContextMenuServiceConstructor { + constructor(container: Container) { + return container.get(MonacoContextMenuService); + } +} + +class MonacoBulkEditServiceConstructor { + constructor(container: Container) { + return container.get(MonacoBulkEditService); + } +} + +class MonacoCommandServiceConstructor { + constructor(container: Container) { + return container.get(MonacoCommandService); + } +} + +class MonacoQuickInputImplementationConstructor { + constructor(container: Container) { + return container.get(MonacoQuickInputImplementation); + } +} + +export namespace MonacoInit { + export function init(container: Container): void { + StandaloneServices.initialize({ + [ICodeEditorService.toString()]: new SyncDescriptor(MonacoEditorServiceConstructor, [container]), + [IConfigurationService.toString()]: new SyncDescriptor(MonacoConfigurationServiceConstructor, [container]), + [ITextModelService.toString()]: new SyncDescriptor(MonacoTextModelServiceConstructor, [container]), + [IContextMenuService.toString()]: new SyncDescriptor(MonacoContextMenuServiceConstructor, [container]), + [IBulkEditService.toString()]: new SyncDescriptor(MonacoBulkEditServiceConstructor, [container]), + [ICommandService.toString()]: new SyncDescriptor(MonacoCommandServiceConstructor, [container]), + [IQuickInputService.toString()]: new SyncDescriptor(MonacoQuickInputImplementationConstructor, [container]), + }); + } +} diff --git a/packages/monaco/src/browser/monaco-keybinding.ts b/packages/monaco/src/browser/monaco-keybinding.ts index ec7e0bcf902d1..cc80a06470d09 100644 --- a/packages/monaco/src/browser/monaco-keybinding.ts +++ b/packages/monaco/src/browser/monaco-keybinding.ts @@ -45,14 +45,14 @@ export class MonacoKeybindingContribution implements KeybindingContribution { registerKeybindings(registry: KeybindingRegistry): void { const defaultKeybindings = KeybindingsRegistry.getDefaultKeybindings(); for (const item of defaultKeybindings) { - const command = this.commands.validate(item.command); - if (command) { + const command = this.commands.validate(item.command || undefined); + if (command && item.keybinding) { const when = (item.when && item.when.serialize()) ?? undefined; let keybinding; if (item.command === MonacoCommands.GO_TO_DEFINITION && !environment.electron.is()) { keybinding = 'ctrlcmd+f11'; } else { - keybinding = MonacoResolvedKeybinding.toKeybinding(item.keybinding); + keybinding = MonacoResolvedKeybinding.toKeybinding(item.keybinding.chords); } registry.registerKeybinding({ command, keybinding, when }); } diff --git a/packages/monaco/src/browser/monaco-quick-input-service.ts b/packages/monaco/src/browser/monaco-quick-input-service.ts index 408d3c09aafb8..580142ad4d418 100644 --- a/packages/monaco/src/browser/monaco-quick-input-service.ts +++ b/packages/monaco/src/browser/monaco-quick-input-service.ts @@ -16,21 +16,21 @@ import { ApplicationShell, - InputBox, InputOptions, KeybindingRegistry, NormalizedQuickInputButton, PickOptions, + InputBox, InputOptions, KeybindingRegistry, PickOptions, QuickInputButton, QuickInputHideReason, QuickInputService, QuickPick, QuickPickItem, - QuickPickItemButtonEvent, QuickPickItemHighlights, QuickPickOptions, QuickPickSeparator, codiconArray + QuickPickItemButtonEvent, QuickPickItemHighlights, QuickPickOptions, QuickPickSeparator } from '@theia/core/lib/browser'; import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, - IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, QuickPickInput + IQuickInputService, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickWidget, QuickPickInput } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput'; -import { IQuickInputOptions, IQuickInputStyles, QuickInputController } from '@theia/monaco-editor-core/esm/vs/base/parts/quickinput/browser/quickInput'; +import { IQuickInputOptions, IQuickInputStyles } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/browser/quickInput'; +import { QuickInputController } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/browser/quickInputController'; import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; import { IQuickAccessController } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickAccess'; import { QuickAccessController } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/browser/quickAccess'; -import { ContextKeyService as VSCodeContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/browser/contextKeyService'; -import { IContextKey } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; import { IListOptions, List } from '@theia/monaco-editor-core/esm/vs/base/browser/ui/list/listWidget'; import * as monaco from '@theia/monaco-editor-core'; import { ResolvedKeybinding } from '@theia/monaco-editor-core/esm/vs/base/common/keybindings'; @@ -38,11 +38,18 @@ import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { IMatch } from '@theia/monaco-editor-core/esm/vs/base/common/filters'; import { IListRenderer, IListVirtualDelegate } from '@theia/monaco-editor-core/esm/vs/base/browser/ui/list/list'; -import { Event } from '@theia/core'; +import { CancellationToken, Event } from '@theia/core'; import { MonacoColorRegistry } from './monaco-color-registry'; import { ThemeService } from '@theia/core/lib/browser/theming'; import { IStandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/common/standaloneTheme'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { + activeContrastBorder, asCssVariable, pickerGroupBorder, pickerGroupForeground, quickInputBackground, quickInputForeground, quickInputListFocusBackground, + quickInputListFocusForeground, quickInputListFocusIconForeground, quickInputTitleBackground, widgetBorder, widgetShadow +} from '@theia/monaco-editor-core/esm/vs/platform/theme/common/colorRegistry'; + +import { + defaultButtonStyles, defaultCountBadgeStyles, defaultInputBoxStyles, defaultKeybindingLabelStyles, defaultProgressBarStyles, defaultToggleStyles, getListStyles +} from '@theia/monaco-editor-core/esm/vs/platform/theme/browser/defaultStyles'; // Copied from @vscode/src/vs/base/parts/quickInput/browser/quickInputList.ts export interface IListElement { @@ -62,6 +69,7 @@ export interface IListElement { @injectable() export class MonacoQuickInputImplementation implements IQuickInputService { + declare readonly _serviceBrand: undefined; controller: QuickInputController; @@ -76,9 +84,6 @@ export class MonacoQuickInputImplementation implements IQuickInputService { @inject(ThemeService) protected readonly themeService: ThemeService; - @inject(VSCodeContextKeyService) - protected readonly contextKeyService: VSCodeContextKeyService; - protected container: HTMLElement; private quickInputList: List; @@ -93,25 +98,29 @@ export class MonacoQuickInputImplementation implements IQuickInputService { this.initContainer(); this.initController(); this.quickAccess = new QuickAccessController(this, StandaloneServices.get(IInstantiationService)); - this.inQuickOpen = this.contextKeyService.createKey('inQuickOpen', false); + this.inQuickOpen = StandaloneServices.get(IContextKeyService).createKey('inQuickOpen', false); this.controller.onShow(() => { this.container.style.top = this.shell.mainPanel.node.getBoundingClientRect().top + 'px'; this.inQuickOpen.set(true); }); this.controller.onHide(() => this.inQuickOpen.set(false)); - this.themeService.initialized.then(() => this.controller.applyStyles(this.getStyles())); + this.themeService.initialized.then(() => this.controller.applyStyles(this.computeStyles())); // Hook into the theming service of Monaco to ensure that the updates are ready. - StandaloneServices.get(IStandaloneThemeService).onDidColorThemeChange(() => this.controller.applyStyles(this.getStyles())); + StandaloneServices.get(IStandaloneThemeService).onDidColorThemeChange(() => this.controller.applyStyles(this.computeStyles())); window.addEventListener('resize', () => this.updateLayout()); } setContextKey(key: string | undefined): void { if (key) { - this.contextKeyService.createKey(key, undefined); + StandaloneServices.get(IContextKeyService).createKey(key, undefined); } } + createQuickWidget(): IQuickWidget { + return this.controller.createQuickWidget(); + } + createQuickPick(): IQuickPick { return this.controller.createQuickPick(); } @@ -184,7 +193,7 @@ export class MonacoQuickInputImplementation implements IQuickInputService { } private initController(): void { - this.controller = new QuickInputController(this.getOptions()); + this.controller = new QuickInputController(this.getOptions(), StandaloneServices.get(IStandaloneThemeService)); this.updateLayout(); } @@ -202,73 +211,53 @@ export class MonacoQuickInputImplementation implements IQuickInputService { const options: IQuickInputOptions = { idPrefix: 'quickInput_', container: this.container, - styles: { widget: {}, list: {}, inputBox: {}, countBadge: {}, button: {}, progressBar: {}, keybindingLabel: {}, }, + styles: this.computeStyles(), ignoreFocusOut: () => false, - isScreenReaderOptimized: () => false, // TODO change to true once support is added. backKeybindingLabel: () => undefined, setContextKey: (id?: string) => this.setContextKey(id), returnFocus: () => this.container.focus(), createList: ( user: string, container: HTMLElement, delegate: IListVirtualDelegate, renderers: IListRenderer[], listOptions: IListOptions ): List => this.quickInputList = new List(user, container, delegate, renderers, listOptions), + linkOpenerDelegate: () => { + // @monaco-uplift: not sure what to do here + } }; return options; } // @monaco-uplift // Keep the styles up to date with https://github.com/microsoft/vscode/blob/7888ff3a6b104e9e2e3d0f7890ca92dd0828215f/src/vs/platform/quickinput/browser/quickInput.ts#L171. - private getStyles(): IQuickInputStyles { + private computeStyles(): IQuickInputStyles { return { widget: { - quickInputBackground: this.colorRegistry.getColor('quickInput.background'), - quickInputForeground: this.colorRegistry.getColor('quickInput.foreground'), - quickInputTitleBackground: this.colorRegistry.getColor('quickInputTitle.background') - }, - list: { - listBackground: this.colorRegistry.getColor('quickInput.background'), - listInactiveFocusForeground: this.colorRegistry.getColor('quickInputList.focusForeground'), - listInactiveSelectionIconForeground: this.colorRegistry.getColor('quickInputList.focusIconForeground'), - listInactiveFocusBackground: this.colorRegistry.getColor('quickInputList.focusBackground'), - listFocusOutline: this.colorRegistry.getColor('activeContrastBorder'), - listInactiveFocusOutline: this.colorRegistry.getColor('activeContrastBorder'), - pickerGroupBorder: this.colorRegistry.getColor('pickerGroup.border'), - pickerGroupForeground: this.colorRegistry.getColor('pickerGroup.foreground') - }, - inputBox: { - inputForeground: this.colorRegistry.getColor('inputForeground'), - inputBackground: this.colorRegistry.getColor('inputBackground'), - inputBorder: this.colorRegistry.getColor('inputBorder'), - inputValidationInfoBackground: this.colorRegistry.getColor('inputValidation.infoBackground'), - inputValidationInfoForeground: this.colorRegistry.getColor('inputValidation.infoForeground'), - inputValidationInfoBorder: this.colorRegistry.getColor('inputValidation.infoBorder'), - inputValidationWarningBackground: this.colorRegistry.getColor('inputValidation.warningBackground'), - inputValidationWarningForeground: this.colorRegistry.getColor('inputValidation.warningForeground'), - inputValidationWarningBorder: this.colorRegistry.getColor('inputValidation.warningBorder'), - inputValidationErrorBackground: this.colorRegistry.getColor('inputValidation.errorBackground'), - inputValidationErrorForeground: this.colorRegistry.getColor('inputValidation.errorForeground'), - inputValidationErrorBorder: this.colorRegistry.getColor('inputValidation.errorBorder'), - }, - countBadge: { - badgeBackground: this.colorRegistry.getColor('badge.background'), - badgeForeground: this.colorRegistry.getColor('badge.foreground'), - badgeBorder: this.colorRegistry.getColor('contrastBorder') - }, - button: { - buttonForeground: this.colorRegistry.getColor('button.foreground'), - buttonBackground: this.colorRegistry.getColor('button.background'), - buttonHoverBackground: this.colorRegistry.getColor('button.hoverBackground'), - buttonBorder: this.colorRegistry.getColor('contrastBorder') - }, - progressBar: { - progressBarBackground: this.colorRegistry.getColor('progressBar.background') - }, - keybindingLabel: { - keybindingLabelBackground: this.colorRegistry.getColor('keybindingLabe.background'), - keybindingLabelForeground: this.colorRegistry.getColor('keybindingLabel.foreground'), - keybindingLabelBorder: this.colorRegistry.getColor('keybindingLabel.border'), - keybindingLabelBottomBorder: this.colorRegistry.getColor('keybindingLabel.bottomBorder'), - keybindingLabelShadow: this.colorRegistry.getColor('widget.shadow') + quickInputBackground: asCssVariable(quickInputBackground), + quickInputForeground: asCssVariable(quickInputForeground), + quickInputTitleBackground: asCssVariable(quickInputTitleBackground), + widgetBorder: asCssVariable(widgetBorder), + widgetShadow: asCssVariable(widgetShadow), }, + inputBox: defaultInputBoxStyles, + toggle: defaultToggleStyles, + countBadge: defaultCountBadgeStyles, + button: defaultButtonStyles, + progressBar: defaultProgressBarStyles, + keybindingLabel: defaultKeybindingLabelStyles, + list: getListStyles({ + listBackground: quickInputBackground, + listFocusBackground: quickInputListFocusBackground, + listFocusForeground: quickInputListFocusForeground, + // Look like focused when inactive. + listInactiveFocusForeground: quickInputListFocusForeground, + listInactiveSelectionIconForeground: quickInputListFocusIconForeground, + listInactiveFocusBackground: quickInputListFocusBackground, + listFocusOutline: activeContrastBorder, + listInactiveFocusOutline: activeContrastBorder, + }), + pickerGroup: { + pickerGroupBorder: asCssVariable(pickerGroupBorder), + pickerGroupForeground: asCssVariable(pickerGroupForeground), + } }; } } @@ -282,7 +271,8 @@ export class MonacoQuickInputService implements QuickInputService { protected readonly keybindingRegistry: KeybindingRegistry; get backButton(): QuickInputButton { - return this.monacoService.backButton; + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + return this.monacoService.backButton as QuickInputButton; } get onShow(): Event { return this.monacoService.onShow; } @@ -293,7 +283,8 @@ export class MonacoQuickInputService implements QuickInputService { } createInputBox(): InputBox { - return this.monacoService.createInputBox(); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + return this.monacoService.createInputBox() as InputBox; } input(options?: InputOptions, token?: monaco.CancellationToken): Promise { @@ -309,39 +300,9 @@ export class MonacoQuickInputService implements QuickInputService { } async pick = PickOptions>( - picks: Promise[]> | QuickPickInput[], options?: O, token?: monaco.CancellationToken - ): Promise<(O extends { canPickMany: true; } ? T[] : T) | undefined> { - type M = T & { buttons?: NormalizedQuickInputButton[] }; - type R = (O extends { canPickMany: true; } ? T[] : T); - - const monacoPicks: Promise[]> = new Promise(async resolve => { - const updatedPicks = (await picks).map(pick => { - if (pick.type !== 'separator') { - const icon = pick.iconPath; - // @monaco-uplift - // Other kind of icons (URI and URI dark/light) shall be supported once monaco editor has been upgraded to at least 1.81. - // see https://github.com/eclipse-theia/theia/pull/12945#issue-1913645228 - if (ThemeIcon.isThemeIcon(icon)) { - const codicon = codiconArray(icon.id); - if (pick.iconClasses) { - pick.iconClasses.push(...codicon); - } else { - pick.iconClasses = codicon; - } - } - pick.buttons &&= pick.buttons.map(QuickInputButton.normalize); - } - return pick as M; - }); - resolve(updatedPicks); - }); - const monacoOptions = options as IPickOptions; - const picked = await this.monacoService.pick(monacoPicks, monacoOptions, token); - if (!picked) { return picked; } - if (options?.canPickMany) { - return (Array.isArray(picked) ? picked : [picked]) as R; - } - return Array.isArray(picked) ? picked[0] : picked; + picks: Promise[]> | QuickPickInput[], options?: O, token?: CancellationToken + ): Promise { + return this.monacoService.pick(picks, options, token); } showQuickPick(items: Array, options?: QuickPickOptions): Promise { @@ -394,7 +355,8 @@ export class MonacoQuickInputService implements QuickInputService { }); wrapped.onDidTriggerButton((button: IQuickInputButton) => { if (options.onDidTriggerButton) { - options.onDidTriggerButton(button); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + options.onDidTriggerButton(button as QuickInputButton); } }); wrapped.onDidTriggerItemButton((event: QuickPickItemButtonEvent) => { @@ -577,7 +539,14 @@ class MonacoQuickPick extends MonacoQuickInput implemen } get items(): readonly (T | QuickPickSeparator)[] { - return this.wrapped.items.map(item => QuickPickSeparator.is(item) ? item : item.item); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + return this.wrapped.items.map(item => { + if (item instanceof MonacoQuickPickItem) { + return item.item; + } else { + return item; + } + }); } set items(itemList: readonly (T | QuickPickSeparator)[]) { @@ -610,12 +579,14 @@ class MonacoQuickPick extends MonacoQuickInput implemen readonly onDidAccept: Event<{ inBackground: boolean }> = this.wrapped.onDidAccept; readonly onDidChangeValue: Event = this.wrapped.onDidChangeValue; - readonly onDidTriggerButton: Event = this.wrapped.onDidTriggerButton; + + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + readonly onDidTriggerButton: Event = this.wrapped.onDidTriggerButton as Event; readonly onDidTriggerItemButton: Event> = Event.map(this.wrapped.onDidTriggerItemButton, (evt: IQuickPickItemButtonEvent>) => ({ item: evt.item.item, button: evt.button - })); + })) as Event>; readonly onDidChangeActive: Event = Event.map( this.wrapped.onDidChangeActive, (items: MonacoQuickPickItem[]) => items.map(item => item.item)); @@ -630,7 +601,7 @@ class MonacoQuickPick extends MonacoQuickInput implemen const monacoReferences: MonacoQuickPickItem[] = []; for (const item of items) { for (const wrappedItem of source) { - if (!QuickPickSeparator.is(wrappedItem) && wrappedItem.item === item) { + if (wrappedItem instanceof MonacoQuickPickItem && wrappedItem.item === item) { monacoReferences.push(wrappedItem); } } @@ -663,7 +634,7 @@ export class MonacoQuickPickItem implements IQuickPickI this.detail = item.detail; this.keybinding = item.keySequence ? new MonacoResolvedKeybinding(item.keySequence, kbRegistry) : undefined; this.iconClasses = item.iconClasses; - this.buttons = item.buttons?.map(QuickInputButton.normalize); + this.buttons = item.buttons; this.alwaysShow = item.alwaysShow; this.highlights = item.highlights; } diff --git a/packages/monaco/src/browser/monaco-resolved-keybinding.ts b/packages/monaco/src/browser/monaco-resolved-keybinding.ts index 526af0b90c390..347b85d96b373 100644 --- a/packages/monaco/src/browser/monaco-resolved-keybinding.ts +++ b/packages/monaco/src/browser/monaco-resolved-keybinding.ts @@ -16,7 +16,7 @@ import { KeyCode as MonacoKeyCode } from '@theia/monaco-editor-core/esm/vs/base/common/keyCodes'; import { - ChordKeybinding, KeybindingModifier, ResolvedKeybinding, ResolvedKeybindingPart, ScanCodeBinding, SimpleKeybinding + ResolvedKeybinding, ResolvedChord, SingleModifierChord, KeyCodeChord, Chord } from '@theia/monaco-editor-core/esm/vs/base/common/keybindings'; import { ElectronAcceleratorLabelProvider, UILabelProvider, UserSettingsLabelProvider } from '@theia/monaco-editor-core/esm/vs/base/common/keybindingLabels'; import { USLayoutResolvedKeybinding } from '@theia/monaco-editor-core/esm/vs/platform/keybinding/common/usLayoutResolvedKeybinding'; @@ -28,15 +28,15 @@ import { KEY_CODE_MAP } from './monaco-keycode-map'; export class MonacoResolvedKeybinding extends ResolvedKeybinding { - protected readonly parts: ResolvedKeybindingPart[]; + protected readonly chords: ResolvedChord[]; constructor(protected readonly keySequence: KeySequence, keybindingService: KeybindingRegistry) { super(); - this.parts = keySequence.map(keyCode => { + this.chords = keySequence.map(keyCode => { // eslint-disable-next-line no-null/no-null const keyLabel = keyCode.key ? keybindingService.acceleratorForKey(keyCode.key) : null; const keyAriaLabel = keyLabel; - return new ResolvedKeybindingPart( + return new ResolvedChord( keyCode.ctrl, keyCode.shift, keyCode.alt, @@ -48,43 +48,43 @@ export class MonacoResolvedKeybinding extends ResolvedKeybinding { } getLabel(): string | null { - return UILabelProvider.toLabel(MonacoPlatform.OS, this.parts, p => p.keyLabel); + return UILabelProvider.toLabel(MonacoPlatform.OS, this.chords, p => p.keyLabel); } getAriaLabel(): string | null { - return UILabelProvider.toLabel(MonacoPlatform.OS, this.parts, p => p.keyAriaLabel); + return UILabelProvider.toLabel(MonacoPlatform.OS, this.chords, p => p.keyAriaLabel); } getElectronAccelerator(): string | null { - if (this.isChord()) { + if (this.hasMultipleChords()) { // Electron cannot handle chords // eslint-disable-next-line no-null/no-null return null; } - return ElectronAcceleratorLabelProvider.toLabel(MonacoPlatform.OS, this.parts, p => p.keyLabel); + return ElectronAcceleratorLabelProvider.toLabel(MonacoPlatform.OS, this.chords, p => p.keyLabel); } getUserSettingsLabel(): string | null { - return UserSettingsLabelProvider.toLabel(MonacoPlatform.OS, this.parts, p => p.keyLabel); + return UserSettingsLabelProvider.toLabel(MonacoPlatform.OS, this.chords, p => p.keyLabel); } isWYSIWYG(): boolean { return true; } - isChord(): boolean { - return this.parts.length > 1; + hasMultipleChords(): boolean { + return this.chords.length > 1; } - getDispatchParts(): (string | null)[] { + getDispatchChords(): (string | null)[] { return this.keySequence.map(keyCode => USLayoutResolvedKeybinding.getDispatchStr(this.toKeybinding(keyCode))); } - getSingleModifierDispatchParts(): (KeybindingModifier | null)[] { + getSingleModifierDispatchChords(): (SingleModifierChord | null)[] { return this.keySequence.map(keybinding => this.getSingleModifierDispatchPart(keybinding)); } - protected getSingleModifierDispatchPart(code: KeyCode): KeybindingModifier | null { + protected getSingleModifierDispatchPart(code: KeyCode): SingleModifierChord | null { if (code.key?.keyCode === undefined) { return null; // eslint-disable-line no-null/no-null } @@ -103,8 +103,8 @@ export class MonacoResolvedKeybinding extends ResolvedKeybinding { return null; // eslint-disable-line no-null/no-null } - private toKeybinding(keyCode: KeyCode): SimpleKeybinding { - return new SimpleKeybinding( + private toKeybinding(keyCode: KeyCode): KeyCodeChord { + return new KeyCodeChord( keyCode.ctrl, keyCode.shift, keyCode.alt, @@ -113,16 +113,16 @@ export class MonacoResolvedKeybinding extends ResolvedKeybinding { ); } - public getParts(): ResolvedKeybindingPart[] { - return this.parts; + public getChords(): ResolvedChord[] { + return this.chords; } - static toKeybinding(keybindings: Array): string { + static toKeybinding(keybindings: Array): string { return keybindings.map(binding => this.keyCode(binding)).join(' '); } - static keyCode(keybinding: SimpleKeybinding | ScanCodeBinding): KeyCode { - const keyCode = keybinding instanceof SimpleKeybinding ? keybinding.keyCode : USLayoutResolvedKeybinding['_scanCodeToKeyCode'](keybinding.scanCode); + static keyCode(keybinding: Chord): KeyCode { + const keyCode = keybinding instanceof KeyCodeChord ? keybinding.keyCode : USLayoutResolvedKeybinding['_scanCodeToKeyCode'](keybinding.scanCode); const sequence: Keystroke = { first: Key.getKey(this.monaco2BrowserKeyCode(keyCode & 0xff)), modifiers: [] @@ -146,8 +146,8 @@ export class MonacoResolvedKeybinding extends ResolvedKeybinding { return KeyCode.createKeyCode(sequence); } - static keySequence(keybinding: ChordKeybinding): KeySequence { - return keybinding.parts.map(part => this.keyCode(part)); + static keySequence(keybinding: Chord[]): KeySequence { + return keybinding.map(part => this.keyCode(part)); } private static monaco2BrowserKeyCode(keyCode: MonacoKeyCode): number { diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index d4440b4d80946..bd1ecf07c5a97 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -194,6 +194,6 @@ export class MonacoTextModelService implements ITextModelService { } canHandleResource(resource: monaco.Uri): boolean { - return this.fileService.canHandleResource(new URI(resource)); + return this.fileService.canHandleResource(URI.fromComponents(resource)); } } diff --git a/packages/monaco/src/browser/monaco-workspace.ts b/packages/monaco/src/browser/monaco-workspace.ts index b832a74b89a0a..394c1b52a368e 100644 --- a/packages/monaco/src/browser/monaco-workspace.ts +++ b/packages/monaco/src/browser/monaco-workspace.ts @@ -230,7 +230,7 @@ export class MonacoWorkspace { }); } - async applyBulkEdit(edits: ResourceEdit[], options?: IBulkEditOptions): Promise { + async applyBulkEdit(edits: ResourceEdit[], options?: IBulkEditOptions): Promise { try { let totalEdits = 0; let totalFiles = 0; @@ -264,12 +264,12 @@ export class MonacoWorkspace { } const ariaSummary = this.getAriaSummary(totalEdits, totalFiles); - return { ariaSummary, success: true }; + return { ariaSummary, isApplied: true }; } catch (e) { console.error('Failed to apply Resource edits:', e); return { ariaSummary: `Error applying Resource edits: ${e.toString()}`, - success: false + isApplied: false }; } } @@ -369,27 +369,27 @@ export class MonacoWorkspace { const options = edit.options || {}; if (edit.newResource && edit.oldResource) { // rename - if (options.overwrite === undefined && options.ignoreIfExists && await this.fileService.exists(new URI(edit.newResource))) { + if (options.overwrite === undefined && options.ignoreIfExists && await this.fileService.exists(URI.fromComponents(edit.newResource))) { return; // not overwriting, but ignoring, and the target file exists } - await this.fileService.move(new URI(edit.oldResource), new URI(edit.newResource), { overwrite: options.overwrite }); + await this.fileService.move(URI.fromComponents(edit.oldResource), URI.fromComponents(edit.newResource), { overwrite: options.overwrite }); } else if (!edit.newResource && edit.oldResource) { // delete file - if (await this.fileService.exists(new URI(edit.oldResource))) { + if (await this.fileService.exists(URI.fromComponents(edit.oldResource))) { let useTrash = this.filePreferences['files.enableTrash']; - if (useTrash && !(this.fileService.hasCapability(new URI(edit.oldResource), FileSystemProviderCapabilities.Trash))) { + if (useTrash && !(this.fileService.hasCapability(URI.fromComponents(edit.oldResource), FileSystemProviderCapabilities.Trash))) { useTrash = false; // not supported by provider } - await this.fileService.delete(new URI(edit.oldResource), { useTrash, recursive: options.recursive }); + await this.fileService.delete(URI.fromComponents(edit.oldResource), { useTrash, recursive: options.recursive }); } else if (!options.ignoreIfNotExists) { throw new Error(`${edit.oldResource} does not exist and can not be deleted`); } } else if (edit.newResource && !edit.oldResource) { // create file - if (options.overwrite === undefined && options.ignoreIfExists && await this.fileService.exists(new URI(edit.newResource))) { + if (options.overwrite === undefined && options.ignoreIfExists && await this.fileService.exists(URI.fromComponents(edit.newResource))) { return; // not overwriting, but ignoring, and the target file exists } - await this.fileService.create(new URI(edit.newResource), undefined, { overwrite: options.overwrite }); + await this.fileService.create(URI.fromComponents(edit.newResource), undefined, { overwrite: options.overwrite }); } } } diff --git a/packages/monaco/src/browser/simple-monaco-editor.ts b/packages/monaco/src/browser/simple-monaco-editor.ts index 61096e5437796..eda4874ada840 100644 --- a/packages/monaco/src/browser/simple-monaco-editor.ts +++ b/packages/monaco/src/browser/simple-monaco-editor.ts @@ -136,7 +136,7 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab } protected getInstantiatorWithOverrides(override?: EditorServiceOverrides): IInstantiationService { - const instantiator = StandaloneServices.initialize({}); + const instantiator = StandaloneServices.get(IInstantiationService); if (override) { const overrideServices = new ServiceCollection(...override); return instantiator.createChild(overrideServices); diff --git a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts index 0f0eeca3b923a..2f4b8220e62d0 100644 --- a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts +++ b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts @@ -178,7 +178,7 @@ export class MonacoTextmateService implements FrontendApplicationContribution { protected waitForLanguage(language: string, cb: () => {}): Disposable { const languageService = StandaloneServices.get(ILanguageService) as LanguageService; - if (languageService['_encounteredLanguages'].has(language)) { + if (languageService['_requestedBasicLanguages'].has(language)) { cb(); return Disposable.NULL; } diff --git a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts b/packages/notebook/src/browser/service/notebook-cell-context-manager.ts index 200e3e2f4dff9..d49a2bc861cd1 100644 --- a/packages/notebook/src/browser/service/notebook-cell-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-cell-context-manager.ts @@ -67,7 +67,6 @@ export class NotebookCellContextManager implements NotebookCellContextManager, D dispose(): void { this.toDispose.dispose(); - this.currentStore?.dispose(); this.onDidChangeContextEmitter.dispose(); } } diff --git a/packages/output/package.json b/packages/output/package.json index fa994e74ac7f5..000281e99cabd 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -6,7 +6,7 @@ "@theia/core": "1.46.0", "@theia/editor": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2" }, @@ -48,4 +48,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/output/src/browser/output-editor-factory.ts b/packages/output/src/browser/output-editor-factory.ts index 4c1706ec68e81..c78803da024a7 100644 --- a/packages/output/src/browser/output-editor-factory.ts +++ b/packages/output/src/browser/output-editor-factory.ts @@ -35,10 +35,10 @@ export class OutputEditorFactory implements MonacoEditorFactory { readonly scheme: string = OutputUri.SCHEME; - create(model: MonacoEditorModel, defaultsOptions: MonacoEditor.IOptions, defaultOverrides: EditorServiceOverrides): MonacoEditor { + create(model: MonacoEditorModel, defaultsOptions: MonacoEditor.IOptions): MonacoEditor { const uri = new URI(model.uri); const options = this.createOptions(model, defaultsOptions); - const overrides = this.createOverrides(model, defaultOverrides); + const overrides = this.createOverrides(model); return new MonacoEditor(uri, model, document.createElement('div'), this.services, options, overrides); } @@ -62,13 +62,7 @@ export class OutputEditorFactory implements MonacoEditorFactory { }; } - protected *createOverrides(model: MonacoEditorModel, defaultOverrides: EditorServiceOverrides): EditorServiceOverrides { + protected *createOverrides(model: MonacoEditorModel): EditorServiceOverrides { yield [IContextMenuService, this.contextMenuService]; - for (const [identifier, provider] of defaultOverrides) { - if (identifier !== IContextMenuService) { - yield [identifier, provider]; - } - } } - } diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 8958523bb232d..ccdd5870dabd6 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -8,7 +8,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/navigator": "1.46.0", "@theia/plugin": "1.46.0", "@theia/plugin-ext": "1.46.0", @@ -58,4 +58,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index bdaa99e2df2d5..f1ccdb48d1986 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -17,7 +17,7 @@ "@theia/markers": "1.46.0", "@theia/messages": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/navigator": "1.46.0", "@theia/notebook": "1.46.0", "@theia/output": "1.46.0", @@ -97,4 +97,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts index 2c3ed04df81b9..38fb3fbffec78 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts @@ -335,12 +335,12 @@ export interface DocumentDropEditProviderMetadata { } export interface DataTransferFileDTO { + readonly id: string; readonly name: string; readonly uri?: UriComponents; } export interface DataTransferItemDTO { - readonly id: string; readonly asString: string; readonly fileData: DataTransferFileDTO | undefined; readonly uriListData?: ReadonlyArray; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 0364b384cf4a0..30bd564c9a560 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -114,7 +114,7 @@ import type { import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/shell-terminal-protocol'; import { ThemeType } from '@theia/core/lib/common/theme'; import { Disposable } from '@theia/core/lib/common/disposable'; -import { isString, isObject, PickOptions, QuickInputButtonHandle } from '@theia/core/lib/common'; +import { isString, isObject, QuickInputButtonHandle } from '@theia/core/lib/common'; import { Severity } from '@theia/core/lib/common/severity'; import { DebugConfiguration, DebugSessionOptions } from '@theia/debug/lib/common/debug-configuration'; import * as notebookCommon from '@theia/notebook/lib/common'; @@ -482,8 +482,6 @@ export interface StatusBarMessageRegistryMain { $dispose(id: string): void; } -export type Item = string | theia.QuickPickItem; - export interface QuickOpenExt { $onItemSelected(handle: number): void; $validateInput(input: string): Promise | undefined; @@ -501,7 +499,6 @@ export interface QuickOpenExt { token?: theia.CancellationToken): Promise | undefined>; showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; showQuickPick(itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; showInput(options?: theia.InputBoxOptions, token?: theia.CancellationToken): PromiseLike; // showWorkspaceFolderPick(options?: theia.WorkspaceFolderPickOptions, token?: theia.CancellationToken): Promise @@ -621,22 +618,36 @@ export interface WorkspaceFolderPickOptionsMain { ignoreFocusOut?: boolean; } -export type TransferQuickPickItems = TransferQuickPickItemValue | TransferQuickPickSeparator; - -export interface TransferQuickPickItemValue extends theia.QuickPickItem { +export interface TransferQuickPickItem { handle: number; - type?: 'item' + kind: 'item' | 'separator', + label: string; + iconPath?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon; + description?: string; + detail?: string; + picked?: boolean; + alwaysShow?: boolean; + buttons?: readonly TransferQuickInputButton[]; } -export interface TransferQuickPickSeparator extends theia.QuickPickItem { - handle: number; - type: 'separator'; +export interface TransferQuickPickOptions { + title?: string; + placeHolder?: string; + matchOnDescription?: boolean; + matchOnDetail?: boolean; + matchOnLabel?: boolean; + autoFocusOnList?: boolean; + ignoreFocusLost?: boolean; + canPickMany?: boolean; + contextKey?: string; + activeItem?: Promise | T; + onDidFocus?: (entry: T) => void; } -export interface TransferQuickInputButton extends theia.QuickInputButton { - iconPath: theia.Uri | { light: theia.Uri, dark: theia.Uri } | ThemeIcon; - iconClass?: string; +export interface TransferQuickInputButton { handle?: number; + readonly iconPath: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon; + readonly tooltip?: string | undefined; } export type TransferQuickInput = TransferQuickPick | TransferInputBox; @@ -655,7 +666,7 @@ export interface TransferQuickPick extends BaseTransferQuickInput { value?: string; placeholder?: string; buttons?: TransferQuickInputButton[]; - items?: TransferQuickPickItems[]; + items?: TransferQuickPickItem[]; activeItems?: ReadonlyArray; selectedItems?: ReadonlyArray; canSelectMany?: boolean; @@ -685,8 +696,8 @@ export interface IInputBoxOptions { } export interface QuickOpenMain { - $show(instance: number, options: PickOptions, token: CancellationToken): Promise; - $setItems(instance: number, items: TransferQuickPickItems[]): Promise; + $show(instance: number, options: TransferQuickPickOptions, token: CancellationToken): Promise; + $setItems(instance: number, items: TransferQuickPickItem[]): Promise; $setError(instance: number, error: Error): Promise; $input(options: theia.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise; $createOrUpdate(params: TransferQuickInput): Promise; diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 64e35536a529c..c298d7df1195b 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -189,10 +189,10 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport this.updateStoragePath()); const languageService = (StandaloneServices.get(ILanguageService) as LanguageService); - for (const language of languageService['_encounteredLanguages'] as Set) { + for (const language of languageService['_requestedBasicLanguages'] as Set) { this.activateByLanguage(language); } - languageService.onDidEncounterLanguage(language => this.activateByLanguage(language)); + languageService.onDidRequestBasicLanguageFeatures(language => this.activateByLanguage(language)); this.commands.onWillExecuteCommand(event => this.ensureCommandHandlerRegistration(event)); this.debugSessionManager.onWillStartDebugSession(event => this.ensureDebugActivation(event)); this.debugSessionManager.onWillResolveDebugConfiguration(event => this.ensureDebugActivation(event, 'onDebugResolve', event.debugType)); diff --git a/packages/plugin-ext/src/main/browser/comments/comments-contribution.ts b/packages/plugin-ext/src/main/browser/comments/comments-contribution.ts index a26c302bcf97a..ee144563b105d 100644 --- a/packages/plugin-ext/src/main/browser/comments/comments-contribution.ts +++ b/packages/plugin-ext/src/main/browser/comments/comments-contribution.ts @@ -26,6 +26,7 @@ import { CommandRegistry, DisposableCollection, MenuModelRegistry } from '@theia import { URI } from '@theia/core/shared/vscode-uri'; import { CommentsContextKeyService } from './comments-context-key-service'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { Uri } from '@theia/plugin'; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -64,14 +65,16 @@ export class CommentsContribution { if (editor instanceof MonacoDiffEditor) { const originalEditorModel = editor.diffEditor.getOriginalEditor().getModel(); if (originalEditorModel) { - const originalComments = await this.commentService.getComments(originalEditorModel.uri); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + const originalComments = await this.commentService.getComments(originalEditorModel.uri as Uri); if (originalComments) { this.rangeDecorator.update(editor.diffEditor.getOriginalEditor(), originalComments.filter(c => !!c)); } } const modifiedEditorModel = editor.diffEditor.getModifiedEditor().getModel(); if (modifiedEditorModel) { - const modifiedComments = await this.commentService.getComments(modifiedEditorModel.uri); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + const modifiedComments = await this.commentService.getComments(modifiedEditorModel.uri as Uri); if (modifiedComments) { this.rangeDecorator.update(editor.diffEditor.getModifiedEditor(), modifiedComments.filter(c => !!c)); } @@ -164,7 +167,8 @@ export class CommentsContribution { const editorModel = this.editor && this.editor.getModel(); const editorURI = this.editor && editorModel && editorModel.uri; if (editorURI) { - const comments = await this.commentService.getComments(editorURI); + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + const comments = await this.commentService.getComments(editorURI as Uri); this.setComments(comments.filter(c => !!c)); } } diff --git a/packages/plugin-ext/src/main/browser/data-transfer/data-transfer-type-converters.ts b/packages/plugin-ext/src/main/browser/data-transfer/data-transfer-type-converters.ts index b3c5c175ed473..c2e281e31f816 100644 --- a/packages/plugin-ext/src/main/browser/data-transfer/data-transfer-type-converters.ts +++ b/packages/plugin-ext/src/main/browser/data-transfer/data-transfer-type-converters.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { IDataTransferItem, VSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer'; +import { IDataTransferItem, IReadonlyVSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer'; import { DataTransferDTO, DataTransferItemDTO } from '../../../common/plugin-api-rpc-model'; import { URI } from '../../../plugin/types-impl'; @@ -24,7 +24,6 @@ export namespace DataTransferItem { if (mime === 'text/uri-list') { return { - id: item.id, asString: '', fileData: undefined, uriListData: serializeUriList(stringValue), @@ -33,9 +32,8 @@ export namespace DataTransferItem { const fileValue = item.asFile(); return { - id: item.id, asString: stringValue, - fileData: fileValue ? { name: fileValue.name, uri: fileValue.uri } : undefined, + fileData: fileValue ? { id: fileValue.id, name: fileValue.name, uri: fileValue.uri } : undefined, }; } @@ -57,10 +55,10 @@ export namespace DataTransferItem { } export namespace DataTransfer { - export async function toDataTransferDTO(value: VSDataTransfer): Promise { + export async function toDataTransferDTO(value: IReadonlyVSDataTransfer): Promise { return { items: await Promise.all( - Array.from(value.entries()) + Array.from(value) .map( async ([mime, item]) => [mime, await DataTransferItem.from(mime, item)] ) diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts index 6b6dcbbe6d5fb..99347fd5356d5 100644 --- a/packages/plugin-ext/src/main/browser/languages-main.ts +++ b/packages/plugin-ext/src/main/browser/languages-main.ts @@ -42,7 +42,7 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { SerializedDocumentFilter, MarkerData, Range, RelatedInformation, MarkerSeverity, DocumentLink, WorkspaceSymbolParams, CodeAction, CompletionDto, - CodeActionProviderDocumentation, InlayHint, InlayHintLabelPart, CodeActionContext, DocumentDropEditProviderMetadata + CodeActionProviderDocumentation, InlayHint, InlayHintLabelPart, CodeActionContext, DocumentDropEditProviderMetadata, SignatureHelpContext } from '../../common/plugin-api-rpc-model'; import { RPCProtocol } from '../../common/rpc-protocol'; import { MonacoLanguages, WorkspaceSymbolProvider } from '@theia/monaco/lib/browser/monaco-languages'; @@ -84,7 +84,7 @@ import { import { ITextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; import { CodeActionTriggerKind, SnippetString } from '../../plugin/types-impl'; import { DataTransfer } from './data-transfer/data-transfer-type-converters'; -import { VSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer'; +import { IReadonlyVSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer'; import { FileUploadService } from '@theia/filesystem/lib/browser/file-upload-service'; /** @@ -647,7 +647,9 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { protected async provideSignatureHelp(handle: number, model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken, context: monaco.languages.SignatureHelpContext): Promise> { - const value = await this.proxy.$provideSignatureHelp(handle, model.uri, position, context, token); + + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + const value = await this.proxy.$provideSignatureHelp(handle, model.uri, position, context as SignatureHelpContext, token); if (!value) { return undefined; } @@ -749,7 +751,7 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { } protected async provideDocumentDropEdits(handle: number, model: ITextModel, position: monaco.IPosition, - dataTransfer: VSDataTransfer, token: CancellationToken): Promise { + dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { await this.fileUploadService.upload(new URI(), { source: dataTransfer, leaveInTemp: true }); const edit = await this.proxy.$provideDocumentDropEdits(handle, model.uri, position, await DataTransfer.toDataTransferDTO(dataTransfer), token); if (edit) { @@ -759,6 +761,7 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { // yieldTo: edit.yieldTo?.map(yTo => { // return 'mimeType' in yTo ? yTo : { providerId: DocumentOnDropEditAdapter.toInternalProviderId(yTo.extensionId, yTo.providerId) }; // }), + label: 'no label', insertText: edit.insertText instanceof SnippetString ? { snippet: edit.insertText.value } : edit.insertText, additionalEdit: toMonacoWorkspaceEdit(edit?.additionalEdit) }; @@ -888,7 +891,8 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable { }; }, resolveInlayHint: async (hint, token): Promise => { - const dto: InlayHintDto = hint; + // need to cast because of vscode issue https://github.com/microsoft/vscode/issues/190584 + const dto: InlayHintDto = hint as InlayHintDto; if (typeof dto.cacheId !== 'number') { return hint; } diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index a7728f3816015..2755e258d5053 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -45,7 +45,6 @@ import { EditorModelService } from './text-editor-model-service'; import { OpenerService } from '@theia/core/lib/browser/opener-service'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; -import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; import { MainFileSystemEventService } from './main-file-system-event-service'; import { LabelServiceMainImpl } from './label-service-main'; import { TimelineMainImpl } from './timeline-main'; @@ -115,8 +114,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, new NotebookKernelsMainImpl(rpc, container)); const bulkEditService = container.get(MonacoBulkEditService); - const monacoEditorService = container.get(MonacoEditorService); - const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, bulkEditService, monacoEditorService); + const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, bulkEditService); rpc.set(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN, editorsMain); // start listening only after all clients are subscribed to events diff --git a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts index 4c23f436a7b8e..9410875ce1f1c 100644 --- a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts @@ -31,7 +31,7 @@ import { PluginMenuCommandAdapter, ReferenceCountingSet } from './plugin-menu-co import { ContextKeyExpr } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { PluginSharedStyle } from '../plugin-shared-style'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; @injectable() export class MenusContributionPointHandler { diff --git a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts index edd4584b67d73..96d02bbec2f60 100644 --- a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts @@ -47,13 +47,13 @@ import { PluginIconService } from './plugin-icon-service'; import { PluginIconThemeService } from './plugin-icon-theme-service'; import { ContributionProvider } from '@theia/core/lib/common'; import * as monaco from '@theia/monaco-editor-core'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; import { ContributedTerminalProfileStore, TerminalProfileStore } from '@theia/terminal/lib/browser/terminal-profile-service'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; import { PluginTerminalRegistry } from './plugin-terminal-registry'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { LanguageService } from '@theia/core/lib/browser/language-service'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; @injectable() export class PluginContributionHandler { diff --git a/packages/plugin-ext/src/main/browser/plugin-icon-service.ts b/packages/plugin-ext/src/main/browser/plugin-icon-service.ts index caef198ea31c0..18ad070c67a99 100644 --- a/packages/plugin-ext/src/main/browser/plugin-icon-service.ts +++ b/packages/plugin-ext/src/main/browser/plugin-icon-service.ts @@ -14,18 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { asCSSPropertyValue } from '@theia/monaco-editor-core/esm/vs/base/browser/dom'; import { Endpoint } from '@theia/core/lib/browser'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { getIconRegistry } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/iconRegistry'; import { inject, injectable } from '@theia/core/shared/inversify'; import { URI } from '@theia/core/shared/vscode-uri'; -import { IconFontDefinition, IconContribution as Icon } from '@theia/core/lib/browser/icon-registry'; import { MonacoIconRegistry } from '@theia/monaco/lib/browser/monaco-icon-registry'; import * as path from 'path'; import { IconContribution, DeployedPlugin, IconDefinition } from '../../common/plugin-protocol'; -import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; -import { UnthemedProductIconTheme } from '@theia/monaco-editor-core/esm/vs/platform/theme/browser/iconsStyleSheet'; @injectable() export class PluginIconService implements Disposable { @@ -45,29 +40,19 @@ export class PluginIconService implements Disposable { } else { this.registerRegularIcon(contribution, defaultIcon.id); } - this.updateStyle(contribution); return Disposable.NULL; } - updateStyle(contribution: IconContribution): void { - this.updateStyleElement(); - const css = this.getCSS(contribution); - if (css) { - this.styleElement.innerText = css; - } - const toRemoveStyleElement = Disposable.create(() => this.styleElement.remove()); - this.toDispose.push(toRemoveStyleElement); - } - dispose(): void { this.toDispose.dispose(); } protected registerFontIcon(contribution: IconContribution, defaultIcon: IconDefinition): void { - const location = defaultIcon.location; - const format = getFileExtension(location); - const fontId = getFontId(contribution.extensionId, location); - const definition = this.iconRegistry.registerIconFont(fontId, { src: [{ location: URI.file(location), format }] }); + const location = this.toPluginUrl(contribution.extensionId, getIconRelativePath(URI.parse(defaultIcon.location).path)); + const format = getFileExtension(location.path); + const fontId = getFontId(contribution.extensionId, location.path); + + const definition = this.iconRegistry.registerIconFont(fontId, { src: [{ location: location, format }] }); this.iconRegistry.registerIcon(contribution.id, { fontCharacter: defaultIcon.fontCharacter, font: { @@ -81,59 +66,10 @@ export class PluginIconService implements Disposable { this.iconRegistry.registerIcon(contribution.id, { id: defaultIconId }, contribution.description); } - protected updateStyleElement(): void { - if (!this.styleElement) { - const styleElement = document.createElement('style'); - styleElement.type = 'text/css'; - styleElement.media = 'screen'; - styleElement.id = 'contributedIconsStyles'; - document.head.appendChild(styleElement); - this.styleElement = styleElement; - } - } - protected getCSS(iconContribution: IconContribution, themeService?: IThemeService): string | undefined { - const iconRegistry = getIconRegistry(); - const productIconTheme = themeService ? themeService.getProductIconTheme() : new UnthemedProductIconTheme(); - const usedFontIds: { [id: string]: IconFontDefinition } = {}; - const formatIconRule = (contribution: Icon): string | undefined => { - const definition = productIconTheme.getIcon(contribution); - if (!definition) { - return undefined; - } - const fontContribution = definition.font; - if (fontContribution) { - usedFontIds[fontContribution.id] = fontContribution.definition; - return `.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; font-family: ${asCSSPropertyValue(iconContribution.extensionId)}; }`; - } - // default font (codicon) - return `.codicon-${contribution.id}:before { content: '${definition.fontCharacter}'; }`; - }; - - const rules = []; - for (const contribution of iconRegistry.getIcons()) { - const rule = formatIconRule(contribution); - if (rule) { - rules.push(rule); - } - } - for (const id in usedFontIds) { - if (id) { - const definition = usedFontIds[id]; - const fontWeight = definition.weight ? `font-weight: ${definition.weight};` : ''; - const fontStyle = definition.style ? `font-style: ${definition.style};` : ''; - const src = definition.src.map(icon => - `${this.toPluginUrl(iconContribution.extensionId, getIconRelativePath(icon.location.path))} format('${icon.format}')`) - .join(', '); - rules.push(`@font-face { src: ${src}; font-family: ${asCSSPropertyValue(iconContribution.extensionId)};${fontWeight}${fontStyle} font-display: block; }`); - } - } - return rules.join('\n'); - } - - protected toPluginUrl(id: string, relativePath: string): string { - return `url('${new Endpoint({ + protected toPluginUrl(id: string, relativePath: string): URI { + return URI.from(new Endpoint({ path: `hostedPlugin/${this.formatExtensionId(id)}/${encodeURIComponent(relativePath)}` - }).getRestUrl().toString()}')`; + }).getRestUrl().toComponents()); } protected formatExtensionId(id: string): string { diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index c65d81296886e..33777f26b0996 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -23,39 +23,47 @@ import { QuickOpenMain, MAIN_RPC_CONTEXT, TransferInputBox, - TransferQuickPickItems, + TransferQuickPickItem, TransferQuickInput, TransferQuickInputButton, - TransferQuickPickItemValue + TransferQuickPickOptions } from '../../common/plugin-api-rpc'; import { InputOptions, - PickOptions, + QuickInput, QuickInputButton, QuickInputButtonHandle, - QuickInputService + QuickInputService, + QuickPickItem, + codiconArray } from '@theia/core/lib/browser'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; import { MonacoQuickInputService } from '@theia/monaco/lib/browser/monaco-quick-input-service'; import { QuickInputButtons } from '../../plugin/types-impl'; -import { getIconPathOrClass } from '../../plugin/quick-open'; import * as monaco from '@theia/monaco-editor-core'; -import { IQuickPickItem, IQuickInput } from '@theia/monaco-editor-core/esm/vs/base/parts/quickinput/common/quickInput'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { UriComponents } from '../../common/uri-components'; +import { URI } from '@theia/core/shared/vscode-uri'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; +import { isUriComponents } from '@theia/monaco-editor-core/esm/vs/base/common/uri'; export interface QuickInputSession { - input: IQuickInput; - handlesToItems: Map; + input: QuickInput; + handlesToItems: Map; } +interface IconPath { + dark: URI, + light?: URI +}; + export class QuickOpenMainImpl implements QuickOpenMain, Disposable { private quickInputService: QuickInputService; private proxy: QuickOpenExt; private delegate: MonacoQuickInputService; private readonly items: Record = {}; @@ -71,33 +79,74 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { this.toDispose.dispose(); } - async $show(instance: number, options: PickOptions, token: CancellationToken): Promise { - const contents = new Promise((resolve, reject) => { + async $show(instance: number, options: TransferQuickPickOptions, token: CancellationToken): Promise { + const contents = new Promise((resolve, reject) => { this.items[instance] = { resolve, reject }; }); - options = { + const activeItem = await options.activeItem; + const transformedOptions = { ...options, onDidFocus: (el: any) => { if (el) { - this.proxy.$onItemSelected((el).handle); + this.proxy.$onItemSelected(Number.parseInt((el).id!)); } - } + }, + activeItem: activeItem ? this.toQuickPickItem(activeItem) : undefined }; - const result = await this.delegate.pick(contents, options, token); + const result = await this.delegate.pick(contents, transformedOptions, token); if (Array.isArray(result)) { - return result.map(({ handle }) => handle); + return result.map(({ id }) => Number.parseInt(id!)); } else if (result) { - return result.handle; + return Number.parseInt(result.id!); } return undefined; } - $setItems(instance: number, items: TransferQuickPickItems[]): Promise { + private normalizeIconPath(path: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon | undefined): { + iconPath?: IconPath + iconClasses?: string[] + } { + let iconClasses; + if (ThemeIcon.isThemeIcon(path)) { + const codicon = codiconArray(path.id); + iconClasses = codicon; + } + let iconPath; + if (isUriComponents(path)) { + iconPath = { dark: URI.from(path) }; + } else if (path && 'dark' in path) { + iconPath = { dark: URI.from(path.dark), light: URI.from(path.light) }; + } + return { + iconPath, + iconClasses + }; + } + + private toQuickPickItem(item: undefined): undefined; + private toQuickPickItem(item: TransferQuickPickItem): QuickPickItem + private toQuickPickItem(item: TransferQuickPickItem | undefined): QuickPickItem | undefined { + if (!item) { + return undefined; + } + return { + ...this.normalizeIconPath(item.iconPath), + type: 'item', + id: item.handle.toString(), + label: item.label, + description: item.description, + detail: item.detail, + alwaysShow: item.alwaysShow, + buttons: item.buttons ? this.convertToQuickInputButtons(item.buttons) : undefined + }; + } + + $setItems(instance: number, items: TransferQuickPickItem[]): Promise { if (this.items[instance]) { - this.items[instance].resolve(items); + this.items[instance].resolve(items.map(item => this.toQuickPickItem(item))); delete this.items[instance]; } return Promise.resolve(); @@ -201,17 +250,17 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { quickPick.onDidAccept(() => { this.proxy.$acceptOnDidAccept(sessionId); }); - quickPick.onDidChangeActive((items: Array) => { - this.proxy.$onDidChangeActive(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + quickPick.onDidChangeActive((items: QuickPickItem[]) => { + this.proxy.$onDidChangeActive(sessionId, items.map(item => Number.parseInt(item.id!))); }); - quickPick.onDidChangeSelection((items: Array) => { - this.proxy.$onDidChangeSelection(sessionId, items.map(item => (item as TransferQuickPickItems).handle)); + quickPick.onDidChangeSelection((items: QuickPickItem[]) => { + this.proxy.$onDidChangeSelection(sessionId, items.map(item => Number.parseInt(item.id!))); }); quickPick.onDidTriggerButton((button: QuickInputButtonHandle) => { this.proxy.$acceptOnDidTriggerButton(sessionId, button); }); quickPick.onDidTriggerItemButton(e => { - this.proxy.$onDidTriggerItemButton(sessionId, (e.item as TransferQuickPickItems).handle, (e.button as TransferQuickPickItems).handle); + this.proxy.$onDidTriggerItemButton(sessionId, Number.parseInt(e.item.id!), (e.button as TransferQuickPickItem).handle); }); quickPick.onDidChangeValue((value: string) => { this.proxy.$acceptDidChangeValue(sessionId, value); @@ -260,7 +309,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } } else if (param === 'items') { handlesToItems.clear(); - params[param].forEach((item: TransferQuickPickItems) => { + params[param].forEach((item: TransferQuickPickItem) => { handlesToItems.set(item.handle, item); }); (input as any)[param] = params[param]; @@ -314,9 +363,9 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { return Promise.resolve(undefined); } - private convertToQuickInputButtons(buttons: Array): Array { + private convertToQuickInputButtons(buttons: readonly TransferQuickInputButton[]): QuickInputButton[] { return buttons.map((button, i) => ({ - ...getIconPathOrClass(button), + ...this.normalizeIconPath(button.iconPath), tooltip: button.tooltip, handle: button === QuickInputButtons.Back ? -1 : i, } as QuickInputButton)); diff --git a/packages/plugin-ext/src/main/browser/scm-main.ts b/packages/plugin-ext/src/main/browser/scm-main.ts index 68af564e04111..ade01b4bc59d4 100644 --- a/packages/plugin-ext/src/main/browser/scm-main.ts +++ b/packages/plugin-ext/src/main/browser/scm-main.ts @@ -41,8 +41,8 @@ import { Splice } from '../../common/arrays'; import { UriComponents } from '../../common/uri-components'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { PluginSharedStyle } from './plugin-shared-style'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; import { IconUrl } from '../../common'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; export class PluginScmResourceGroup implements ScmResourceGroup { diff --git a/packages/plugin-ext/src/main/browser/text-editors-main.ts b/packages/plugin-ext/src/main/browser/text-editors-main.ts index 879a709a04c45..eab1b28fdb123 100644 --- a/packages/plugin-ext/src/main/browser/text-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editors-main.ts @@ -40,12 +40,13 @@ import { TextEditorMain } from './text-editor-main'; import { disposed } from '../../common/errors'; import { toMonacoWorkspaceEdit } from './languages-main'; import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; -import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service'; import { theiaUritoUriComponents, UriComponents } from '../../common/uri-components'; import { Endpoint } from '@theia/core/lib/browser/endpoint'; import * as monaco from '@theia/monaco-editor-core'; import { ResourceEdit } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; import { IDecorationRenderOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; export class TextEditorsMainImpl implements TextEditorsMain, Disposable { @@ -59,7 +60,6 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { private readonly documents: DocumentsMain, rpc: RPCProtocol, private readonly bulkEditService: MonacoBulkEditService, - private readonly monacoEditorService: MonacoEditorService, ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT); this.toDispose.push(editorsAndDocuments); @@ -131,8 +131,8 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { const workspaceEdit = toMonacoWorkspaceEdit(dto); try { const edits = ResourceEdit.convert(workspaceEdit); - const { success } = await this.bulkEditService.apply(edits, { respectAutoSaveConfig: metadata?.isRefactoring }); - return success; + const { isApplied } = await this.bulkEditService.apply(edits, { respectAutoSaveConfig: metadata?.isRefactoring }); + return isApplied; } catch { return false; } @@ -145,9 +145,9 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { return Promise.resolve(this.editorsAndDocuments.getEditor(id)!.insertSnippet(template, ranges, opts)); } - $registerTextEditorDecorationType(key: string, options: DecorationRenderOptions | IDecorationRenderOptions): void { + $registerTextEditorDecorationType(key: string, options: DecorationRenderOptions): void { this.injectRemoteUris(options); - this.monacoEditorService.registerDecorationType('Plugin decoration', key, options as IDecorationRenderOptions); + StandaloneServices.get(ICodeEditorService).registerDecorationType('Plugin decoration', key, options as IDecorationRenderOptions); this.toDispose.push(Disposable.create(() => this.$removeTextEditorDecorationType(key))); } @@ -177,7 +177,7 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { } $removeTextEditorDecorationType(key: string): void { - this.monacoEditorService.removeDecorationType(key); + StandaloneServices.get(ICodeEditorService).removeDecorationType(key); } $tryHideEditor(id: string): Promise { diff --git a/packages/plugin-ext/src/main/browser/theme-icon-override.ts b/packages/plugin-ext/src/main/browser/theme-icon-override.ts index 5ac10215ddb86..d22f101520463 100644 --- a/packages/plugin-ext/src/main/browser/theme-icon-override.ts +++ b/packages/plugin-ext/src/main/browser/theme-icon-override.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { getIconRegistry } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/iconRegistry'; // @monaco-uplift // Keep this up-to-date with the table at https://code.visualstudio.com/api/references/icons-in-labels#icon-listing @@ -27,11 +27,12 @@ const codeIconMap: Record = { 'callhierarchy-outgoing': 'call-outgoing', 'callstack-view-icon': 'debug-alt', 'callstack-view-session': 'bug', + 'chat-editor-label-icon': 'comment-discussion', 'comments-view-icon': 'comment-discussion', 'debug-breakpoint': 'debug-breakpoint', 'debug-breakpoint-conditional': 'debug-breakpoint-conditional', 'debug-breakpoint-conditional-disabled': 'debug-breakpoint-conditional-disabled', - 'debug-breakpoint-conditional-verified': 'debug-breakpoint-conditional-unverified', + 'debug-breakpoint-conditional-unverified': 'debug-breakpoint-conditional-unverified', 'debug-breakpoint-data': 'debug-breakpoint-data', 'debug-breakpoint-data-disabled': 'debug-breakpoint-data-disabled', 'debug-breakpoint-data-unverified': 'debug-breakpoint-data-unverified', @@ -173,9 +174,11 @@ const codeIconMap: Record = { 'remote-explorer-view-icon': 'remote-explorer', 'review-comment-collapse': 'chevron-up', 'run-view-icon': 'debug-alt', + 'runtime-extensions-editor-label-icon': ' extensions', 'search-clear-results': 'clear-all', 'search-collapse-results': 'collapse-all', 'search-details': 'ellipsis', + 'search-editor-label-icon': 'search', 'search-expand-results': 'expand-all', 'search-hide-replace': 'chevron-right', 'search-new-editor': 'new-file', @@ -190,6 +193,7 @@ const codeIconMap: Record = { 'settings-add': 'add', 'settings-discard': 'discard', 'settings-edit': 'edit', + 'settings-editor-label-icon': 'settings', 'settings-folder-dropdown': 'triangle-down', 'settings-group-collapsed': 'chevron-right', 'settings-group-expanded': 'chevron-down', @@ -229,24 +233,14 @@ const codeIconMap: Record = { 'watch-expressions-add-function-breakpoint': 'add', 'watch-expressions-remove-all': 'close-all', 'watch-view-icon': 'debug-alt', - 'widget-close': 'close' + 'widget-close': 'close', + 'workspace-trust-editor-label-icon': ' shield' }; -const originalAsCSSSelector = ThemeIcon.asCSSSelector; -const originalAsClassName = ThemeIcon.asClassName; -const originalAsClassNameArray = ThemeIcon.asClassNameArray; +const registry = getIconRegistry(); -function buildMappedIcon(icon: ThemeIcon): ThemeIcon { - const id = codeIconMap[icon.id] ?? icon.id; - const newIcon: ThemeIcon = { - ...icon, - id - }; - return newIcon; +for (const key in codeIconMap) { + if (codeIconMap.hasOwnProperty(key)) { + registry.registerIcon(key, { id: codeIconMap[key] }, key); + } } - -Object.assign(ThemeIcon, { - asCSSSelector: (icon: ThemeIcon) => originalAsCSSSelector(buildMappedIcon(icon)), - asClassName: (icon: ThemeIcon) => originalAsClassName(buildMappedIcon(icon)), - asClassNameArray: (icon: ThemeIcon) => originalAsClassNameArray(buildMappedIcon(icon)) -}); diff --git a/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts b/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts index 011ee0638e937..c5670f830cbe2 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-tree-view-node-label-provider.ts @@ -20,7 +20,7 @@ import { LabelProviderContribution, LabelProvider, URIIconReference } from '@the import { TreeLabelProvider } from '@theia/core/lib/browser/tree/tree-label-provider'; import { TreeViewNode } from './tree-view-widget'; import { TreeNode } from '@theia/core/lib/browser/tree/tree'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; @injectable() export class PluginTreeViewNodeLabelProvider implements LabelProviderContribution { diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 9698c259428f8..f11ad8383cfc7 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -40,7 +40,6 @@ import { DebugConsoleContribution } from '@theia/debug/lib/browser/console/debug import { TreeViewWidget } from './tree-view-widget'; import { SEARCH_VIEW_CONTAINER_ID } from '@theia/search-in-workspace/lib/browser/search-in-workspace-factory'; import { TEST_VIEW_CONTAINER_ID } from '@theia/test/lib/browser/view/test-view-contribution'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; import { WebviewView, WebviewViewResolver } from '../webview-views/webview-views'; import { WebviewWidget, WebviewWidgetIdentifier } from '../webview/webview'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; @@ -48,6 +47,7 @@ import { v4 } from 'uuid'; import { nls } from '@theia/core'; import { TheiaDockPanel } from '@theia/core/lib/browser/shell/theia-dock-panel'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; export const PLUGIN_VIEW_FACTORY_ID = 'plugin-view'; export const PLUGIN_VIEW_CONTAINER_FACTORY_ID = 'plugin-view-container'; diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts index cf8d1c1a47de4..a7c755d6516d5 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts @@ -75,7 +75,6 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti this.id = this.options.id; const localContext = this.contextKeyService.createScoped(this.node); localContext.setContext('view', this.options.viewId); - this.toDispose.push(localContext); } get onDidChangeDescription(): Event { diff --git a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx index f1f47f3aafa47..4524a90359991 100644 --- a/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx +++ b/packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx @@ -669,7 +669,7 @@ export class TreeViewWidget extends TreeViewWelcomeWidget { this.model.proxy!.$dragStarted(this.options.id, selectedNodes.map(selected => selected.id), CancellationToken.None).then(maybeUris => { if (maybeUris) { - this.applicationShell.addAdditionalDraggedEditorUris(maybeUris.map(URI.fromComponents)); + this.applicationShell.addAdditionalDraggedEditorUris(maybeUris.map(uri => URI.fromComponents(uri))); } }); } @@ -921,8 +921,7 @@ export class TreeViewWidget extends TreeViewWelcomeWidget { menuPath: contextMenuPath, anchor: { x, y }, args, - contextKeyService, - onHide: () => contextKeyService.dispose(), + contextKeyService }), 10); } } diff --git a/packages/plugin-ext/src/plugin/known-commands.ts b/packages/plugin-ext/src/plugin/known-commands.ts index 26233eeaa35a1..e8bf476056d14 100755 --- a/packages/plugin-ext/src/plugin/known-commands.ts +++ b/packages/plugin-ext/src/plugin/known-commands.ts @@ -261,6 +261,7 @@ export namespace KnownCommands { mappings['closeReferenceSearch'] = ['closeReferenceSearch', CONVERT_VSCODE_TO_MONACO]; mappings['goToNextReference'] = ['goToNextReference', CONVERT_VSCODE_TO_MONACO]; mappings['goToPreviousReference'] = ['goToPreviousReference', CONVERT_VSCODE_TO_MONACO]; + mappings['setContext'] = ['_setContext', CONVERT_VSCODE_TO_MONACO]; // eslint-disable-next-line @typescript-eslint/no-explicit-any const CONVERT_MONACO_TO_VSCODE = (args: any | undefined) => { diff --git a/packages/plugin-ext/src/plugin/markdown-string.ts b/packages/plugin-ext/src/plugin/markdown-string.ts index 51dff7c905859..217cb9f1c8a98 100644 --- a/packages/plugin-ext/src/plugin/markdown-string.ts +++ b/packages/plugin-ext/src/plugin/markdown-string.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { Mutable } from '@theia/core'; -import { MarkdownStringImpl as BaseMarkdownString, MarkdownString as MarkdownStringInterface } from '@theia/core/lib/common/markdown-rendering'; +import { MarkdownStringImpl as BaseMarkdownString, MarkdownString as MarkdownStringInterface, MarkdownStringTrustedOptions } from '@theia/core/lib/common/markdown-rendering'; import * as pluginAPI from '@theia/plugin'; import { es5ClassCompat } from '../common/types'; import { URI } from './types-impl'; @@ -49,11 +49,11 @@ export class MarkdownString implements pluginAPI.MarkdownString { this.#delegate.value = value; } - get isTrusted(): boolean | undefined { + get isTrusted(): boolean | MarkdownStringTrustedOptions | undefined { return this.#delegate.isTrusted; } - set isTrusted(value: boolean | undefined) { + set isTrusted(value: boolean | MarkdownStringTrustedOptions | undefined) { this.#delegate.isTrusted = value; } diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index 56523f0932507..c5ca8c39c830b 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { QuickOpenExt, PLUGIN_RPC_CONTEXT as Ext, QuickOpenMain, TransferInputBox, Plugin, - Item, TransferQuickInputButton, TransferQuickPickItems, TransferQuickInput + TransferQuickInputButton, TransferQuickInput, TransferQuickPickItem } from '../common/plugin-api-rpc'; import * as theia from '@theia/plugin'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; @@ -31,7 +31,7 @@ import { PluginPackage } from '../common/plugin-protocol'; import { QuickInputButtonHandle } from '@theia/core/lib/browser'; import { MaybePromise } from '@theia/core/lib/common/types'; import Severity from '@theia/monaco-editor-core/esm/vs/base/common/severity'; -import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; const canceledName = 'Canceled'; /** @@ -63,21 +63,7 @@ export function getDarkIconUri(iconPath: URI | { light: URI; dark: URI; }): URI return typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath; } -export function getIconPathOrClass(button: theia.QuickInputButton): { iconPath: { dark: URI; light?: URI | undefined; } | undefined; iconClass: string | undefined; } { - const iconPathOrIconClass = getIconUris(button.iconPath); - let iconPath: { dark: URI; light?: URI | undefined } | undefined; - let iconClass: string | undefined; - if ('id' in iconPathOrIconClass) { - iconClass = MonacoThemeIcon.asClassName(iconPathOrIconClass); - } else { - iconPath = iconPathOrIconClass; - } - - return { - iconPath, - iconClass - }; -} +type Item = theia.QuickPickItem | string; export class QuickOpenExtImpl implements QuickOpenExt { private proxy: QuickOpenMain; @@ -91,13 +77,13 @@ export class QuickOpenExtImpl implements QuickOpenExt { } /* eslint-disable max-len */ - showQuickPick(itemsOrItemsPromise: Array | Promise>, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; + showQuickPick(itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token: theia.CancellationToken = CancellationToken.None): Promise { this.onDidSelectItem = undefined; - const itemsPromise = >Promise.resolve(itemsOrItemsPromise); + const itemsPromise = Promise.resolve(itemsOrItemsPromise); const instance = ++this._instances; @@ -118,7 +104,7 @@ export class QuickOpenExtImpl implements QuickOpenExt { return undefined; } return itemsPromise.then(async items => { - const pickItems: Array = convertToTransferQuickPickItems(items); + const pickItems = convertToTransferQuickPickItems(items); if (options && typeof options.onDidSelectItem === 'function') { this.onDidSelectItem = handle => { @@ -646,14 +632,14 @@ export class QuickPickExt extends QuickInputExt i this._itemsToHandles.set(item, i); }); - const pickItems: TransferQuickPickItems[] = []; + const pickItems: TransferQuickPickItem[] = []; for (let handle = 0; handle < items.length; handle++) { const item = items[handle]; if (item.kind === QuickPickItemKind.Separator) { - pickItems.push({ type: 'separator', label: item.label, handle }); + pickItems.push({ kind: 'separator', label: item.label, handle }); } else { pickItems.push({ - kind: item.kind, + kind: 'item', label: item.label, iconPath: item.iconPath ? getIconUris(item.iconPath) : undefined, description: item.description, diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index 79219e4103157..d4833476c9186 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -26,7 +26,7 @@ import * as Converter from './type-converters'; import { Disposable, EnvironmentVariableMutatorType, TerminalExitReason, ThemeIcon } from './types-impl'; import { NO_ROOT_URI, SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/shell-terminal-protocol'; import { ProvidedTerminalLink } from '../common/plugin-api-rpc-model'; -import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; export function getIconUris(iconPath: theia.TerminalOptions['iconPath']): { id: string } | undefined { if (ThemeIcon.is(iconPath)) { diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index ba0d8140c3435..7c040d7df115f 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -35,6 +35,7 @@ import { CellRange, isTextStreamMime } from '@theia/notebook/lib/common'; import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering'; import { TestItemDTO, TestMessageDTO } from '../common/test-types'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; const SIDE_GROUP = -2; const ACTIVE_GROUP = -1; @@ -1212,23 +1213,57 @@ export function fromColorPresentation(colorPresentation: theia.ColorPresentation }; } -export function convertToTransferQuickPickItems(items: rpc.Item[]): rpc.TransferQuickPickItems[] { - return items.map((item, index) => { +export function convertIconPath(iconPath: types.URI | { light: types.URI; dark: types.URI } | theia.ThemeIcon | undefined): + UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon | undefined { + if (!iconPath) { + return undefined; + } + if (iconPath instanceof types.URI) { + return iconPath.toJSON(); + } else if ('dark' in iconPath) { + return { + dark: iconPath.dark.toJSON(), + light: iconPath.light?.toJSON() + }; + } else if (ThemeIcon.isThemeIcon(iconPath)) { + return { + id: iconPath.id, + color: iconPath.color ? { id: iconPath.color.id } : undefined + }; + } else { + return undefined; + } +} + +export function convertQuickInputButton(button: theia.QuickInputButton, index: number): rpc.TransferQuickInputButton { + const iconPath = convertIconPath(button.iconPath); + if (!iconPath) { + throw new Error(`Could not convert icon path: '${button.iconPath}'`); + } + return { + handle: index, + iconPath: iconPath, + tooltip: button.tooltip + }; +} + +export function convertToTransferQuickPickItems(items: (theia.QuickPickItem | string)[]): rpc.TransferQuickPickItem[] { + return items.map((item, index) => { if (typeof item === 'string') { - return { type: 'item', label: item, handle: index }; + return { kind: 'item', label: item, handle: index }; } else if (item.kind === QuickPickItemKind.Separator) { - return { type: 'separator', label: item.label, handle: index }; + return { kind: 'separator', label: item.label, handle: index }; } else { const { label, description, iconPath, detail, picked, alwaysShow, buttons } = item; return { - type: 'item', + kind: 'item', label, description, - iconPath, + iconPath: convertIconPath(iconPath), detail, picked, alwaysShow, - buttons, + buttons: buttons ? buttons.map(convertQuickInputButton) : undefined, handle: index, }; } @@ -1403,7 +1438,7 @@ export namespace DataTransferItem { return { name: file.name, uri: URI.revive(file.uri), - data: () => resolveFileData(item.id), + data: () => resolveFileData(file.id), }; } }(''); diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 1f63607b7cd5b..64fe79fd285d1 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -5,7 +5,7 @@ "dependencies": { "@theia/core": "1.46.0", "@theia/metrics": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/plugin": "1.46.0", "@theia/plugin-ext": "1.46.0" }, @@ -48,4 +48,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index e1cbea324ebed..f187bfffd0b5e 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -3,7 +3,7 @@ // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. +// http://www.eclipse.org/legal/epl-2.0.g // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse @@ -682,8 +682,12 @@ export module '@theia/plugin' { /** * Indicates that this markdown string is from a trusted source. Only *trusted* * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. + * + * Defaults to `false` (commands are disabled). + * + * If this is an object, only the set of commands listed in `enabledCommands` are allowed. */ - isTrusted?: boolean; + isTrusted?: boolean | { readonly enabledCommands: readonly string[] }; /** * Indicates that this markdown string can contain {@link ThemeIcon ThemeIcons}, e.g. `$(zap)`. diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 32d66867944f6..e64681fedd75b 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -7,7 +7,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/userstorage": "1.46.0", "@theia/workspace": "1.46.0", "async-mutex": "^0.3.1", @@ -53,4 +53,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/task/package.json b/packages/task/package.json index e86a3ecfa0a33..ce7ef70fa32c0 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -8,7 +8,7 @@ "@theia/filesystem": "1.46.0", "@theia/markers": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/process": "1.46.0", "@theia/terminal": "1.46.0", "@theia/userstorage": "1.46.0", @@ -57,4 +57,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 03ae12987d1a7..38eb066cf8513 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -32,7 +32,7 @@ "@theia/file-search": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", - "@theia/monaco-editor-core": "1.72.3", + "@theia/monaco-editor-core": "1.83.101", "@theia/search-in-workspace": "1.46.0", "@theia/userstorage": "1.46.0", "@theia/workspace": "1.46.0", @@ -48,4 +48,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 43bec804e016e..7306b5d7bfec2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1707,10 +1707,10 @@ dependencies: defer-to-connect "^2.0.0" -"@theia/monaco-editor-core@1.72.3": - version "1.72.3" - resolved "https://registry.yarnpkg.com/@theia/monaco-editor-core/-/monaco-editor-core-1.72.3.tgz#911d674c6e0c490442a355cfaa52beec919a025e" - integrity sha512-2FK5m0G5oxiqCv0ZrjucMx5fVgQ9Jqv0CgxGvSzDc4wRrauBdeBoX90J99BEIOJ8Jp3W0++GoRBdh0yQNIGL2g== +"@theia/monaco-editor-core@1.83.101": + version "1.83.101" + resolved "https://registry.yarnpkg.com/@theia/monaco-editor-core/-/monaco-editor-core-1.83.101.tgz#a0577396fb4c69540536df2d7fed2de4399c4fde" + integrity sha512-UaAi6CEvain/qbGD3o6Ufe8plLyzAVQ53p9Ke+MoBYDhb391+r+MuK++JtITqIrXqoa8OCjbt8wQxEFSNNO0Mw== "@tootallnate/once@1", "@tootallnate/once@^1.1.2": version "1.1.2" From f442b7dba25be1098dd2694486719767d6febc70 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Fri, 16 Feb 2024 14:16:08 +0100 Subject: [PATCH 090/441] Install vsix files from the explorer view (#13269) (#13291) --- packages/vsx-registry/package.json | 1 + .../src/browser/vsx-extension-commands.ts | 5 ++ .../browser/vsx-extensions-contribution.ts | 76 +++++++++++++------ .../src/browser/vsx-extensions-widget.tsx | 11 ++- packages/vsx-registry/tsconfig.json | 3 + 5 files changed, 71 insertions(+), 25 deletions(-) diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 65f6aa0e4ffc8..9cb6671888d99 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -5,6 +5,7 @@ "dependencies": { "@theia/core": "1.46.0", "@theia/filesystem": "1.46.0", + "@theia/navigator": "1.46.0", "@theia/ovsx-client": "1.46.0", "@theia/plugin-ext": "1.46.0", "@theia/plugin-ext-vscode": "1.46.0", diff --git a/packages/vsx-registry/src/browser/vsx-extension-commands.ts b/packages/vsx-registry/src/browser/vsx-extension-commands.ts index e79ba161d5ed3..091563201bca9 100644 --- a/packages/vsx-registry/src/browser/vsx-extension-commands.ts +++ b/packages/vsx-registry/src/browser/vsx-extension-commands.ts @@ -36,6 +36,11 @@ export namespace VSXExtensionsCommands { label: nls.localizeByDefault('Install from VSIX') + '...', dialogLabel: nls.localizeByDefault('Install from VSIX') }; + export const INSTALL_VSIX_FILE: Command = Command.toDefaultLocalizedCommand({ + id: 'vsxExtensions.installVSIX', + label: 'Install Extension VSIX', + category: EXTENSIONS_CATEGORY, + }); export const INSTALL_ANOTHER_VERSION: Command = { id: 'vsxExtensions.installAnotherVersion' }; diff --git a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts index b301d3a0fc75d..3cffe55b311da 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts +++ b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts @@ -14,29 +14,32 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { DateTime } from 'luxon'; -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; -import debounce = require('@theia/core/shared/lodash.debounce'); -import { Command, CommandRegistry } from '@theia/core/lib/common/command'; -import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; -import { VSXExtensionsViewContainer } from './vsx-extensions-view-container'; -import { VSXExtensionsModel } from './vsx-extensions-model'; +import { CommonMenus, LabelProvider, PreferenceService, QuickInputService, QuickPickItem } from '@theia/core/lib/browser'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; -import { Color } from '@theia/core/lib/common/color'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution'; -import { MenuModelRegistry, MessageService, nls } from '@theia/core/lib/common'; +import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; +import { MenuModelRegistry, MessageService, SelectionService, nls } from '@theia/core/lib/common'; +import { Color } from '@theia/core/lib/common/color'; +import { Command, CommandRegistry } from '@theia/core/lib/common/command'; +import URI from '@theia/core/lib/common/uri'; +import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { FileDialogService, OpenFileDialogProps } from '@theia/filesystem/lib/browser'; -import { LabelProvider, PreferenceService, QuickPickItem, QuickInputService, CommonMenus } from '@theia/core/lib/browser'; +import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution'; +import { OVSXApiFilter, VSXExtensionRaw } from '@theia/ovsx-client'; import { VscodeCommands } from '@theia/plugin-ext-vscode/lib/browser/plugin-vscode-commands-contribution'; -import { VSXExtensionsContextMenu, VSXExtension } from './vsx-extension'; -import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; -import { BUILTIN_QUERY, INSTALLED_QUERY, RECOMMENDED_QUERY } from './vsx-extensions-search-model'; +import { DateTime } from 'luxon'; +import { OVSXClientProvider } from '../common/ovsx-client-provider'; import { IGNORE_RECOMMENDATIONS_ID } from './recommended-extensions/recommended-extensions-preference-contribution'; +import { VSXExtension, VSXExtensionsContextMenu } from './vsx-extension'; import { VSXExtensionsCommands } from './vsx-extension-commands'; -import { VSXExtensionRaw, OVSXApiFilter } from '@theia/ovsx-client'; -import { OVSXClientProvider } from '../common/ovsx-client-provider'; +import { VSXExtensionsModel } from './vsx-extensions-model'; +import { BUILTIN_QUERY, INSTALLED_QUERY, RECOMMENDED_QUERY } from './vsx-extensions-search-model'; +import { VSXExtensionsViewContainer } from './vsx-extensions-view-container'; +import debounce = require('@theia/core/shared/lodash.debounce'); export namespace VSXCommands { export const TOGGLE_EXTENSIONS: Command = { @@ -57,6 +60,7 @@ export class VSXExtensionsContribution extends AbstractViewContribution this.installFromVSIX() }); + commands.registerCommand(VSXExtensionsCommands.INSTALL_VSIX_FILE, + UriAwareCommandHandler.MonoSelect(this.selectionService, { + execute: fileURI => this.installVsixFile(fileURI), + isEnabled: fileURI => fileURI.scheme === 'file' && fileURI.path.ext === '.vsix' + }) + ); + commands.registerCommand(VSXExtensionsCommands.INSTALL_ANOTHER_VERSION, { // Check downloadUrl to ensure we have an idea of where to look for other versions. isEnabled: (extension: VSXExtension) => !extension.builtin && !!extension.downloadUrl, @@ -143,6 +154,11 @@ export class VSXExtensionsContribution extends AbstractViewContribution { + const extensionName = this.labelProvider.getName(fileURI); + try { + await this.commandRegistry.executeCommand(VscodeCommands.INSTALL_FROM_VSIX.id, fileURI); + this.messageService.info(nls.localizeByDefault('Completed installing {0} extension from VSIX.', extensionName)); + } catch (e) { + this.messageService.error(nls.localize('theia/vsx-registry/failedInstallingVSIX', 'Failed to install {0} from VSIX.', extensionName)); + console.warn(e); + } + } + /** * Given an extension, displays a quick pick of other compatible versions and installs the selected version. * diff --git a/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx b/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx index 6b79ed3dea49f..63e2a38579cc7 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx +++ b/packages/vsx-registry/src/browser/vsx-extensions-widget.tsx @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable, interfaces, postConstruct, inject } from '@theia/core/shared/inversify'; -import { TreeModel, TreeNode } from '@theia/core/lib/browser'; +import { Message, TreeModel, TreeNode } from '@theia/core/lib/browser'; import { SourceTreeWidget } from '@theia/core/lib/browser/source-tree'; import { VSXExtensionsSource, VSXExtensionsSourceOptions } from './vsx-extensions-source'; import { nls } from '@theia/core/lib/common/nls'; @@ -153,4 +153,13 @@ export class VSXExtensionsWidget extends SourceTreeWidget implements BadgeWidget } return super.renderTree(model); } + + protected override onAfterShow(msg: Message): void { + super.onAfterShow(msg); + if (this.options.id === VSXExtensionsSourceOptions.INSTALLED) { + // This is needed when an Extension was installed outside of the extension view. + // E.g. using explorer context menu. + this.doUpdateRows(); + } + } } diff --git a/packages/vsx-registry/tsconfig.json b/packages/vsx-registry/tsconfig.json index ba51f2ea3a5ee..c2d9957cb1436 100644 --- a/packages/vsx-registry/tsconfig.json +++ b/packages/vsx-registry/tsconfig.json @@ -18,6 +18,9 @@ { "path": "../filesystem" }, + { + "path": "../navigator" + }, { "path": "../plugin-ext" }, From 81111d1adc6e6bdd1c0bb6764085b9fb0ddb038e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 19 Feb 2024 16:08:34 +0100 Subject: [PATCH 091/441] Upgrade msgpackr to 1.10.1. Fixes #13363 (#13365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- examples/api-tests/src/saveable.spec.js | 2 +- examples/api-tests/src/typescript.spec.js | 1 - packages/core/package.json | 2 +- .../src/plugin/languages/link-provider.ts | 4 +- .../plugin-ext/src/plugin/type-converters.ts | 3 +- yarn.lock | 90 +++++++++---------- 6 files changed, 52 insertions(+), 50 deletions(-) diff --git a/examples/api-tests/src/saveable.spec.js b/examples/api-tests/src/saveable.spec.js index e4cec574174a6..5a50637b786ca 100644 --- a/examples/api-tests/src/saveable.spec.js +++ b/examples/api-tests/src/saveable.spec.js @@ -16,7 +16,7 @@ // @ts-check describe('Saveable', function () { - this.timeout(5000); + this.timeout(30000); const { assert } = chai; diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index 4b2cda473bf99..ff2da90e99141 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -776,7 +776,6 @@ SPAN { assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength); }); - it('Can execute code actions', async function () { const editor = await openEditor(demoFileUri); /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionController').CodeActionController} */ diff --git a/packages/core/package.json b/packages/core/package.json index e169e74cd67b0..7c642850c2f28 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -55,7 +55,7 @@ "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "markdown-it": "^12.3.2", - "msgpackr": "1.6.1", + "msgpackr": "^1.10.1", "nsfw": "^2.2.4", "p-debounce": "^2.1.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/plugin-ext/src/plugin/languages/link-provider.ts b/packages/plugin-ext/src/plugin/languages/link-provider.ts index bb06c3be251bf..67050de012db8 100644 --- a/packages/plugin-ext/src/plugin/languages/link-provider.ts +++ b/packages/plugin-ext/src/plugin/languages/link-provider.ts @@ -33,7 +33,9 @@ export class LinkProviderAdapter { provideLinks(resource: URI, token: theia.CancellationToken): Promise { const document = this.documents.getDocumentData(resource); if (!document) { - return Promise.reject(new Error(`There is no document for ${resource}`)); + // not all documents are replicated to the plugin host (e.g. breakpoint input) + console.warn(`There is no document for ${resource}`); + return Promise.resolve(undefined); } const doc = document.document; diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 7c040d7df115f..22f679de04007 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -748,9 +748,10 @@ export function isModelRange(arg: unknown): arg is model.Range { export function isUriComponents(arg: unknown): arg is UriComponents { return isObject(arg) && typeof arg.scheme === 'string' && + (arg['$mid'] === 1 || ( typeof arg.path === 'string' && typeof arg.query === 'string' && - typeof arg.fragment === 'string'; + typeof arg.fragment === 'string')); } export function isModelCallHierarchyItem(arg: unknown): arg is model.CallHierarchyItem { diff --git a/yarn.lock b/yarn.lock index 7306b5d7bfec2..a8d20abc7a481 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1174,35 +1174,35 @@ dependencies: cross-spawn "^7.0.1" -"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76" - integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ== +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz#44d752c1a2dc113f15f781b7cc4f53a307e3fa38" + integrity sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ== -"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066" - integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw== +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz#f954f34355712212a8e06c465bc06c40852c6bb3" + integrity sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw== -"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9" - integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA== +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz#45c63037f045c2b15c44f80f0393fa24f9655367" + integrity sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg== -"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce" - integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg== +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz#35707efeafe6d22b3f373caf9e8775e8920d1399" + integrity sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA== -"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2" - integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw== +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz#091b1218b66c341f532611477ef89e83f25fae4f" + integrity sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA== -"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64" - integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA== +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz#0f164b726869f71da3c594171df5ebc1c4b0a407" + integrity sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -8178,26 +8178,26 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -msgpackr-extract@^2.0.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262" - integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog== +msgpackr-extract@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz#e05ec1bb4453ddf020551bcd5daaf0092a2c279d" + integrity sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A== dependencies: - node-gyp-build-optional-packages "5.0.3" + node-gyp-build-optional-packages "5.0.7" optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0" - "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0" - "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0" - "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0" - -msgpackr@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.6.1.tgz#4f3c94d6a5b819b838ffc736eddaf60eba436d20" - integrity sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA== + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" + +msgpackr@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" + integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== optionalDependencies: - msgpackr-extract "^2.0.2" + msgpackr-extract "^3.0.2" multer@1.4.4-lts.1: version "1.4.4-lts.1" @@ -8360,10 +8360,10 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-gyp-build-optional-packages@5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17" - integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA== +node-gyp-build-optional-packages@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz#5d2632bbde0ab2f6e22f1bbac2199b07244ae0b3" + integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: version "4.6.1" From c9bbfe5502af485ea1f7b6d45c2f4136ec6ab046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 20 Feb 2024 15:09:10 +0100 Subject: [PATCH 092/441] Support workspace.save(URI) and workspace.saveAs(URI) (#13393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13352 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../core/src/browser/save-resource-service.ts | 9 +++--- .../src/browser/shell/application-shell.ts | 4 ++- .../filesystem-save-resource-service.ts | 5 +-- .../plugin-ext/src/common/plugin-api-rpc.ts | 2 ++ .../plugin-ext/src/common/uri-components.ts | 6 +--- .../custom-editors/custom-editor-widget.ts | 4 ++- .../browser/editors-and-documents-main.ts | 32 ++++++++++++++++--- .../browser/selection-provider-command.ts | 3 +- .../src/main/browser/text-editors-main.ts | 14 ++++++-- .../plugin-ext/src/plugin/plugin-context.ts | 7 ++++ .../plugin-ext/src/plugin/text-editors.ts | 11 +++++-- packages/plugin/src/theia.d.ts | 23 +++++++++++++ .../workspace-frontend-contribution.ts | 2 +- 13 files changed, 96 insertions(+), 26 deletions(-) diff --git a/packages/core/src/browser/save-resource-service.ts b/packages/core/src/browser/save-resource-service.ts index 6eb5bd876e43d..82de2244315ab 100644 --- a/packages/core/src/browser/save-resource-service.ts +++ b/packages/core/src/browser/save-resource-service.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import { inject, injectable } from 'inversify'; -import { MessageService, UNTITLED_SCHEME } from '../common'; +import { MessageService, UNTITLED_SCHEME, URI } from '../common'; import { Navigatable, NavigatableWidget } from './navigatable-types'; import { Saveable, SaveableSource, SaveOptions } from './saveable'; import { Widget } from './widgets'; @@ -41,11 +41,12 @@ export class SaveResourceService { * * No op if the widget is not saveable. */ - async save(widget: Widget | undefined, options?: SaveOptions): Promise { + async save(widget: Widget | undefined, options?: SaveOptions): Promise { if (this.canSaveNotSaveAs(widget)) { await Saveable.save(widget, options); + return NavigatableWidget.getUri(widget); } else if (this.canSaveAs(widget)) { - await this.saveAs(widget, options); + return this.saveAs(widget, options); } } @@ -53,7 +54,7 @@ export class SaveResourceService { return false; } - saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { + saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { return Promise.reject('Unsupported: The base SaveResourceService does not support saveAs action.'); } } diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index 3e86fb211fe78..d8a271210b963 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -1229,7 +1229,9 @@ export class ApplicationShell extends Widget { Saveable.apply( widget, () => this.widgets.filter((maybeSaveable): maybeSaveable is Widget & SaveableSource => !!Saveable.get(maybeSaveable)), - (toSave, options) => this.saveResourceService.save(toSave, options), + async (toSave, options) => { + await this.saveResourceService.save(toSave, options); + }, ); if (ApplicationShell.TrackableWidgetProvider.is(widget)) { for (const toTrack of widget.getTrackableWidgets()) { diff --git a/packages/filesystem/src/browser/filesystem-save-resource-service.ts b/packages/filesystem/src/browser/filesystem-save-resource-service.ts index d35d772293296..f992350d5fa78 100644 --- a/packages/filesystem/src/browser/filesystem-save-resource-service.ts +++ b/packages/filesystem/src/browser/filesystem-save-resource-service.ts @@ -47,7 +47,7 @@ export class FilesystemSaveResourceService extends SaveResourceService { /** * Save `sourceWidget` to a new file picked by the user. */ - override async saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { + override async saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { let exist: boolean = false; let overwrite: boolean = false; let selected: URI | undefined; @@ -68,10 +68,11 @@ export class FilesystemSaveResourceService extends SaveResourceService { } } while ((selected && exist && !overwrite) || (selected?.isEqual(uri) && !canSave)); if (selected && selected.isEqual(uri)) { - await this.save(sourceWidget, options); + return this.save(sourceWidget, options); } else if (selected) { try { await this.copyAndSave(sourceWidget, selected, overwrite); + return selected; } catch (e) { console.warn(e); } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 30bd564c9a560..7539bd03dd869 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1301,6 +1301,8 @@ export interface TextEditorsMain { $tryApplyEdits(id: string, modelVersionId: number, edits: SingleEditOperation[], opts: ApplyEditsOptions): Promise; $tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto, metadata?: WorkspaceEditMetadataDto): Promise; $tryInsertSnippet(id: string, template: string, selections: Range[], opts: UndoStopOptions): Promise; + $save(uri: UriComponents): PromiseLike; + $saveAs(uri: UriComponents): PromiseLike; $saveAll(includeUntitled?: boolean): Promise; // $getDiffInformation(id: string): Promise; } diff --git a/packages/plugin-ext/src/common/uri-components.ts b/packages/plugin-ext/src/common/uri-components.ts index f67a95657f3bd..447cecc930818 100644 --- a/packages/plugin-ext/src/common/uri-components.ts +++ b/packages/plugin-ext/src/common/uri-components.ts @@ -18,7 +18,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import URI, { UriComponents } from '@theia/core/lib/common/uri'; +import { UriComponents } from '@theia/core/lib/common/uri'; export { UriComponents }; @@ -79,7 +79,3 @@ export namespace Schemes { export const webviewPanel = 'webview-panel'; } - -export function theiaUritoUriComponents(uri: URI): UriComponents { - return uri.toComponents(); -} diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts index cee4ee0fc9416..4f2e183a9063a 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts @@ -41,7 +41,9 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, Saveable.apply( this, () => this.shell.widgets.filter(widget => !!Saveable.get(widget)), - (widget, options) => this.saveService.save(widget, options), + async (widget, options) => { + await this.saveService.save(widget, options); + }, ); } get saveable(): Saveable { diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index 125cfad683fa6..fc4fb43d05afc 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -30,8 +30,9 @@ import { EditorModelService } from './text-editor-model-service'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { TextEditorMain } from './text-editor-main'; -import { DisposableCollection, Emitter } from '@theia/core'; +import { DisposableCollection, Emitter, URI } from '@theia/core'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; +import { SaveResourceService } from '@theia/core/lib/browser/save-resource-service'; export class EditorsAndDocumentsMain implements Disposable { @@ -41,7 +42,8 @@ export class EditorsAndDocumentsMain implements Disposable { private readonly textEditors = new Map(); private readonly modelService: EditorModelService; - private readonly editorService: EditorManager; + private readonly editorManager: EditorManager; + private readonly saveResourceService: SaveResourceService; private readonly onTextEditorAddEmitter = new Emitter(); private readonly onTextEditorRemoveEmitter = new Emitter(); @@ -60,10 +62,11 @@ export class EditorsAndDocumentsMain implements Disposable { constructor(rpc: RPCProtocol, container: interfaces.Container) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT); - this.editorService = container.get(EditorManager); + this.editorManager = container.get(EditorManager); this.modelService = container.get(EditorModelService); + this.saveResourceService = container.get(SaveResourceService); - this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorService, this.modelService); + this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService); this.toDispose.push(this.stateComputer); this.toDispose.push(this.onTextEditorAddEmitter); this.toDispose.push(this.onTextEditorRemoveEmitter); @@ -167,12 +170,31 @@ export class EditorsAndDocumentsMain implements Disposable { return this.textEditors.get(id); } + async save(uri: URI): Promise { + const editor = await this.editorManager.getByUri(uri); + if (!editor) { + return undefined; + } + return this.saveResourceService.save(editor); + } + + async saveAs(uri: URI): Promise { + const editor = await this.editorManager.getByUri(uri); + if (!editor) { + return undefined; + } + if (!this.saveResourceService.canSaveAs(editor)) { + return undefined; + } + return this.saveResourceService.saveAs(editor); + } + saveAll(includeUntitled?: boolean): Promise { return this.modelService.saveAll(includeUntitled); } hideEditor(id: string): Promise { - for (const editorWidget of this.editorService.all) { + for (const editorWidget of this.editorManager.all) { const monacoEditor = MonacoEditor.get(editorWidget); if (monacoEditor) { if (id === new EditorSnapshot(monacoEditor).id) { diff --git a/packages/plugin-ext/src/main/browser/selection-provider-command.ts b/packages/plugin-ext/src/main/browser/selection-provider-command.ts index 081ce28490d8c..700e6e577cc30 100644 --- a/packages/plugin-ext/src/main/browser/selection-provider-command.ts +++ b/packages/plugin-ext/src/main/browser/selection-provider-command.ts @@ -19,7 +19,6 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { UriAwareCommandHandler, UriCommandHandler } from '@theia/core/lib/common/uri-command-handler'; import URI from '@theia/core/lib/common/uri'; import { SelectionService } from '@theia/core'; -import { theiaUritoUriComponents } from '../../common/uri-components'; export namespace SelectionProviderCommands { export const GET_SELECTED_CONTEXT: Command = { @@ -36,7 +35,7 @@ export class SelectionProviderCommandContribution implements CommandContribution commands.registerCommand(SelectionProviderCommands.GET_SELECTED_CONTEXT, this.newMultiUriAwareCommandHandler({ isEnabled: () => true, isVisible: () => false, - execute: (selectedUris: URI[]) => selectedUris.map(uri => theiaUritoUriComponents(uri)) + execute: (selectedUris: URI[]) => selectedUris.map(uri => uri.toComponents()) })); } diff --git a/packages/plugin-ext/src/main/browser/text-editors-main.ts b/packages/plugin-ext/src/main/browser/text-editors-main.ts index eab1b28fdb123..3786b211e26db 100644 --- a/packages/plugin-ext/src/main/browser/text-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editors-main.ts @@ -14,7 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { URI } from '@theia/core/shared/vscode-uri'; import { TextEditorsMain, MAIN_RPC_CONTEXT, @@ -40,13 +39,14 @@ import { TextEditorMain } from './text-editor-main'; import { disposed } from '../../common/errors'; import { toMonacoWorkspaceEdit } from './languages-main'; import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; -import { theiaUritoUriComponents, UriComponents } from '../../common/uri-components'; +import { UriComponents } from '../../common/uri-components'; import { Endpoint } from '@theia/core/lib/browser/endpoint'; import * as monaco from '@theia/monaco-editor-core'; import { ResourceEdit } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; import { IDecorationRenderOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; +import { URI } from '@theia/core'; export class TextEditorsMainImpl implements TextEditorsMain, Disposable { @@ -171,7 +171,7 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { protected toRemoteUri(uri?: UriComponents): UriComponents | undefined { if (uri && uri.scheme === 'file') { - return theiaUritoUriComponents(this.fileEndpoint.withQuery(URI.revive(uri).toString())); + return this.fileEndpoint.withQuery(URI.fromComponents(uri).toString()).toComponents(); } return uri; } @@ -200,6 +200,14 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { return Promise.resolve(); } + $save(uri: UriComponents): PromiseLike { + return this.editorsAndDocuments.save(URI.fromComponents(uri)).then(u => u?.toComponents()); + } + + $saveAs(uri: UriComponents): PromiseLike { + return this.editorsAndDocuments.saveAs(URI.fromComponents(uri)).then(u => u?.toComponents()); + } + $saveAll(includeUntitled?: boolean): Promise { return this.editorsAndDocuments.saveAll(includeUntitled); } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index bc4ed5e144977..a6c8abba3b432 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -722,6 +722,13 @@ export function createAPIFactory( callbackOrToken?: CancellationToken | ((result: theia.TextSearchResult) => void), token?: CancellationToken): Promise { return workspaceExt.findTextInFiles(query, optionsOrCallback, callbackOrToken, token); }, + save(uri: theia.Uri): PromiseLike { + return editors.save(uri); + }, + + saveAs(uri: theia.Uri): PromiseLike { + return editors.saveAs(uri); + }, saveAll(includeUntitled?: boolean): PromiseLike { return editors.saveAll(includeUntitled); }, diff --git a/packages/plugin-ext/src/plugin/text-editors.ts b/packages/plugin-ext/src/plugin/text-editors.ts index 365a56acaad18..50ed6a3095335 100644 --- a/packages/plugin-ext/src/plugin/text-editors.ts +++ b/packages/plugin-ext/src/plugin/text-editors.ts @@ -21,11 +21,10 @@ import { Emitter, Event } from '@theia/core/lib/common/event'; import { EditorsAndDocumentsExtImpl } from './editors-and-documents'; import { TextEditorExt } from './text-editor'; import * as Converters from './type-converters'; -import { TextEditorSelectionChangeKind } from './types-impl'; +import { TextEditorSelectionChangeKind, URI } from './types-impl'; import { IdGenerator } from '../common/id-generator'; export class TextEditorsExtImpl implements TextEditorsExt { - private readonly _onDidChangeTextEditorSelection = new Emitter(); private readonly _onDidChangeTextEditorOptions = new Emitter(); private readonly _onDidChangeTextEditorVisibleRanges = new Emitter(); @@ -124,6 +123,14 @@ export class TextEditorsExtImpl implements TextEditorsExt { return this.proxy.$tryApplyWorkspaceEdit(dto, metadata); } + save(uri: theia.Uri): PromiseLike { + return this.proxy.$save(uri).then(components => URI.revive(components)); + } + + saveAs(uri: theia.Uri): PromiseLike { + return this.proxy.$saveAs(uri).then(components => URI.revive(components)); + } + saveAll(includeUntitled?: boolean): PromiseLike { return this.proxy.$saveAll(includeUntitled); } diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index f187bfffd0b5e..1b29e93ae106c 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -7532,6 +7532,29 @@ export module '@theia/plugin' { */ export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; + /** + * Saves the editor identified by the given resource and returns the resulting resource or `undefined` + * if save was not successful or no editor with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved. + * + * @param uri the associated uri for the opened editor to save. + * @returns A thenable that resolves when the save operation has finished. + */ + export function save(uri: Uri): Thenable; + + /** + * Saves the editor identified by the given resource to a new file name as provided by the user and + * returns the resulting resource or `undefined` if save was not successful or cancelled or no editor + * with the given resource was found. + * + * **Note** that an editor with the provided resource must be opened in order to be saved as. + * + * @param uri the associated uri for the opened editor to save as. + * @returns A thenable that resolves when the save-as operation has finished. + */ + export function saveAs(uri: Uri): Thenable; + /** * Save all dirty files. * diff --git a/packages/workspace/src/browser/workspace-frontend-contribution.ts b/packages/workspace/src/browser/workspace-frontend-contribution.ts index 6a5fb2060cf95..d8c28f14dd7be 100644 --- a/packages/workspace/src/browser/workspace-frontend-contribution.ts +++ b/packages/workspace/src/browser/workspace-frontend-contribution.ts @@ -456,7 +456,7 @@ export class WorkspaceFrontendContribution implements CommandContribution, Keybi } async saveAs(widget: Widget & SaveableSource & Navigatable): Promise { - return this.saveService.saveAs(widget); + await this.saveService.saveAs(widget); } protected updateWorkspaceStateKey(): WorkspaceState { From 384557b1626dbfa7bbd66f4b0c42dbf5fb868b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 20 Feb 2024 15:09:39 +0100 Subject: [PATCH 093/441] Update WorkspaceEdit metadata typing (#13395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13355 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/plugin-ext/src/plugin/types-impl.ts | 8 ++++---- packages/plugin/src/theia.d.ts | 12 ++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 2115d74cabf74..d89f2d78194fa 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1869,13 +1869,13 @@ export class WorkspaceEdit implements theia.WorkspaceEdit { } set(uri: URI, edits: ReadonlyArray): void; - set(uri: URI, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, theia.WorkspaceEditEntryMetadata]>): void; + set(uri: URI, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, theia.WorkspaceEditEntryMetadata | undefined]>): void; set(uri: URI, edits: ReadonlyArray): void; - set(uri: URI, edits: ReadonlyArray<[NotebookEdit, theia.WorkspaceEditEntryMetadata]>): void; + set(uri: URI, edits: ReadonlyArray<[NotebookEdit, theia.WorkspaceEditEntryMetadata | undefined]>): void; set(uri: URI, edits: ReadonlyArray): void { + | NotebookEdit | [NotebookEdit, theia.WorkspaceEditEntryMetadata | undefined] + | [TextEdit | SnippetTextEdit, theia.WorkspaceEditEntryMetadata | undefined]>): void { if (!edits) { // remove all text edits for `uri` for (let i = 0; i < this._edits.length; i++) { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 1b29e93ae106c..f02bebf55377d 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -10281,15 +10281,7 @@ export module '@theia/plugin' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; - - /** - * Set (and replace) text edits or snippet edits with metadata for a resource. - * - * @param uri A resource identifier. - * @param edits An array of edits. - */ - set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata]>): void; + set(uri: Uri, edits: ReadonlyArray<[TextEdit | SnippetTextEdit, WorkspaceEditEntryMetadata | undefined]>): void; /** * Set (and replace) notebook edits for a resource. @@ -10305,7 +10297,7 @@ export module '@theia/plugin' { * @param uri A resource identifier. * @param edits An array of edits. */ - set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata]>): void; + set(uri: Uri, edits: ReadonlyArray<[NotebookEdit, WorkspaceEditEntryMetadata | undefined]>): void; /** * Get the text edits for a resource. From f83986a84131aad327980145475052169eb8a3fd Mon Sep 17 00:00:00 2001 From: Jan Bicker Date: Tue, 20 Feb 2024 16:01:15 +0100 Subject: [PATCH 094/441] Add react-perfect-scrollbar directly to notebook (#13338) --- packages/notebook/package.json | 1 + .../src/browser/notebook-editor-widget.tsx | 18 +++++++++++++----- packages/notebook/src/browser/style/index.css | 15 +++++++++++++-- yarn.lock | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 243a82fb92647..5325113d98608 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -7,6 +7,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", + "react-perfect-scrollbar": "^1.5.8", "uuid": "^8.3.2" }, "publishConfig": { diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index ca70be1b17a5a..24f6f63179794 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -30,6 +30,8 @@ import { NotebookEditorWidgetService } from './service/notebook-editor-widget-se import { NotebookMainToolbarRenderer } from './view/notebook-main-toolbar'; import { Deferred } from '@theia/core/lib/common/promise-util'; +const PerfectScrollbar = require('react-perfect-scrollbar'); + export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory'); export function createNotebookEditorWidgetContainer(parent: interfaces.Container, props: NotebookEditorProps): interfaces.Container { @@ -102,6 +104,10 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString(); this.node.tabIndex = -1; + this.scrollOptions = { + suppressScrollY: true + }; + this.title.closable = true; this.update(); @@ -145,12 +151,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected render(): ReactNode { if (this._model) { - return
+ return
{this.notebookMainToolbarRenderer.render(this._model)} - + + +
; } else { return
; diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 2c9b1844a0641..a668fc5c06d60 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -161,9 +161,20 @@ flex-direction: column; } +.theia-notebook-main-container { + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +.theia-notebook-scroll-container { + flex: 1; + overflow: hidden; + position: relative; +} + .theia-notebook-main-toolbar { - position: sticky; - top: 0; background: var(--theia-editor-background); display: flex; flex-direction: row; diff --git a/yarn.lock b/yarn.lock index a8d20abc7a481..7d7a29d8e2fa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9700,7 +9700,7 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-perfect-scrollbar@^1.5.3: +react-perfect-scrollbar@^1.5.3, react-perfect-scrollbar@^1.5.8: version "1.5.8" resolved "https://registry.yarnpkg.com/react-perfect-scrollbar/-/react-perfect-scrollbar-1.5.8.tgz#380959387a325c5c9d0268afc08b3f73ed5b3078" integrity sha512-bQ46m70gp/HJtiBOF3gRzBISSZn8FFGNxznTdmTG8AAwpxG1bJCyn7shrgjEvGSQ5FJEafVEiosY+ccER11OSA== From 25a84b2f8da0c398e08eb1b811df2e7aab420916 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 20 Feb 2024 18:10:35 +0100 Subject: [PATCH 095/441] Improve title rendering on menu bar change (#13317) --- .../menu/electron-menu-contribution.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts index 199757021dfbd..0b77749c37bf3 100644 --- a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts +++ b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts @@ -18,7 +18,7 @@ import { inject, injectable, postConstruct } from 'inversify'; import { Command, CommandContribution, CommandRegistry, isOSX, isWindows, MenuModelRegistry, MenuContribution, Disposable, nls } from '../../common'; import { codicon, ConfirmDialog, KeybindingContribution, KeybindingRegistry, PreferenceScope, Widget, - FrontendApplication, FrontendApplicationContribution, CommonMenus, CommonCommands, Dialog, Message, ApplicationShell, + FrontendApplication, FrontendApplicationContribution, CommonMenus, CommonCommands, Dialog, Message, ApplicationShell, PreferenceService, animationFrame, } from '../../browser'; import { ElectronMainMenuFactory } from './electron-main-menu-factory'; import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state'; @@ -441,6 +441,9 @@ export class CustomTitleWidget extends Widget { @inject(ApplicationShell) protected readonly applicationShell: ApplicationShell; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + constructor() { super(); this.id = 'theia-custom-title'; @@ -452,6 +455,11 @@ export class CustomTitleWidget extends Widget { this.windowTitleService.onDidChangeTitle(title => { this.updateTitle(title); }); + this.preferenceService.onPreferenceChanged(e => { + if (e.preferenceName === 'window.menuBarVisibility') { + animationFrame().then(() => this.adjustTitleToCenter()); + } + }); } protected override onResize(msg: Widget.ResizeMessage): void { From f6c8fe58c8f3079e1d84eba8c2ad6274f55c247c Mon Sep 17 00:00:00 2001 From: Hanksha Date: Tue, 20 Feb 2024 18:12:14 +0100 Subject: [PATCH 096/441] Fix memory leak in DockerPanelRenderer and ToolbarAwareTabBar (#13327) The DockerPanelRenderer was not disposing the core preference listener. The ToolbarAwareTabBar was not disposing the TabBarToolbar. Signed-off-by: Vivien Jovet Co-authored-by: Vivien Jovet --- .../src/tests/theia-application-shell.test.ts | 67 +++++++++++++++++++ examples/playwright/src/theia-welcome-view.ts | 31 +++++++++ .../src/browser/shell/application-shell.ts | 7 +- packages/core/src/browser/shell/tab-bars.ts | 6 +- 4 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 examples/playwright/src/tests/theia-application-shell.test.ts create mode 100644 examples/playwright/src/theia-welcome-view.ts diff --git a/examples/playwright/src/tests/theia-application-shell.test.ts b/examples/playwright/src/tests/theia-application-shell.test.ts new file mode 100644 index 0000000000000..779fc5358c18f --- /dev/null +++ b/examples/playwright/src/tests/theia-application-shell.test.ts @@ -0,0 +1,67 @@ +// ***************************************************************************** +// Copyright (C) 2023 Toro Cloud Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + +import { test } from '@playwright/test'; +import { TheiaApp } from '../theia-app'; +import { TheiaAppLoader } from '../theia-app-loader'; +import { TheiaExplorerView } from '../theia-explorer-view'; +import { TheiaTextEditor } from '../theia-text-editor'; +import { TheiaWelcomeView } from '../theia-welcome-view'; +import { TheiaWorkspace } from '../theia-workspace'; + +test.describe('Theia Application Shell', () => { + test.describe.configure({ + timeout: 120000 + }); + + let app: TheiaApp; + + test.beforeAll(async ({ playwright, browser }) => { + const ws = new TheiaWorkspace(['src/tests/resources/sample-files1']); + app = await TheiaAppLoader.load({ playwright, browser }, ws); + + // The welcome view must be closed because the memory leak only occurs when there are + // no tabs left open. + const welcomeView = new TheiaWelcomeView(app); + + if (await welcomeView.isTabVisible()) { + await welcomeView.close(); + } + }); + + test.afterAll(async () => { + await app.page.close(); + }); + + /** + * The aim of this test is to detect memory leaks when opening and closing editors many times. + * Remove the skip and run the test, check the logs for any memory leak warnings. + * It should take less than 2min to run, if it takes longer than that, just increase the timeout. + */ + test.skip('should open and close a text editor many times', async () => { + for (let i = 0; i < 200; i++) { + const explorer = await app.openView(TheiaExplorerView); + + const fileStatNode = await explorer.getFileStatNodeByLabel('sample.txt'); + const contextMenu = await fileStatNode.openContextMenu(); + await contextMenu.clickMenuItem('Open'); + + const textEditor = new TheiaTextEditor('sample.txt', app); + await textEditor.waitForVisible(); + + await textEditor.close(); + } + }); +}); diff --git a/examples/playwright/src/theia-welcome-view.ts b/examples/playwright/src/theia-welcome-view.ts new file mode 100644 index 0000000000000..ce68a7f02f2df --- /dev/null +++ b/examples/playwright/src/theia-welcome-view.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2023 Toro Cloud Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + +import { TheiaApp } from './theia-app'; +import { TheiaView } from './theia-view'; +import { normalizeId } from './util'; + +const TheiaWelcomeViewData = { + tabSelector: normalizeId('#shell-tab-getting.started.widget'), + viewSelector: normalizeId('#getting.started.widget'), + viewName: 'Welcome' +}; + +export class TheiaWelcomeView extends TheiaView { + + constructor(app: TheiaApp) { + super(TheiaWelcomeViewData, app); + } +} diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index d8a271210b963..da6e9e99f256a 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -132,16 +132,19 @@ export class DockPanelRenderer implements DockLayout.IRenderer { getDynamicTabOptions()); this.tabBarClasses.forEach(c => tabBar.addClass(c)); renderer.tabBar = tabBar; - tabBar.disposed.connect(() => renderer.dispose()); renderer.contextMenuPath = SHELL_TABBAR_CONTEXT_MENU; tabBar.currentChanged.connect(this.onCurrentTabChanged, this); - this.corePreferences.onPreferenceChanged(change => { + const prefChangeDisposable = this.corePreferences.onPreferenceChanged(change => { if (change.preferenceName === 'workbench.tab.shrinkToFit.enabled' || change.preferenceName === 'workbench.tab.shrinkToFit.minimumSize' || change.preferenceName === 'workbench.tab.shrinkToFit.defaultSize') { tabBar.dynamicTabOptions = getDynamicTabOptions(); } }); + tabBar.disposed.connect(() => { + prefChangeDisposable.dispose(); + renderer.dispose(); + }); this.onDidCreateTabBarEmitter.fire(tabBar); return tabBar; } diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts index c43f42c73bbcf..794f59d917c73 100644 --- a/packages/core/src/browser/shell/tab-bars.ts +++ b/packages/core/src/browser/shell/tab-bars.ts @@ -170,8 +170,8 @@ export class TabBarRenderer extends TabBar.Renderer { const hover = this.tabBar && (this.tabBar.orientation === 'horizontal' && this.corePreferences?.['window.tabbar.enhancedPreview'] === 'classic') ? { title: title.caption } : { - onmouseenter: this.handleMouseEnterEvent - }; + onmouseenter: this.handleMouseEnterEvent + }; return h.li( { @@ -967,7 +967,7 @@ export class ToolbarAwareTabBar extends ScrollableTabBar { protected override onBeforeDetach(msg: Message): void { if (this.toolbar && this.toolbar.isAttached) { - Widget.detach(this.toolbar); + this.toolbar.dispose(); } super.onBeforeDetach(msg); } From 50bb4fca794a85d7d6da66b0526bd74cbd495a77 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 20 Feb 2024 18:23:39 +0100 Subject: [PATCH 097/441] Use common uuid generator everywhere (#13255) --- package.json | 2 - packages/core/package.json | 3 +- packages/core/src/browser/tooltip-service.tsx | 4 +- packages/core/src/common/index.ts | 1 + packages/core/src/common/uuid.ts | 75 +------------------ .../electron-main-application-module.ts | 4 +- packages/filesystem/package.json | 2 - .../node/disk-file-system-provider.spec.ts | 6 +- .../src/node/disk-file-system-provider.ts | 4 +- .../node/download/file-download-handler.ts | 10 +-- packages/mini-browser/package.json | 1 - .../environment/mini-browser-environment.ts | 4 +- packages/notebook/package.json | 3 +- .../notebook-execution-state-service.ts | 5 +- packages/plugin-ext/package.json | 1 - .../main/browser/comments/comments-main.ts | 4 +- .../custom-editors/custom-editor-opener.tsx | 4 +- .../renderers/cell-output-webview.tsx | 4 +- .../main/browser/view/plugin-view-registry.ts | 4 +- .../plugin-ext/src/main/node/errors.spec.ts | 4 +- .../plugin-ext/src/plugin/debug/debug-ext.ts | 4 +- packages/plugin-ext/src/plugin/env.ts | 6 +- .../src/plugin/languages/diagnostics.ts | 4 +- .../src/plugin/node/env-node-ext.ts | 4 +- .../src/plugin/preference-registry.ts | 4 +- packages/plugin-ext/src/plugin/tests.ts | 4 +- packages/plugin-ext/src/plugin/webviews.ts | 5 +- packages/remote/package.json | 3 +- .../ssh/remote-ssh-connection-provider.ts | 4 +- packages/typehierarchy/package.json | 4 +- .../src/browser/tree/typehierarchy-tree.ts | 4 +- packages/vsx-registry/package.json | 3 +- yarn.lock | 12 +-- 33 files changed, 64 insertions(+), 142 deletions(-) diff --git a/package.json b/package.json index df4da5f8cce40..0ed1ed6324896 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ "@types/node": "18", "@types/sinon": "^10.0.6", "@types/temp": "^0.9.1", - "@types/uuid": "^7.0.3", "@typescript-eslint/eslint-plugin": "^4.8.1", "@typescript-eslint/eslint-plugin-tslint": "^4.8.1", "@typescript-eslint/parser": "^4.8.1", @@ -56,7 +55,6 @@ "typedoc": "^0.22.11", "typedoc-plugin-external-module-map": "1.3.2", "typescript": "~4.5.5", - "uuid": "^8.0.0", "yargs": "^15.3.1" }, "scripts": { diff --git a/packages/core/package.json b/packages/core/package.json index 7c642850c2f28..8678059ca038f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -30,6 +30,7 @@ "@types/react-dom": "^18.0.6", "@types/route-parser": "^0.1.1", "@types/safer-buffer": "^2.1.0", + "@types/uuid": "^9.0.8", "@types/ws": "^8.5.5", "@types/yargs": "^15", "@vscode/codicons": "*", @@ -68,7 +69,7 @@ "safer-buffer": "^2.1.2", "socket.io": "^4.5.3", "socket.io-client": "^4.5.3", - "uuid": "^8.3.2", + "uuid": "^9.0.1", "vscode-languageserver-protocol": "^3.17.2", "vscode-uri": "^2.1.1", "ws": "^8.14.1", diff --git a/packages/core/src/browser/tooltip-service.tsx b/packages/core/src/browser/tooltip-service.tsx index 67fbc10285b56..0857960a434be 100644 --- a/packages/core/src/browser/tooltip-service.tsx +++ b/packages/core/src/browser/tooltip-service.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import ReactTooltip from 'react-tooltip'; import { ReactRenderer, RendererHost } from './widgets/react-renderer'; import { CorePreferences } from './core-preferences'; -import { v4 } from 'uuid'; +import { generateUuid } from '../common/uuid'; export const TooltipService = Symbol('TooltipService'); @@ -59,7 +59,7 @@ export class TooltipServiceImpl extends ReactRenderer implements TooltipService @inject(RendererHost) @optional() host?: RendererHost ) { super(host); - this.tooltipId = v4(); + this.tooltipId = generateUuid(); } @postConstruct() diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index a3f5aeb125e57..ada952513f326 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -46,5 +46,6 @@ export * from './strings'; export * from './telemetry'; export * from './types'; export { default as URI } from './uri'; +export * from './uuid'; export * from './view-column'; export * from './version'; diff --git a/packages/core/src/common/uuid.ts b/packages/core/src/common/uuid.ts index 1994e29282d30..41561cebc593a 100644 --- a/packages/core/src/common/uuid.ts +++ b/packages/core/src/common/uuid.ts @@ -21,7 +21,7 @@ // based on https://github.com/microsoft/vscode/blob/1.72.2/src/vs/base/common/uuid.ts -import { v5 } from 'uuid'; +import { v4, v5 } from 'uuid'; const _UUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -29,76 +29,9 @@ export function isUUID(value: string): boolean { return _UUIDPattern.test(value); } -declare const crypto: undefined | { - // https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#browser_compatibility - getRandomValues?(data: Uint8Array): Uint8Array; - // https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID#browser_compatibility - randomUUID?(): string; -}; - -export const generateUuid = (function (): () => string { - - // use `randomUUID` if possible - if (typeof crypto === 'object' && typeof crypto.randomUUID === 'function') { - return crypto.randomUUID.bind(crypto); - } - - // use `randomValues` if possible - let getRandomValues: (bucket: Uint8Array) => Uint8Array; - if (typeof crypto === 'object' && typeof crypto.getRandomValues === 'function') { - getRandomValues = crypto.getRandomValues.bind(crypto); - - } else { - getRandomValues = function (bucket: Uint8Array): Uint8Array { - for (let i = 0; i < bucket.length; i++) { - bucket[i] = Math.floor(Math.random() * 256); - } - return bucket; - }; - } - - // prep-work - const _data = new Uint8Array(16); - const _hex: string[] = []; - for (let i = 0; i < 256; i++) { - _hex.push(i.toString(16).padStart(2, '0')); - } - - // eslint-disable-next-line @typescript-eslint/no-shadow - return function generateUuid(): string { - // get data - getRandomValues(_data); - - // set version bits - _data[6] = (_data[6] & 0x0f) | 0x40; - _data[8] = (_data[8] & 0x3f) | 0x80; - - // print as string - let i = 0; - let result = ''; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += '-'; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += '-'; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += '-'; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += '-'; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - result += _hex[_data[i++]]; - return result; - }; -})(); +export function generateUuid(): string { + return v4(); +} const NAMESPACE = '4c90ee4f-d952-44b1-83ca-f04121ab8e05'; /** diff --git a/packages/core/src/electron-main/electron-main-application-module.ts b/packages/core/src/electron-main/electron-main-application-module.ts index 0148dbc6fd77c..a512efb722012 100644 --- a/packages/core/src/electron-main/electron-main-application-module.ts +++ b/packages/core/src/electron-main/electron-main-application-module.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { ContainerModule } from 'inversify'; -import { v4 } from 'uuid'; +import { generateUuid } from '../common/uuid'; import { bindContributionProvider } from '../common/contribution-provider'; import { RpcConnectionHandler } from '../common/messaging/proxy-factory'; import { ElectronSecurityToken } from '../electron-common/electron-token'; @@ -29,7 +29,7 @@ import { ElectronSecurityTokenService } from './electron-security-token-service' import { ElectronMessagingService } from './messaging/electron-messaging-service'; import { ElectronConnectionHandler } from './messaging/electron-connection-handler'; -const electronSecurityToken: ElectronSecurityToken = { value: v4() }; +const electronSecurityToken: ElectronSecurityToken = { value: generateUuid() }; // eslint-disable-next-line @typescript-eslint/no-explicit-any (global as any)[ElectronSecurityToken] = electronSecurityToken; diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index fa832e1cfca3e..05cb26103fa10 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -8,7 +8,6 @@ "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", "@types/tar-fs": "^1.16.1", - "@types/uuid": "^7.0.3", "async-mutex": "^0.3.1", "body-parser": "^1.18.3", "browserfs": "^1.4.3", @@ -19,7 +18,6 @@ "stat-mode": "^1.0.0", "tar-fs": "^1.16.2", "trash": "^7.2.0", - "uuid": "^8.0.0", "vscode-languageserver-textdocument": "^1.0.1" }, "publishConfig": { diff --git a/packages/filesystem/src/node/disk-file-system-provider.spec.ts b/packages/filesystem/src/node/disk-file-system-provider.spec.ts index 641d8342d2fe3..a2c84055175b2 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.spec.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.spec.ts @@ -25,7 +25,7 @@ import { equal, fail } from 'assert'; import { promises as fs } from 'fs'; import { join } from 'path'; import * as temp from 'temp'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { FilePermission, FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode } from '../common/files'; import { DiskFileSystemProvider } from './disk-file-system-provider'; import { bindFileSystemWatcherServer } from './filesystem-backend-module'; @@ -53,7 +53,7 @@ describe('disk-file-system-provider', () => { describe('stat', () => { it("should omit the 'permissions' property of the stat if the file can be both read and write", async () => { const tempDirPath = tracked.mkdirSync(); - const tempFilePath = join(tempDirPath, `${v4()}.txt`); + const tempFilePath = join(tempDirPath, `${generateUuid()}.txt`); await fs.writeFile(tempFilePath, 'some content', { encoding: 'utf8' }); let content = await fs.readFile(tempFilePath, { encoding: 'utf8' }); @@ -70,7 +70,7 @@ describe('disk-file-system-provider', () => { it("should set the 'permissions' property to `Readonly` if the file can be read but not write", async () => { const tempDirPath = tracked.mkdirSync(); - const tempFilePath = join(tempDirPath, `${v4()}.txt`); + const tempFilePath = join(tempDirPath, `${generateUuid()}.txt`); await fs.writeFile(tempFilePath, 'readonly content', { encoding: 'utf8', }); diff --git a/packages/filesystem/src/node/disk-file-system-provider.ts b/packages/filesystem/src/node/disk-file-system-provider.ts index de2132e8a7886..9aad02ded5ecb 100644 --- a/packages/filesystem/src/node/disk-file-system-provider.ts +++ b/packages/filesystem/src/node/disk-file-system-provider.ts @@ -24,7 +24,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { basename, dirname, normalize, join } from 'path'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import * as os from 'os'; import * as fs from 'fs'; import { @@ -530,7 +530,7 @@ export class DiskFileSystemProvider implements Disposable, protected async rimrafMove(path: string): Promise { try { - const pathInTemp = join(os.tmpdir(), v4()); + const pathInTemp = join(os.tmpdir(), generateUuid()); try { await promisify(rename)(path, pathInTemp); } catch (error) { diff --git a/packages/filesystem/src/node/download/file-download-handler.ts b/packages/filesystem/src/node/download/file-download-handler.ts index 7a54513e6bcae..5ebf73e627da7 100644 --- a/packages/filesystem/src/node/download/file-download-handler.ts +++ b/packages/filesystem/src/node/download/file-download-handler.ts @@ -17,7 +17,7 @@ import * as os from 'os'; import * as fs from '@theia/core/shared/fs-extra'; import * as path from 'path'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { Request, Response } from '@theia/core/shared/express'; import { inject, injectable } from '@theia/core/shared/inversify'; import { OK, BAD_REQUEST, METHOD_NOT_ALLOWED, NOT_FOUND, INTERNAL_SERVER_ERROR, REQUESTED_RANGE_NOT_SATISFIABLE, PARTIAL_CONTENT } from 'http-status-codes'; @@ -135,12 +135,12 @@ export abstract class FileDownloadHandler { end: (isNaN(end) || end > statSize - 1) ? (statSize - 1) : end }; } - protected async archive(inputPath: string, outputPath: string = path.join(os.tmpdir(), v4()), entries?: string[]): Promise { + protected async archive(inputPath: string, outputPath: string = path.join(os.tmpdir(), generateUuid()), entries?: string[]): Promise { await this.directoryArchiver.archive(inputPath, outputPath, entries); return outputPath; } - protected async createTempDir(downloadId: string = v4()): Promise { + protected async createTempDir(downloadId: string = generateUuid()): Promise { const outputPath = path.join(os.tmpdir(), downloadId); await fs.mkdir(outputPath); return outputPath; @@ -221,7 +221,7 @@ export class SingleFileDownloadHandler extends FileDownloadHandler { return; } try { - const downloadId = v4(); + const downloadId = generateUuid(); const options: PrepareDownloadOptions = { filePath, downloadId, remove: false }; if (!stat.isDirectory()) { await this.prepareDownload(request, response, options); @@ -271,7 +271,7 @@ export class MultiFileDownloadHandler extends FileDownloadHandler { } } try { - const downloadId = v4(); + const downloadId = generateUuid(); const outputRootPath = await this.createTempDir(downloadId); const distinctUris = Array.from(new Set(body.uris.map(uri => new URI(uri)))); const tarPaths = []; diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 8f6ee15eb0961..d9bcf8a352759 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -8,7 +8,6 @@ "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", - "uuid": "^8.0.0", "vhost": "^3.0.2" }, "publishConfig": { diff --git a/packages/mini-browser/src/browser/environment/mini-browser-environment.ts b/packages/mini-browser/src/browser/environment/mini-browser-environment.ts index f5253f43708d6..b13b910d5638d 100644 --- a/packages/mini-browser/src/browser/environment/mini-browser-environment.ts +++ b/packages/mini-browser/src/browser/environment/mini-browser-environment.ts @@ -18,7 +18,7 @@ import { Endpoint, FrontendApplicationContribution } from '@theia/core/lib/brows import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { MiniBrowserEndpoint } from '../../common/mini-browser-endpoint'; /** @@ -71,7 +71,7 @@ export class MiniBrowserEnvironment implements FrontendApplicationContribution { * Throws if `hostPatternPromise` is not yet resolved. */ getRandomEndpoint(): Endpoint { - return this.getEndpoint(v4()); + return this.getEndpoint(generateUuid()); } protected async getHostPattern(): Promise { diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 5325113d98608..188c1096f41c7 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -7,8 +7,7 @@ "@theia/editor": "1.46.0", "@theia/filesystem": "1.46.0", "@theia/monaco": "1.46.0", - "react-perfect-scrollbar": "^1.5.8", - "uuid": "^8.3.2" + "react-perfect-scrollbar": "^1.5.8" }, "publishConfig": { "access": "public" diff --git a/packages/notebook/src/browser/service/notebook-execution-state-service.ts b/packages/notebook/src/browser/service/notebook-execution-state-service.ts index 3512293d78b4b..5ac7645373be9 100644 --- a/packages/notebook/src/browser/service/notebook-execution-state-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-state-service.ts @@ -18,7 +18,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableCollection, Emitter, URI } from '@theia/core'; +import { Disposable, DisposableCollection, Emitter, URI, generateUuid } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { NotebookService } from './notebook-service'; import { @@ -27,7 +27,6 @@ import { } from '../../common'; import { CellPartialInternalMetadataEditByHandle, CellEditOperation } from '../notebook-types'; import { NotebookModel } from '../view-model/notebook-model'; -import { v4 } from 'uuid'; export type CellExecuteUpdate = CellExecuteOutputEdit | CellExecuteOutputItemEdit | CellExecutionStateUpdate; @@ -178,7 +177,7 @@ export class CellExecution implements Disposable { editType: CellEditType.PartialInternalMetadata, handle: this.cellHandle, internalMetadata: { - executionId: v4(), + executionId: generateUuid(), runStartTime: undefined, runEndTime: undefined, lastRunSuccess: undefined, diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index f1ccdb48d1986..9db620bcb8cdf 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -46,7 +46,6 @@ "mime": "^2.4.4", "ps-tree": "^1.2.0", "semver": "^7.5.4", - "uuid": "^8.0.0", "vhost": "^3.0.2", "vscode-textmate": "^9.0.0" }, diff --git a/packages/plugin-ext/src/main/browser/comments/comments-main.ts b/packages/plugin-ext/src/main/browser/comments/comments-main.ts index 4aab5f5d45914..c4e6051be9764 100644 --- a/packages/plugin-ext/src/main/browser/comments/comments-main.ts +++ b/packages/plugin-ext/src/main/browser/comments/comments-main.ts @@ -38,7 +38,7 @@ import { URI } from '@theia/core/shared/vscode-uri'; import { CancellationToken } from '@theia/core/lib/common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { interfaces } from '@theia/core/shared/inversify'; -import { v4 as uuidv4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { CommentsContribution } from './comments-contribution'; /*--------------------------------------------------------------------------------------------- @@ -392,7 +392,7 @@ export class CommentsMainImp implements CommentsMain { } $registerCommentController(handle: number, id: string, label: string): void { - const providerId = uuidv4(); + const providerId = generateUuid(); this.handlers.set(handle, providerId); const provider = new CommentController(this.proxy, this.commentService, handle, providerId, id, label, {}); diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx index 245ab57cb1e29..838c3ad08b162 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx @@ -19,7 +19,7 @@ import URI from '@theia/core/lib/common/uri'; import { ApplicationShell, OpenHandler, Widget, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; import { CustomEditor, CustomEditorPriority, CustomEditorSelector } from '../../../common'; import { CustomEditorWidget } from './custom-editor-widget'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { Emitter } from '@theia/core'; import { match } from '@theia/core/lib/common/glob'; @@ -77,7 +77,7 @@ export class CustomEditorOpener implements OpenHandler { const uriString = uri.toString(); let widgetPromise = this.pendingWidgetPromises.get(uriString); if (!widgetPromise) { - const id = v4(); + const id = generateUuid(); widgetPromise = this.widgetManager.getOrCreateWidget(CustomEditorWidget.FACTORY_ID, { id }); this.pendingWidgetPromises.set(uriString, widgetPromise); widget = await widgetPromise; diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index dbf7179be3df1..c0be337bfe6d1 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -21,7 +21,7 @@ import * as React from '@theia/core/shared/react'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { NotebookRendererMessagingService, CellOutputWebview, NotebookRendererRegistry, NotebookEditorWidgetService, NotebookCellOutputsSplice } from '@theia/notebook/lib/browser'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import { WebviewWidget } from '../../webview/webview'; import { Message, WidgetManager } from '@theia/core/lib/browser'; @@ -65,7 +65,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @inject(QuickPickService) protected readonly quickPickService: QuickPickService; - readonly id = v4(); + readonly id = generateUuid(); protected readonly elementRef = React.createRef(); protected outputPresentationListeners: DisposableCollection = new DisposableCollection(); diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index f11ad8383cfc7..4da0239b8ad4c 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -43,7 +43,7 @@ import { TEST_VIEW_CONTAINER_ID } from '@theia/test/lib/browser/view/test-view-c import { WebviewView, WebviewViewResolver } from '../webview-views/webview-views'; import { WebviewWidget, WebviewWidgetIdentifier } from '../webview/webview'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { nls } from '@theia/core'; import { TheiaDockPanel } from '@theia/core/lib/browser/shell/theia-dock-panel'; import { Deferred } from '@theia/core/lib/common/promise-util'; @@ -440,7 +440,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { protected async createNewWebviewView(viewId: string): Promise { const webview = await this.widgetManager.getOrCreateWidget( WebviewWidget.FACTORY_ID, { - id: v4(), + id: generateUuid(), viewId, }); webview.setContentOptions({ allowScripts: true }); diff --git a/packages/plugin-ext/src/main/node/errors.spec.ts b/packages/plugin-ext/src/main/node/errors.spec.ts index 56a20052ba287..b013492a91296 100644 --- a/packages/plugin-ext/src/main/node/errors.spec.ts +++ b/packages/plugin-ext/src/main/node/errors.spec.ts @@ -17,13 +17,13 @@ import { rejects } from 'assert'; import { strictEqual } from 'assert/strict'; import { promises as fs } from 'fs'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { isENOENT } from '../../common/errors'; describe('errors', () => { describe('errno-exception', () => { it('should be ENOENT error', async () => { - await rejects(fs.readFile(v4()), reason => isENOENT(reason)); + await rejects(fs.readFile(generateUuid()), reason => isENOENT(reason)); }); it('should not be ENOENT error (no code)', () => { diff --git a/packages/plugin-ext/src/plugin/debug/debug-ext.ts b/packages/plugin-ext/src/plugin/debug/debug-ext.ts index d982385f30b82..af9486cab4dca 100644 --- a/packages/plugin-ext/src/plugin/debug/debug-ext.ts +++ b/packages/plugin-ext/src/plugin/debug/debug-ext.ts @@ -28,7 +28,7 @@ import { DEBUG_SCHEME, SCHEME_PATTERN } from '@theia/debug/lib/common/debug-uri- import { Disposable, Breakpoint as BreakpointExt, SourceBreakpoint, FunctionBreakpoint, Location, Range, URI as URIImpl } from '../types-impl'; import { PluginDebugAdapterSession } from './plugin-debug-adapter-session'; import { PluginDebugAdapterTracker } from './plugin-debug-adapter-tracker'; -import uuid = require('uuid'); +import { generateUuid } from '@theia/core/lib/common/uuid'; import { DebugAdapter } from '@theia/debug/lib/common/debug-model'; import { PluginDebugAdapterCreator } from './plugin-debug-adapter-creator'; import { NodeDebugAdapterCreator } from '../node/debug/plugin-node-debug-adapter-creator'; @@ -345,7 +345,7 @@ export class DebugExtImpl implements DebugExt { } async $createDebugSession(debugConfiguration: DebugConfiguration, workspaceFolderUri: string | undefined): Promise { - const sessionId = uuid.v4(); + const sessionId = generateUuid(); const parentSession = debugConfiguration.parentSessionId ? this.sessions.get(debugConfiguration.parentSessionId) : undefined; const theiaSession: theia.DebugSession = { diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 5913179816f87..14d2bda0b9878 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -19,7 +19,7 @@ import * as theia from '@theia/plugin'; import { RPCProtocol } from '../common/rpc-protocol'; import { EnvMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; import { QueryParameters } from '../common/env'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; @injectable() export abstract class EnvExtImpl { @@ -38,8 +38,8 @@ export abstract class EnvExtImpl { private _remoteName: string | undefined; constructor() { - this.envSessionId = v4(); - this.envMachineId = v4(); + this.envSessionId = generateUuid(); + this.envMachineId = generateUuid(); this._remoteName = undefined; } diff --git a/packages/plugin-ext/src/plugin/languages/diagnostics.ts b/packages/plugin-ext/src/plugin/languages/diagnostics.ts index ea74544b09ea2..5710b5bf804e6 100644 --- a/packages/plugin-ext/src/plugin/languages/diagnostics.ts +++ b/packages/plugin-ext/src/plugin/languages/diagnostics.ts @@ -22,7 +22,7 @@ import { MarkerData } from '../../common/plugin-api-rpc-model'; import { RPCProtocol } from '../../common/rpc-protocol'; import { PLUGIN_RPC_CONTEXT, LanguagesMain } from '../../common/plugin-api-rpc'; import { URI } from '@theia/core/shared/vscode-uri'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; export class DiagnosticCollection implements theia.DiagnosticCollection { private static DIAGNOSTICS_PRIORITY = [ @@ -288,7 +288,7 @@ export class Diagnostics { } private getNextId(): string { - return v4(); + return generateUuid(); } private getAllDiagnosticsForResource(uri: URI): theia.Diagnostic[] { diff --git a/packages/plugin-ext/src/plugin/node/env-node-ext.ts b/packages/plugin-ext/src/plugin/node/env-node-ext.ts index 18ac72b3038d0..92e3f2b594d18 100644 --- a/packages/plugin-ext/src/plugin/node/env-node-ext.ts +++ b/packages/plugin-ext/src/plugin/node/env-node-ext.ts @@ -18,7 +18,7 @@ import { injectable } from '@theia/core/shared/inversify'; import * as mac from 'macaddress'; import { EnvExtImpl } from '../env'; import { createHash } from 'crypto'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import fs = require('fs'); /** @@ -36,7 +36,7 @@ export class EnvNodeExtImpl extends EnvExtImpl { mac.one((err, macAddress) => { if (err) { - this.macMachineId = v4(); + this.macMachineId = generateUuid(); } else { this.macMachineId = createHash('sha256').update(macAddress, 'utf8').digest('hex'); } diff --git a/packages/plugin-ext/src/plugin/preference-registry.ts b/packages/plugin-ext/src/plugin/preference-registry.ts index 7d579ce1c5ef7..29863cfb265b6 100644 --- a/packages/plugin-ext/src/plugin/preference-registry.ts +++ b/packages/plugin-ext/src/plugin/preference-registry.ts @@ -25,7 +25,7 @@ import { IConfigurationOverrides } from '@theia/monaco-editor-core/esm/vs/platfo import { Configuration, ConfigurationModel, ConfigurationModelParser } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configurationModels'; import { Workspace, WorkspaceFolder } from '@theia/monaco-editor-core/esm/vs/platform/workspace/common/workspace'; import * as theia from '@theia/plugin'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import { PLUGIN_RPC_CONTEXT, PreferenceChangeExt, PreferenceData, PreferenceRegistryExt, PreferenceRegistryMain @@ -75,7 +75,7 @@ function lookUp(tree: any, key: string): any { export class TheiaWorkspace extends Workspace { constructor(ext: WorkspaceExtImpl) { const folders = (ext.workspaceFolders ?? []).map(folder => new WorkspaceFolder(folder)); - super(v4(), folders, false, ext.workspaceFile ?? null, () => isOSX || isWindows); + super(generateUuid(), folders, false, ext.workspaceFile ?? null, () => isOSX || isWindows); } } diff --git a/packages/plugin-ext/src/plugin/tests.ts b/packages/plugin-ext/src/plugin/tests.ts index 9f1083651123b..48106408d3d47 100644 --- a/packages/plugin-ext/src/plugin/tests.ts +++ b/packages/plugin-ext/src/plugin/tests.ts @@ -34,7 +34,7 @@ import { isDefined } from '@theia/core/lib/common/types'; import { TestingExt, PLUGIN_RPC_CONTEXT, TestingMain } from '../common/plugin-api-rpc'; import { CommandRegistryImpl } from './command-registry'; import { RPCProtocol } from '../common/rpc-protocol'; -import { v4 as uuidv4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import * as Convert from './type-converters'; import { TestItemImpl, TestItemCollection } from './test-item'; import { AccumulatingTreeDeltaEmitter, TreeDelta } from '@theia/test/lib/common/tree-delta'; @@ -257,7 +257,7 @@ class TestRun implements theia.TestRun { readonly name: string, readonly isPersisted: boolean, isRunning: boolean) { - this.id = uuidv4(); + this.id = generateUuid(); this.tokenSource = new CancellationTokenSource(); this.token = this.tokenSource.token; diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index 72a2e903fb656..5ad4356ad832b 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { v4 } from 'uuid'; +import { generateUuid, hashValue } from '@theia/core/lib/common/uuid'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { Plugin, WebviewsExt, WebviewPanelViewState, WebviewsMain, PLUGIN_RPC_CONTEXT, WebviewInitData, /* WebviewsMain, PLUGIN_RPC_CONTEXT */ } from '../common/plugin-api-rpc'; import * as theia from '@theia/plugin'; @@ -24,7 +24,6 @@ import { fromViewColumn, toViewColumn, toWebviewPanelShowOptions } from './type- import { Disposable, WebviewPanelTargetArea, URI } from './types-impl'; import { WorkspaceExtImpl } from './workspace'; import { PluginIconPath } from './plugin-icon-path'; -import { hashValue } from '@theia/core/lib/common/uuid'; @injectable() export class WebviewsExtImpl implements WebviewsExt { @@ -118,7 +117,7 @@ export class WebviewsExtImpl implements WebviewsExt { options: theia.WebviewPanelOptions & theia.WebviewOptions, plugin: Plugin ): theia.WebviewPanel { - const viewId = v4(); + const viewId = generateUuid(); const webviewShowOptions = toWebviewPanelShowOptions(showOptions); const webviewOptions = WebviewImpl.toWebviewOptions(options, this.workspace, plugin); this.proxy.$createWebviewPanel(viewId, viewType, title, webviewShowOptions, webviewOptions); diff --git a/packages/remote/package.json b/packages/remote/package.json index 770098e651bcd..cd52c26ea0ccb 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -15,8 +15,7 @@ "ssh2": "^1.12.0", "ssh2-sftp-client": "^9.1.0", "socket.io": "^4.5.3", - "socket.io-client": "^4.5.3", - "uuid": "^8.0.0" + "socket.io-client": "^4.5.3" }, "publishConfig": { "access": "public" diff --git a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts index 3de1a97bea596..a0b20e321c235 100644 --- a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts +++ b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts @@ -27,7 +27,7 @@ import { RemoteConnection, RemoteExecOptions, RemoteExecResult, RemoteExecTester import { Deferred, timeout } from '@theia/core/lib/common/promise-util'; import { SSHIdentityFileCollector, SSHKey } from './ssh-identity-file-collector'; import { RemoteSetupService } from '../setup/remote-setup-service'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; @injectable() export class RemoteSSHConnectionProviderImpl implements RemoteSSHConnectionProvider { @@ -92,7 +92,7 @@ export class RemoteSSHConnectionProviderImpl implements RemoteSSHConnectionProvi .on('ready', async () => { const connection = new RemoteSSHConnection({ client: sshClient, - id: v4(), + id: generateUuid(), name: hostUrl.hostname, type: 'SSH' }); diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 5df8530b51a38..4efab626c0c08 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -4,9 +4,7 @@ "description": "Theia - Type Hierarchy Extension", "dependencies": { "@theia/core": "1.46.0", - "@theia/editor": "1.46.0", - "@types/uuid": "^7.0.3", - "uuid": "^8.0.0" + "@theia/editor": "1.46.0" }, "publishConfig": { "access": "public" diff --git a/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts index 517fe17ebc3e4..65aaaa016e965 100644 --- a/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts +++ b/packages/typehierarchy/src/browser/tree/typehierarchy-tree.ts @@ -17,7 +17,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { injectable } from '@theia/core/shared/inversify'; -import { v4 } from 'uuid'; +import { generateUuid } from '@theia/core/lib/common/uuid'; import URI from '@theia/core/lib/common/uri'; import { Location } from '@theia/editor/lib/browser/editor'; import { TreeDecoration, DecoratedTreeNode } from '@theia/core/lib/browser/tree/tree-decorator'; @@ -134,7 +134,7 @@ export namespace TypeHierarchyTree { resolved = true; } const node = { - id: v4(), + id: generateUuid(), name: item.name, description: item.detail, parent: undefined, diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 9cb6671888d99..49ed7002fcbd4 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -13,8 +13,7 @@ "@theia/workspace": "1.46.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", - "semver": "^7.5.4", - "uuid": "^8.0.0" + "semver": "^7.5.4" }, "publishConfig": { "access": "public" diff --git a/yarn.lock b/yarn.lock index 7d7a29d8e2fa7..944fc49ab9b01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2272,10 +2272,10 @@ dependencies: "@types/node" "*" -"@types/uuid@^7.0.3": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.6.tgz#455e838428ae709f82c85c677dcf5115f2061ac1" - integrity sha512-U/wu4HTp6T2dUmKqDtOUKS9cYhawuf8txqKF3Jp1iMDG8fP5HtjSldcN0g4m+/h7XHU1to1/HDCT0qeeUiu0EA== +"@types/uuid@^9.0.8": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== "@types/vscode-notebook-renderer@^1.72.0": version "1.72.1" @@ -11629,12 +11629,12 @@ uuid@^7.0.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -uuid@^8.0.0, uuid@^8.3.2: +uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== From 4d7f225e8c87c51152ed605b3f47460f0163a408 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Wed, 21 Feb 2024 11:15:00 +0100 Subject: [PATCH 098/441] Perform yarn upgrade (#13312) The commit performs a `yarn upgrade` of the framework to better represent what downstream applications pull with our version ranges, and to resolve known security vulnerabilities which were pulled by our lockfile. The changes also make sure that our declared ranges for dependencies are correct and fixes any compilation errors. Contributed on behalf of STMicroelectronics Signed-off-by: Johannes Faltermeier --- yarn.lock | 2231 ++++++++++++++++++++++++++++------------------------- 1 file changed, 1191 insertions(+), 1040 deletions(-) diff --git a/yarn.lock b/yarn.lock index 944fc49ab9b01..20a36d8c48255 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,46 +22,46 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.22.13" + "@babel/highlight" "^7.23.4" chalk "^2.4.2" -"@babel/compat-data@^7.22.20", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.20.tgz#8df6e96661209623f1975d66c35ffca66f3306d0" - integrity sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.10.0", "@babel/core@^7.7.5": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.0.tgz#f8259ae0e52a123eb40f552551e647b506a94d83" - integrity sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.23.0" - "@babel/helpers" "^7.23.0" - "@babel/parser" "^7.23.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.7" + "@babel/parser" "^7.23.6" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.0" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== +"@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.23.0" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -73,40 +73,40 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== dependencies: "@babel/types" "^7.22.15" -"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" - integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.15" - browserslist "^4.21.9" + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.22.11", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz#97a61b385e57fe458496fad19f8e63b63c867de4" - integrity sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg== +"@babel/helper-create-class-features-plugin@^7.22.15": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" + integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-replace-supers" "^7.22.20" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== @@ -115,10 +115,21 @@ regexpu-core "^5.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" - integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== +"@babel/helper-define-polyfill-provider@^0.4.4": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz#64df615451cb30e94b59a9696022cffac9a10088" + integrity sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-define-polyfill-provider@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" + integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -126,7 +137,7 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5": +"@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== @@ -146,24 +157,24 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.22.15": +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== dependencies: "@babel/types" "^7.23.0" -"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": +"@babel/helper-module-imports@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e" - integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw== +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-module-imports" "^7.22.15" @@ -183,7 +194,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": +"@babel/helper-remap-async-to-generator@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== @@ -192,7 +203,7 @@ "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-wrap-function" "^7.22.20" -"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": +"@babel/helper-replace-supers@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== @@ -222,20 +233,20 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" - integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== "@babel/helper-wrap-function@^7.22.20": version "7.22.20" @@ -246,44 +257,52 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.23.0": - version "7.23.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.1.tgz#44e981e8ce2b9e99f8f0b703f3326a4636c16d15" - integrity sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA== +"@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== dependencies: "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.0" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: "@babel/helper-validator-identifier" "^7.22.20" chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz#02dc8a03f613ed5fdc29fb2f728397c78146c962" - integrity sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz#2aeb91d337d4e1a1e7ce85b76a37f5301781200f" - integrity sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.15" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" + integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" @@ -325,17 +344,17 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" - integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== +"@babel/plugin-syntax-import-assertions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-attributes@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" - integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== +"@babel/plugin-syntax-import-attributes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" + integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -417,211 +436,211 @@ "@babel/helper-create-regexp-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" -"@babel/plugin-transform-arrow-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" - integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz#3b153af4a6b779f340d5b80d3f634f55820aefa3" - integrity sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w== +"@babel/plugin-transform-async-generator-functions@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz#3aa0b4f2fa3788b5226ef9346cf6d16ec61f99cd" + integrity sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.9" + "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-transform-async-to-generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" - integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== +"@babel/plugin-transform-async-to-generator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== dependencies: - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" -"@babel/plugin-transform-block-scoped-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" - integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== +"@babel/plugin-transform-block-scoped-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.15": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz#8744d02c6c264d82e1a4bc5d2d501fd8aff6f022" - integrity sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g== +"@babel/plugin-transform-block-scoping@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" - integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== +"@babel/plugin-transform-class-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" + integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-class-static-block@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz#dc8cc6e498f55692ac6b4b89e56d87cec766c974" - integrity sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g== +"@babel/plugin-transform-class-static-block@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5" + integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.11" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-transform-classes@^7.10.0", "@babel/plugin-transform-classes@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz#aaf4753aee262a232bbc95451b4bdf9599c65a0b" - integrity sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw== +"@babel/plugin-transform-classes@^7.10.0", "@babel/plugin-transform-classes@^7.23.8": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-replace-supers" "^7.22.20" "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" - integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== +"@babel/plugin-transform-computed-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.5" + "@babel/template" "^7.22.15" -"@babel/plugin-transform-destructuring@^7.22.15": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz#6447aa686be48b32eaf65a73e0e2c0bd010a266c" - integrity sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg== +"@babel/plugin-transform-destructuring@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" - integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== +"@babel/plugin-transform-dotall-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-duplicate-keys@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" - integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dynamic-import@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz#2c7722d2a5c01839eaf31518c6ff96d408e447aa" - integrity sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA== +"@babel/plugin-transform-dynamic-import@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143" + integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-transform-exponentiation-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" - integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== +"@babel/plugin-transform-exponentiation-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-export-namespace-from@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz#b3c84c8f19880b6c7440108f8929caf6056db26c" - integrity sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw== +"@babel/plugin-transform-export-namespace-from@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191" + integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-transform-for-of@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz#f64b4ccc3a4f131a996388fae7680b472b306b29" - integrity sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA== +"@babel/plugin-transform-for-of@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" + integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" -"@babel/plugin-transform-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" - integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== +"@babel/plugin-transform-function-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== dependencies: - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-json-strings@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz#689a34e1eed1928a40954e37f74509f48af67835" - integrity sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw== +"@babel/plugin-transform-json-strings@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d" + integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-transform-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" - integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== +"@babel/plugin-transform-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-logical-assignment-operators@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz#24c522a61688bde045b7d9bc3c2597a4d948fc9c" - integrity sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ== +"@babel/plugin-transform-logical-assignment-operators@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5" + integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" - integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== +"@babel/plugin-transform-member-expression-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-amd@^7.22.5": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz#05b2bc43373faa6d30ca89214731f76f966f3b88" - integrity sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw== +"@babel/plugin-transform-modules-amd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== dependencies: - "@babel/helper-module-transforms" "^7.23.0" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.22.15": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz#b3dba4757133b2762c00f4f94590cf6d52602481" - integrity sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ== +"@babel/plugin-transform-modules-commonjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== dependencies: - "@babel/helper-module-transforms" "^7.23.0" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.22.11": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz#77591e126f3ff4132a40595a6cccd00a6b60d160" - integrity sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg== +"@babel/plugin-transform-modules-systemjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" + integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.23.0" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.20" -"@babel/plugin-transform-modules-umd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" - integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== +"@babel/plugin-transform-modules-umd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": @@ -632,210 +651,211 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-new-target@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" - integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== +"@babel/plugin-transform-new-target@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz#debef6c8ba795f5ac67cd861a81b744c5d38d9fc" - integrity sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg== +"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e" + integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-transform-numeric-separator@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz#498d77dc45a6c6db74bb829c02a01c1d719cbfbd" - integrity sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg== +"@babel/plugin-transform-numeric-separator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29" + integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-transform-object-rest-spread@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz#21a95db166be59b91cde48775310c0df6e1da56f" - integrity sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q== +"@babel/plugin-transform-object-rest-spread@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83" + integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g== dependencies: - "@babel/compat-data" "^7.22.9" + "@babel/compat-data" "^7.23.3" "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.15" + "@babel/plugin-transform-parameters" "^7.23.3" -"@babel/plugin-transform-object-super@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" - integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== +"@babel/plugin-transform-object-super@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" -"@babel/plugin-transform-optional-catch-binding@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz#461cc4f578a127bb055527b3e77404cad38c08e0" - integrity sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ== +"@babel/plugin-transform-optional-catch-binding@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017" + integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.15": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz#73ff5fc1cf98f542f09f29c0631647d8ad0be158" - integrity sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g== +"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017" + integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-transform-parameters@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz#719ca82a01d177af358df64a514d64c2e3edb114" - integrity sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ== +"@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-methods@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" - integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== +"@babel/plugin-transform-private-methods@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" + integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-private-property-in-object@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz#ad45c4fc440e9cb84c718ed0906d96cf40f9a4e1" - integrity sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ== +"@babel/plugin-transform-private-property-in-object@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5" + integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.11" + "@babel/helper-create-class-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" - integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== +"@babel/plugin-transform-property-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-regenerator@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz#8ceef3bd7375c4db7652878b0241b2be5d0c3cca" - integrity sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw== +"@babel/plugin-transform-regenerator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" regenerator-transform "^0.15.2" -"@babel/plugin-transform-reserved-words@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" - integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== +"@babel/plugin-transform-reserved-words@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-runtime@^7.10.0": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz#3a625c4c05a39e932d7d34f5d4895cdd0172fdc9" - integrity sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz#52bbd20054855beb9deae3bee9ceb05289c343e6" + integrity sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw== dependencies: "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" semver "^6.3.1" -"@babel/plugin-transform-shorthand-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" - integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== +"@babel/plugin-transform-shorthand-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" - integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== +"@babel/plugin-transform-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" -"@babel/plugin-transform-sticky-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" - integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== +"@babel/plugin-transform-sticky-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-template-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" - integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== +"@babel/plugin-transform-template-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-typeof-symbol@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" - integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== +"@babel/plugin-transform-typeof-symbol@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-escapes@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9" - integrity sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg== +"@babel/plugin-transform-unicode-escapes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-property-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" - integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== +"@babel/plugin-transform-unicode-property-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" + integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" - integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== +"@babel/plugin-transform-unicode-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-unicode-sets-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" - integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== +"@babel/plugin-transform-unicode-sets-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" + integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-create-regexp-features-plugin" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.10.0": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.20.tgz#de9e9b57e1127ce0a2f580831717f7fb677ceedb" - integrity sha512-11MY04gGC4kSzlPHRfvVkNAZhUxOvm7DCJ37hPDnUENwe06npjIRAfInEMTGSb4LZK5ZgDFkv5hw0lGebHeTyg== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.8.tgz#7d6f8171ea7c221ecd28059e65ad37c20e441e3e" + integrity sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA== dependencies: - "@babel/compat-data" "^7.22.20" - "@babel/helper-compilation-targets" "^7.22.15" + "@babel/compat-data" "^7.23.5" + "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.15" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.15" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.15" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-class-properties" "^7.12.13" "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.22.5" - "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-assertions" "^7.23.3" + "@babel/plugin-syntax-import-attributes" "^7.23.3" "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" @@ -847,59 +867,58 @@ "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.15" - "@babel/plugin-transform-async-to-generator" "^7.22.5" - "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.15" - "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.11" - "@babel/plugin-transform-classes" "^7.22.15" - "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.15" - "@babel/plugin-transform-dotall-regex" "^7.22.5" - "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.11" - "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.11" - "@babel/plugin-transform-for-of" "^7.22.15" - "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.11" - "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" - "@babel/plugin-transform-member-expression-literals" "^7.22.5" - "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.15" - "@babel/plugin-transform-modules-systemjs" "^7.22.11" - "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + "@babel/plugin-transform-async-generator-functions" "^7.23.7" + "@babel/plugin-transform-async-to-generator" "^7.23.3" + "@babel/plugin-transform-block-scoped-functions" "^7.23.3" + "@babel/plugin-transform-block-scoping" "^7.23.4" + "@babel/plugin-transform-class-properties" "^7.23.3" + "@babel/plugin-transform-class-static-block" "^7.23.4" + "@babel/plugin-transform-classes" "^7.23.8" + "@babel/plugin-transform-computed-properties" "^7.23.3" + "@babel/plugin-transform-destructuring" "^7.23.3" + "@babel/plugin-transform-dotall-regex" "^7.23.3" + "@babel/plugin-transform-duplicate-keys" "^7.23.3" + "@babel/plugin-transform-dynamic-import" "^7.23.4" + "@babel/plugin-transform-exponentiation-operator" "^7.23.3" + "@babel/plugin-transform-export-namespace-from" "^7.23.4" + "@babel/plugin-transform-for-of" "^7.23.6" + "@babel/plugin-transform-function-name" "^7.23.3" + "@babel/plugin-transform-json-strings" "^7.23.4" + "@babel/plugin-transform-literals" "^7.23.3" + "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" + "@babel/plugin-transform-member-expression-literals" "^7.23.3" + "@babel/plugin-transform-modules-amd" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.3" + "@babel/plugin-transform-modules-umd" "^7.23.3" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.11" - "@babel/plugin-transform-numeric-separator" "^7.22.11" - "@babel/plugin-transform-object-rest-spread" "^7.22.15" - "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.11" - "@babel/plugin-transform-optional-chaining" "^7.22.15" - "@babel/plugin-transform-parameters" "^7.22.15" - "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.11" - "@babel/plugin-transform-property-literals" "^7.22.5" - "@babel/plugin-transform-regenerator" "^7.22.10" - "@babel/plugin-transform-reserved-words" "^7.22.5" - "@babel/plugin-transform-shorthand-properties" "^7.22.5" - "@babel/plugin-transform-spread" "^7.22.5" - "@babel/plugin-transform-sticky-regex" "^7.22.5" - "@babel/plugin-transform-template-literals" "^7.22.5" - "@babel/plugin-transform-typeof-symbol" "^7.22.5" - "@babel/plugin-transform-unicode-escapes" "^7.22.10" - "@babel/plugin-transform-unicode-property-regex" "^7.22.5" - "@babel/plugin-transform-unicode-regex" "^7.22.5" - "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" + "@babel/plugin-transform-numeric-separator" "^7.23.4" + "@babel/plugin-transform-object-rest-spread" "^7.23.4" + "@babel/plugin-transform-object-super" "^7.23.3" + "@babel/plugin-transform-optional-catch-binding" "^7.23.4" + "@babel/plugin-transform-optional-chaining" "^7.23.4" + "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-private-methods" "^7.23.3" + "@babel/plugin-transform-private-property-in-object" "^7.23.4" + "@babel/plugin-transform-property-literals" "^7.23.3" + "@babel/plugin-transform-regenerator" "^7.23.3" + "@babel/plugin-transform-reserved-words" "^7.23.3" + "@babel/plugin-transform-shorthand-properties" "^7.23.3" + "@babel/plugin-transform-spread" "^7.23.3" + "@babel/plugin-transform-sticky-regex" "^7.23.3" + "@babel/plugin-transform-template-literals" "^7.23.3" + "@babel/plugin-transform-typeof-symbol" "^7.23.3" + "@babel/plugin-transform-unicode-escapes" "^7.23.3" + "@babel/plugin-transform-unicode-property-regex" "^7.23.3" + "@babel/plugin-transform-unicode-regex" "^7.23.3" + "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.19" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" core-js-compat "^3.31.0" semver "^6.3.1" @@ -918,13 +937,13 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.10.0", "@babel/runtime@^7.8.4": - version "7.23.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d" - integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.22.5": +"@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== @@ -933,28 +952,28 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.0.tgz#18196ddfbcf4ccea324b7f6d3ada00d8c5a99c53" - integrity sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw== +"@babel/traverse@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.4.4": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.4.4": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== dependencies: - "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" @@ -993,6 +1012,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@gar/promisify@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -1079,29 +1103,29 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@lerna/child-process@7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.3.0.tgz#c56488a8a881f22a64793bf9339c5a2450a18559" - integrity sha512-rA+fGUo2j/LEq6w1w8s6oVikLbJTWoIDVpYMc7bUCtwDOUuZKMQiRtjmpavY3fTm7ltu42f4AKflc2A70K4wvA== +"@lerna/child-process@7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511" + integrity sha512-je+kkrfcvPcwL5Tg8JRENRqlbzjdlZXyaR88UcnCdNW0AJ1jX9IfHRys1X7AwSroU2ug8ESNC+suoBw1vX833Q== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@7.3.0": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.3.0.tgz#5438c231f617b8e825731390d394f8684af471d5" - integrity sha512-fjgiKjg9VXwQ4ZKKsrXICEKRiC3yo6+FprR0mc55uz0s5e9xupoSGLobUTTBdE7ncNB3ibqml8dfaAn/+ESajQ== +"@lerna/create@7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.4.2.tgz#f845fad1480e46555af98bd39af29571605dddc9" + integrity sha512-1wplFbQ52K8E/unnqB0Tq39Z4e+NEoNrpovEnl6GpsTUrC6WDp8+w0Le2uCBV0hXyemxChduCkLz4/y1H1wTeg== dependencies: - "@lerna/child-process" "7.3.0" + "@lerna/child-process" "7.4.2" "@npmcli/run-script" "6.0.2" "@nx/devkit" ">=16.5.1 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -1225,6 +1249,14 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== + dependencies: + "@gar/promisify" "^1.1.3" + semver "^7.3.5" + "@npmcli/fs@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" @@ -1254,6 +1286,14 @@ npm-bundled "^3.0.0" npm-normalize-package-bin "^3.0.0" +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@npmcli/node-gyp@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" @@ -1584,11 +1624,11 @@ integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@playwright/test@^1.37.1": - version "1.38.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.38.1.tgz#8ef4263e355cd1d8ad7905d471d268e8acb82ed6" - integrity sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ== + version "1.41.1" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.41.1.tgz#6954139ed4a67999f1b17460aa3d184f4b334f18" + integrity sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw== dependencies: - playwright "1.38.1" + playwright "1.41.1" "@sigstore/bundle@^1.1.0": version "1.1.0" @@ -1648,24 +1688,17 @@ dependencies: type-detect "4.0.8" -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== - dependencies: - type-detect "4.0.8" - "@sinonjs/commons@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" - integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== +"@sinonjs/fake-timers@^11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== dependencies: "@sinonjs/commons" "^3.0.0" @@ -1685,7 +1718,7 @@ lodash.get "^4.4.2" type-detect "^4.0.8" -"@sinonjs/text-encoding@^0.7.1": +"@sinonjs/text-encoding@^0.7.2": version "0.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== @@ -1736,23 +1769,23 @@ minimatch "^9.0.0" "@types/archiver@^5.3.2": - version "5.3.3" - resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.3.3.tgz#9cb632a67060602b1658c669b498d51dd8ce08ab" - integrity sha512-0ABdVcXL6jOwNGY+hjWPqrxUvKelBEwNLcuv/SV2vZ4YCH8w9NttFCt+/QqI5zgMX+iX/XqVy89/r7EmLJmMpQ== + version "5.3.4" + resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.3.4.tgz#32172d5a56f165b5b4ac902e366248bf03d3ae84" + integrity sha512-Lj7fLBIMwYFgViVVZHEdExZC3lVYsl+QL0VmdNdIzGZH544jHveYWij6qdnBgJQDnR7pMKliN9z2cPZFEbhyPw== dependencies: "@types/readdir-glob" "*" "@types/bent@^7.0.1": - version "7.3.5" - resolved "https://registry.yarnpkg.com/@types/bent/-/bent-7.3.5.tgz#0676776c1ea70bed464234435b80a6acbc8d9c7d" - integrity sha512-7PTYvy4UERqRPwlz/2KMXyCu08JpvN+SHBOH1Kzp+haZFsX1xrC+RI5qFVERTIDp1XoA+VnfatRmSM7x/0p3vw== + version "7.3.8" + resolved "https://registry.yarnpkg.com/@types/bent/-/bent-7.3.8.tgz#69c3ee49bf6593d831006794e7bd2f84bb573e58" + integrity sha512-yZ09JA1KsA5Fl6Oh/ahK00+H5bV0qCy2bYnyfiFY42wnaMK4n7IDC6HaFe3WW45Zhnak7iqJBKlWD0nVxzrGWg== dependencies: "@types/node" "*" "@types/body-parser@*", "@types/body-parser@^1.16.4", "@types/body-parser@^1.17.0": - version "1.19.3" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.3.tgz#fb558014374f7d9e56c8f34bab2042a3a07d25cd" - integrity sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ== + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== dependencies: "@types/connect" "*" "@types/node" "*" @@ -1775,16 +1808,16 @@ "@types/chai" "*" "@types/chai-string@^1.4.0": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@types/chai-string/-/chai-string-1.4.3.tgz#06e02d74deed77c2bfccccae44ece6e57a8ecedd" - integrity sha512-bLp5xMQ7Ml0fWa05IPpLjIznTkNbuBE3GtRTzKrp0d10IavlBFcu9vXP2liWaXta79unO693q3kuRxD7g2YYGw== + version "1.4.5" + resolved "https://registry.yarnpkg.com/@types/chai-string/-/chai-string-1.4.5.tgz#988ff37526386e9c354219b163d7ecf81bab8d2d" + integrity sha512-IecXRMSnpUvRnTztdpSdjcmcW7EdNme65bfDCQMi7XrSEPGmyDYYTEfc5fcactWDA6ioSm8o7NUqg9QxjBCCEw== dependencies: "@types/chai" "*" "@types/chai@*", "@types/chai@^4.2.7": - version "4.3.7" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.7.tgz#5457bc3dce72f20ae533366682a6298471d1c610" - integrity sha512-/k+vesl92vMvMygmQrFe9Aimxi6oQXFUX9mA5HanTrKUSAMoLauSi6PNFOdRw0oeqilaW600GNx2vSaT2f8aIQ== + version "4.3.11" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" + integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== "@types/chai@4.3.0": version "4.3.0" @@ -1792,9 +1825,9 @@ integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== "@types/connect@*": - version "3.4.36" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" - integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" @@ -1809,23 +1842,23 @@ integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== "@types/cors@^2.8.12": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.14.tgz#94eeb1c95eda6a8ab54870a3bf88854512f43a92" - integrity sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ== + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== dependencies: "@types/node" "*" "@types/decompress@^4.2.2", "@types/decompress@^4.2.4": - version "4.2.5" - resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.5.tgz#07ed5b350303b945017692e87a653a09df166915" - integrity sha512-LdL+kbcKGs9TzvB/K+OBGzPfDoP6gwwTsykYjodlzUJUUYp/43c1p1jE5YTtz3z4Ml90iruvBXbJ6+kDvb3WSQ== + version "4.2.7" + resolved "https://registry.yarnpkg.com/@types/decompress/-/decompress-4.2.7.tgz#604f69b69d519ecb74dea1ea0829f159b85e1332" + integrity sha512-9z+8yjKr5Wn73Pt17/ldnmQToaFHZxK0N1GHysuk/JIPT8RIdQeoInM01wWPgypRcvb6VH1drjuFpQ4zmY437g== dependencies: "@types/node" "*" "@types/diff@^3.2.2": - version "3.5.6" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-3.5.6.tgz#2524928a13888cebb59dc18e0c793022e7d02dfd" - integrity sha512-5BV7iGX/NmFGqAQn+YDBK++kO7IbZf0mIn8mwdJACIpZsMUqJvEin0riqNDbmS3SQL8u00dGnbC0FFJQptTSWw== + version "3.5.8" + resolved "https://registry.yarnpkg.com/@types/diff/-/diff-3.5.8.tgz#24434e47c2ef1cbcdf96afa43c6ea2fd8e4add93" + integrity sha512-CZ5vepL87+M8PxRIvJjR181Erahch2w7Jev/XJm+Iot/SOvJh8QqH/N79b+vsKtYF6fFzoPieiiq2c5tzmXR9A== "@types/dompurify@^2.2.2": version "2.4.0" @@ -1840,37 +1873,37 @@ integrity sha512-6dhZJLbA7aOwkYB2GDGdIqJ20wmHnkDzaxV9PJXe7O02I2dSFTERzRB6JrX6cWKaS+VqhhY7cQUMCbO5kloFUw== "@types/eslint-scope@^3.7.3": - version "3.7.5" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.5.tgz#e28b09dbb1d9d35fdfa8a884225f00440dfc5a3e" - integrity sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA== + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.44.3" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.3.tgz#96614fae4875ea6328f56de38666f582d911d962" - integrity sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g== + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== dependencies: "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453" - integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA== +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/express-http-proxy@^1.6.3": - version "1.6.4" - resolved "https://registry.yarnpkg.com/@types/express-http-proxy/-/express-http-proxy-1.6.4.tgz#42917facb194ab476c06f381838f211f4717a7dc" - integrity sha512-V0THpGPqxR85uHARStjYSKObI7ett4qA1JtiRqv/rv7pAt8IYFCtieLeq0GPnVYeR1BghgGQYlEZK7JPMUPrDQ== + version "1.6.6" + resolved "https://registry.yarnpkg.com/@types/express-http-proxy/-/express-http-proxy-1.6.6.tgz#386c6f4c61a2d26ab8817ba1c2b2aac80e5638c9" + integrity sha512-J8ZqHG76rq1UB716IZ3RCmUhg406pbWxsM3oFCFccl5xlWUPzoR4if6Og/cE4juK8emH0H9quZa5ltn6ZdmQJg== dependencies: "@types/express" "*" "@types/express-serve-static-core@^4.17.33": - version "4.17.37" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz#7e4b7b59da9142138a2aaa7621f5abedce8c7320" - integrity sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg== + version "4.17.41" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6" + integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA== dependencies: "@types/node" "*" "@types/qs" "*" @@ -1878,9 +1911,9 @@ "@types/send" "*" "@types/express@*", "@types/express@^4.16.0": - version "4.17.18" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.18.tgz#efabf5c4495c1880df1bdffee604b143b29c4a95" - integrity sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ== + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "^4.17.33" @@ -1888,9 +1921,9 @@ "@types/serve-static" "*" "@types/fs-extra@^4.0.2": - version "4.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.13.tgz#0499ef6ab6dd1c9e05c5247383867a47f5929e0b" - integrity sha512-rMZ7c4t5/EQc2FD7OTbS5XPHCR4hUSVwkiTN0/CXaLDTwxE3IPNMrCKEroLDSYB0K7UTpEH6TAcN30ff+MJw9w== + version "4.0.15" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.15.tgz#82d70b4a2e5e3dd17474ce9e8fe951a03aeddd31" + integrity sha512-zU/EU2kZ1tv+p4pswQLntA7dFQq84wXrSCfmLjZvMbLjf4N46cPOWHg+WKfc27YnEOQ0chVFlBui55HRsvzHPA== dependencies: "@types/node" "*" @@ -1917,28 +1950,28 @@ highlight.js "*" "@types/http-cache-semantics@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz#abe102d06ccda1efdf0ed98c10ccf7f36a785a41" - integrity sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== "@types/http-errors@*": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2" - integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg== + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== "@types/jsdom@^21.1.1": - version "21.1.3" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.3.tgz#a88c5dc65703e1b10b2a7839c12db49662b43ff0" - integrity sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA== + version "21.1.6" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.6.tgz#bcbc7b245787ea863f3da1ef19aa1dcfb9271a1b" + integrity sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw== dependencies: "@types/node" "*" "@types/tough-cookie" "*" parse5 "^7.0.0" "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.13" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" - integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" @@ -1953,14 +1986,14 @@ "@types/node" "*" "@types/linkify-it@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.3.tgz#15a0712296c5041733c79efe233ba17ae5a7587b" - integrity sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g== + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.5.tgz#1e78a3ac2428e6d7e6c05c1665c242023a4601d8" + integrity sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw== "@types/lodash.clonedeep@^4.5.3": - version "4.5.7" - resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197" - integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw== + version "4.5.9" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz#ea48276c7cc18d080e00bb56cf965bcceb3f0fc1" + integrity sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q== dependencies: "@types/lodash" "*" @@ -1972,16 +2005,16 @@ "@types/lodash" "*" "@types/lodash.throttle@^4.1.3": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/lodash.throttle/-/lodash.throttle-4.1.7.tgz#4ef379eb4f778068022310ef166625f420b6ba58" - integrity sha512-znwGDpjCHQ4FpLLx19w4OXDqq8+OvREa05H89obtSyXyOFKL3dDjCslsmfBz0T2FU8dmf5Wx1QvogbINiGIu9g== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz#f17a6ae084f7c0117bd7df145b379537bc9615c5" + integrity sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g== dependencies: "@types/lodash" "*" "@types/lodash@*": - version "4.14.199" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.199.tgz#c3edb5650149d847a277a8961a7ad360c474e9bf" - integrity sha512-Vrjz5N5Ia4SEzWWgIVwnHNEnb1UE1XMkvY5DGXrAeOGE9imk0hgTHh5GyDjLDJi9OTCn9oo9dXH1uToK1VRfrg== + version "4.14.202" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" + integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== "@types/long@^4.0.0": version "4.0.2" @@ -2001,9 +2034,9 @@ "@types/markdown-it" "*" "@types/markdown-it@*": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-13.0.2.tgz#1557e77789fc86e93fd4b8f0f8f8535ec97a8518" - integrity sha512-Tla7hH9oeXHOlJyBFdoqV61xWE9FZf/y2g+gFVwQ2vE1/eBzjUno5JCd3Hdb5oATve5OF6xNjZ/4VIZhVVx+hA== + version "13.0.7" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-13.0.7.tgz#4a495115f470075bd4434a0438ac477a49c2e152" + integrity sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA== dependencies: "@types/linkify-it" "*" "@types/mdurl" "*" @@ -2017,24 +2050,24 @@ "@types/mdurl" "*" "@types/mdurl@*": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.3.tgz#d0aefccdd1a96f4bec76047d6b314601f0b0f3de" - integrity sha512-T5k6kTXak79gwmIOaDF2UUQXFbnBE0zBUzF20pz7wDYu0RQMzWg+Ml/Pz50214NsFHBITkoi5VtdjFZnJ2ijjA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.5.tgz#3e0d2db570e9fb6ccb2dc8fde0be1d79ac810d39" + integrity sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA== "@types/mime-types@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.2.tgz#b4fe6996d2f32975b6603b26b4e4b3b6c92c9901" - integrity sha512-q9QGHMGCiBJCHEvd4ZLdasdqXv570agPsUW0CeIm/B8DzhxsYMerD0l3IlI+EQ1A2RWHY2mmM9x1YIuuWxisCg== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.4.tgz#93a1933e24fed4fb9e4adc5963a63efcbb3317a2" + integrity sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w== "@types/mime@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.2.tgz#c1ae807f13d308ee7511a5b81c74f327028e66e8" - integrity sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" + integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== "@types/mime@^1": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.3.tgz#bbe64987e0eb05de150c305005055c7ad784a9ce" - integrity sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg== + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== "@types/mime@^2.0.1": version "2.0.3" @@ -2052,51 +2085,51 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/minimist@^1.2.0": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.3.tgz#dd249cef80c6fff2ba6a0d4e5beca913e04e25f8" - integrity sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A== + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== "@types/mocha@^10.0.0": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.2.tgz#96d63314255540a36bf24da094cce7a13668d73b" - integrity sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w== + version "10.0.6" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.6.tgz#818551d39113081048bdddbef96701b4e8bb9d1b" + integrity sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg== "@types/multer@^1.4.7": - version "1.4.8" - resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.8.tgz#8d98c36f6a4e0b228a9f262cd66e881d7cd64039" - integrity sha512-VMZOW6mnmMMhA5m3fsCdXBwFwC+a+27/8gctNMuQC4f7UtWcF79KAFGoIfKZ4iqrElgWIa3j5vhMJDp0iikQ1g== + version "1.4.11" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" + integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== dependencies: "@types/express" "*" "@types/mustache@^4.1.2": - version "4.2.3" - resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.2.3.tgz#11ae9d7cd67c60746e3125baa8e5989db781d704" - integrity sha512-MG+oI3oelPKLN2gpkel08v6Tp6zU2zZQRq+eSpRsFtLNTd2kxZolOHQTmQQs0wqXTLOqs+ri3tRUaagH5u0quw== + version "4.2.5" + resolved "https://registry.yarnpkg.com/@types/mustache/-/mustache-4.2.5.tgz#9129f0d6857f976e00e171bbb3460e4b702f84ef" + integrity sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA== "@types/node-abi@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/node-abi/-/node-abi-3.0.1.tgz#6a8d3d6f94e9be4b9a427efac48c43c6b14af660" - integrity sha512-3fTqMJ2QY/0nKHvLIKDVO9LRx5Wres+O8J05H+cmwOw8jN/eyVm7tpCnEfrjt2jjpXlgLNEehyvqWomX5gOPqg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/node-abi/-/node-abi-3.0.3.tgz#a8334d75fe45ccd4cdb2a6c1ae82540a7a76828c" + integrity sha512-5oos6sivyXcDEuVC5oX3+wLwfgrGZu4NIOn826PGAjPCHsqp2zSPTGU7H1Tv+GZBOiDUY3nBXY1MdaofSEt4fw== "@types/node-fetch@^2.5.7": - version "2.6.6" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.6.tgz#b72f3f4bc0c0afee1c0bc9cff68e041d01e3e779" - integrity sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw== + version "2.6.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" + integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== dependencies: "@types/node" "*" form-data "^4.0.0" "@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^16.11.26", "@types/node@^18.11.18": - version "18.18.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.8.tgz#2b285361f2357c8c8578ec86b5d097c7f464cfd6" - integrity sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ== + version "18.19.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.9.tgz#6c2624c3a05bfa3a2735c533f95597ffacbb5608" + integrity sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw== dependencies: undici-types "~5.26.4" "@types/normalize-package-data@^2.4.0": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz#9b0e3e8533fe5024ad32d6637eb9589988b6fdca" - integrity sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/p-queue@^2.3.1": version "2.3.2" @@ -2104,59 +2137,59 @@ integrity sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ== "@types/prop-types@*": - version "15.7.8" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.8.tgz#805eae6e8f41bd19e88917d2ea200dc992f405d3" - integrity sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ== + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== "@types/proxy-from-env@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/proxy-from-env/-/proxy-from-env-1.0.2.tgz#f0eb1c492b98d64986df19a16ee3f196b25d3306" - integrity sha512-69oo0n4YzfxciixHK4c7AgEc4QDYQ4NVIWb/LpgqKqf3bpDqqxyEDTBvGDKQvO7PGO6bnsq932qZtJosAf66ow== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/proxy-from-env/-/proxy-from-env-1.0.4.tgz#0a0545768f2d6c16b81a84ffefb53b423807907c" + integrity sha512-TPR9/bCZAr3V1eHN4G3LD3OLicdJjqX1QRXWuNcCYgE66f/K8jO2ZRtHxI2D9MbnuUP6+qiKSS8eUHp6TFHGCw== dependencies: "@types/node" "*" "@types/ps-tree@^1.1.0": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@types/ps-tree/-/ps-tree-1.1.3.tgz#12a05ebbdc253ed2b2a6055560667e60814791d0" - integrity sha512-J8IrehehphLtxpABSekURTw9jthrlLcM4llH1I2fZ0zKaxq8jI/O1+Q/tabAJgBY/ffoqDxPRNYBM1lFUXm0lw== + version "1.1.6" + resolved "https://registry.yarnpkg.com/@types/ps-tree/-/ps-tree-1.1.6.tgz#fbb22dabe3d64b79295f37ce0afb7320a26ac9a6" + integrity sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ== "@types/qs@*": - version "6.9.8" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.8.tgz#f2a7de3c107b89b441e071d5472e6b726b4adf45" - integrity sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg== + version "6.9.11" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== "@types/range-parser@*": - version "1.2.5" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.5.tgz#38bd1733ae299620771bd414837ade2e57757498" - integrity sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA== + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/react-dom@^18.0.6": - version "18.2.12" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.12.tgz#58479c463d1e0b7f1ee7cd80e09186189f9ec32d" - integrity sha512-QWZuiA/7J/hPIGocXreCRbx7wyoeet9ooxfbSA+zbIWqyQEE7GMtRn4A37BdYyksnN+/NDnWgfxZH9UVGDw1hg== + version "18.2.18" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" + integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0.15": - version "18.2.27" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.27.tgz#746e52b06f3ccd5d7a724fd53769b70792601440" - integrity sha512-Wfv7B7FZiR2r3MIqbAlXoY1+tXm4bOqfz4oRr+nyXdBqapDBZ0l/IGcSlAfvxIHEEJjkPU0MYAc/BlFPOcrgLw== + version "18.2.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1" + integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/readdir-glob@*": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.2.tgz#688a206aa258a3a5d17c7b053da3b9e04eabf431" - integrity sha512-vwAYrNN/8yhp/FJRU6HUSD0yk6xfoOS8HrZa8ZL7j+X8hJpaC1hTcAiXX2IxaAkkvrz9mLyoEhYZTE3cEYvA9Q== + version "1.1.5" + resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.5.tgz#21a4a98898fc606cb568ad815f2a0eedc24d412a" + integrity sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg== dependencies: "@types/node" "*" "@types/responselike@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.1.tgz#1dd57e54509b3b95c7958e52709567077019d65d" - integrity sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== dependencies: "@types/node" "*" @@ -2169,67 +2202,67 @@ "@types/node" "*" "@types/route-parser@^0.1.1": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@types/route-parser/-/route-parser-0.1.5.tgz#5fee03ea01d2d457603eab3e46f4fa52573fb5c8" - integrity sha512-W17Tv0Y3uecmsqisMC5HwobDSEy7RXQfBxnbcBnVP0f6QbxFWCK+dEtC0u259nZFRgTYXHKaKbZzCtMgiYYAqg== + version "0.1.7" + resolved "https://registry.yarnpkg.com/@types/route-parser/-/route-parser-0.1.7.tgz#76d324537c9f0aaf65c96588c6ab5f3b84ae1505" + integrity sha512-haO+3HVio/4w+yuMJTjqfSo0ivOV8WnXaOReVD6QN729UGBEyizWNGc2Jd0OLsJDucIod4aJSsPLBeLj2uzMCQ== "@types/safer-buffer@^2.1.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@types/safer-buffer/-/safer-buffer-2.1.1.tgz#7d504d3d5b9cba87723543d0da3f47649d4feb52" - integrity sha512-L/QB8WCfXIRPguK8h3L+o1QO9b2NltRpj6y8dYusvzGPJhPZtw9lWYb9gmLvf30qS7j6cZ/wUBXXu36UEtH1XQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/@types/safer-buffer/-/safer-buffer-2.1.3.tgz#901b21c2da54344cf73a205fb5d400592a43b5fd" + integrity sha512-5o3RcpBa7mUFnnnoMa9UIGOf9naD4DCKKMYzqkm9OSY7NNTd26k7adNC+fphcVRI9BUJglBs2yJQiRZYqBF/8w== dependencies: "@types/node" "*" "@types/scheduler@*": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.4.tgz#fedc3e5b15c26dc18faae96bf1317487cb3658cf" - integrity sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ== + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== "@types/semver@^7.5.0": - version "7.5.3" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04" - integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw== + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== "@types/send@*": - version "0.17.2" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.2.tgz#af78a4495e3c2b79bfbdac3955fdd50e03cc98f2" - integrity sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw== + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== dependencies: "@types/mime" "^1" "@types/node" "*" "@types/serve-static@*": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.3.tgz#2cfacfd1fd4520bbc3e292cca432d5e8e2e3ee61" - integrity sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg== + version "1.15.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== dependencies: "@types/http-errors" "*" "@types/mime" "*" "@types/node" "*" "@types/sinon@^10.0.6": - version "10.0.19" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.19.tgz#752b752bc40bb5af0bb1aec29bde49b139b91d35" - integrity sha512-MWZNGPSchIdDfb5FL+VFi4zHsHbNOTQEgjqFQk7HazXSXwUU9PAX3z9XBqb3AJGYr9YwrtCtaSMsT3brYsN/jQ== + version "10.0.20" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.20.tgz#f1585debf4c0d99f9938f4111e5479fb74865146" + integrity sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg== dependencies: "@types/sinonjs__fake-timers" "*" "@types/sinonjs__fake-timers@*": - version "8.1.3" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.3.tgz#575789c5cf6d410cb288b0b4affaf7e6da44ca51" - integrity sha512-4g+2YyWe0Ve+LBh+WUm1697PD0Kdi6coG1eU0YjQbwx61AZ8XbEpL1zIT6WjuUKrCMCROpEaYQPDjBnDouBVAQ== + version "8.1.5" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" + integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== "@types/ssh2-sftp-client@^9.0.0": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/ssh2-sftp-client/-/ssh2-sftp-client-9.0.1.tgz#653e6088d34a1f7ffa32253d7fbcf853a318109e" - integrity sha512-jz3I1vFxUezHNOl5Bppj1AiltsVh3exudiLJI3ImOz80pSWMDb+aCT5qBHSWfQyJd5QOUEV7/+jSewIVNvwzrg== + version "9.0.3" + resolved "https://registry.yarnpkg.com/@types/ssh2-sftp-client/-/ssh2-sftp-client-9.0.3.tgz#dffe7fce6dd49fbda0823508ff0ce11c111dba13" + integrity sha512-pkCiS/NYvfc8S6xq3TvHAIPhQvBcl9Z1kMFxS8yNsqxmg/8ozzglnT4TrfpYBR1hlBky3r+fYntdZ5WnvvlKoQ== dependencies: "@types/ssh2" "*" "@types/ssh2@*", "@types/ssh2@^1.11.11": - version "1.11.14" - resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.11.14.tgz#d3bebf27fa508add5ddb65d0c945ca5329e669fb" - integrity sha512-O/U38mvV4jVVrdtZz8KpmitkmeD/PUDeDNNueQhm34166dmaqb1iZ3sfarSxBArM2/iX4PZVJY3EOta0Zks9hw== + version "1.11.19" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.11.19.tgz#4f2ec691b0674ea1590915fe5114a9aeae0eb41d" + integrity sha512-ydbQAqEcdNVy2t1w7dMh6eWMr+iOgtEkqM/3K9RMijMaok/ER7L8GW6PwsOypHCN++M+c8S/UR9SgMqNIFstbA== dependencies: "@types/node" "^18.11.18" @@ -2242,28 +2275,28 @@ "@types/tar-stream" "*" "@types/tar-stream@*": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-3.1.1.tgz#a0d936ec27c732e5287a84055b849637ee609974" - integrity sha512-/1E+a09mAFQwhlEHqiS3LuNWIBiyrn0HqUWZk2IyGzodu9zkXbaT5vl94iGlZGnG2IONVFZd84SFhns3MhhAQQ== + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-3.1.3.tgz#f61427229691eda1b7d5719f34acdc4fc8a558ce" + integrity sha512-Zbnx4wpkWBMBSu5CytMbrT5ZpMiF55qgM+EpHzR4yIDu7mv52cej8hTkOc6K+LzpkOAbxwn/m7j3iO+/l42YkQ== dependencies: "@types/node" "*" "@types/temp@^0.9.1": - version "0.9.2" - resolved "https://registry.yarnpkg.com/@types/temp/-/temp-0.9.2.tgz#5f134aaf5c9fd6ca2e3f93a6f84f19c6fbd1477c" - integrity sha512-n5sIDpSsilIPZU7i9R3Ts0JZEGKNz3tGF2q7I74QedOFRkdHTNIbbIt8DA3+GyqoNsi1UO9MXENmqd2VlDYAmg== + version "0.9.4" + resolved "https://registry.yarnpkg.com/@types/temp/-/temp-0.9.4.tgz#69bd4b0e8fc4d54db06bd1b613c19292d333350b" + integrity sha512-+VfWIwrlept2VBTj7Y2wQnI/Xfscy1u8Pyj/puYwss6V1IblXn1x7S0S9eFh6KyBolgLCm+rUFzhFAbdkR691g== dependencies: "@types/node" "*" "@types/tough-cookie@*", "@types/tough-cookie@^4.0.0": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.3.tgz#3d06b6769518450871fbc40770b7586334bdfd90" - integrity sha512-THo502dA5PzG/sfQH+42Lw3fvmYkceefOspdCwpHRul8ik2Jv1K8I5OZz1AT3/rs46kwgMCe9bSBmDLYkkOMGg== + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== "@types/trusted-types@*": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.4.tgz#2b38784cd16957d3782e8e2b31c03bc1d13b4d65" - integrity sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ== + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== "@types/unzipper@^0.9.2": version "0.9.2" @@ -2278,14 +2311,14 @@ integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== "@types/vscode-notebook-renderer@^1.72.0": - version "1.72.1" - resolved "https://registry.yarnpkg.com/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.1.tgz#5605079fa6f0fa1dddc57ce11315eec0d1a8d869" - integrity sha512-yr8mLZfyuRBa1VcQZbZOmQaNymi3aPNGxVCE9UKdyyJCf8OCP+Lqwjm603H0LKkocvmYHUPXYbdBWjwbc+BX1Q== + version "1.72.3" + resolved "https://registry.yarnpkg.com/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.3.tgz#013380d8ddfb9a5dc4b831ff025721afa0fd308b" + integrity sha512-MfmEI3A2McbUV2WaijoTgLOAs9chwHN4WmqOedl3jdtlbzJBWIQ9ZFmQdzPa3lYr5j8DJhRg3KB5AIM/BBfg9Q== "@types/vscode@^1.50.0": - version "1.83.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.83.0.tgz#f787d1d94d0b258b9bb97947396b47c1d364e90f" - integrity sha512-3mUtHqLAVz9hegut9au4xehuBrzRE3UJiQMpoEHkNl6XHliihO7eATx2BMHs0odsmmrwjJrlixx/Pte6M3ygDQ== + version "1.85.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.85.0.tgz#46beb07f0f626665b52d1e2294382b2bc63b602e" + integrity sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g== "@types/which@^1.3.2": version "1.3.2" @@ -2298,28 +2331,28 @@ integrity sha512-JdO/UpPm9RrtQBNVcZdt3M7j3mHO/kXaea9LBGx3UgWJd1f9BkIWP7jObLBG6ZtRyqp7KzLFEsaPhWcidVittA== "@types/ws@^8.5.5": - version "8.5.6" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.6.tgz#e9ad51f0ab79b9110c50916c9fcbddc36d373065" - integrity sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg== + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "21.0.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.1.tgz#07773d7160494d56aa882d7531aac7319ea67c3b" - integrity sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^15": - version "15.0.16" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.16.tgz#258009dc52907e8f03041eb64ffdac297ba4b208" - integrity sha512-2FeD5qezW3FvLpZ0JpfuaEWepgNLl9b2gQYiz/ce0NhoB1W/D+VZu98phITXkADYerfr/jb7JcDcVhITsc9bwg== + version "15.0.19" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" + integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== dependencies: "@types/yargs-parser" "*" "@types/yauzl@^2.9.1": - version "2.10.1" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.1.tgz#4e8f299f0934d60f36c74f59cb5a8483fd786691" - integrity sha512-CHzgNU3qYBnp/O4S3yv2tXPlvMTq0YWSTVg2/JYLqWZGHwwgJGAwd00poay/11asPq8wLFwHzubyInqHIFmmiw== + version "2.10.3" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" + integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== dependencies: "@types/node" "*" @@ -2456,14 +2489,14 @@ integrity sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw== "@vscode/codicons@*": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@vscode/codicons/-/codicons-0.0.33.tgz#a56243ab5492801fff04e53c0aab0d18a6521751" - integrity sha512-VdgpnD75swH9hpXjd34VBgQ2w2quK63WljodlUcOoJDPKiV+rPjHrcUc2sjLCNKxhl6oKqmsZgwOWcDAY2GKKQ== + version "0.0.35" + resolved "https://registry.yarnpkg.com/@vscode/codicons/-/codicons-0.0.35.tgz#7424a647f39c6e71c86c1edf12bfc27196c8fba1" + integrity sha512-7iiKdA5wHVYSbO7/Mm0hiHD3i4h+9hKUe1O4hISAe/nHhagMwb2ZbFC8jU6d7Cw+JNT2dWXN2j+WHbkhT5/l2w== "@vscode/debugprotocol@^1.51.0": - version "1.63.0" - resolved "https://registry.yarnpkg.com/@vscode/debugprotocol/-/debugprotocol-1.63.0.tgz#f6d16c382765d2533e515939ac2857aa1ed7ba35" - integrity sha512-7gewwv69pA7gcJUhtJsru5YN7E1AwwnlBrF5mJY4R/NGInOUqOYOWHlqQwG+4AXn0nXWbcn26MHgaGI9Q26SqA== + version "1.64.0" + resolved "https://registry.yarnpkg.com/@vscode/debugprotocol/-/debugprotocol-1.64.0.tgz#f20d998b96474a8ca1aab868fcda08be38fa1f41" + integrity sha512-Zhf3KvB+J04M4HPE2yCvEILGVtPixXUQMLBvx4QcAtjhc5lnwlZbbt80LCsZO2B+2BH8RMgVXk3QQ5DEzEne2Q== "@vscode/proxy-agent@^0.13.2": version "0.13.2" @@ -2480,17 +2513,18 @@ "@vscode/windows-ca-certs" "^0.3.1" "@vscode/ripgrep@^1.14.2": - version "1.15.5" - resolved "https://registry.yarnpkg.com/@vscode/ripgrep/-/ripgrep-1.15.5.tgz#26025884bbc3a8b40dfc29f5bda4b87b47bd7356" - integrity sha512-PVvKNEmtnlek3i4MJMaB910dz46CKQqcIY2gKR3PSlfz/ZPlSYuSuyQMS7iK20KL4hGUdSbWt964B5S5EIojqw== + version "1.15.9" + resolved "https://registry.yarnpkg.com/@vscode/ripgrep/-/ripgrep-1.15.9.tgz#92279f7f28e1e49ad9a89603e10b17a4c7f9f5f1" + integrity sha512-4q2PXRvUvr3bF+LsfrifmUZgSPmCNcUZo6SbEAZgArIChchkezaxLoIeQMJe/z3CCKStvaVKpBXLxN3Z8lQjFQ== dependencies: - https-proxy-agent "^5.0.0" + https-proxy-agent "^7.0.2" proxy-from-env "^1.1.0" + yauzl "^2.9.2" "@vscode/vsce@^2.15.0": - version "2.21.1" - resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.21.1.tgz#793c78d992483b428611a3927211a9640041be14" - integrity sha512-f45/aT+HTubfCU2oC7IaWnH9NjOWp668ML002QiFObFRVUCoLtcwepp9mmql/ArFUy+HCHp54Xrq4koTcOD6TA== + version "2.22.0" + resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.22.0.tgz#1eb3ebc6b89581a150bb44dd7d731a064019b617" + integrity sha512-8df4uJiM3C6GZ2Sx/KilSKVxsetrTBBIUb3c0W4B1EWHcddioVs5mkyDKtMNP0khP/xBILVSzlXxhV+nm2rC9A== dependencies: azure-devops-node-api "^11.0.1" chalk "^2.4.2" @@ -2735,9 +2769,9 @@ acorn-jsx@^5.3.1: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.0.2: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== acorn@^7.4.0: version "7.4.1" @@ -2745,9 +2779,9 @@ acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.1.0, acorn@^8.7.1, acorn@^8.8.2: - version "8.10.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" - integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== add-stream@^1.0.0: version "1.0.0" @@ -2761,6 +2795,13 @@ agent-base@6, agent-base@^6.0.2: dependencies: debug "4" +agent-base@^7.0.2: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -2816,23 +2857,23 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.3, ajv@^8.9.0: uri-js "^4.2.2" allure-commandline@^2.23.1: - version "2.24.1" - resolved "https://registry.yarnpkg.com/allure-commandline/-/allure-commandline-2.24.1.tgz#28eb1f28c031fa2ac0742fe85798d29202db678e" - integrity sha512-eNto3ipBq+O2B/f8/OwiS3E8R7jYENs3qv8jT7wMZmziYLANsISC9tX/FfEqR3FDiQlEOjkP7iyTEZ3ph53FPg== + version "2.26.0" + resolved "https://registry.yarnpkg.com/allure-commandline/-/allure-commandline-2.26.0.tgz#9c08fad9f8a9f73130273bb69d4a1e0cf75da62c" + integrity sha512-udKAxlKUfhD6iH3YuR8nPF72nxHYGJItqgqSvI1pJCLlO1hJHhctfwkQbtKgXugRB6d0Uf8AYUSIXobojRVPOg== -allure-js-commons@2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-2.9.2.tgz#47a2e31d1e476aa565fd4c467e6e1f3540774f6a" - integrity sha512-Qvi+zMZQruklqcnqG/zHCnE209v1YiWGhO3H2aPW2aXC8Ockqd01a+w2lP4Qqp3SfC+WQDeAK2+pp+v+eNl8xQ== +allure-js-commons@2.11.1: + version "2.11.1" + resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-2.11.1.tgz#cd4d123af49d1b2064d821bc355e221676eb1964" + integrity sha512-A7Eiofwj46JBbK2XsM9FKmbhTYrdok+5M2EzI5ueJ/S+T12xvINBrrKdtjkqFvz/oH9qA/iKHawlJc4MSQbxLQ== dependencies: properties "^1.2.1" allure-playwright@^2.5.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/allure-playwright/-/allure-playwright-2.9.2.tgz#27e86f37921a456632830e9c9820188ad5844aad" - integrity sha512-N0X1c1GGLg74vdDAuq86KCekuvQ5BaqqpgcBpJj5x3y/RlQPBn84wlg8PT/ViKQM4EdbNFMXOXpa5Opufv6qCg== + version "2.11.1" + resolved "https://registry.yarnpkg.com/allure-playwright/-/allure-playwright-2.11.1.tgz#6f6194791fb9b9178bb45becd1ee769faa4a8158" + integrity sha512-xzSFJ5Xrc8AxMM9fkpvvEOwjcuGWUiksx3mQiWFHALpUS1cgsJxm5M30omgqe6/rfbMBsIM2w4ufMts4N37+2w== dependencies: - allure-js-commons "2.9.2" + allure-js-commons "2.11.1" anser@^2.0.1: version "2.1.1" @@ -3026,7 +3067,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.6: +array-includes@^3.1.6, array-includes@^3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== @@ -3054,7 +3095,7 @@ array-uniq@^1.0.1: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== -array.prototype.findlastindex@^1.2.2: +array.prototype.findlastindex@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== @@ -3065,7 +3106,7 @@ array.prototype.findlastindex@^1.2.2: es-shim-unscopables "^1.0.0" get-intrinsic "^1.2.1" -array.prototype.flat@^1.3.1: +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== @@ -3075,7 +3116,7 @@ array.prototype.flat@^1.3.1: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: +array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== @@ -3154,9 +3195,9 @@ async-mutex@^0.3.1: tslib "^2.3.1" async-mutex@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f" - integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA== + version "0.4.1" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.1.tgz#bccf55b96f2baf8df90ed798cb5544a1f6ee4c2c" + integrity sha512-WfoBo4E/TbCX1G95XTjbWTE3X2XLG0m1Xbv2cwOtuPdyH9CZvnaA5nCt1ucjaKEgW2A5IF71hxrRhr83Je5xjA== dependencies: tslib "^2.4.0" @@ -3168,9 +3209,9 @@ async@^2.1.4, async@^2.6.4: lodash "^4.17.14" async@^3.2.3, async@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== asynciterator.prototype@^1.0.0: version "1.0.0" @@ -3220,11 +3261,11 @@ axios@^0.21.1: follow-redirects "^1.14.0" axios@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.1.tgz#11fbaa11fc35f431193a9564109c88c1f27b585f" - integrity sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A== + version "1.6.6" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.6.tgz#878db45401d91fe9e53aed8ac962ed93bde8dd1c" + integrity sha512-XZLZDFfXKM9U/Y/B4nNynfCRUqNyVZ4sBC/n9GDRCkq9vd2mIvKjKKsbIh1WPmHmNbg6ND7cTBY3Y2+u1G3/2Q== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.4" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -3246,29 +3287,29 @@ babel-loader@^8.2.2: make-dir "^3.1.0" schema-utils "^2.6.5" -babel-plugin-polyfill-corejs2@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" - integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== +babel-plugin-polyfill-corejs2@^0.4.7: + version "0.4.8" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" + integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.5.0" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.3: - version "0.8.4" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.4.tgz#1fac2b1dcef6274e72b3c72977ed8325cb330591" - integrity sha512-9l//BZZsPR+5XjyJMPtZSK4jv0BsTO1zDac2GC6ygx9WLGlcsnRd1Co0B2zT5fF5Ic6BZy+9m3HNZ3QcOeDKfg== +babel-plugin-polyfill-corejs3@^0.8.7: + version "0.8.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz#941855aa7fdaac06ed24c730a93450d2b2b76d04" + integrity sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" - core-js-compat "^3.32.2" + "@babel/helper-define-polyfill-provider" "^0.4.4" + core-js-compat "^3.33.1" -babel-plugin-polyfill-regenerator@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" - integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== +babel-plugin-polyfill-regenerator@^0.5.4: + version "0.5.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" + integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.5.0" babel-polyfill@^6.2.0: version "6.26.0" @@ -3331,9 +3372,9 @@ bent@^7.1.0: is-stream "^2.0.0" big-integer@^1.6.17: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== big.js@^5.2.2: version "5.2.2" @@ -3468,14 +3509,14 @@ browserfs@^1.4.3: async "^2.1.4" pako "^1.0.4" -browserslist@^4.14.5, browserslist@^4.21.9, browserslist@^4.22.1: - version "4.22.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" - integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== +browserslist@^4.21.10, browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== dependencies: - caniuse-lite "^1.0.30001541" - electron-to-chromium "^1.4.535" - node-releases "^2.0.13" + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" update-browserslist-db "^1.0.13" buffer-alloc-unsafe@^1.1.0: @@ -3581,6 +3622,30 @@ bytesish@^0.4.1: resolved "https://registry.yarnpkg.com/bytesish/-/bytesish-0.4.4.tgz#f3b535a0f1153747427aee27256748cff92347e6" integrity sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ== +cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== + dependencies: + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" + tar "^6.1.11" + unique-filename "^2.0.0" + cacache@^17.0.0: version "17.1.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.4.tgz#b3ff381580b47e85c6e64f801101508e26604b35" @@ -3627,13 +3692,14 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" callsites@^3.0.0: version "3.1.0" @@ -3659,10 +3725,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001541: - version "1.0.30001547" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz#d4f92efc488aab3c7f92c738d3977c2a3180472b" - integrity sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA== +caniuse-lite@^1.0.30001565: + version "1.0.30001580" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001580.tgz#e3c76bc6fe020d9007647044278954ff8cd17d1e" + integrity sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA== caseless@~0.12.0: version "0.12.0" @@ -3679,7 +3745,7 @@ chai-string@^1.4.0: resolved "https://registry.yarnpkg.com/chai-string/-/chai-string-1.5.0.tgz#0bdb2d8a5f1dbe90bc78ec493c1c1c180dd4d3d2" integrity sha512-sydDC3S3pNAQMYwJrs6dQX0oBQ6KfIPuOZ78n7rocW0eJJlsHPh2t3kwW7xfwYA/1Bf6/arGtSUo16rxR2JFlw== -chai@4.3.10, chai@^4.3.10: +chai@4.3.10: version "4.3.10" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== @@ -3692,6 +3758,19 @@ chai@4.3.10, chai@^4.3.10: pathval "^1.1.1" type-detect "^4.0.8" +chai@^4.3.10: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + chainsaw@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" @@ -3829,9 +3908,9 @@ cli-spinners@2.6.1: integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== cli-spinners@^2.5.0: - version "2.9.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.1.tgz#9c0b9dad69a6d47cbb4333c14319b060ed395a35" - integrity sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ== + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-width@^3.0.0: version "3.0.0" @@ -4079,10 +4158,10 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -conventional-changelog-angular@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz#a9a9494c28b7165889144fd5b91573c4aa9ca541" - integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== +conventional-changelog-angular@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz#5eec8edbff15aa9b1680a8dcfbd53e2d7eb2ba7a" + integrity sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ== dependencies: compare-func "^2.0.0" @@ -4197,12 +4276,12 @@ copy-webpack-plugin@^8.1.1: schema-utils "^3.0.0" serialize-javascript "^5.0.1" -core-js-compat@^3.31.0, core-js-compat@^3.32.2: - version "3.33.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.0.tgz#24aa230b228406450b2277b7c8bfebae932df966" - integrity sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw== +core-js-compat@^3.31.0, core-js-compat@^3.33.1: + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" + integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== dependencies: - browserslist "^4.22.1" + browserslist "^4.22.2" core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" @@ -4247,7 +4326,7 @@ cosmiconfig@^8.2.0: parse-json "^5.2.0" path-type "^4.0.0" -cpu-features@~0.0.8: +cpu-features@~0.0.9: version "0.0.9" resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.9.tgz#5226b92f0f1c63122b0a3eb84cb8335a4de499fc" integrity sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ== @@ -4293,18 +4372,18 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" css-loader@^6.2.0: - version "6.8.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" - integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== + version "6.9.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.9.1.tgz#9ec9a434368f2bdfeffbf8f6901a1ce773586c6b" + integrity sha512-OzABOh0+26JKFdMzlK6PY1u5Zx8+Ck7CVRlcGNZoY9qwJjdfu2VWFuprTIpPW+Av5TZTVViYWcFQaEEQURLknQ== dependencies: icss-utils "^5.1.0" - postcss "^8.4.21" + postcss "^8.4.33" postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.3" - postcss-modules-scope "^3.0.0" + postcss-modules-local-by-default "^4.0.4" + postcss-modules-scope "^3.1.1" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.8" + semver "^7.5.4" css-select@^5.1.0: version "5.1.0" @@ -4335,9 +4414,9 @@ cssstyle@^3.0.0: rrweb-cssom "^0.6.0" csstype@^3.0.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" - integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== dargs@^7.0.0: version "7.0.0" @@ -4539,10 +4618,10 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-data-property@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" - integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== dependencies: get-intrinsic "^1.2.1" gopd "^1.0.1" @@ -4553,7 +4632,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -4727,9 +4806,9 @@ dotenv-expand@~10.0.0: integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== dotenv@~16.3.1: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + version "16.3.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" + integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== drivelist@^9.0.2: version "9.2.4" @@ -4828,10 +4907,10 @@ electron-store@^8.0.0: conf "^10.2.0" type-fest "^2.17.0" -electron-to-chromium@^1.4.535: - version "1.4.548" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.548.tgz#e695d769e0e801fa6d438b63f6bc9b80372000d6" - integrity sha512-R77KD6mXv37DOyKLN/eW1rGS61N6yHOfapNSX9w+y9DdPG83l9Gkuv7qkCFZ4Ta4JPhrjgQfYbv4Y3TnM1Hi2Q== +electron-to-chromium@^1.4.601: + version "1.4.645" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.645.tgz#117f964252eb2f0ff00fc7360cb3080e2cf66e3c" + integrity sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw== electron-window@^0.8.0: version "0.8.1" @@ -4884,9 +4963,9 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: once "^1.4.0" engine.io-client@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.2.tgz#8709e22c291d4297ae80318d3c8baeae71f0e002" - integrity sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg== + version "6.5.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.3.tgz#4cf6fa24845029b238f83c628916d9149c399bc5" + integrity sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" @@ -4900,9 +4979,9 @@ engine.io-parser@~5.2.1: integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== engine.io@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.3.tgz#80b0692912cef3a417e1b7433301d6397bf0374b" - integrity sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw== + version "6.5.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" + integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" @@ -4959,9 +5038,9 @@ envinfo@7.8.1: integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== envinfo@^7.7.3: - version "7.10.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" - integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== err-code@^2.0.2: version "2.0.3" @@ -4983,25 +5062,25 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" - integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: array-buffer-byte-length "^1.0.0" arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function.prototype.name "^1.1.6" - get-intrinsic "^1.2.1" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" internal-slot "^1.0.5" is-array-buffer "^3.0.2" is-callable "^1.2.7" @@ -5011,7 +5090,7 @@ es-abstract@^1.22.1: is-string "^1.0.7" is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.3" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.5.1" @@ -5025,7 +5104,7 @@ es-abstract@^1.22.1: typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.11" + which-typed-array "^1.1.13" es-iterator-helpers@^1.0.12: version "1.0.15" @@ -5048,25 +5127,25 @@ es-iterator-helpers@^1.0.12: safe-array-concat "^1.0.1" es-module-lexer@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.1.tgz#c1b0dd5ada807a3b3155315911f364dc4e909db1" - integrity sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q== + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - has "^1.0.3" + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -5118,7 +5197,7 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-import-resolver-node@^0.3.7: +eslint-import-resolver-node@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== @@ -5144,27 +5223,27 @@ eslint-plugin-deprecation@~1.2.1: tsutils "^3.0.0" eslint-plugin-import@^2.27.5: - version "2.28.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz#63b8b5b3c409bfc75ebaf8fb206b07ab435482c4" - integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== - dependencies: - array-includes "^3.1.6" - array.prototype.findlastindex "^1.2.2" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" + eslint-import-resolver-node "^0.3.9" eslint-module-utils "^2.8.0" - has "^1.0.3" - is-core-module "^2.13.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.6" - object.groupby "^1.0.0" - object.values "^1.1.6" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" semver "^6.3.1" - tsconfig-paths "^3.14.2" + tsconfig-paths "^3.15.0" eslint-plugin-no-null@latest: version "1.0.2" @@ -5492,9 +5571,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.2.5, fast-glob@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -5523,9 +5602,9 @@ fastest-levenshtein@^1.0.12: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== dependencies: reusify "^1.0.4" @@ -5679,9 +5758,9 @@ fix-path@^3.0.0: shell-path "^2.1.0" flat-cache@^3.0.4: - version "3.1.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.1.tgz#a02a15fdec25a8f844ff7cc658f03dd99eb4609b" - integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: flatted "^3.2.9" keyv "^4.5.3" @@ -5697,10 +5776,10 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== font-awesome@^4.7.0: version "4.7.0" @@ -5774,9 +5853,9 @@ fs-extra@^10.0.0: universalify "^2.0.0" fs-extra@^11.1.0, fs-extra@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -5810,7 +5889,7 @@ fs-extra@^9.0.8: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -5849,10 +5928,10 @@ fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: version "1.1.6" @@ -5917,20 +5996,20 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0, get-func-name@^2.0.2: +get-func-name@^2.0.1, get-func-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-package-type@^0.1.0: version "0.1.0" @@ -6135,9 +6214,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.23.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" - integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" @@ -6243,12 +6322,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" @@ -6272,11 +6351,6 @@ has-unicode@2.0.1, has-unicode@^2.0.0, has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -has@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" - integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== - hasha@^5.0.0: version "5.2.2" resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" @@ -6285,6 +6359,13 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -6348,7 +6429,7 @@ htmlparser2@^8.0.1: domutils "^3.0.1" entities "^4.4.0" -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.1: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -6431,6 +6512,14 @@ https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: agent-base "6" debug "4" +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -6497,9 +6586,9 @@ ignore-walk@^5.0.1: minimatch "^5.0.1" ignore-walk@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.3.tgz#0fcdb6decaccda35e308a7b0948645dd9523b7bb" - integrity sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA== + version "6.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.4.tgz#89950be94b4f522225eb63a13c56badb639190e9" + integrity sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw== dependencies: minimatch "^9.0.0" @@ -6514,9 +6603,9 @@ ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.0.4, ignore@^5.1.8, ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== image-size@~0.5.0: version "0.5.5" @@ -6554,6 +6643,11 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -6607,12 +6701,12 @@ inquirer@^8.2.4: wrap-ansi "^6.0.1" internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" + get-intrinsic "^1.2.2" + hasown "^2.0.0" side-channel "^1.0.4" interpret@^2.2.0: @@ -6621,9 +6715,9 @@ interpret@^2.2.0: integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== inversify@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/inversify/-/inversify-6.0.1.tgz#b20d35425d5d8c5cd156120237aad0008d969f02" - integrity sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ== + version "6.0.2" + resolved "https://registry.yarnpkg.com/inversify/-/inversify-6.0.2.tgz#dc7fa0348213d789d35ffb719dea9685570989c7" + integrity sha512-i9m8j/7YIv4mDuYXUAcrpKPSaju/CIly9AHK5jvCBeoiM/2KEsuCQTTP+rzSWWpLYWRukdXFSl6ZTk2/uumbiA== ip@^2.0.0: version "2.0.0" @@ -6690,12 +6784,12 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" @@ -6953,11 +7047,6 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -6979,9 +7068,9 @@ isobject@^3.0.1: integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-hook@^3.0.0: version "3.0.0" @@ -7180,9 +7269,9 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-parse-even-better-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" - integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" + integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== json-schema-traverse@^0.4.1: version "0.4.1" @@ -7221,7 +7310,7 @@ json5@^2.1.2, json5@^2.2.2, json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonc-parser@3.2.0, jsonc-parser@^3.0.0, jsonc-parser@^3.2.0: +jsonc-parser@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== @@ -7231,6 +7320,11 @@ jsonc-parser@^2.2.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +jsonc-parser@^3.0.0, jsonc-parser@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" + integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7262,10 +7356,10 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: object.assign "^4.1.4" object.values "^1.1.6" -just-extend@^4.0.2: - version "4.2.1" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" - integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== +just-extend@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" + integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== just-performance@4.3.0: version "4.3.0" @@ -7308,12 +7402,12 @@ lazystream@^1.0.0: readable-stream "^2.0.5" lerna@^7.1.1: - version "7.3.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.3.0.tgz#efecafbdce15694e2f6841256e073a3a2061053e" - integrity sha512-Dt8TH+J+c9+3MhTYcm5OxnNzXb87WG7GPNj3kidjYJjJY7KxIMDNU37qBTYRWA1h3wAeNKBplXVQYUPkGcYgkQ== + version "7.4.2" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" + integrity sha512-gxavfzHfJ4JL30OvMunmlm4Anw7d7Tq6tdVHzUukLdS9nWnxCN/QB21qR+VJYp5tcyXogHKbdUEGh6qmeyzxSA== dependencies: - "@lerna/child-process" "7.3.0" - "@lerna/create" "7.3.0" + "@lerna/child-process" "7.4.2" + "@lerna/create" "7.4.2" "@npmcli/run-script" "6.0.2" "@nx/devkit" ">=16.5.1 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -7323,7 +7417,7 @@ lerna@^7.1.1: clone-deep "4.0.1" cmd-shim "6.0.1" columnify "1.6.0" - conventional-changelog-angular "6.0.0" + conventional-changelog-angular "7.0.0" conventional-changelog-core "5.0.1" conventional-recommended-bump "7.0.1" cosmiconfig "^8.2.0" @@ -7459,9 +7553,9 @@ lines-and-columns@^1.1.6: integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== lines-and-columns@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" - integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + version "2.0.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" + integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== linkify-it@^3.0.1: version "3.0.3" @@ -7649,11 +7743,11 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: js-tokens "^3.0.0 || ^4.0.0" loupe@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== dependencies: - get-func-name "^2.0.0" + get-func-name "^2.0.1" lowercase-keys@^2.0.0: version "2.0.0" @@ -7688,9 +7782,9 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== "lru-cache@^9.1.1 || ^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + version "10.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" + integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== lunr@^2.3.9: version "2.3.9" @@ -7745,7 +7839,29 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.0.3, make-fetch-happen@^11.1.1: +make-fetch-happen@^10.0.3: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== + dependencies: + agentkeepalive "^4.2.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^7.7.1" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + promise-retry "^2.0.1" + socks-proxy-agent "^7.0.0" + ssri "^9.0.0" + +make-fetch-happen@^11.0.0, make-fetch-happen@^11.0.1, make-fetch-happen@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== @@ -7917,9 +8033,9 @@ min-indent@^1.0.0: integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== mini-css-extract-plugin@^2.6.1: - version "2.7.6" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d" - integrity sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw== + version "2.7.7" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.7.tgz#4acf02f362c641c38fb913bfcb7ca2fc4a7cf339" + integrity sha512-+0n11YGyRavUR3IlaOzJ0/4Il1avMvJ1VJfhWfCn24ITQXhRr1gghbhhrda6tgtNcpZaWKdSuwKq20Jb7fnlyw== dependencies: schema-utils "^4.0.0" @@ -7993,6 +8109,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== + dependencies: + minipass "^3.1.6" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + minipass-fetch@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" @@ -8033,7 +8160,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -8238,7 +8365,7 @@ mute-stream@~1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -nan@^2.14.0, nan@^2.17.0: +nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: version "2.18.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== @@ -8264,10 +8391,10 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== napi-build-utils@^1.0.1: version "1.0.2" @@ -8300,20 +8427,20 @@ neo-async@^2.6.2: integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nise@^5.1.0: - version "5.1.4" - resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.4.tgz#491ce7e7307d4ec546f5a659b2efe94a18b4bbc0" - integrity sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg== + version "5.1.7" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.7.tgz#03ca96539efb306612eb60a8c5d6beeb208e27e5" + integrity sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ== dependencies: - "@sinonjs/commons" "^2.0.0" - "@sinonjs/fake-timers" "^10.0.2" - "@sinonjs/text-encoding" "^0.7.1" - just-extend "^4.0.2" - path-to-regexp "^1.7.0" + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/text-encoding" "^0.7.2" + just-extend "^6.2.0" + path-to-regexp "^6.2.1" node-abi@*, node-abi@^3.0.0, node-abi@^3.3.0: - version "3.48.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.48.0.tgz#122d132ae1ac097b0d711144560b17922de026ab" - integrity sha512-uWR/uwQyVV2iN5+Wkf1/oQxOR9YjU7gBclJLg2qK7GDvVohcnY6LaBXKV89N79EQFyN4/e43O32yQYE5QdFYTA== + version "3.54.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" + integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== dependencies: semver "^7.3.5" @@ -8366,20 +8493,20 @@ node-gyp-build-optional-packages@5.0.7: integrity sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w== node-gyp-build@^4.2.1, node-gyp-build@^4.3.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e" - integrity sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ== + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== node-gyp@^9.0.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" - integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== + version "9.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" + integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^11.0.3" + make-fetch-happen "^10.0.3" nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" @@ -8413,10 +8540,10 @@ node-pty@0.11.0-beta17: dependencies: nan "^2.14.0" -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== node-ssh@^12.0.1: version "12.0.5" @@ -8739,10 +8866,10 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1 resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== object-keys@^1.1.1: version "1.1.1" @@ -8750,12 +8877,12 @@ object-keys@^1.1.1: integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" @@ -8768,7 +8895,7 @@ object.entries@^1.1.6: define-properties "^1.2.0" es-abstract "^1.22.1" -object.fromentries@^2.0.6: +object.fromentries@^2.0.6, object.fromentries@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== @@ -8777,7 +8904,7 @@ object.fromentries@^2.0.6: define-properties "^1.2.0" es-abstract "^1.22.1" -object.groupby@^1.0.0: +object.groupby@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== @@ -8795,7 +8922,7 @@ object.hasown@^1.1.2: define-properties "^1.2.0" es-abstract "^1.22.1" -object.values@^1.1.6: +object.values@^1.1.6, object.values@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== @@ -9183,12 +9310,10 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== -path-to-regexp@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" - integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== - dependencies: - isarray "0.0.1" +path-to-regexp@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" + integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== path-type@^3.0.0: version "3.0.0" @@ -9285,17 +9410,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.38.1: - version "1.38.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.38.1.tgz#75a3c470aa9576b7d7c4e274de3d79977448ba08" - integrity sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg== +playwright-core@1.41.1: + version "1.41.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.1.tgz#9c152670010d9d6f970f34b68e3e935d3c487431" + integrity sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg== -playwright@1.38.1: - version "1.38.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.38.1.tgz#82ecd9bc4f4f64dbeee8a11c31793748e2528130" - integrity sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow== +playwright@1.41.1: + version "1.41.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.1.tgz#83325f34165840d019355c2a78a50f21ed9b9c85" + integrity sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ== dependencies: - playwright-core "1.38.1" + playwright-core "1.41.1" optionalDependencies: fsevents "2.3.2" @@ -9313,19 +9438,19 @@ postcss-modules-extract-imports@^3.0.0: resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" - integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== +postcss-modules-local-by-default@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz#7cbed92abd312b94aaea85b68226d3dec39a14e6" + integrity sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz#32cfab55e84887c079a19bbb215e721d683ef134" + integrity sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA== dependencies: postcss-selector-parser "^6.0.4" @@ -9337,9 +9462,9 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.13" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" - integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -9349,12 +9474,12 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.21: - version "8.4.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" - integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== +postcss@^8.4.33: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== dependencies: - nanoid "^3.3.6" + nanoid "^3.3.7" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -9555,9 +9680,9 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== puppeteer-core@19.7.2: version "19.7.2" @@ -9866,9 +9991,9 @@ redent@^3.0.0: strip-indent "^3.0.0" reflect-metadata@^0.1.10: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + version "0.1.14" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.14.tgz#24cf721fe60677146bb77eeb0e1f9dece3d65859" + integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== reflect.getprototypeof@^1.0.4: version "1.0.4" @@ -9905,9 +10030,9 @@ regenerator-runtime@^0.11.0: integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== regenerator-runtime@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" - integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== regenerator-transform@^0.15.2: version "0.15.2" @@ -10006,20 +10131,20 @@ resolve-package-path@^4.0.3: path-root "^0.1.1" resolve@^1.10.0, resolve@^1.14.2, resolve@^1.22.4, resolve@^1.3.2, resolve@^1.9.0: - version "1.22.6" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" - integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -10123,12 +10248,12 @@ rxjs@^7.5.5: tslib "^2.1.0" safe-array-concat@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" - integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" has-symbols "^1.0.3" isarray "^2.0.5" @@ -10143,12 +10268,12 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" + integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2, safer-buffer@~2.1.0: @@ -10297,9 +10422,9 @@ serialize-javascript@^5.0.1: randombytes "^2.1.0" serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -10318,6 +10443,17 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" @@ -10488,9 +10624,9 @@ socket.io-adapter@~2.5.2: ws "~8.11.0" socket.io-client@^4.5.3: - version "4.7.2" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.2.tgz#f2f13f68058bd4e40f94f2a1541f275157ff2c08" - integrity sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w== + version "4.7.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.4.tgz#5f0e060ff34ac0a4b4c5abaaa88e0d1d928c64c8" + integrity sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.2" @@ -10506,9 +10642,9 @@ socket.io-parser@~4.2.4: debug "~4.3.1" socket.io@^4.5.3: - version "4.7.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.2.tgz#22557d76c3f3ca48f82e73d68b7add36a22df002" - integrity sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw== + version "4.7.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.4.tgz#2401a2d7101e4bdc64da80b140d5d8b6a8c7738b" + integrity sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw== dependencies: accepts "~1.3.4" base64id "~2.0.0" @@ -10614,9 +10750,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + version "2.4.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" + integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -10672,15 +10808,15 @@ ssh2-sftp-client@^9.1.0: ssh2 "^1.12.0" ssh2@^1.12.0, ssh2@^1.5.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.14.0.tgz#8f68440e1b768b66942c9e4e4620b2725b3555bb" - integrity sha512-AqzD1UCqit8tbOKoj6ztDDi1ffJZ2rV2SwlgrVVrHPkV5vWqGJOVp5pmtj18PunkPJAuKQsnInyKV+/Nb2bUnA== + version "1.15.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b" + integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw== dependencies: asn1 "^0.2.6" bcrypt-pbkdf "^1.0.2" optionalDependencies: - cpu-features "~0.0.8" - nan "^2.17.0" + cpu-features "~0.0.9" + nan "^2.18.0" ssri@^10.0.0, ssri@^10.0.1: version "10.0.5" @@ -10689,7 +10825,7 @@ ssri@^10.0.0, ssri@^10.0.1: dependencies: minipass "^7.0.3" -ssri@^9.0.1: +ssri@^9.0.0, ssri@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== @@ -11052,21 +11188,21 @@ temp@^0.9.1: mkdirp "^0.5.1" rimraf "~2.6.2" -terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.16.8: - version "5.21.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.21.0.tgz#d2b27e92b5e56650bc83b6defa00a110f0b124b2" - integrity sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw== +terser@^5.26.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -11204,10 +11340,10 @@ ts-md5@^1.2.2: resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511" integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg== -tsconfig-paths@^3.14.2: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.2" @@ -11429,9 +11565,9 @@ typedoc@^0.22.11: shiki "^0.10.1" "typescript@>=3 < 6": - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== typescript@~4.5.5: version "4.5.5" @@ -11515,6 +11651,13 @@ union@~0.5.0: dependencies: qs "^6.4.0" +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== + dependencies: + unique-slug "^3.0.0" + unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" @@ -11522,6 +11665,13 @@ unique-filename@^3.0.0: dependencies: unique-slug "^4.0.0" +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + dependencies: + imurmurhash "^0.1.4" + unique-slug@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" @@ -11530,9 +11680,9 @@ unique-slug@^4.0.0: imurmurhash "^0.1.4" universal-user-agent@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" - integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + version "6.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" + integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== universalify@^0.1.0: version "0.1.2" @@ -11545,9 +11695,9 @@ universalify@^0.2.0: integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" @@ -11793,11 +11943,12 @@ webpack-cli@4.7.0: webpack-merge "^5.7.3" webpack-merge@^5.7.3: - version "5.9.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" - integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.2.3: @@ -11806,18 +11957,18 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.76.0: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== + version "5.90.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.0.tgz#313bfe16080d8b2fee6e29b6c986c0714ad4290e" + integrity sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" + "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.11.5" "@webassemblyjs/wasm-edit" "^1.11.5" "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" enhanced-resolve "^5.15.0" es-module-lexer "^1.2.1" @@ -11831,7 +11982,7 @@ webpack@^5.76.0: neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" + terser-webpack-plugin "^5.3.10" watchpack "^2.4.0" webpack-sources "^3.2.3" @@ -11912,13 +12063,13 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== -which-typed-array@^1.1.11, which-typed-array@^1.1.9: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" @@ -12077,9 +12228,9 @@ ws@8.11.0, ws@~8.11.0: integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== ws@^8.13.0, ws@^8.14.1: - version "8.14.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== xdg-basedir@^4.0.0: version "4.0.0" @@ -12255,7 +12406,7 @@ yargs@^17.0.1, yargs@^17.6.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yauzl@^2.10.0, yauzl@^2.3.1, yauzl@^2.4.2: +yauzl@^2.10.0, yauzl@^2.3.1, yauzl@^2.4.2, yauzl@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== From 120a822b87fd9a26fe63298f29ad5e2065570fc8 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Wed, 21 Feb 2024 12:50:22 +0100 Subject: [PATCH 099/441] Show decorations in the editor tabs (#13301) (#13371) --- packages/core/src/browser/core-preferences.ts | 6 +++ .../src/browser/shell/tab-bar-decorator.ts | 42 ++++++++++++++++--- packages/core/src/browser/shell/tab-bars.ts | 34 ++++++++++++++- packages/core/src/browser/style/tabs.css | 6 +++ 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 74fc83ffa8105..8f54f5419f645 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -200,6 +200,11 @@ export const corePreferenceSchema: PreferenceSchema = { 'description': nls.localizeByDefault('Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, such as when forcing an editor to open in a specific group or to the side of the currently active group.'), 'default': false }, + 'workbench.editor.decorations.badges': { + 'type': 'boolean', + 'description': nls.localizeByDefault('Controls whether editor file decorations should use badges.'), + 'default': true + }, 'workbench.commandPalette.history': { type: 'number', default: 50, @@ -295,6 +300,7 @@ export interface CoreConfiguration { 'workbench.editor.mouseBackForwardToNavigate': boolean; 'workbench.editor.closeOnFileDelete': boolean; 'workbench.editor.revealIfOpen': boolean; + 'workbench.editor.decorations.badges': boolean; 'workbench.colorTheme': string; 'workbench.iconTheme': string; 'workbench.silentNotifications': boolean; diff --git a/packages/core/src/browser/shell/tab-bar-decorator.ts b/packages/core/src/browser/shell/tab-bar-decorator.ts index 4b48d848876b1..7b9d6a9756e9a 100644 --- a/packages/core/src/browser/shell/tab-bar-decorator.ts +++ b/packages/core/src/browser/shell/tab-bar-decorator.ts @@ -17,9 +17,12 @@ import debounce = require('lodash.debounce'); import { Title, Widget } from '@phosphor/widgets'; import { inject, injectable, named } from 'inversify'; -import { Event, Emitter, ContributionProvider } from '../../common'; -import { WidgetDecoration } from '../widget-decoration'; +import { ContributionProvider, Emitter, Event } from '../../common'; +import { ColorRegistry } from '../color-registry'; +import { Decoration, DecorationsService, DecorationsServiceImpl } from '../decorations-service'; import { FrontendApplicationContribution } from '../frontend-application-contribution'; +import { Navigatable } from '../navigatable-types'; +import { WidgetDecoration } from '../widget-decoration'; export const TabBarDecorator = Symbol('TabBarDecorator'); @@ -53,6 +56,12 @@ export class TabBarDecoratorService implements FrontendApplicationContribution { @inject(ContributionProvider) @named(TabBarDecorator) protected readonly contributions: ContributionProvider; + @inject(DecorationsService) + protected readonly decorationsService: DecorationsServiceImpl; + + @inject(ColorRegistry) + protected readonly colors: ColorRegistry; + initialize(): void { this.contributions.getContributions().map(decorator => decorator.onDidChangeDecorations(this.fireDidChangeDecorations)); } @@ -66,11 +75,32 @@ export class TabBarDecoratorService implements FrontendApplicationContribution { */ getDecorations(title: Title): WidgetDecoration.Data[] { const decorators = this.contributions.getContributions(); - let all: WidgetDecoration.Data[] = []; + const decorations: WidgetDecoration.Data[] = []; for (const decorator of decorators) { - const decorations = decorator.decorate(title); - all = all.concat(decorations); + decorations.push(...decorator.decorate(title)); } - return all; + if (Navigatable.is(title.owner)) { + const resourceUri = title.owner.getResourceUri(); + if (resourceUri) { + const serviceDecorations = this.decorationsService.getDecoration(resourceUri, false); + decorations.push(...serviceDecorations.map(d => this.fromDecoration(d))); + } + } + return decorations; + } + + protected fromDecoration(decoration: Decoration): WidgetDecoration.Data { + const colorVariable = decoration.colorId && this.colors.toCssVariableName(decoration.colorId); + return { + tailDecorations: [ + { + data: decoration.letter ? decoration.letter : '', + fontData: { + color: colorVariable && `var(${colorVariable})` + }, + tooltip: decoration.tooltip ? decoration.tooltip : '' + } + ] + }; } } diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts index 794f59d917c73..d8c4332647a83 100644 --- a/packages/core/src/browser/shell/tab-bars.ts +++ b/packages/core/src/browser/shell/tab-bars.ts @@ -17,7 +17,7 @@ import PerfectScrollbar from 'perfect-scrollbar'; import { TabBar, Title, Widget } from '@phosphor/widgets'; import { VirtualElement, h, VirtualDOM, ElementInlineStyle } from '@phosphor/virtualdom'; -import { Disposable, DisposableCollection, MenuPath, notEmpty, SelectionService, CommandService, nls } from '../../common'; +import { Disposable, DisposableCollection, MenuPath, notEmpty, SelectionService, CommandService, nls, ArrayUtils } from '../../common'; import { ContextMenuRenderer } from '../context-menu-renderer'; import { Signal, Slot } from '@phosphor/signaling'; import { Message, MessageLoop } from '@phosphor/messaging'; @@ -188,6 +188,7 @@ export class TabBarRenderer extends TabBar.Renderer { { className: 'theia-tab-icon-label' }, this.renderIcon(data, isInSidePanel), this.renderLabel(data, isInSidePanel), + this.renderTailDecorations(data, isInSidePanel), this.renderBadge(data, isInSidePanel), this.renderLock(data, isInSidePanel) ), @@ -289,6 +290,37 @@ export class TabBarRenderer extends TabBar.Renderer { return h.div({ className: 'p-TabBar-tabLabel', style }, data.title.label); } + protected renderTailDecorations(renderData: SideBarRenderData, isInSidePanel?: boolean): VirtualElement[] { + if (!this.corePreferences?.get('workbench.editor.decorations.badges')) { + return []; + } + const tailDecorations = ArrayUtils.coalesce(this.getDecorationData(renderData.title, 'tailDecorations')).flat(); + if (tailDecorations === undefined || tailDecorations.length === 0) { + return []; + } + let dotDecoration: WidgetDecoration.TailDecoration.AnyPartial | undefined; + const otherDecorations: WidgetDecoration.TailDecoration.AnyPartial[] = []; + tailDecorations.reverse().forEach(decoration => { + const partial = decoration as WidgetDecoration.TailDecoration.AnyPartial; + if (WidgetDecoration.TailDecoration.isDotDecoration(partial)) { + dotDecoration ||= partial; + } else if (partial.data || partial.icon || partial.iconClass) { + otherDecorations.push(partial); + } + }); + const decorationsToRender = dotDecoration ? [dotDecoration, ...otherDecorations] : otherDecorations; + return decorationsToRender.map((decoration, index) => { + const { tooltip, data, fontData, color, icon, iconClass } = decoration; + const iconToRender = icon ?? iconClass; + const className = ['p-TabBar-tail', 'flex'].join(' '); + const style = fontData ? fontData : color ? { color } : undefined; + const content = (data ? data : iconToRender + ? h.span({ className: this.getIconClass(iconToRender, iconToRender === 'circle' ? [WidgetDecoration.Styles.DECORATOR_SIZE_CLASS] : []) }) + : '') + (index !== decorationsToRender.length - 1 ? ',' : ''); + return h.span({ key: ('tailDecoration_' + index), className, style, title: tooltip ?? content }, content); + }); + } + renderBadge(data: SideBarRenderData, isInSidePanel?: boolean): VirtualElement { const totalBadge = this.getDecorationData(data.title, 'badge').reduce((sum, badge) => sum! + badge!, 0); if (!totalBadge) { diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css index ef247de6fcfcd..a77ae0ad9f172 100644 --- a/packages/core/src/browser/style/tabs.css +++ b/packages/core/src/browser/style/tabs.css @@ -123,6 +123,12 @@ white-space: nowrap; } +.p-TabBar-tail { + padding-left: 5px; + text-align: center; + justify-content: center; +} + .p-TabBar.theia-app-centers .p-TabBar-tabLabelWrapper { display: flex; } From ac08b27102136bbe72e0b98b0169c7f3cb059854 Mon Sep 17 00:00:00 2001 From: Torbjorn-Svensson Date: Wed, 21 Feb 2024 15:05:29 +0100 Subject: [PATCH 100/441] deps: Bump nano to 10.1.3 to avoid axios <1.6.0 (#13328) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit axios 1.6.0 fixes CVE-2023-45857 (https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459). Contributed by STMicroelectronics Signed-off-by: Torbjörn SVENSSON --- dev-packages/application-package/package.json | 2 +- yarn.lock | 60 ++++++++----------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 9bab3e6f6d5c7..564b0c76bf671 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -36,7 +36,7 @@ "deepmerge": "^4.2.2", "fs-extra": "^4.0.2", "is-electron": "^2.1.0", - "nano": "^9.0.5", + "nano": "^10.1.3", "resolve-package-path": "^4.0.3", "semver": "^7.5.4", "write-json-file": "^2.2.0" diff --git a/yarn.lock b/yarn.lock index 20a36d8c48255..5f9f053ced5c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2288,7 +2288,7 @@ dependencies: "@types/node" "*" -"@types/tough-cookie@*", "@types/tough-cookie@^4.0.0": +"@types/tough-cookie@*": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== @@ -3245,21 +3245,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios-cookiejar-support@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/axios-cookiejar-support/-/axios-cookiejar-support-1.0.1.tgz#7b32af7d932508546c68b1fc5ba8f562884162e1" - integrity sha512-IZJxnAJ99XxiLqNeMOqrPbfR7fRyIfaoSLdPUf4AMQEGkH8URs0ghJK/xtqBsD+KsSr3pKl4DEQjCn834pHMig== - dependencies: - is-redirect "^1.0.0" - pify "^5.0.0" - -axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== - dependencies: - follow-redirects "^1.14.0" - axios@^1.0.0: version "1.6.6" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.6.tgz#878db45401d91fe9e53aed8ac962ed93bde8dd1c" @@ -3269,6 +3254,15 @@ axios@^1.0.0: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.6.2: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + dependencies: + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + azure-devops-node-api@^11.0.1: version "11.2.0" resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz#bf04edbef60313117a0507415eed4790a420ad6b" @@ -5776,7 +5770,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== -follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.4: +follow-redirects@^1.0.0, follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== @@ -6925,11 +6919,6 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -8370,16 +8359,14 @@ nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== -nano@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/nano/-/nano-9.0.5.tgz#2b767819f612907a3ac09b21f2929d4097407262" - integrity sha512-fEAhwAdXh4hDDnC8cYJtW6D8ivOmpvFAqT90+zEuQREpRkzA/mJPcI4EKv15JUdajaqiLTXNoKK6PaRF+/06DQ== +nano@^10.1.3: + version "10.1.3" + resolved "https://registry.yarnpkg.com/nano/-/nano-10.1.3.tgz#5cb1ad14add4c9c82d53a79159848dafa84e7a13" + integrity sha512-q/hKQJJH3FhkkuJ3ojbgDph2StlSXFBPNkpZBZlsvZDbuYfxKJ4VtunEeilthcZtuIplIk1zVX5o2RgKTUTO+Q== dependencies: - "@types/tough-cookie" "^4.0.0" - axios "^0.21.1" - axios-cookiejar-support "^1.0.1" - qs "^6.9.4" - tough-cookie "^4.0.0" + axios "^1.6.2" + node-abort-controller "^3.0.1" + qs "^6.11.0" nanoid@3.3.1: version "3.3.1" @@ -8451,6 +8438,11 @@ node-abi@^2.21.0, node-abi@^2.7.0: dependencies: semver "^5.4.1" +node-abort-controller@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-addon-api@^3.0.0, node-addon-api@^3.0.2, node-addon-api@^3.1.0, node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" @@ -9364,7 +9356,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@5.0.0, pify@^5.0.0: +pify@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== @@ -9729,7 +9721,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.4.0, qs@^6.9.1, qs@^6.9.4: +qs@^6.11.0, qs@^6.4.0, qs@^6.9.1: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -11277,7 +11269,7 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tough-cookie@^4.0.0, tough-cookie@^4.1.2: +tough-cookie@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== From 6e8031b731a00a1801db7b31f88e519d12d1b0e9 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 21 Feb 2024 15:51:23 +0100 Subject: [PATCH 101/441] Improve readonly editor behavior (#13403) --- .gitpod.yml | 9 ++-- .../browser/api-samples-frontend-module.ts | 2 + .../sample-file-system-capabilities.ts | 48 +++++++++++++++++ packages/core/src/browser/widgets/widget.ts | 8 +++ packages/core/src/common/resource.ts | 6 ++- packages/editor/src/browser/editor-widget.ts | 9 +++- packages/editor/src/browser/editor.ts | 4 +- .../filesystem/src/browser/file-resource.ts | 16 +++++- .../monaco/src/browser/monaco-editor-model.ts | 9 ++-- .../src/browser/monaco-editor-provider.ts | 3 -- packages/monaco/src/browser/monaco-editor.ts | 24 +++++++-- .../src/browser/simple-monaco-editor.ts | 9 +++- .../notebook-actions-contribution.ts | 53 ++++++++++++------- .../notebook-cell-actions-contribution.ts | 39 ++++++++------ .../notebook-cell-resource-resolver.ts | 31 ++++++++--- .../src/browser/notebook-editor-widget.tsx | 19 ++++++- .../notebook-model-resolver-service.ts | 24 +++++---- .../src/browser/service/notebook-service.ts | 8 +-- packages/notebook/src/browser/style/index.css | 5 +- .../browser/view-model/notebook-cell-model.ts | 8 +-- .../src/browser/view-model/notebook-model.ts | 41 +++++++++----- .../browser/view/notebook-cell-list-view.tsx | 45 ++++++++++++---- .../view/notebook-cell-toolbar-factory.tsx | 2 + .../browser/view/notebook-cell-toolbar.tsx | 4 +- .../browser/view/notebook-main-toolbar.tsx | 8 ++- .../custom-editors/custom-editors-main.ts | 2 +- 26 files changed, 325 insertions(+), 111 deletions(-) create mode 100644 examples/api-samples/src/browser/file-system/sample-file-system-capabilities.ts diff --git a/.gitpod.yml b/.gitpod.yml index a03d38cc692cc..1deab0c9a8306 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -12,13 +12,10 @@ ports: - port: 9339 # Node.js debug port onOpen: ignore tasks: - - init: yarn --network-timeout 100000 && yarn build:examples && yarn download:plugins + - init: yarn --network-timeout 100000 && yarn browser build && yarn download:plugins command: > jwm & - yarn --cwd examples/browser start ../.. --hostname=0.0.0.0 -github: - prebuilds: - pullRequestsFromForks: true + yarn browser start ../.. --hostname=0.0.0.0 vscode: extensions: - - dbaeumer.vscode-eslint@2.0.0:CwAMx4wYz1Kq39+1Aul4VQ== + - dbaeumer.vscode-eslint diff --git a/examples/api-samples/src/browser/api-samples-frontend-module.ts b/examples/api-samples/src/browser/api-samples-frontend-module.ts index eae887e3fa360..fe6c53d05527c 100644 --- a/examples/api-samples/src/browser/api-samples-frontend-module.ts +++ b/examples/api-samples/src/browser/api-samples-frontend-module.ts @@ -29,6 +29,7 @@ import { bindMonacoPreferenceExtractor } from './monaco-editor-preferences/monac import { rebindOVSXClientFactory } from '../common/vsx/sample-ovsx-client-factory'; import { bindSampleAppInfo } from './vsx/sample-frontend-app-info'; import { bindTestSample } from './test/sample-test-contribution'; +import { bindSampleFileSystemCapabilitiesCommands } from './file-system/sample-file-system-capabilities'; export default new ContainerModule(( bind: interfaces.Bind, @@ -47,5 +48,6 @@ export default new ContainerModule(( bindMonacoPreferenceExtractor(bind); bindSampleAppInfo(bind); bindTestSample(bind); + bindSampleFileSystemCapabilitiesCommands(bind); rebindOVSXClientFactory(rebind); }); diff --git a/examples/api-samples/src/browser/file-system/sample-file-system-capabilities.ts b/examples/api-samples/src/browser/file-system/sample-file-system-capabilities.ts new file mode 100644 index 0000000000000..e5ebf2e8418ea --- /dev/null +++ b/examples/api-samples/src/browser/file-system/sample-file-system-capabilities.ts @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (C) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { CommandContribution, CommandRegistry } from '@theia/core'; +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; +import { RemoteFileSystemProvider } from '@theia/filesystem/lib/common/remote-file-system-provider'; +import { FileSystemProviderCapabilities } from '@theia/filesystem/lib/common/files'; + +@injectable() +export class SampleFileSystemCapabilities implements CommandContribution { + + @inject(RemoteFileSystemProvider) + protected readonly remoteFileSystemProvider: RemoteFileSystemProvider; + + registerCommands(commands: CommandRegistry): void { + commands.registerCommand({ + id: 'toggleFileSystemReadonly', + label: 'Toggle File System Readonly' + }, { + execute: () => { + const readonly = (this.remoteFileSystemProvider.capabilities & FileSystemProviderCapabilities.Readonly) !== 0; + if (readonly) { + this.remoteFileSystemProvider['setCapabilities'](this.remoteFileSystemProvider.capabilities & ~FileSystemProviderCapabilities.Readonly); + } else { + this.remoteFileSystemProvider['setCapabilities'](this.remoteFileSystemProvider.capabilities | FileSystemProviderCapabilities.Readonly); + } + } + }); + } + +} + +export function bindSampleFileSystemCapabilitiesCommands(bind: interfaces.Bind): void { + bind(CommandContribution).to(SampleFileSystemCapabilities).inSingletonScope(); +} diff --git a/packages/core/src/browser/widgets/widget.ts b/packages/core/src/browser/widgets/widget.ts index 8ea9b5959e141..9a71b96074208 100644 --- a/packages/core/src/browser/widgets/widget.ts +++ b/packages/core/src/browser/widgets/widget.ts @@ -381,12 +381,20 @@ export function pin(title: Title): void { } } +export function isLocked(title: Title): boolean { + return title.className.includes(LOCKED_CLASS); +} + export function lock(title: Title): void { if (!title.className.includes(LOCKED_CLASS)) { title.className += ` ${LOCKED_CLASS}`; } } +export function unlock(title: Title): void { + title.className = title.className.replace(LOCKED_CLASS, '').trim(); +} + export function togglePinned(title?: Title): void { if (title) { if (isPinned(title)) { diff --git a/packages/core/src/common/resource.ts b/packages/core/src/common/resource.ts index 4c62063e94478..abe188733a925 100644 --- a/packages/core/src/common/resource.ts +++ b/packages/core/src/common/resource.ts @@ -25,6 +25,7 @@ import { CancellationToken } from './cancellation'; import { ApplicationError } from './application-error'; import { ReadableStream, Readable } from './stream'; import { SyncReferenceCollection, Reference } from './reference'; +import { MarkdownString } from './markdown-rendering'; export interface ResourceVersion { } @@ -55,7 +56,10 @@ export interface Resource extends Disposable { * Undefined if a resource did not read content yet. */ readonly encoding?: string | undefined; - readonly isReadonly?: boolean; + + readonly onDidChangeReadOnly?: Event; + + readonly readOnly?: boolean | MarkdownString; /** * Reads latest content of this resource. * diff --git a/packages/editor/src/browser/editor-widget.ts b/packages/editor/src/browser/editor-widget.ts index 708dba0994997..d31e089b09843 100644 --- a/packages/editor/src/browser/editor-widget.ts +++ b/packages/editor/src/browser/editor-widget.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { Disposable, SelectionService, Event, UNTITLED_SCHEME, DisposableCollection } from '@theia/core/lib/common'; -import { Widget, BaseWidget, Message, Saveable, SaveableSource, Navigatable, StatefulWidget, lock, TabBar, DockPanel } from '@theia/core/lib/browser'; +import { Widget, BaseWidget, Message, Saveable, SaveableSource, Navigatable, StatefulWidget, lock, TabBar, DockPanel, unlock } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { find } from '@theia/core/shared/@phosphor/algorithm'; import { TextEditor } from './editor'; @@ -38,6 +38,13 @@ export class EditorWidget extends BaseWidget implements SaveableSource, Navigata this.toDispose.push(this.toDisposeOnTabbarChange); this.toDispose.push(this.editor.onSelectionChanged(() => this.setSelection())); this.toDispose.push(this.editor.onFocusChanged(() => this.setSelection())); + this.toDispose.push(this.editor.onDidChangeReadOnly(isReadonly => { + if (isReadonly) { + lock(this.title); + } else { + unlock(this.title); + } + })); this.toDispose.push(Disposable.create(() => { if (this.selectionService.selection === this.editor) { this.selectionService.selection = undefined; diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index 8a425e91970f6..ce792c7a6929e 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -20,6 +20,7 @@ import URI from '@theia/core/lib/common/uri'; import { Event, Disposable, TextDocumentContentChangeDelta, Reference, isObject } from '@theia/core/lib/common'; import { Saveable, Navigatable, Widget } from '@theia/core/lib/browser'; import { EditorDecoration } from './decorations/editor-decoration'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; export { Position, Range, Location }; @@ -207,7 +208,8 @@ export interface TextEditor extends Disposable, TextEditorSelection, Navigatable readonly node: HTMLElement; readonly uri: URI; - readonly isReadonly: boolean; + readonly isReadonly: boolean | MarkdownString; + readonly onDidChangeReadOnly: Event; readonly document: TextEditorDocument; readonly onDocumentContentChanged: Event; diff --git a/packages/filesystem/src/browser/file-resource.ts b/packages/filesystem/src/browser/file-resource.ts index ef515c4a31ff3..bc8c69387d449 100644 --- a/packages/filesystem/src/browser/file-resource.ts +++ b/packages/filesystem/src/browser/file-resource.ts @@ -27,6 +27,7 @@ import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import { GENERAL_MAX_FILE_SIZE_MB } from './filesystem-preferences'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { nls } from '@theia/core'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; export interface FileResourceVersion extends ResourceVersion { readonly encoding: string; @@ -54,6 +55,9 @@ export class FileResource implements Resource { protected readonly onDidChangeContentsEmitter = new Emitter(); readonly onDidChangeContents: Event = this.onDidChangeContentsEmitter.event; + protected readonly onDidChangeReadOnlyEmitter = new Emitter(); + readonly onDidChangeReadOnly: Event = this.onDidChangeReadOnlyEmitter.event; + protected _version: FileResourceVersion | undefined; get version(): FileResourceVersion | undefined { return this._version; @@ -61,7 +65,7 @@ export class FileResource implements Resource { get encoding(): string | undefined { return this._version?.encoding; } - get isReadonly(): boolean { + get readOnly(): boolean { return this.options.isReadonly || this.fileService.hasCapability(this.uri, FileSystemProviderCapabilities.Readonly); } @@ -71,6 +75,7 @@ export class FileResource implements Resource { protected readonly options: FileResourceOptions ) { this.toDispose.push(this.onDidChangeContentsEmitter); + this.toDispose.push(this.onDidChangeReadOnlyEmitter); this.toDispose.push(this.fileService.onDidFilesChange(event => { if (event.contains(this.uri)) { this.sync(); @@ -220,17 +225,24 @@ export class FileResource implements Resource { saveContents?: Resource['saveContents']; saveContentChanges?: Resource['saveContentChanges']; protected updateSavingContentChanges(): void { - if (this.isReadonly) { + let changed = false; + if (this.readOnly) { + changed = Boolean(this.saveContents); delete this.saveContentChanges; delete this.saveContents; delete this.saveStream; } else { + changed = !Boolean(this.saveContents); this.saveContents = this.doWrite; this.saveStream = this.doWrite; if (this.fileService.hasCapability(this.uri, FileSystemProviderCapabilities.Update)) { this.saveContentChanges = this.doSaveContentChanges; } } + if (changed) { + // Only actually bother to call the event if the value has changed. + this.onDidChangeReadOnlyEmitter.fire(this.readOnly); + } } protected doSaveContentChanges: Resource['saveContentChanges'] = async (changes, options) => { const version = options?.version || this._version; diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index f2f44cd6776b8..32a4731254cab 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -32,6 +32,7 @@ import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common import { IModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/model'; import { createTextBufferFactoryFromStream } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel'; import { editorGeneratedPreferenceProperties } from '@theia/editor/lib/browser/editor-generated-preference-schema'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; export { TextDocumentSaveReason @@ -81,6 +82,8 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo protected readonly onDidChangeEncodingEmitter = new Emitter(); readonly onDidChangeEncoding = this.onDidChangeEncodingEmitter.event; + readonly onDidChangeReadOnly: Event = this.resource.onDidChangeReadOnly ?? Event.None; + private preferredEncoding: string | undefined; private contentEncoding: string | undefined; @@ -302,11 +305,11 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo return this.m2p.asRange(this.model.validateRange(this.p2m.asRange(range))); } - get readOnly(): boolean { - return this.resource.saveContents === undefined; + get readOnly(): boolean | MarkdownString { + return this.resource.readOnly ?? false; } - isReadonly(): boolean { + isReadonly(): boolean | MarkdownString { return this.readOnly; } diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 0b928dc71ddd7..f2c6c0a65d3ba 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -26,7 +26,6 @@ import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { EditorServiceOverrides, MonacoEditor, MonacoEditorServices } from './monaco-editor'; import { MonacoEditorModel, WillSaveMonacoModelEvent } from './monaco-editor-model'; import { MonacoWorkspace } from './monaco-workspace'; -import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import { ContributionProvider } from '@theia/core'; import { KeybindingRegistry, OpenerService, open, WidgetOpenerOptions, FormatType } from '@theia/core/lib/browser'; import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; @@ -86,8 +85,6 @@ export class MonacoEditorProvider { @inject(MonacoWorkspace) protected readonly workspace: MonacoWorkspace, @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences, @inject(MonacoDiffNavigatorFactory) protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, - /** @deprecated since 1.6.0 */ - @inject(ApplicationServer) protected readonly applicationServer: ApplicationServer, ) { } diff --git a/packages/monaco/src/browser/monaco-editor.ts b/packages/monaco/src/browser/monaco-editor.ts index be9029fb2bde3..a01845db1df3f 100644 --- a/packages/monaco/src/browser/monaco-editor.ts +++ b/packages/monaco/src/browser/monaco-editor.ts @@ -50,6 +50,7 @@ import { IInstantiationService, ServiceIdentifier } from '@theia/monaco-editor-c import { ICodeEditor, IMouseTargetMargin } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; import { IStandaloneEditorConstructionOptions, StandaloneEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; import { ServiceCollection } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/serviceCollection'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; export type ServicePair = [ServiceIdentifier, T]; @@ -86,6 +87,7 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { protected readonly onFocusChangedEmitter = new Emitter(); protected readonly onDocumentContentChangedEmitter = new Emitter(); protected readonly onMouseDownEmitter = new Emitter(); + readonly onDidChangeReadOnly = this.document.onDidChangeReadOnly; protected readonly onLanguageChangedEmitter = new Emitter(); readonly onLanguageChanged = this.onLanguageChangedEmitter.event; protected readonly onScrollChangedEmitter = new Emitter(); @@ -117,7 +119,10 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { this.autoSizing = options && options.autoSizing !== undefined ? options.autoSizing : false; this.minHeight = options && options.minHeight !== undefined ? options.minHeight : -1; this.maxHeight = options && options.maxHeight !== undefined ? options.maxHeight : -1; - this.toDispose.push(this.create(options, override)); + this.toDispose.push(this.create({ + ...MonacoEditor.createReadOnlyOptions(document.readOnly), + ...options + }, override)); this.addHandlers(this.editor); } @@ -199,6 +204,9 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { this.toDispose.push(codeEditor.onDidScrollChange(e => { this.onScrollChangedEmitter.fire(undefined); })); + this.toDispose.push(this.onDidChangeReadOnly(readOnly => { + codeEditor.updateOptions(MonacoEditor.createReadOnlyOptions(readOnly)); + })); } getVisibleRanges(): Range[] { @@ -221,8 +229,8 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { return this.onDocumentContentChangedEmitter.event; } - get isReadonly(): boolean { - return this.document.isReadonly(); + get isReadonly(): boolean | MarkdownString { + return this.document.readOnly; } get cursor(): Position { @@ -642,4 +650,14 @@ export namespace MonacoEditor { return candidate && candidate.getControl() === control; }); } + + export function createReadOnlyOptions(readOnly?: boolean | MarkdownString): monaco.editor.IEditorOptions { + if (typeof readOnly === 'boolean') { + return { readOnly }; + } + if (readOnly) { + return { readOnly: true, readOnlyMessage: readOnly }; + } + return {}; + } } diff --git a/packages/monaco/src/browser/simple-monaco-editor.ts b/packages/monaco/src/browser/simple-monaco-editor.ts index eda4874ada840..de2a1d525f2e7 100644 --- a/packages/monaco/src/browser/simple-monaco-editor.ts +++ b/packages/monaco/src/browser/simple-monaco-editor.ts @@ -43,6 +43,7 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab readonly onEncodingChanged = this.document.onDidChangeEncoding; protected readonly onResizeEmitter = new Emitter(); readonly onDidResize = this.onResizeEmitter.event; + readonly onDidChangeReadOnly = this.document.onDidChangeReadOnly; constructor( readonly uri: URI, @@ -62,7 +63,10 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab this.onLanguageChangedEmitter, this.onScrollChangedEmitter ]); - this.toDispose.push(this.create(options, override)); + this.toDispose.push(this.create({ + ...MonacoEditor.createReadOnlyOptions(document.readOnly), + ...options + }, override)); this.addHandlers(this.editor); this.editor.setModel(document.textEditorModel); } @@ -125,6 +129,9 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab this.toDispose.push(codeEditor.onDidScrollChange(e => { this.onScrollChangedEmitter.fire(undefined); })); + this.toDispose.push(this.onDidChangeReadOnly(readOnly => { + codeEditor.updateOptions(MonacoEditor.createReadOnlyOptions(readOnly)); + })); } setLanguage(languageId: string): void { diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index fcd1c94e4c141..4795d8da7b582 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, CommandContribution, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; +import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { ApplicationShell, codicon, CommonCommands } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; @@ -100,37 +100,52 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon } }); - commands.registerCommand(NotebookCommands.ADD_NEW_MARKDOWN_CELL_COMMAND, { - execute: (notebookModel: NotebookModel) => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Markup) - }); + commands.registerCommand(NotebookCommands.ADD_NEW_MARKDOWN_CELL_COMMAND, this.editableCommandHandler( + notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Markup) + )); - commands.registerCommand(NotebookCommands.ADD_NEW_CODE_CELL_COMMAND, { - execute: (notebookModel: NotebookModel) => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Code) - }); + commands.registerCommand(NotebookCommands.ADD_NEW_CODE_CELL_COMMAND, this.editableCommandHandler( + notebookModel => commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, notebookModel, CellKind.Code) + )); - commands.registerCommand(NotebookCommands.SELECT_KERNEL_COMMAND, { - execute: (notebookModel: NotebookModel) => this.notebookKernelQuickPickService.showQuickPick(notebookModel) - }); + commands.registerCommand(NotebookCommands.SELECT_KERNEL_COMMAND, this.editableCommandHandler( + notebookModel => this.notebookKernelQuickPickService.showQuickPick(notebookModel) + )); - commands.registerCommand(NotebookCommands.EXECUTE_NOTEBOOK_COMMAND, { - execute: (notebookModel: NotebookModel) => this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells) - }); + commands.registerCommand(NotebookCommands.EXECUTE_NOTEBOOK_COMMAND, this.editableCommandHandler( + notebookModel => this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells) + )); - commands.registerCommand(NotebookCommands.CLEAR_ALL_OUTPUTS_COMMAND, { - execute: (notebookModel: NotebookModel) => - notebookModel.cells.forEach(cell => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] })) - }); + commands.registerCommand(NotebookCommands.CLEAR_ALL_OUTPUTS_COMMAND, this.editableCommandHandler( + notebookModel => notebookModel.cells.forEach(cell => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] })) + )); commands.registerHandler(CommonCommands.UNDO.id, { - isEnabled: () => this.shell.activeWidget instanceof NotebookEditorWidget, + isEnabled: () => { + const widget = this.shell.activeWidget; + return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly); + }, execute: () => (this.shell.activeWidget as NotebookEditorWidget).undo() }); commands.registerHandler(CommonCommands.REDO.id, { - isEnabled: () => this.shell.activeWidget instanceof NotebookEditorWidget, + isEnabled: () => { + const widget = this.shell.activeWidget; + return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly); + }, execute: () => (this.shell.activeWidget as NotebookEditorWidget).redo() }); } + protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { + return { + isEnabled: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), + isVisible: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), + execute: (notebookModel: NotebookModel) => { + execute(notebookModel); + } + }; + } + registerMenus(menus: MenuModelRegistry): void { // independent submenu for plugins to add commands menus.registerIndependentSubmenu(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR, 'Notebook Main Toolbar'); diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 39071c329623d..b3e1eade2be7d 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, CommandContribution, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; +import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; import { codicon } from '@theia/core/lib/browser'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { NotebookModel } from '../view-model/notebook-model'; @@ -172,30 +172,39 @@ export class NotebookCellActionContribution implements MenuContribution, Command } registerCommands(commands: CommandRegistry): void { - commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => cell.requestEdit() }); + commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => cell.requestEdit())); commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => cell.requestStopEdit() }); - commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, { - execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => notebookModel.applyEdits([{ + commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, + this.editableCellCommandHandler((notebookModel, cell) => notebookModel.applyEdits([{ editType: CellEditType.Replace, index: notebookModel.cells.indexOf(cell), count: 1, cells: [] - }], true) - }); + }], true))); commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND); - commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, { - execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => this.notebookExecutionService.executeNotebookCells(notebookModel, [cell]) - }); + commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, this.editableCellCommandHandler( + (notebookModel, cell) => this.notebookExecutionService.executeNotebookCells(notebookModel, [cell]) + )); commands.registerCommand(NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND, { execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => this.notebookExecutionService.cancelNotebookCells(notebookModel, [cell]) }); - commands.registerCommand(NotebookCellCommands.CLEAR_OUTPUTS_COMMAND, { - execute: (_, cell: NotebookCellModel) => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] }) - }); - commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, { - execute: (_, __, output: NotebookCellOutputModel) => output.requestOutputPresentationUpdate() - }); + commands.registerCommand(NotebookCellCommands.CLEAR_OUTPUTS_COMMAND, this.editableCellCommandHandler( + (_, cell) => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] }) + )); + commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( + (_, __, output) => output?.requestOutputPresentationUpdate() + )); + } + + protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { + return { + isEnabled: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), + isVisible: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), + execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => { + execute(notebookModel, cell, output); + } + }; } } diff --git a/packages/notebook/src/browser/notebook-cell-resource-resolver.ts b/packages/notebook/src/browser/notebook-cell-resource-resolver.ts index 9e2db9b92b1c4..1d6af928cc9b8 100644 --- a/packages/notebook/src/browser/notebook-cell-resource-resolver.ts +++ b/packages/notebook/src/browser/notebook-cell-resource-resolver.ts @@ -14,20 +14,37 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Emitter, Resource, ResourceReadOptions, ResourceResolver, URI } from '@theia/core'; +import { Event, Emitter, Resource, ResourceReadOptions, ResourceResolver, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import { CellUri } from '../common'; import { NotebookService } from './service/notebook-service'; import { NotebookCellModel } from './view-model/notebook-cell-model'; +import { NotebookModel } from './view-model/notebook-model'; export class NotebookCellResource implements Resource { - protected readonly didChangeContentsEmitter = new Emitter(); - readonly onDidChangeContents = this.didChangeContentsEmitter.event; + protected readonly onDidChangeContentsEmitter = new Emitter(); + get onDidChangeContents(): Event { + return this.onDidChangeContentsEmitter.event; + } + + get onDidChangeReadOnly(): Event | undefined { + return this.notebook.onDidChangeReadOnly; + } + + get readOnly(): boolean | MarkdownString | undefined { + return this.notebook.readOnly; + } + + protected cell: NotebookCellModel; + protected notebook: NotebookModel; - private cell: NotebookCellModel; + uri: URI; - constructor(public uri: URI, cell: NotebookCellModel) { + constructor(uri: URI, notebook: NotebookModel, cell: NotebookCellModel) { + this.uri = uri; + this.notebook = notebook; this.cell = cell; } @@ -36,7 +53,7 @@ export class NotebookCellResource implements Resource { } dispose(): void { - this.didChangeContentsEmitter.dispose(); + this.onDidChangeContentsEmitter.dispose(); } } @@ -69,7 +86,7 @@ export class NotebookCellResourceResolver implements ResourceResolver { throw new Error(`No cell found with handle '${parsedUri.handle}' in '${parsedUri.notebook}'`); } - return new NotebookCellResource(uri, notebookCellModel); + return new NotebookCellResource(uri, notebookModel, notebookCellModel); } } diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 24f6f63179794..10a24e06f0598 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -16,7 +16,7 @@ import * as React from '@theia/core/shared/react'; import { CommandRegistry, MenuModelRegistry, URI } from '@theia/core'; -import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable } from '@theia/core/lib/browser'; +import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock } from '@theia/core/lib/browser'; import { ReactNode } from '@theia/core/shared/react'; import { CellKind } from '../common'; import { CellRenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view'; @@ -29,6 +29,7 @@ import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol'; import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service'; import { NotebookMainToolbarRenderer } from './view/notebook-main-toolbar'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; const PerfectScrollbar = require('react-perfect-scrollbar'); @@ -83,6 +84,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected readonly onDidChangeModelEmitter = new Emitter(); readonly onDidChangeModel = this.onDidChangeModelEmitter.event; + protected readonly onDidChangeReadOnlyEmitter = new Emitter(); + readonly onDidChangeReadOnly = this.onDidChangeReadOnlyEmitter.event; + protected readonly renderers = new Map(); protected _model?: NotebookModel; protected _ready: Deferred = new Deferred(); @@ -112,6 +116,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.update(); this.toDispose.push(this.onDidChangeModelEmitter); + this.toDispose.push(this.onDidChangeReadOnlyEmitter); this.renderers.set(CellKind.Markup, this.markdownCellRenderer); this.renderers.set(CellKind.Code, this.codeCellRenderer); @@ -122,6 +127,18 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this._model = await this.props.notebookData; this.saveable.delegate = this._model; this.toDispose.push(this._model); + this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => { + if (readOnly) { + lock(this.title); + } else { + unlock(this.title); + } + this.onDidChangeReadOnlyEmitter.fire(readOnly); + this.update(); + })); + if (this._model.readOnly) { + lock(this.title); + } // Ensure that the model is loaded before adding the editor this.notebookEditorService.addNotebookEditor(this); this.update(); diff --git a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts index 03f42c4997701..ff7e8d368ecd9 100644 --- a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts +++ b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Emitter, URI } from '@theia/core'; +import { Emitter, Resource, ResourceProvider, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { UriComponents } from '@theia/core/lib/common/uri'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; @@ -34,6 +34,9 @@ export class NotebookModelResolverService { @inject(FileService) protected fileService: FileService; + @inject(ResourceProvider) + protected resourceProvider: ResourceProvider; + @inject(NotebookService) protected notebookService: NotebookService; @@ -60,9 +63,9 @@ export class NotebookModelResolverService { throw new Error(`Missing viewType for '${resource}'`); } - const notebookData = await this.resolveExistingNotebookData(resource, viewType!); - - const notebookModel = await this.notebookService.createNotebookModel(notebookData, viewType, resource); + const actualResource = await this.resourceProvider(resource); + const notebookData = await this.resolveExistingNotebookData(actualResource, viewType!); + const notebookModel = await this.notebookService.createNotebookModel(notebookData, viewType, actualResource); notebookModel.onDirtyChanged(() => this.onDidChangeDirtyEmitter.fire(notebookModel)); notebookModel.onDidSaveNotebook(() => this.onDidSaveNotebookEmitter.fire(notebookModel.uri.toComponents())); @@ -103,8 +106,8 @@ export class NotebookModelResolverService { return this.resolve(resource, viewType); } - protected async resolveExistingNotebookData(resource: URI, viewType: string): Promise { - if (resource.scheme === 'untitled') { + protected async resolveExistingNotebookData(resource: Resource, viewType: string): Promise { + if (resource.uri.scheme === 'untitled') { return { cells: [ @@ -118,10 +121,11 @@ export class NotebookModelResolverService { metadata: {} }; } else { - const file = await this.fileService.readFile(resource); - - const dataProvider = await this.notebookService.getNotebookDataProvider(viewType); - const notebook = await dataProvider.serializer.toNotebook(file.value); + const [dataProvider, contents] = await Promise.all([ + this.notebookService.getNotebookDataProvider(viewType), + this.fileService.readFile(resource.uri) + ]); + const notebook = await dataProvider.serializer.toNotebook(contents.value); return notebook; } diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index da6e1920e4ba2..583dac957a0f5 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable, DisposableCollection, Emitter, URI } from '@theia/core'; +import { Disposable, DisposableCollection, Emitter, Resource, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { NotebookData, TransientOptions } from '../../common'; @@ -101,14 +101,14 @@ export class NotebookService implements Disposable { }); } - async createNotebookModel(data: NotebookData, viewType: string, uri: URI): Promise { + async createNotebookModel(data: NotebookData, viewType: string, resource: Resource): Promise { const serializer = this.notebookProviders.get(viewType)?.serializer; if (!serializer) { throw new Error('no notebook serializer for ' + viewType); } - const model = this.notebookModelFactory({ data, uri, viewType, serializer }); - this.notebookModels.set(uri.toString(), model); + const model = this.notebookModelFactory({ data, resource, viewType, serializer }); + this.notebookModels.set(resource.uri.toString(), model); // Resolve cell text models right after creating the notebook model // This ensures that all text models are available in the plugin host await Promise.all(model.cells.map(e => e.resolveTextModel())); diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index a668fc5c06d60..eabf9c15d15e7 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -26,11 +26,14 @@ } .theia-notebook-cell { - cursor: grab; display: flex; margin: 10px 0px; } +.theia-notebook-cell.draggable { + cursor: grab; +} + .theia-notebook-cell:hover .theia-notebook-cell-marker { visibility: visible; } diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 1389d59c0dd7b..8a02da4c5c7d7 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -150,7 +150,7 @@ export class NotebookCellModel implements NotebookCell, Disposable { } - textModel: MonacoEditorModel; + protected textModel?: MonacoEditorModel; protected htmlContext: HTMLLIElement; @@ -220,12 +220,14 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidChangeInternalMetadataEmitter.dispose(); this.onDidChangeLanguageEmitter.dispose(); this.notebookCellContextManager.dispose(); - this.textModel.dispose(); + this.textModel?.dispose(); this.toDispose.dispose(); } requestEdit(): void { - this.onDidRequestCellEditChangeEmitter.fire(true); + if (!this.textModel || !this.textModel.readOnly) { + this.onDidRequestCellEditChangeEmitter.fire(true); + } } requestStopEdit(): void { diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 9ec960c104787..a1236f63d18c7 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable, Emitter, URI } from '@theia/core'; +import { Disposable, Emitter, Event, Resource, URI } from '@theia/core'; import { Saveable, SaveOptions } from '@theia/core/lib/browser'; import { CellData, CellEditType, CellUri, NotebookCellInternalMetadata, @@ -28,6 +28,7 @@ import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-mod import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; export const NotebookModelFactory = Symbol('NotebookModelFactory'); @@ -42,10 +43,10 @@ export function createNotebookModelContainer(parent: interfaces.Container, props const NotebookModelProps = Symbol('NotebookModelProps'); export interface NotebookModelProps { - data: NotebookData, - uri: URI, - viewType: string, - serializer: NotebookSerializer, + data: NotebookData; + resource: Resource; + viewType: string; + serializer: NotebookSerializer; } @injectable() @@ -63,6 +64,10 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidChangeContentEmitter = new Emitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; + get onDidChangeReadOnly(): Event { + return this.props.resource.onDidChangeReadOnly ?? Event.None; + } + @inject(FileService) protected readonly fileService: FileService; @@ -81,7 +86,7 @@ export class NotebookModel implements Saveable, Disposable { protected nextHandle: number = 0; - protected _dirty: boolean = false; + protected _dirty = false; set dirty(dirty: boolean) { this._dirty = dirty; @@ -92,13 +97,17 @@ export class NotebookModel implements Saveable, Disposable { return this._dirty; } + get readOnly(): boolean | MarkdownString { + return this.props.resource.readOnly ?? false; + } + selectedCell?: NotebookCellModel; protected dirtyCells: NotebookCellModel[] = []; cells: NotebookCellModel[]; get uri(): URI { - return this.props.uri; + return this.props.resource.uri; } get viewType(): string { @@ -112,7 +121,7 @@ export class NotebookModel implements Saveable, Disposable { this.dirty = false; this.cells = this.props.data.cells.map((cell, index) => this.cellModelFactory({ - uri: CellUri.generate(this.props.uri, index), + uri: CellUri.generate(this.props.resource.uri, index), handle: index, source: cell.source, language: cell.language, @@ -294,7 +303,7 @@ export class NotebookModel implements Saveable, Disposable { } } - private replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void { + protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise { const cells = newCells.map(cell => { const handle = this.nextHandle++; return this.cellModelFactory({ @@ -325,11 +334,15 @@ export class NotebookModel implements Saveable, Disposable { async () => this.replaceCells(start, deleteCount, newCells, false)); } + // Ensure that all text model have been created + // Otherwise we run into a race condition once we fire `onDidChangeContent` + await Promise.all(cells.map(cell => cell.resolveTextModel())); + this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]); } - private changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void { + protected changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void { const newInternalMetadata: NotebookCellInternalMetadata = { ...cell.internalMetadata }; @@ -345,7 +358,7 @@ export class NotebookModel implements Saveable, Disposable { ]); } - private updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void { + protected updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void { const oldMetadata = this.metadata; if (computeUndoRedo) { this.undoRedoService.pushElement(this.uri, @@ -358,7 +371,7 @@ export class NotebookModel implements Saveable, Disposable { this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]); } - private changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void { + protected changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void { if (cell.language === languageId) { return; } @@ -368,7 +381,7 @@ export class NotebookModel implements Saveable, Disposable { this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }]); } - private moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean { + protected moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean { if (computeUndoRedo) { this.undoRedoService.pushElement(this.uri, async () => { this.moveCellToIndex(toIndex, length, fromIndex, false); }, @@ -383,7 +396,7 @@ export class NotebookModel implements Saveable, Disposable { return true; } - private getCellIndexByHandle(handle: number): number { + protected getCellIndexByHandle(handle: number): number { return this.cells.findIndex(c => c.handle === handle); } } diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index f1ea9aab4945a..6b17495120953 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -64,11 +64,13 @@ export class NotebookCellListView extends React.Component - this.onAddNewCell(kind, index)} + this.isEnabled()} + onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, index)} onDrop={e => this.onDrop(e, index)} onDragOver={e => this.onDragOver(e, cell, 'top')} /> {this.shouldRenderDragOverIndicator(cell, 'top') && } -
  • { this.setState({ selectedCell: cell }); this.props.notebookModel.setSelectedCell(cell); @@ -89,7 +91,9 @@ export class NotebookCellListView extends React.Component ) } - this.onAddNewCell(kind, this.props.notebookModel.cells.length)} + this.isEnabled()} + onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, this.props.notebookModel.cells.length)} onDrop={e => this.onDrop(e, this.props.notebookModel.cells.length - 1)} onDragOver={e => this.onDragOver(e, this.props.notebookModel.cells[this.props.notebookModel.cells.length - 1], 'bottom')} /> ; @@ -105,18 +109,33 @@ export class NotebookCellListView extends React.Component, index: number): void { event.stopPropagation(); + if (!this.isEnabled()) { + event.preventDefault(); + return; + } event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString()); event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source); } protected onDragOver(event: React.DragEvent, cell: NotebookCellModel, position?: 'top' | 'bottom'): void { + if (!this.isEnabled()) { + return; + } event.preventDefault(); event.stopPropagation(); // show indicator this.setState({ ...this.state, dragOverIndicator: { cell, position: position ?? event.nativeEvent.offsetY < event.currentTarget.clientHeight / 2 ? 'top' : 'bottom' } }); } + protected isEnabled(): boolean { + return !Boolean(this.props.notebookModel.readOnly); + } + protected onDrop(event: React.DragEvent, dropElementIndex: number): void { + if (!this.isEnabled()) { + this.setState({ dragOverIndicator: undefined }); + return; + } const index = parseInt(event.dataTransfer.getData('text/theia-notebook-cell-index')); const isTargetBelow = index < dropElementIndex; let newIdx = this.state.dragOverIndicator?.position === 'top' ? dropElementIndex : dropElementIndex + 1; @@ -133,15 +152,18 @@ export class NotebookCellListView extends React.Component boolean; onAddNewCell: (type: CellKind) => void; onDrop: (event: React.DragEvent) => void; onDragOver: (event: React.DragEvent) => void; } -export function NotebookCellDivider({ onAddNewCell, onDrop, onDragOver }: NotebookCellDividerProps): React.JSX.Element { +export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver }: NotebookCellDividerProps): React.JSX.Element { const [hover, setHover] = React.useState(false); return
  • setHover(true)} onMouseLeave={() => setHover(false)} onDrop={onDrop} onDragOver={onDragOver}> - {hover &&
    + {hover && isVisible() &&
    ; } + + protected getOrCreateMonacoFontInfo(): BareFontInfo { + if (!this.fontInfo) { + this.fontInfo = this.createFontInfo(); + this.editorPreferences.onPreferenceChanged(e => this.fontInfo = this.createFontInfo()); + } + return this.fontInfo; + } + + protected createFontInfo(): BareFontInfo { + return BareFontInfo.createFromRawSettings({ + fontFamily: this.editorPreferences['editor.fontFamily'], + fontWeight: String(this.editorPreferences['editor.fontWeight']), + fontSize: this.editorPreferences['editor.fontSize'], + fontLigatures: this.editorPreferences['editor.fontLigatures'], + lineHeight: this.editorPreferences['editor.lineHeight'], + letterSpacing: this.editorPreferences['editor.letterSpacing'], + }, PixelRatio.value); + } } export interface NotebookCodeCellStatusProps { diff --git a/packages/notebook/src/browser/view/notebook-viewport-service.ts b/packages/notebook/src/browser/view/notebook-viewport-service.ts new file mode 100644 index 0000000000000..93dc2a2ccf859 --- /dev/null +++ b/packages/notebook/src/browser/view/notebook-viewport-service.ts @@ -0,0 +1,61 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Disposable } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol'; + +/** + * this service is for managing the viewport and scroll state of a notebook editor. + * its used both for restoring scroll state after reopening an editor and for cell to check if they are in the viewport. + */ +@injectable() +export class NotebookViewportService implements Disposable { + + protected onDidChangeViewportEmitter = new Emitter(); + readonly onDidChangeViewport = this.onDidChangeViewportEmitter.event; + + protected _viewportElement: HTMLDivElement | undefined; + + protected resizeObserver: ResizeObserver; + + set viewportElement(element: HTMLDivElement | undefined) { + this._viewportElement = element; + if (element) { + this.onDidChangeViewportEmitter.fire(); + this.resizeObserver?.disconnect(); + this.resizeObserver = new ResizeObserver(() => this.onDidChangeViewportEmitter.fire()); + this.resizeObserver.observe(element); + } + } + + isElementInViewport(element: HTMLElement): boolean { + if (this._viewportElement) { + const rect = element.getBoundingClientRect(); + const viewRect = this._viewportElement.getBoundingClientRect(); + return rect.top < viewRect.top ? rect.bottom > viewRect.top : rect.top < viewRect.bottom; + } + return false; + } + + onScroll(e: HTMLDivElement): void { + this.onDidChangeViewportEmitter.fire(); + } + + dispose(): void { + this.resizeObserver.disconnect(); + } +} diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 662d39c11137c..934cc61308f38 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -194,7 +194,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { this.webviewWidget.setIframeHeight(message.contentHeight + 5); break; case 'did-scroll-wheel': - this.editor.node.children[0].children[1].scrollBy(message.deltaX, message.deltaY); + this.editor.node.getElementsByClassName('theia-notebook-viewport')[0].children[0].scrollBy(message.deltaX, message.deltaY); break; case 'customKernelMessage': this.editor.recieveKernelMessage(message.message); From 82ada4b9df2e9594e15c3a7b3dadf2ec7a0f4f80 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 19 Mar 2024 13:07:35 +0100 Subject: [PATCH 130/441] Extend TextEditorLineNumbersStyle with Interval (#13458) fixes #13447 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/plugin-ext/src/main/browser/text-editor-main.ts | 8 +++++++- packages/plugin-ext/src/plugin/types-impl.ts | 3 ++- packages/plugin/src/theia.d.ts | 6 +++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f1d8b5eb9e55..21929257f8b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [core] Fix quickpick problems found in IDE testing [#13451](https://github.com/eclipse-theia/theia/pull/13451) - contributed on behalf of STMicroelectronics +- [plugin] Extend TextEditorLineNumbersStyle with Interval [#13458](https://github.com/eclipse-theia/theia/pull/13458) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_not_yet_released) diff --git a/packages/plugin-ext/src/main/browser/text-editor-main.ts b/packages/plugin-ext/src/main/browser/text-editor-main.ts index 17656f9a85d2e..000e83509b276 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-main.ts @@ -151,7 +151,7 @@ export class TextEditorMain implements Disposable { } if (typeof newConfiguration.lineNumbers !== 'undefined') { - let lineNumbers: 'on' | 'off' | 'relative'; + let lineNumbers: 'on' | 'off' | 'relative' | 'interval'; switch (newConfiguration.lineNumbers) { case TextEditorLineNumbersStyle.On: lineNumbers = 'on'; @@ -159,6 +159,9 @@ export class TextEditorMain implements Disposable { case TextEditorLineNumbersStyle.Relative: lineNumbers = 'relative'; break; + case TextEditorLineNumbersStyle.Interval: + lineNumbers = 'interval'; + break; default: lineNumbers = 'off'; } @@ -400,6 +403,9 @@ export class TextEditorPropertiesMain { case monaco.editor.RenderLineNumbersType.Relative: lineNumbers = TextEditorLineNumbersStyle.Relative; break; + case monaco.editor.RenderLineNumbersType.Interval: + lineNumbers = TextEditorLineNumbersStyle.Interval; + break; default: lineNumbers = TextEditorLineNumbersStyle.On; break; diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index d89f2d78194fa..0a0e7c5611407 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -153,7 +153,8 @@ export enum StatusBarAlignment { export enum TextEditorLineNumbersStyle { Off = 0, On = 1, - Relative = 2 + Relative = 2, + Interval = 3 } /** diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 5bc64b3a62598..e8015f3e5cce4 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -1897,7 +1897,11 @@ export module '@theia/plugin' { /** * Render the line numbers with values relative to the primary cursor location. */ - Relative = 2 + Relative = 2, + /** + * Render the line numbers on every 10th line number. + */ + Interval = 3 } /** From 44fbdb0bd45084f2b3bfd8a6a058fbaa4f2b5ac4 Mon Sep 17 00:00:00 2001 From: fanyipin <570524947@qq.com> Date: Wed, 20 Mar 2024 15:46:45 +0800 Subject: [PATCH 131/441] add reflect-metadata for git locator-host, build git-locator-host for commonjs (#13487) --- .../application-manager/src/generator/webpack-generator.ts | 5 ++--- packages/git/src/node/git-locator/git-locator-host.ts | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-packages/application-manager/src/generator/webpack-generator.ts b/dev-packages/application-manager/src/generator/webpack-generator.ts index 2db2258a524c6..9fe4f05e7e143 100644 --- a/dev-packages/application-manager/src/generator/webpack-generator.ts +++ b/dev-packages/application-manager/src/generator/webpack-generator.ts @@ -379,6 +379,7 @@ for (const [entryPointName, entryPointPath] of Object.entries({ ${this.ifPackage('@theia/filesystem', "'nsfw-watcher': '@theia/filesystem/lib/node/nsfw-watcher',")} ${this.ifPackage('@theia/plugin-ext-vscode', "'plugin-vscode-init': '@theia/plugin-ext-vscode/lib/node/plugin-vscode-init',")} ${this.ifPackage('@theia/api-provider-sample', "'gotd-api-init': '@theia/api-provider-sample/lib/plugin/gotd-api-init',")} + ${this.ifPackage('@theia/git', "'git-locator-host': '@theia/git/lib/node/git-locator/git-locator-host',")} })) { commonJsLibraries[entryPointName] = { import: require.resolve(entryPointPath), @@ -433,9 +434,7 @@ const config = { ${this.ifPackage('@theia/plugin-ext-headless', () => `// Theia Headless Plugin support: 'plugin-host-headless': require.resolve('@theia/plugin-ext-headless/lib/hosted/node/plugin-host-headless'),`)} ${this.ifPackage('@theia/process', () => `// Make sure the node-pty thread worker can be executed: - 'worker/conoutSocketWorker': require.resolve('node-pty/lib/worker/conoutSocketWorker'),`)} - ${this.ifPackage('@theia/git', () => `// Ensure the git locator process can the started - 'git-locator-host': require.resolve('@theia/git/lib/node/git-locator/git-locator-host'),`)} + 'worker/conoutSocketWorker': require.resolve('node-pty/lib/worker/conoutSocketWorker'),`)} ${this.ifElectron("'electron-main': require.resolve('./src-gen/backend/electron-main'),")} ${this.ifPackage('@theia/dev-container', () => `// VS Code Dev-Container communication: 'dev-container-server': require.resolve('@theia/dev-container/lib/dev-container-server/dev-container-server'),`)} diff --git a/packages/git/src/node/git-locator/git-locator-host.ts b/packages/git/src/node/git-locator/git-locator-host.ts index 1dac54d47a8d6..ac4de50adcfc9 100644 --- a/packages/git/src/node/git-locator/git-locator-host.ts +++ b/packages/git/src/node/git-locator/git-locator-host.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import '@theia/core/shared/reflect-metadata'; import { RpcProxyFactory } from '@theia/core'; import { IPCEntryPoint } from '@theia/core/lib/node/messaging/ipc-protocol'; import { GitLocatorImpl } from './git-locator-impl'; From c29355452cd0c353395b1064b70691441f5e028e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 21 Mar 2024 17:35:36 +0900 Subject: [PATCH 132/441] Fix default translation of `Close Editor` command (#13412) --- packages/core/src/common/nls.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/common/nls.ts b/packages/core/src/common/nls.ts index f829f57623d27..acdc62474e419 100644 --- a/packages/core/src/common/nls.ts +++ b/packages/core/src/common/nls.ts @@ -83,7 +83,9 @@ class LocalizationKeyProvider { private preferredKeys = new Set([ // We only want the `File` translation used in the menu - 'vscode/fileActions.contribution/filesCategory' + 'vscode/fileActions.contribution/filesCategory', + // Needed for `Close Editor` translation + 'vscode/editor.contribution/closeEditor' ]); private data = this.buildData(); From dffb9dc9b81a79b8919acf69b60442c95dbe7b26 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 21 Mar 2024 09:35:51 +0100 Subject: [PATCH 133/441] fix keybindings triggering when cell editor is focused (#13500) Signed-off-by: Jonah Iden --- .../contributions/notebook-cell-actions-contribution.ts | 4 ++-- .../src/browser/service/notebook-context-manager.ts | 4 ++++ packages/notebook/src/browser/view/notebook-cell-editor.tsx | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 73f7c049c966e..2bf784afa9b12 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -328,12 +328,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.TO_CODE_CELL_COMMAND.id, keybinding: 'Y', - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, }, { command: NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND.id, keybinding: 'M', - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, } ); } diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 53fa382868f67..7980fcfa82090 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -113,6 +113,10 @@ export class NotebookContextManager { } + onDidEditorTextFocus(focus: boolean): void { + this.scopedStore.setContext('inputFocus', focus); + } + createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent { return { affects: keys => affectedKeys.some(key => keys.has(key)) }; } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 1ad807a14cfb3..5edc85c4596d5 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -97,6 +97,12 @@ export class CellEditor extends React.Component { this.toDispose.push(this.editor.onDocumentContentChanged(e => { notebookModel.cellDirtyChanged(cell, true); })); + this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => { + this.props.notebookContextManager.onDidEditorTextFocus(true); + })); + this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { + this.props.notebookContextManager.onDidEditorTextFocus(false); + })); } } From cd0200f65261aeca462b6df1b40a3228eb06fc2e Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 21 Mar 2024 09:36:04 +0100 Subject: [PATCH 134/441] added execution order display to code cells (#13502) Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 8 +++---- packages/notebook/src/browser/style/index.css | 24 ++++++++++++++----- .../browser/view/notebook-cell-toolbar.tsx | 2 +- .../browser/view/notebook-code-cell-view.tsx | 22 ++++++++++++++--- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 2bf784afa9b12..1aefe02a12ff3 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -190,10 +190,10 @@ export class NotebookCellActionContribution implements MenuContribution, Command }); // Notebook Cell extra execution options - menus.registerIndependentSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU, - nls.localizeByDefault('More...'), - { role: CompoundMenuNodeRole.Flat, icon: codicon('chevron-down') }); - menus.getMenu(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU).addNode(menus.getMenuNode(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU)); + // menus.registerIndependentSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU, + // nls.localizeByDefault('More...'), + // { role: CompoundMenuNodeRole.Flat, icon: codicon('chevron-down') }); + // menus.getMenu(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU).addNode(menus.getMenuNode(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU)); // code cell output sidebar menu menus.registerSubmenu( diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index e17335df5fbdc..4d0ef1340d27b 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -129,11 +129,28 @@ background-color: var(--theia-editor-background); } -.theia-notebook-cell-sidebar { +.theia-notebook-cell-sidebar-toolbar { display: flex; flex-direction: column; padding: 2px; background-color: var(--theia-editor-background); + flex-grow: 1; +} + +.theia-notebook-cell-sidebar { + display: flex; + flex-direction: column; +} + +.theia-notebook-code-cell-execution-order { + height: 35px; + display: block; + font-family: var(--monaco-monospace-font); + font-size: 10px; + opacity: 0.7; + text-align: center; + white-space: pre; + padding: 5px 0; } .theia-notebook-cell-toolbar-item { @@ -159,11 +176,6 @@ flex-direction: row; } -.theia-notebook-cell-sidebar { - display: flex; - flex-direction: column; -} - .theia-notebook-main-container { display: flex; flex-direction: column; diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx index c67b5a19ad100..548d42062ea42 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx @@ -65,7 +65,7 @@ export class NotebookCellToolbar extends NotebookCellActionBar { export class NotebookCellSidebar extends NotebookCellActionBar { override render(): React.ReactNode { - return
    + return
    {this.state.inlineItems.filter(e => e.isVisible()).map(item => this.renderItem(item))}
    ; } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 8c8b9f026f88d..ac9ef6f85b909 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -66,10 +66,9 @@ export class NotebookCodeCellRenderer implements CellRenderer { render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { return
    -
    +
    {this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, notebookModel, cell)} - {/* cell-execution-order needs an own component. Could be a little more complicated -

    {`[${cell.exec ?? ' '}]`}

    */} +
    { + const listener = cell.onDidChangeInternalMetadata(e => { + setExecutionOrder(cell.internalMetadata.executionOrder ?? ' '); + }); + return () => listener.dispose(); + }, []); + + return {`[${executionOrder}]`}; +} From cffeeac1c435eef3a94e9b454b1615f408c25cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 21 Mar 2024 11:55:33 +0100 Subject: [PATCH 135/441] Add terminal observer API (#13402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder --- .../plugin-ext/src/common/plugin-api-rpc.ts | 15 ++++++ .../src/main/browser/terminal-main.ts | 46 +++++++++++++++++++ .../plugin-ext/src/plugin/plugin-context.ts | 6 +++ .../plugin-ext/src/plugin/terminal-ext.ts | 21 ++++++++- packages/plugin/src/theia-extra.d.ts | 20 ++++++++ .../src/browser/base/terminal-widget.ts | 13 ++++++ .../src/browser/terminal-widget-impl.ts | 31 ++++++++++++- 7 files changed, 150 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 5505515e9b616..bf40c9d1e7c3f 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -301,6 +301,7 @@ export interface TerminalServiceExt { $handleTerminalLink(link: ProvidedTerminalLink): Promise; getEnvironmentVariableCollection(extensionIdentifier: string): theia.GlobalEnvironmentVariableCollection; $setShell(shell: string): void; + $reportOutputMatch(observerId: string, groups: string[]): void; } export interface OutputChannelRegistryExt { createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel, @@ -438,6 +439,20 @@ export interface TerminalServiceMain { * @param providerId id of the terminal link provider to be unregistered. */ $unregisterTerminalLinkProvider(providerId: string): Promise; + + /** + * Register a new terminal observer. + * @param providerId id of the terminal link provider to be registered. + * @param nrOfLinesToMatch the number of lines to match the outputMatcherRegex against + * @param outputMatcherRegex the regex to match the output to + */ + $registerTerminalObserver(id: string, nrOfLinesToMatch: number, outputMatcherRegex: string): unknown; + + /** + * Unregister the terminal observer with the specified id. + * @param providerId id of the terminal observer to be unregistered. + */ + $unregisterTerminalObserver(id: string): unknown; } export interface AutoFocus { diff --git a/packages/plugin-ext/src/main/browser/terminal-main.ts b/packages/plugin-ext/src/main/browser/terminal-main.ts index 590955fc27faa..059b4d9394be8 100644 --- a/packages/plugin-ext/src/main/browser/terminal-main.ts +++ b/packages/plugin-ext/src/main/browser/terminal-main.ts @@ -30,6 +30,13 @@ import { getIconClass } from '../../plugin/terminal-ext'; import { PluginTerminalRegistry } from './plugin-terminal-registry'; import { CancellationToken } from '@theia/core'; import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin'; +import debounce = require('@theia/core/shared/lodash.debounce'); + +interface TerminalObserverData { + nrOfLinesToMatch: number; + outputMatcherRegex: RegExp + disposables: DisposableCollection; +} /** * Plugin api service allows working with terminal emulator. @@ -46,6 +53,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin private readonly terminalLinkProviders: string[] = []; private readonly toDispose = new DisposableCollection(); + private readonly observers = new Map(); constructor(rpc: RPCProtocol, container: interfaces.Container) { this.terminals = container.get(TerminalService); @@ -121,6 +129,8 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin this.extProxy.$terminalOnInput(terminal.id, data); this.extProxy.$terminalStateChanged(terminal.id); })); + + this.observers.forEach((observer, id) => this.observeTerminal(id, terminal, observer)); } $write(id: string, data: string): void { @@ -293,6 +303,42 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin } } + $registerTerminalObserver(id: string, nrOfLinesToMatch: number, outputMatcherRegex: string): void { + const observerData = { + nrOfLinesToMatch: nrOfLinesToMatch, + outputMatcherRegex: new RegExp(outputMatcherRegex, 'm'), + disposables: new DisposableCollection() + }; + this.observers.set(id, observerData); + this.terminals.all.forEach(terminal => { + this.observeTerminal(id, terminal, observerData); + }); + } + + protected observeTerminal(observerId: string, terminal: TerminalWidget, observerData: TerminalObserverData): void { + const doMatch = debounce(() => { + const lineCount = Math.min(observerData.nrOfLinesToMatch, terminal.buffer.length); + const lines = terminal.buffer.getLines(terminal.buffer.length - lineCount, lineCount); + const result = lines.join('\n').match(observerData.outputMatcherRegex); + if (result) { + this.extProxy.$reportOutputMatch(observerId, result.map(value => value)); + } + }); + observerData.disposables.push(terminal.onOutput(output => { + doMatch(); + })); + } + + $unregisterTerminalObserver(id: string): void { + const observer = this.observers.get(id); + if (observer) { + observer.disposables.dispose(); + this.observers.delete(id); + } else { + throw new Error(`Unregistering unknown terminal observer: ${id}`); + } + } + async provideLinks(line: string, terminal: TerminalWidget, cancellationToken?: CancellationToken | undefined): Promise { if (this.terminalLinkProviders.length < 1) { return []; diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index fb2949e261f5b..1114cc2e7244c 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -603,6 +603,12 @@ export function createAPIFactory( registerTerminalQuickFixProvider(id: string, provider: theia.TerminalQuickFixProvider): theia.Disposable { return terminalExt.registerTerminalQuickFixProvider(id, provider); }, + + /** Theia-specific TerminalObserver */ + registerTerminalObserver(observer: theia.TerminalObserver): theia.Disposable { + return terminalExt.registerTerminalObserver(observer); + }, + /** @stubbed ShareProvider */ registerShareProvider: () => Disposable.NULL, }; diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index d4833476c9186..d23967b13ca77 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -49,7 +49,6 @@ export function getIconClass(options: theia.TerminalOptions | theia.ExtensionTer */ @injectable() export class TerminalServiceExtImpl implements TerminalServiceExt { - private readonly proxy: TerminalServiceMain; private readonly _terminals = new Map(); @@ -58,6 +57,7 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { private static nextProviderId = 0; private readonly terminalLinkProviders = new Map(); + private readonly terminalObservers = new Map(); private readonly terminalProfileProviders = new Map(); private readonly onDidCloseTerminalEmitter = new Emitter(); readonly onDidCloseTerminal: theia.Event = this.onDidCloseTerminalEmitter.event; @@ -270,6 +270,25 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { return Disposable.NULL; } + registerTerminalObserver(observer: theia.TerminalObserver): theia.Disposable { + const id = (TerminalServiceExtImpl.nextProviderId++).toString(); + this.terminalObservers.set(id, observer); + this.proxy.$registerTerminalObserver(id, observer.nrOfLinesToMatch, observer.outputMatcherRegex); + return Disposable.create(() => { + this.proxy.$unregisterTerminalObserver(id); + this.terminalObservers.delete(id); + }); + } + + $reportOutputMatch(observerId: string, groups: string[]): void { + const observer = this.terminalObservers.get(observerId); + if (observer) { + observer.matchOccurred(groups); + } else { + throw new Error(`reporting matches for unregistered observer: ${observerId} `); + } + } + protected isExtensionTerminalOptions(options: theia.TerminalOptions | theia.ExtensionTerminalOptions): options is theia.ExtensionTerminalOptions { return 'pty' in options; } diff --git a/packages/plugin/src/theia-extra.d.ts b/packages/plugin/src/theia-extra.d.ts index f1826c780dd75..66a30583d9428 100644 --- a/packages/plugin/src/theia-extra.d.ts +++ b/packages/plugin/src/theia-extra.d.ts @@ -363,6 +363,26 @@ export module '@theia/plugin' { color?: ThemeColor; } + export interface TerminalObserver { + + /** + * A regex to match against the latest terminal output. + */ + readonly outputMatcherRegex: string; + /** + * The maximum number of lines to match the regex against. Maximum is 40 lines. + */ + readonly nrOfLinesToMatch: number; + /** + * Invoked when the regex matched against the terminal contents. + * @param groups The matched groups + */ + matchOccurred(groups: string[]): void; + } + + export namespace window { + export function registerTerminalObserver(observer: TerminalObserver): Disposable; + } } /** diff --git a/packages/terminal/src/browser/base/terminal-widget.ts b/packages/terminal/src/browser/base/terminal-widget.ts index 9350d0fb32dc7..8ccb29477e2dc 100644 --- a/packages/terminal/src/browser/base/terminal-widget.ts +++ b/packages/terminal/src/browser/base/terminal-widget.ts @@ -48,6 +48,15 @@ export interface TerminalSplitLocation { readonly parentTerminal: string; } +export interface TerminalBuffer { + readonly length: number; + /** + * @param start zero based index of the first line to return + * @param length the max number or lines to return + */ + getLines(start: number, length: number): string[]; +} + /** * Terminal UI widget. */ @@ -118,6 +127,10 @@ export abstract class TerminalWidget extends BaseWidget { /** Event that fires when the terminal input data */ abstract onData: Event; + abstract onOutput: Event; + + abstract buffer: TerminalBuffer; + abstract scrollLineUp(): void; abstract scrollLineDown(): void; diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 17992cbdb5f85..42924cfdefc93 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -29,7 +29,8 @@ import { IBaseTerminalServer, TerminalProcessInfo, TerminalExitReason } from '.. import { TerminalWatcher } from '../common/terminal-watcher'; import { TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus, TerminalLocationOptions, - TerminalLocation + TerminalLocation, + TerminalBuffer } from './base/terminal-widget'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { TerminalPreferences } from './terminal-preferences'; @@ -60,6 +61,23 @@ export interface TerminalContribution { onCreate(term: TerminalWidgetImpl): void; } +class TerminalBufferImpl implements TerminalBuffer { + constructor(private readonly term: Terminal) { + } + + get length(): number { + return this.term.buffer.active.length; + }; + getLines(start: number, length: number): string[] { + const result: string[] = []; + for (let i = 0; i < length && this.length - 1 - i >= 0; i++) { + result.push(this.term.buffer.active.getLine(this.length - 1 - i)!.translateToString()); + } + return result; + } + +} + @injectable() export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget, EnhancedPreviewWidget { readonly isExtractable: boolean = true; @@ -123,6 +141,9 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget protected readonly onDataEmitter = new Emitter(); readonly onData: Event = this.onDataEmitter.event; + protected readonly onOutputEmitter = new Emitter(); + readonly onOutput: Event = this.onOutputEmitter.event; + protected readonly onKeyEmitter = new Emitter<{ key: string, domEvent: KeyboardEvent }>(); readonly onKey: Event<{ key: string, domEvent: KeyboardEvent }> = this.onKeyEmitter.event; @@ -134,6 +155,11 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget protected readonly toDisposeOnConnect = new DisposableCollection(); + private _buffer: TerminalBuffer; + override get buffer(): TerminalBuffer { + return this._buffer; + } + @postConstruct() protected init(): void { this.setTitle(this.options.title || TerminalWidgetImpl.LABEL); @@ -174,6 +200,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget fastScrollSensitivity: this.preferences['terminal.integrated.fastScrollSensitivity'], theme: this.themeService.theme }); + this._buffer = new TerminalBufferImpl(this.term); this.fitAddon = new FitAddon(); this.term.loadAddon(this.fitAddon); @@ -711,6 +738,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget write(data: string): void { if (this.termOpened) { this.term.write(data); + this.onOutputEmitter.fire(data); } else { this.initialData += data; } @@ -762,6 +790,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget writeLine(text: string): void { this.term.writeln(text); + this.onOutputEmitter.fire(text + '\n'); } get onTerminalDidClose(): Event { From cf63b2098c31317d367ebe86aa1ebb27c2c6ef9f Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Thu, 21 Mar 2024 12:36:45 +0100 Subject: [PATCH 136/441] Provide "New File" default implementation (#13303) (#13344) --- .../src/tests/theia-getting-started.test.ts | 50 +++++++++++++ .../src/tests/theia-main-menu.test.ts | 21 ++++++ .../browser/common-frontend-contribution.ts | 45 +++++++++++- .../filesystem-frontend-contribution.ts | 71 +++++++++++++++---- .../src/browser/getting-started-widget.tsx | 31 ++++++-- 5 files changed, 198 insertions(+), 20 deletions(-) create mode 100644 examples/playwright/src/tests/theia-getting-started.test.ts diff --git a/examples/playwright/src/tests/theia-getting-started.test.ts b/examples/playwright/src/tests/theia-getting-started.test.ts new file mode 100644 index 0000000000000..e938e71beb265 --- /dev/null +++ b/examples/playwright/src/tests/theia-getting-started.test.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { expect, test } from '@playwright/test'; +import { TheiaApp } from '../theia-app'; +import { TheiaAppLoader } from '../theia-app-loader'; +import { TheiaExplorerView } from '../theia-explorer-view'; + +/** + * Test the Theia welcome page from the getting-started package. + */ +test.describe('Theia Welcome Page', () => { + let app: TheiaApp; + + test.beforeAll(async ({ playwright, browser }) => { + app = await TheiaAppLoader.load({ playwright, browser }); + await app.isMainContentPanelVisible(); + }); + + test.afterAll(async () => { + await app.page.close(); + }); + + test('New File... entry should create a new file.', async () => { + await app.page.getByRole('button', { name: 'New File...' }).click(); + const quickPicker = app.page.getByPlaceholder('Select File Type or Enter'); + await quickPicker.fill('testfile.txt'); + await quickPicker.press('Enter'); + await app.page.getByRole('button', { name: 'Create File' }).click(); + + // check file in workspace exists + const explorer = await app.openView(TheiaExplorerView); + await explorer.refresh(); + await explorer.waitForVisibleFileNodes(); + expect(await explorer.existsFileNode('testfile.txt')).toBe(true); + }); +}); diff --git a/examples/playwright/src/tests/theia-main-menu.test.ts b/examples/playwright/src/tests/theia-main-menu.test.ts index f00fcb6727270..a8807535f533d 100644 --- a/examples/playwright/src/tests/theia-main-menu.test.ts +++ b/examples/playwright/src/tests/theia-main-menu.test.ts @@ -20,6 +20,7 @@ import { TheiaAppLoader } from '../theia-app-loader'; import { TheiaAboutDialog } from '../theia-about-dialog'; import { TheiaMenuBar } from '../theia-main-menu'; import { OSUtil } from '../util'; +import { TheiaExplorerView } from '../theia-explorer-view'; test.describe('Theia Main Menu', () => { @@ -109,4 +110,24 @@ test.describe('Theia Main Menu', () => { expect(await fileDialog.isVisible()).toBe(false); }); + test('Create file via New File menu and cancel', async () => { + const openFileEntry = 'New File...'; + await (await menuBar.openMenu('File')).clickMenuItem(openFileEntry); + const quickPick = app.page.getByPlaceholder('Select File Type or Enter'); + // type file name and press enter + await quickPick.fill('test.txt'); + await quickPick.press('Enter'); + + // check file dialog is opened and accept with "Create File" button + const fileDialog = await app.page.waitForSelector('div[class="dialogBlock"]'); + expect(await fileDialog.isVisible()).toBe(true); + await app.page.locator('#theia-dialog-shell').getByRole('button', { name: 'Create File' }).click(); + expect(await fileDialog.isVisible()).toBe(false); + + // check file in workspace exists + const explorer = await app.openView(TheiaExplorerView); + await explorer.refresh(); + await explorer.waitForVisibleFileNodes(); + expect(await explorer.existsFileNode('test.txt')).toBe(true); + }); }); diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 99376fa1467a5..dc7d940e005c2 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -280,6 +280,15 @@ export namespace CommonCommands { category: VIEW_CATEGORY, label: 'Toggle Menu Bar' }); + /** + * Command Parameters: + * - `fileName`: string + * - `directory`: URI + */ + export const NEW_FILE = Command.toDefaultLocalizedCommand({ + id: 'workbench.action.files.newFile', + category: FILE_CATEGORY + }); export const NEW_UNTITLED_TEXT_FILE = Command.toDefaultLocalizedCommand({ id: 'workbench.action.files.newUntitledTextFile', category: FILE_CATEGORY, @@ -1424,6 +1433,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi const items: QuickPickItemOrSeparator[] = [ { label: nls.localizeByDefault('New Text File'), + description: nls.localizeByDefault('Built-in'), execute: async () => this.commandRegistry.executeCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE.id) }, ...newFileContributions.children @@ -1446,10 +1456,43 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }) ]; + + const CREATE_NEW_FILE_ITEM_ID = 'create-new-file'; + const hasNewFileHandler = this.commandRegistry.getActiveHandler(CommonCommands.NEW_FILE.id) !== undefined; + // Create a "Create New File" item only if there is a NEW_FILE command handler. + const createNewFileItem: QuickPickItem & { value?: string } | undefined = hasNewFileHandler ? { + id: CREATE_NEW_FILE_ITEM_ID, + label: nls.localizeByDefault('Create New File ({0})'), + description: nls.localizeByDefault('Built-in'), + execute: async () => { + if (createNewFileItem?.value) { + const parent = await this.workingDirProvider.getUserWorkingDir(); + // Exec NEW_FILE command with the file name and parent dir as arguments + return this.commandRegistry.executeCommand(CommonCommands.NEW_FILE.id, createNewFileItem.value, parent); + } + } + } : undefined; + this.quickInputService.showQuickPick(items, { title: nls.localizeByDefault('New File...'), placeholder: nls.localizeByDefault('Select File Type or Enter File Name...'), - canSelectMany: false + canSelectMany: false, + onDidChangeValue: picker => { + if (createNewFileItem === undefined) { + return; + } + // Dynamically show or hide the "Create New File" item based on the input value. + if (picker.value) { + createNewFileItem.alwaysShow = true; + createNewFileItem.value = picker.value; + createNewFileItem.label = nls.localizeByDefault('Create New File ({0})', picker.value); + picker.items = [...items, createNewFileItem]; + } else { + createNewFileItem.alwaysShow = false; + createNewFileItem.value = undefined; + picker.items = items.filter(item => item !== createNewFileItem); + } + } }); } diff --git a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts index 13917733ec2c5..78ba910633819 100644 --- a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts +++ b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts @@ -14,27 +14,36 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; -import URI from '@theia/core/lib/common/uri'; -import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; -import { MaybePromise, SelectionService, isCancelled, Emitter } from '@theia/core/lib/common'; -import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { nls } from '@theia/core'; import { - FrontendApplicationContribution, ApplicationShell, - NavigatableWidget, NavigatableWidgetOptions, - Saveable, WidgetManager, StatefulWidget, FrontendApplication, ExpandableTreeNode, - CorePreferences, + ApplicationShell, CommonCommands, + CorePreferences, + ExpandableTreeNode, + FrontendApplication, + FrontendApplicationContribution, + NavigatableWidget, NavigatableWidgetOptions, + OpenerService, + Saveable, + StatefulWidget, + WidgetManager, + open } from '@theia/core/lib/browser'; import { MimeService } from '@theia/core/lib/browser/mime-service'; import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection'; -import { FileSystemPreferences } from './filesystem-preferences'; +import { Emitter, MaybePromise, SelectionService, isCancelled } from '@theia/core/lib/common'; +import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import URI from '@theia/core/lib/common/uri'; +import { environment } from '@theia/core/shared/@theia/application-package/lib/environment'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { UserWorkingDirectoryProvider } from '@theia/core/lib/browser/user-working-directory-provider'; +import { FileChangeType, FileChangesEvent, FileOperation } from '../common/files'; +import { FileDialogService, SaveFileDialogProps } from './file-dialog'; import { FileSelection } from './file-selection'; -import { FileUploadService, FileUploadResult } from './file-upload-service'; import { FileService, UserFileOperationEvent } from './file-service'; -import { FileChangesEvent, FileChangeType, FileOperation } from '../common/files'; -import { Deferred } from '@theia/core/lib/common/promise-util'; -import { nls } from '@theia/core'; +import { FileUploadResult, FileUploadService } from './file-upload-service'; +import { FileSystemPreferences } from './filesystem-preferences'; export namespace FileSystemCommands { @@ -78,6 +87,15 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri @inject(FileService) protected readonly fileService: FileService; + @inject(FileDialogService) + protected readonly fileDialogService: FileDialogService; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(UserWorkingDirectoryProvider) + protected readonly workingDirectory: UserWorkingDirectoryProvider; + protected onDidChangeEditorFileEmitter = new Emitter<{ editor: NavigatableWidget, type: FileChangeType }>(); readonly onDidChangeEditorFile = this.onDidChangeEditorFileEmitter.event; @@ -134,6 +152,11 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri } } }); + commands.registerCommand(CommonCommands.NEW_FILE, { + execute: (...args: unknown[]) => { + this.handleNewFileCommand(args); + } + }); } protected canUpload({ fileStat }: FileSelection): boolean { @@ -155,6 +178,26 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri } } + /** + * Opens a save dialog to create a new file. + * + * @param args The first argument is the name of the new file. The second argument is the parent directory URI. + */ + protected async handleNewFileCommand(args: unknown[]): Promise { + const fileName = (args !== undefined && typeof args[0] === 'string') ? args[0] : undefined; + const title = nls.localizeByDefault('Create File'); + const props: SaveFileDialogProps = { title, saveLabel: title, inputValue: fileName }; + + const dirUri = (args[1] instanceof URI) ? args[1] : await this.workingDirectory.getUserWorkingDir(); + const directory = await this.fileService.resolve(dirUri); + + const filePath = await this.fileDialogService.showSaveDialog(props, directory.isDirectory ? directory : undefined); + if (filePath) { + const file = await this.fileService.createFile(filePath); + open(this.openerService, file.resource); + } + } + protected getSelection(...args: unknown[]): FileSelection | undefined { const { selection } = this.selectionService; return this.toSelection(args[0]) ?? (Array.isArray(selection) ? selection.find(FileSelection.is) : this.toSelection(selection)); diff --git a/packages/getting-started/src/browser/getting-started-widget.tsx b/packages/getting-started/src/browser/getting-started-widget.tsx index 22fa83fe8d769..c40b7c95ce93c 100644 --- a/packages/getting-started/src/browser/getting-started-widget.tsx +++ b/packages/getting-started/src/browser/getting-started-widget.tsx @@ -135,7 +135,7 @@ export class GettingStartedWidget extends ReactWidget {
    - {this.renderOpen()} + {this.renderStart()}
    @@ -176,12 +176,22 @@ export class GettingStartedWidget extends ReactWidget { } /** - * Render the `open` section. - * Displays a collection of `open` commands. + * Render the `Start` section. + * Displays a collection of "start-to-work" related commands like `open` commands and some other. */ - protected renderOpen(): React.ReactNode { + protected renderStart(): React.ReactNode { const requireSingleOpen = isOSX || !environment.electron.is(); + const createFile = ; + const open = requireSingleOpen &&
    -

    {nls.localizeByDefault('Open')}

    +

    {nls.localizeByDefault('Start')}

    + {createFile} {open} {openFile} {openFolder} @@ -392,6 +403,16 @@ export class GettingStartedWidget extends ReactWidget { return paths; } + /** + * Trigger the create file command. + */ + protected doCreateFile = () => this.commandRegistry.executeCommand(CommonCommands.NEW_UNTITLED_FILE.id); + protected doCreateFileEnter = (e: React.KeyboardEvent) => { + if (this.isEnterKey(e)) { + this.doCreateFile(); + } + }; + /** * Trigger the open command. */ From c500d0e2e88f42c3ef1be19b1854a7ecc0da2a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 21 Mar 2024 13:48:58 +0100 Subject: [PATCH 137/441] Make sure we can reopen secondary windows (#13509) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactored to close widget upon browser-case window close - Seems 'close' event is no longer fired in browser case. Using 'unload' Fixes #13214 Contributred on behalf of STMicroelectronics. Signed-off-by: Thomas Mäder --- .../default-secondary-window-service.ts | 50 +++++++++---------- .../electron-secondary-window-service.ts | 12 ++--- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index c175acfddb78f..6bcd00b116489 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -82,37 +82,14 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } createSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - const win = this.doCreateSecondaryWindow(widget, shell); - if (win) { - this.secondaryWindows.push(win); - win.addEventListener('close', () => { - const extIndex = this.secondaryWindows.indexOf(win); - if (extIndex > -1) { - this.secondaryWindows.splice(extIndex, 1); - }; - }); - } - return win; - } - - protected findWindow(windowName: string): Window | undefined { - for (const w of this.secondaryWindows) { - if (w.name === windowName) { - return w; - } - } - return undefined; - } - - protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - let options; const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget); - options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; + let options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; if (this.preferenceService.get('window.secondaryWindowAlwaysOnTop')) { options += ',alwaysOnTop=true'; } const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined; if (newWindow) { + this.secondaryWindows.push(newWindow); newWindow.addEventListener('DOMContentLoaded', () => { newWindow.addEventListener('beforeunload', evt => { const saveable = Saveable.get(widget); @@ -124,17 +101,38 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } }, { capture: true }); - newWindow.addEventListener('close', () => { + newWindow.addEventListener('unload', () => { const saveable = Saveable.get(widget); shell.closeWidget(widget.id, { save: !!saveable && saveable.dirty && saveable.autoSave !== 'off' }); + + const extIndex = this.secondaryWindows.indexOf(newWindow); + if (extIndex > -1) { + this.secondaryWindows.splice(extIndex, 1); + }; }); + this.windowCreated(newWindow, widget, shell); }); } return newWindow; } + protected windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void { + newWindow.addEventListener('unload', () => { + shell.closeWidget(widget.id); + }); + } + + protected findWindow(windowName: string): Window | undefined { + for (const w of this.secondaryWindows) { + if (w.name === windowName) { + return w; + } + } + return undefined; + } + protected findSecondaryWindowCoordinates(widget: ExtractableWidget): (number | undefined)[] { const clientBounds = widget.node.getBoundingClientRect(); const preference = this.preferenceService.get('window.secondaryWindowPlacement'); diff --git a/packages/core/src/electron-browser/window/electron-secondary-window-service.ts b/packages/core/src/electron-browser/window/electron-secondary-window-service.ts index a5bd35cdca469..c9ed6079a4931 100644 --- a/packages/core/src/electron-browser/window/electron-secondary-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-secondary-window-service.ts @@ -24,16 +24,12 @@ export class ElectronSecondaryWindowService extends DefaultSecondaryWindowServic window.electronTheiaCore.focusWindow(win.name); } - protected override doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - const w = super.doCreateSecondaryWindow(widget, shell); - if (w) { - window.electronTheiaCore.setMenuBarVisible(false, w.name); - window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(w.name, () => this.canClose(widget, shell)); - } - return w; + protected override windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void { + window.electronTheiaCore.setMenuBarVisible(false, newWindow.name); + window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(newWindow.name, () => this.canClose(widget, shell)); } private async canClose(widget: ExtractableWidget, shell: ApplicationShell): Promise { - await shell.closeWidget(widget.id, undefined); + await shell.closeWidget(widget.id); return widget.isDisposed; } } From ce3930603775022c1ec8ae5ccda1f80e77a4e3c0 Mon Sep 17 00:00:00 2001 From: Marc Dumais Date: Thu, 21 Mar 2024 09:37:56 -0400 Subject: [PATCH 138/441] [license] Add vscode license, referred-to in copied source code (#13506) In various source files in the code base, code originally from the vscode repository, was re-used. In these instances, there is a copyright header, like below, that refers to the file containing the full license: /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ I think we missed adding the full license text, as is mandated by the MIT license. This commit adds it, renamed for clarity. I think it should still be easy to find, and should be less likely to cause confusion about what license is the main one, in this repository/project. Signed-off-by: Marc Dumais --- LICENSE-vscode.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 LICENSE-vscode.txt diff --git a/LICENSE-vscode.txt b/LICENSE-vscode.txt new file mode 100644 index 0000000000000..701c01487abff --- /dev/null +++ b/LICENSE-vscode.txt @@ -0,0 +1,29 @@ +Below is the full text of vscode's MIT license, copied from the link below (it has not changed except cosmetically, since the Theia project was started): + +https://github.com/microsoft/vscode/blob/2dd03eaebe21473f3af6df2a229199b9aa138e97/LICENSE.txt + +This license covers code originally copied from the vscode repository and integrated in this project. + +----- + +MIT License + +Copyright (c) 2015 - present Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 3cf66f9f1a8fd0acb6d9682bd43fc3f45cd57086 Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Thu, 21 Mar 2024 16:27:04 +0100 Subject: [PATCH 139/441] Avoid disposal of QuickInputExt on hide (#13485) Remove implicit `dispose()` in `QuickInputExt.hide()`. This prevented quick input/quick pick instances from being shown again after they were hidden. This aligns the behavior with VS Code. Fixes https://github.com/eclipse-theia/theia/issues/13072 Contributed on behalf of STMicroelectronics --- packages/plugin-ext/src/plugin/quick-open.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index c5ca8c39c830b..c74eed1fa65f1 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -455,7 +455,6 @@ export class QuickInputExt implements theia.QuickInput { hide(): void { this.quickOpenMain.$hide(); - this.dispose(); } protected convertURL(iconPath: URI | { light: string | URI; dark: string | URI } | ThemeIcon): From c2b0704669fa3e4b399bc6f13b7fac89730012c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Fri, 22 Mar 2024 09:33:02 +0100 Subject: [PATCH 140/441] Check for disposed before sending update message (#13454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #13441 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../shell/tab-bar-toolbar/tab-bar-toolbar.tsx | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx index 092c5b90313fe..eda4d12ad9956 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx @@ -70,11 +70,11 @@ export class TabBarToolbar extends ReactWidget { @postConstruct() protected init(): void { - this.toDispose.push(this.keybindings.onKeybindingsChanged(() => this.update())); + this.toDispose.push(this.keybindings.onKeybindingsChanged(() => this.maybeUpdate())); this.toDispose.push(this.contextKeyService.onDidChange(e => { if (e.affects(this.keybindingContextKeys)) { - this.update(); + this.maybeUpdate(); } })); } @@ -90,7 +90,7 @@ export class TabBarToolbar extends ReactWidget { if ('command' in item) { this.commands.getAllHandlers(item.command).forEach(handler => { if (handler.onDidChangeEnabled) { - this.toDisposeOnUpdateItems.push(handler.onDidChangeEnabled(() => this.update())); + this.toDisposeOnUpdateItems.push(handler.onDidChangeEnabled(() => this.maybeUpdate())); } }); } @@ -113,7 +113,7 @@ export class TabBarToolbar extends ReactWidget { } else { this.hide(); } - this.update(); + this.maybeUpdate(); } updateTarget(current?: Widget): void { @@ -130,7 +130,7 @@ export class TabBarToolbar extends ReactWidget { if (current) { const resetCurrent = () => { this.setCurrent(undefined); - this.update(); + this.maybeUpdate(); }; current.disposed.connect(resetCurrent); this.toDisposeOnSetCurrent.push(Disposable.create(() => @@ -144,7 +144,7 @@ export class TabBarToolbar extends ReactWidget { if (contextKeys.size > 0) { this.contextKeyListener = this.contextKeyService.onDidChange(event => { if (event.affects(contextKeys)) { - this.update(); + this.maybeUpdate(); } }); } @@ -321,8 +321,8 @@ export class TabBarToolbar extends ReactWidget { protected renderMenuItem(item: TabBarToolbarItem & MenuToolbarItem): React.ReactNode { const icon = typeof item.icon === 'function' ? item.icon() : item.icon ?? 'ellipsis'; return
    + className={TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM + ' enabled menu'} + onClick={this.showPopupMenu.bind(this, item.menuPath)}>
    @@ -397,9 +397,15 @@ export class TabBarToolbar extends ReactWidget { } else if (item.menuPath) { this.renderMoreContextMenu(this.toAnchor(e), item.menuPath); } - this.update(); + this.maybeUpdate(); }; + protected maybeUpdate(): void { + if (!this.isDisposed) { + this.update(); + } + } + protected onMouseDownEvent = (e: React.MouseEvent) => { if (e.button === 0) { e.currentTarget.classList.add('active'); @@ -419,5 +425,4 @@ export namespace TabBarToolbar { export const TAB_BAR_TOOLBAR_ITEM = 'item'; } - } From 1a033819ad45994f7a4bff294a59a3e5f0619738 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 22 Mar 2024 14:44:57 +0100 Subject: [PATCH 141/441] Support remote port forwarding (#13439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * basics for dev-container support Signed-off-by: Jonah Iden * basic creating and connecting to container working Signed-off-by: Jonah Iden * open workspace when opening container Signed-off-by: Jonah Iden * save and reuse last USed container per workspace Signed-off-by: Jonah Iden * restart container if running Signed-off-by: Jonah Iden * better container creation extension features Signed-off-by: Jonah Iden * added dockerfile support Signed-off-by: Jonah Iden * rebuild container if devcontainer.json has been changed since last use Signed-off-by: Jonah Iden * fix build Signed-off-by: Jonah Iden * fixed checking if container needs rebuild Signed-off-by: Jonah Iden * working port forwarding via exec instance Signed-off-by: Jonah Iden * review changes Signed-off-by: Jonah Iden * fix import Signed-off-by: Jonah Iden * smaller fixes and added support for multiple devcontainer configuration files Signed-off-by: Jonah Iden * basic output window for devcontainer build Signed-off-by: Jonah Iden * smaller review changes and nicer dockerfile.json detection code Signed-off-by: Jonah Iden * fixed build and docuemented implemented devcontainer.json properties Signed-off-by: Jonah Iden * Fix unneeded URI conversion (#13415) * Fix quickpick problems found in IDE testing (#13451) Fixes #13450, #13449 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder * Fix rending of quickpick buttons (#13342) Ensure that the Theia specific wrapper for the MonacoQuickPickItem properly forwards assignments of the "buttons" property to the wrapped item. Fixes #13076 Contributed on behalf of STMicroelectronics * electron: allow accessing the metrics endpoint for performance analysis (#13380) By default, when running Theia in Electron, all endpoints are protected by the ElectronTokenValidator. This patch allows accessing the '/metrics' endpoint without a token, which enables us to collect metrics for performance analysis. For this, ElectronTokenValidator is extended to allow access to the metrics endpoint. All other endpoints are still protected. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich * fixed renaming and moving of open notebooks (#13467) * fixed renameing of open notebooks Signed-off-by: Jonah Iden * fixed moving of notebook editors to other areas Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden * [playwright] Update documentation Since a recent enhancement/refactoring of @theia/playwright, to permit using it in Theia Electron applications, the way to load an application has changed. This commit is an attempt to update the examples that are part of the documentation. I validated the changes in the "theia-playwright-template" repository, and so I have adapted the sample code to that repo's linting rules (using single quotes instead of double). It's possible that other things have changed, that I have not yet encountered, but this should be a good step forward, at least for those just getting started integrating playwright to test their Theia-based app. Signed-off-by: Marc Dumais * basics for dev-container support Signed-off-by: Jonah Iden * basic creating and connecting to container working Signed-off-by: Jonah Iden * added dockerfile support Signed-off-by: Jonah Iden * added port forwarding inlcuding ui Signed-off-by: Jonah Iden * basic port/address validation Signed-off-by: Jonah Iden * fixed allready forwarded port checking Signed-off-by: Jonah Iden * rebase fixes Signed-off-by: Jonah Iden * removed unused file Signed-off-by: Jonah Iden * review changes Signed-off-by: Jonah Iden * fixed widget focus and message margin Signed-off-by: Jonah Iden * default port binding now shows as 0.0.0.0 Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden Signed-off-by: Thomas Mäder Signed-off-by: Olaf Lessenich Signed-off-by: Marc Dumais Co-authored-by: Alexander Taran Co-authored-by: Thomas Mäder Co-authored-by: Tobias Ortmayr Co-authored-by: Olaf Lessenich Co-authored-by: Marc Dumais --- .../remote-container-connection-provider.ts | 11 +- .../port-forwading-contribution.ts | 33 +++++ .../port-forwarding-service.ts | 84 +++++++++++ .../port-forwarding-widget.tsx | 140 ++++++++++++++++++ .../remote-frontend-module.ts | 25 +++- .../style/port-forwarding-widget.css | 44 ++++++ .../remote-port-forwarding-provider.ts | 29 ++++ .../electron-node/remote-backend-module.ts | 6 + .../remote-port-forwarding-provider.ts | 50 +++++++ .../remote/src/electron-node/remote-types.ts | 2 +- .../ssh/remote-ssh-connection-provider.ts | 4 +- 11 files changed, 420 insertions(+), 8 deletions(-) create mode 100644 packages/remote/src/electron-browser/port-forwarding/port-forwading-contribution.ts create mode 100644 packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts create mode 100644 packages/remote/src/electron-browser/port-forwarding/port-forwarding-widget.tsx create mode 100644 packages/remote/src/electron-browser/style/port-forwarding-widget.css create mode 100644 packages/remote/src/electron-common/remote-port-forwarding-provider.ts create mode 100644 packages/remote/src/electron-node/remote-port-forwarding-provider.ts diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 6c80b89e57c7b..214b98b3a19ad 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -149,6 +149,13 @@ interface ContainerTerminalSession { executeCommand(cmd: string, args?: string[]): Promise<{ stdout: string, stderr: string }>; } +interface ContainerTerminalSession { + execution: Docker.Exec, + stdout: WriteStream, + stderr: WriteStream, + executeCommand(cmd: string, args?: string[]): Promise<{ stdout: string, stderr: string }>; +} + export class RemoteDockerContainerConnection implements RemoteConnection { id: string; @@ -179,12 +186,12 @@ export class RemoteDockerContainerConnection implements RemoteConnection { this.container = options.container; } - async forwardOut(socket: Socket): Promise { + async forwardOut(socket: Socket, port?: number): Promise { const node = `${this.remoteSetupResult.nodeDirectory}/bin/node`; const devContainerServer = `${this.remoteSetupResult.applicationDirectory}/backend/dev-container-server.js`; try { const ttySession = await this.container.exec({ - Cmd: ['sh', '-c', `${node} ${devContainerServer} -target-port=${this.remotePort}`], + Cmd: ['sh', '-c', `${node} ${devContainerServer} -target-port=${port ?? this.remotePort}`], AttachStdin: true, AttachStdout: true, AttachStderr: true }); diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwading-contribution.ts b/packages/remote/src/electron-browser/port-forwarding/port-forwading-contribution.ts new file mode 100644 index 0000000000000..0f45e5465abf1 --- /dev/null +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwading-contribution.ts @@ -0,0 +1,33 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import { AbstractViewContribution } from '@theia/core/lib/browser'; +import { injectable } from '@theia/core/shared/inversify'; +import { PortForwardingWidget, PORT_FORWARDING_WIDGET_ID } from './port-forwarding-widget'; + +@injectable() +export class PortForwardingContribution extends AbstractViewContribution { + constructor() { + super({ + widgetId: PORT_FORWARDING_WIDGET_ID, + widgetName: nls.localizeByDefault('Ports'), + defaultWidgetOptions: { + area: 'bottom' + } + }); + } +} diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts new file mode 100644 index 0000000000000..109cfb098a30d --- /dev/null +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts @@ -0,0 +1,84 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Emitter } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { RemotePortForwardingProvider } from '../../electron-common/remote-port-forwarding-provider'; + +export interface ForwardedPort { + localPort?: number; + address?: string; + origin?: string; + editing: boolean; +} + +@injectable() +export class PortForwardingService { + + @inject(RemotePortForwardingProvider) + readonly provider: RemotePortForwardingProvider; + + protected readonly onDidChangePortsEmitter = new Emitter(); + readonly onDidChangePorts = this.onDidChangePortsEmitter.event; + + forwardedPorts: ForwardedPort[] = []; + + forwardNewPort(origin?: string): ForwardedPort { + const index = this.forwardedPorts.push({ editing: true, origin }); + return this.forwardedPorts[index - 1]; + } + + updatePort(port: ForwardedPort, newAdress: string): void { + const connectionPort = new URLSearchParams(location.search).get('port'); + if (!connectionPort) { + // if there is no open remote connection we can't forward a port + return; + } + + const parts = newAdress.split(':'); + if (parts.length === 2) { + port.address = parts[0]; + port.localPort = parseInt(parts[1]); + } else { + port.localPort = parseInt(parts[0]); + } + + port.editing = false; + + this.provider.forwardPort(parseInt(connectionPort), { port: port.localPort!, address: port.address }); + this.onDidChangePortsEmitter.fire(); + } + + removePort(port: ForwardedPort): void { + const index = this.forwardedPorts.indexOf(port); + if (index !== -1) { + this.forwardedPorts.splice(index, 1); + this.provider.portRemoved({ port: port.localPort! }); + this.onDidChangePortsEmitter.fire(); + } + } + + isValidAddress(address: string): boolean { + const match = address.match(/^(.*:)?\d+$/); + if (!match) { + return false; + } + + const port = parseInt(address.includes(':') ? address.split(':')[1] : address); + + return !this.forwardedPorts.some(p => p.localPort === port); + } +} diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-widget.tsx b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-widget.tsx new file mode 100644 index 0000000000000..9333e43f628cc --- /dev/null +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-widget.tsx @@ -0,0 +1,140 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as React from '@theia/core/shared/react'; +import { ReactNode } from '@theia/core/shared/react'; +import { OpenerService, ReactWidget } from '@theia/core/lib/browser'; +import { nls, URI } from '@theia/core'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { ForwardedPort, PortForwardingService } from './port-forwarding-service'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; + +export const PORT_FORWARDING_WIDGET_ID = 'port-forwarding-widget'; + +@injectable() +export class PortForwardingWidget extends ReactWidget { + + @inject(PortForwardingService) + protected readonly portForwardingService: PortForwardingService; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(ClipboardService) + protected readonly clipboardService: ClipboardService; + + @postConstruct() + protected init(): void { + this.id = PORT_FORWARDING_WIDGET_ID; + this.node.tabIndex = -1; + this.title.label = nls.localizeByDefault('Ports'); + this.title.caption = this.title.label; + this.title.closable = true; + this.update(); + + this.portForwardingService.onDidChangePorts(() => this.update()); + } + + protected render(): ReactNode { + if (this.portForwardingService.forwardedPorts.length === 0) { + return
    +

    + {nls.localizeByDefault('No forwarded ports. Forward a port to access your locally running services over the internet.\n[Forward a Port]({0})').split('\n')[0]} +

    + {this.renderForwardPortButton()} +
    ; + } + + return
    + + + + + + + + + + + {this.portForwardingService.forwardedPorts.map(port => ( + + {this.renderPortColumn(port)} + {this.renderAddressColumn(port)} + + + + ))} + {!this.portForwardingService.forwardedPorts.some(port => port.editing) && } + +
    {nls.localizeByDefault('Port')}{nls.localizeByDefault('Address')}{nls.localizeByDefault('Running Process')}{nls.localizeByDefault('Origin')}
    {port.origin ? nls.localizeByDefault(port.origin) : ''}
    {this.renderForwardPortButton()}
    +
    ; + } + + protected renderForwardPortButton(): ReactNode { + return ; + } + + protected renderAddressColumn(port: ForwardedPort): ReactNode { + const address = `${port.address ?? '0.0.0.0'}:${port.localPort}`; + return +
    + { + if (e.ctrlKey) { + const uri = new URI(`http://${address}`); + (await this.openerService.getOpener(uri)).open(uri); + } + }} title={nls.localizeByDefault('Follow link') + ' (ctrl/cmd + click)'}> + {port.localPort ? address : ''} + + { + port.localPort && + { + this.clipboardService.writeText(address); + }}> + } +
    + ; + } + + protected renderPortColumn(port: ForwardedPort): ReactNode { + return port.editing ? + : + +
    + {port.localPort} + { + this.portForwardingService.removePort(port); + this.update(); + }}> +
    + ; + } + +} + +function PortEditingInput({ port, service }: { port: ForwardedPort, service: PortForwardingService }): React.JSX.Element { + const [error, setError] = React.useState(false); + return e.key === 'Enter' && !error && service.updatePort(port, e.currentTarget.value)} + onKeyUp={e => setError(!service.isValidAddress(e.currentTarget.value))}>; + +} diff --git a/packages/remote/src/electron-browser/remote-frontend-module.ts b/packages/remote/src/electron-browser/remote-frontend-module.ts index f2ddaf3d9e2d4..599c9ef3906d3 100644 --- a/packages/remote/src/electron-browser/remote-frontend-module.ts +++ b/packages/remote/src/electron-browser/remote-frontend-module.ts @@ -16,7 +16,7 @@ import { bindContributionProvider, CommandContribution } from '@theia/core'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, WebSocketConnectionProvider } from '@theia/core/lib/browser'; +import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; import { RemoteSSHContribution } from './remote-ssh-contribution'; import { RemoteSSHConnectionProvider, RemoteSSHConnectionProviderPath } from '../electron-common/remote-ssh-connection-provider'; import { RemoteFrontendContribution } from './remote-frontend-contribution'; @@ -26,6 +26,12 @@ import { RemoteStatusService, RemoteStatusServicePath } from '../electron-common import { ElectronFileDialogService } from '@theia/filesystem/lib/electron-browser/file-dialog/electron-file-dialog-service'; import { RemoteElectronFileDialogService } from './remote-electron-file-dialog-service'; import { bindRemotePreferences } from './remote-preferences'; +import { PortForwardingWidget, PORT_FORWARDING_WIDGET_ID } from './port-forwarding/port-forwarding-widget'; +import { PortForwardingContribution } from './port-forwarding/port-forwading-contribution'; +import { PortForwardingService } from './port-forwarding/port-forwarding-service'; +import { RemotePortForwardingProvider, RemoteRemotePortForwardingProviderPath } from '../electron-common/remote-port-forwarding-provider'; +import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; +import '../../src/electron-browser/style/port-forwarding-widget.css'; export default new ContainerModule((bind, _, __, rebind) => { bind(RemoteFrontendContribution).toSelf().inSingletonScope(); @@ -42,8 +48,21 @@ export default new ContainerModule((bind, _, __, rebind) => { bind(RemoteService).toSelf().inSingletonScope(); + bind(PortForwardingWidget).toSelf(); + bind(WidgetFactory).toDynamicValue(context => ({ + id: PORT_FORWARDING_WIDGET_ID, + createWidget: () => context.container.get(PortForwardingWidget) + })); + + bindViewContribution(bind, PortForwardingContribution); + bind(PortForwardingService).toSelf().inSingletonScope(); + bind(RemoteSSHConnectionProvider).toDynamicValue(ctx => - WebSocketConnectionProvider.createLocalProxy(ctx.container, RemoteSSHConnectionProviderPath)).inSingletonScope(); + ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteSSHConnectionProviderPath)).inSingletonScope(); bind(RemoteStatusService).toDynamicValue(ctx => - WebSocketConnectionProvider.createLocalProxy(ctx.container, RemoteStatusServicePath)).inSingletonScope(); + ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteStatusServicePath)).inSingletonScope(); + + bind(RemotePortForwardingProvider).toDynamicValue(ctx => + ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteRemotePortForwardingProviderPath)).inSingletonScope(); + }); diff --git a/packages/remote/src/electron-browser/style/port-forwarding-widget.css b/packages/remote/src/electron-browser/style/port-forwarding-widget.css new file mode 100644 index 0000000000000..7eafdb5e6af4e --- /dev/null +++ b/packages/remote/src/electron-browser/style/port-forwarding-widget.css @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (C) 2024 TypeFox and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + ********************************************************************************/ + +.port-table { + width: 100%; + margin: calc(var(--theia-ui-padding) * 2); + table-layout: fixed; +} + +.port-table-header { + text-align: left; +} + +.forward-port-button { + margin-left: 0; + width: 100%; +} + +.button-cell { + display: flex; + padding-right: var(--theia-ui-padding); +} + +.forwarded-address:hover { + cursor: pointer; + text-decoration: underline; +} + +.port-edit-input-error { + outline-color: var(--theia-inputValidation-errorBorder); +} diff --git a/packages/remote/src/electron-common/remote-port-forwarding-provider.ts b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts new file mode 100644 index 0000000000000..6f01e01da2ccd --- /dev/null +++ b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts @@ -0,0 +1,29 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export const RemoteRemotePortForwardingProviderPath = '/remote/port-forwarding'; + +export const RemotePortForwardingProvider = Symbol('RemoteSSHConnectionProvider'); + +export interface ForwardedPort { + port: number; + address?: string; +} + +export interface RemotePortForwardingProvider { + forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise; + portRemoved(port: ForwardedPort): Promise; +} diff --git a/packages/remote/src/electron-node/remote-backend-module.ts b/packages/remote/src/electron-node/remote-backend-module.ts index 733929479ce44..2197bb28a3d8b 100644 --- a/packages/remote/src/electron-node/remote-backend-module.ts +++ b/packages/remote/src/electron-node/remote-backend-module.ts @@ -37,11 +37,17 @@ import { RemoteCopyContribution, RemoteCopyRegistry } from './setup/remote-copy- import { MainCopyContribution } from './setup/main-copy-contribution'; import { RemoteNativeDependencyContribution } from './setup/remote-native-dependency-contribution'; import { AppNativeDependencyContribution } from './setup/app-native-dependency-contribution'; +import { RemotePortForwardingProviderImpl } from './remote-port-forwarding-provider'; +import { RemotePortForwardingProvider, RemoteRemotePortForwardingProviderPath } from '../electron-common/remote-port-forwarding-provider'; export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(RemoteSSHConnectionProviderImpl).toSelf().inSingletonScope(); bind(RemoteSSHConnectionProvider).toService(RemoteSSHConnectionProviderImpl); bindBackendService(RemoteSSHConnectionProviderPath, RemoteSSHConnectionProvider); + + bind(RemotePortForwardingProviderImpl).toSelf().inSingletonScope(); + bind(RemotePortForwardingProvider).toService(RemotePortForwardingProviderImpl); + bindBackendService(RemoteRemotePortForwardingProviderPath, RemotePortForwardingProvider); }); export default new ContainerModule((bind, _unbind, _isBound, rebind) => { diff --git a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts new file mode 100644 index 0000000000000..23c8e4ec9f579 --- /dev/null +++ b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ForwardedPort, RemotePortForwardingProvider } from '../electron-common/remote-port-forwarding-provider'; +import { createServer, Server } from 'net'; +import { RemoteConnectionService } from './remote-connection-service'; + +@injectable() +export class RemotePortForwardingProviderImpl implements RemotePortForwardingProvider { + + @inject(RemoteConnectionService) + protected readonly connectionService: RemoteConnectionService; + + protected forwardedPorts: Map = new Map(); + + async forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise { + const currentConnection = this.connectionService.getConnectionFromPort(connectionPort); + if (!currentConnection) { + throw new Error(`No connection found for port ${connectionPort}`); + } + + const server = createServer(socket => { + currentConnection?.forwardOut(socket, portToForward.port); + }).listen(portToForward.port, portToForward.address); + this.forwardedPorts.set(portToForward.port, server); + } + + async portRemoved(forwardedPort: ForwardedPort): Promise { + const proxy = this.forwardedPorts.get(forwardedPort.port); + if (proxy) { + proxy.close(); + this.forwardedPorts.delete(forwardedPort.port); + } + } + +} diff --git a/packages/remote/src/electron-node/remote-types.ts b/packages/remote/src/electron-node/remote-types.ts index e50811629d0cc..9c829ee201499 100644 --- a/packages/remote/src/electron-node/remote-types.ts +++ b/packages/remote/src/electron-node/remote-types.ts @@ -49,7 +49,7 @@ export interface RemoteConnection extends Disposable { localPort: number; remotePort: number; onDidDisconnect: Event; - forwardOut(socket: net.Socket): void; + forwardOut(socket: net.Socket, port?: number): void; /** * execute a single command on the remote machine diff --git a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts index a0b20e321c235..7c9342c0dd8ff 100644 --- a/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts +++ b/packages/remote/src/electron-node/ssh/remote-ssh-connection-provider.ts @@ -278,8 +278,8 @@ export class RemoteSSHConnection implements RemoteConnection { return sftpClient; } - forwardOut(socket: net.Socket): void { - this.client.forwardOut(socket.localAddress!, socket.localPort!, '127.0.0.1', this.remotePort, (err, stream) => { + forwardOut(socket: net.Socket, port?: number): void { + this.client.forwardOut(socket.localAddress!, socket.localPort!, '127.0.0.1', port ?? this.remotePort, (err, stream) => { if (err) { console.debug('Proxy message rejected', err); } else { From 0871e3135224015f2de776c2acd2855b9dbd230d Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 25 Mar 2024 09:42:07 +0100 Subject: [PATCH 142/441] fixed cell execution height css (#13515) Signed-off-by: Jonah Iden --- packages/notebook/src/browser/style/index.css | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 4d0ef1340d27b..9f1733a3fb3b6 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -143,7 +143,6 @@ } .theia-notebook-code-cell-execution-order { - height: 35px; display: block; font-family: var(--monaco-monospace-font); font-size: 10px; From d344b2f16e5a9ff1db0fbd4b001f86fbadca569b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 25 Mar 2024 10:49:35 +0100 Subject: [PATCH 143/441] Add an exception for @types/qs to the IP check baseline. (#13524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package is from DefinitelyTyped and therefore MIT-licensed. https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/13990 Contributed on behalf of STMicroelectronices Signed-off-by: Thomas Mäder --- dependency-check-baseline.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dependency-check-baseline.json b/dependency-check-baseline.json index a2d415608c293..5790ba796de8f 100644 --- a/dependency-check-baseline.json +++ b/dependency-check-baseline.json @@ -1,9 +1,3 @@ { - "npm/npmjs/-/allure-commandline/2.23.0": "Approved https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/9379", - "npm/npmjs/-/eslint-plugin-deprecation/1.2.1": "Approved as 'works-with': https://dev.eclipse.org/ipzilla/show_bug.cgi?id=22573", - "npm/npmjs/-/jschardet/2.3.0": "Approved for Eclipse Theia: https://dev.eclipse.org/ipzilla/show_bug.cgi?id=22481", - "npm/npmjs/-/jsdom/11.12.0": "Approved as 'works-with': https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23640https://dev.eclipse.org/ipzilla/show_bug.cgi?id=23640", - "npm/npmjs/-/lzma-native/8.0.6": "Approved as 'works-with': https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/1850", - "npm/npmjs/-/normalize-package-data/3.0.3": "Under review, licence believed to be BSD-2-Clause: https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/11614", - "npm/npmjs/-/playwright-core/1.22.2": "Approved as 'works-with': https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/2734" + "npm/npmjs/@types/qs/6.9.11": "Pending https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/13990" } From 774651f486ef902b3ed5925ecb849df03e6d4daa Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 25 Mar 2024 17:01:46 +0100 Subject: [PATCH 144/441] improved focusing of the notebook cell editors (#13516) * imporved focusing of the notebook editor Signed-off-by: Jonah Iden * fixed cursor staying in old cell when changing selected cell Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../src/browser/view-model/notebook-cell-model.ts | 8 ++++++++ .../src/browser/view-model/notebook-model.ts | 4 ++++ .../src/browser/view/notebook-cell-editor.tsx | 13 +++++++++++++ .../src/browser/view/notebook-code-cell-view.tsx | 5 +++-- .../browser/view/notebook-markdown-cell-view.tsx | 2 +- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 015295117a3ff..8020c320c3f53 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -100,6 +100,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onDidRequestCellEditChangeEmitter = new Emitter(); readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event; + protected readonly onWillFocusCellEditorEmitter = new Emitter(); + readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event; + @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @inject(MonacoTextModelService) @@ -213,6 +216,11 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidRequestCellEditChangeEmitter.fire(false); } + requestFocusEditor(): void { + this.requestEdit(); + this.onWillFocusCellEditorEmitter.fire(); + } + spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void { if (splice.deleteCount > 0 && splice.newOutputs.length > 0) { const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 7e88e23358eab..803cbc202e98b 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -344,6 +344,10 @@ export class NotebookModel implements Saveable, Disposable { this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]); + if (cells.length > 0) { + this.setSelectedCell(cells[cells.length - 1]); + cells[cells.length - 1].requestEdit(); + } } protected changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void { diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 5edc85c4596d5..0e5fb95c13b31 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -53,6 +53,16 @@ export class CellEditor extends React.Component { override componentDidMount(): void { this.disposeEditor(); + this.toDispose.push(this.props.cell.onWillFocusCellEditor(() => { + this.editor?.getControl().focus(); + })); + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(() => { + if (this.props.notebookModel.selectedCell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { + if (document.activeElement && 'blur' in document.activeElement) { + (document.activeElement as HTMLElement).blur(); + } + } + })); if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) { this.initEditor(); } else { @@ -103,6 +113,9 @@ export class CellEditor extends React.Component { this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { this.props.notebookContextManager.onDidEditorTextFocus(false); })); + if (cell.editing && notebookModel.selectedCell === cell) { + this.editor.getControl().focus(); + } } } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index ac9ef6f85b909..de66115a85b77 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -76,7 +76,7 @@ export class NotebookCodeCellRenderer implements CellRenderer { notebookContextManager={this.notebookContextManager} notebookViewportService={this.notebookViewportService} fontInfo={this.getOrCreateMonacoFontInfo()} /> - + cell.requestFocusEditor()}>
    @@ -110,6 +110,7 @@ export class NotebookCodeCellRenderer implements CellRenderer { export interface NotebookCodeCellStatusProps { cell: NotebookCellModel; executionStateService: NotebookExecutionStateService; + onClick: () => void; } export interface NotebookCodeCellStatusState { @@ -152,7 +153,7 @@ export class NotebookCodeCellStatus extends React.Component + return
    this.props.onClick()}>
    {this.renderExecutionState()}
    diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index 5d2d0cff1e605..19b8148af05bb 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -54,7 +54,7 @@ interface MarkdownCellProps { } function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element { - const [editMode, setEditMode] = React.useState(false); + const [editMode, setEditMode] = React.useState(cell.editing); React.useEffect(() => { const listener = cell.onDidRequestCellEditChange(cellEdit => setEditMode(cellEdit)); From 4b413bd22df1a508923073d47fdc6e048ded76fc Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 25 Mar 2024 17:02:27 +0100 Subject: [PATCH 145/441] fixed undo redo keybindings for notebook editor (#13518) Signed-off-by: Jonah Iden --- packages/monaco/src/browser/monaco-command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index 2b8e13fe188e1..cc2983784bacd 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -163,7 +163,7 @@ export class MonacoEditorCommandHandlers implements CommandContribution { const action = editor && editor.getAction(id); return !!action && action.isSupported(); } - if (!!EditorExtensionsRegistry.getEditorCommand(id)) { + if (!!EditorExtensionsRegistry.getEditorCommand(id) || MonacoCommands.COMMON_ACTIONS.has(id)) { return !!editor; } return true; From 29aa6359ea47e5fd7de72df465527e46f659b139 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 25 Mar 2024 17:02:44 +0100 Subject: [PATCH 146/441] custom widgets support for notebook outputs (#13517) * working different ipywidget like ipydatagrid Signed-off-by: Jonah Iden * better focus management for notebook editors Signed-off-by: Jonah Iden * fixed notebook editor fucos focus Signed-off-by: Jonah Iden * update extensions with associated notebooks after workspace trusted Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../filesystem/src/browser/file-service.ts | 14 ++++----- .../src/browser/notebook-editor-widget.tsx | 10 +++++++ .../service/notebook-editor-widget-service.ts | 27 +++++++++-------- .../notebooks/notebook-kernels-main.ts | 25 +++++++++------- .../renderers/cell-output-webview.tsx | 4 +++ .../renderers/output-webview-internal.ts | 8 +++++ .../src/plugin/notebook/notebook-kernels.ts | 30 ++++++++++++++----- .../plugin-ext/src/plugin/plugin-context.ts | 4 +-- packages/plugin-ext/src/plugin/webviews.ts | 9 ++++++ 9 files changed, 92 insertions(+), 39 deletions(-) diff --git a/packages/filesystem/src/browser/file-service.ts b/packages/filesystem/src/browser/file-service.ts index 61f03400f56e4..f237cd7560bc5 100644 --- a/packages/filesystem/src/browser/file-service.ts +++ b/packages/filesystem/src/browser/file-service.ts @@ -237,12 +237,12 @@ export interface FileSystemProviderCapabilitiesChangeEvent { } export interface FileSystemProviderReadOnlyMessageChangeEvent { - /** The affected file system provider for which this event was fired. */ - provider: FileSystemProvider; - /** The uri for which the provider is registered */ - scheme: string; - /** The new read only message */ - message: MarkdownString | undefined; + /** The affected file system provider for which this event was fired. */ + provider: FileSystemProvider; + /** The uri for which the provider is registered */ + scheme: string; + /** The new read only message */ + message: MarkdownString | undefined; } /** @@ -378,7 +378,7 @@ export class FileService { providerDisposables.push(provider.onFileWatchError(() => this.handleFileWatchError())); providerDisposables.push(provider.onDidChangeCapabilities(() => this.onDidChangeFileSystemProviderCapabilitiesEmitter.fire({ provider, scheme }))); if (ReadOnlyMessageFileSystemProvider.is(provider)) { - providerDisposables.push(provider.onDidChangeReadOnlyMessage(message => this.onDidChangeFileSystemProviderReadOnlyMessageEmitter.fire({ provider, scheme, message}))); + providerDisposables.push(provider.onDidChangeReadOnlyMessage(message => this.onDidChangeFileSystemProviderReadOnlyMessageEmitter.fire({ provider, scheme, message }))); } return Disposable.create(() => { diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 533f7c6c850ef..19a1fce82c1d2 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -243,4 +243,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.viewportService.dispose(); super.dispose(); } + + protected override onAfterShow(msg: Message): void { + super.onAfterShow(msg); + this.notebookEditorService.notebookEditorFocusChanged(this, true); + } + + protected override onAfterHide(msg: Message): void { + super.onAfterHide(msg); + this.notebookEditorService.notebookEditorFocusChanged(this, false); + } } diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index 8038b6cdf90c2..5c64eb35e2c0f 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -50,18 +50,7 @@ export class NotebookEditorWidgetService { @postConstruct() protected init(): void { this.applicationShell.onDidChangeActiveWidget(event => { - if (event.newValue instanceof NotebookEditorWidget) { - if (event.newValue !== this.focusedEditor) { - this.focusedEditor = event.newValue; - this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true); - this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor); - } - } else if (event.newValue) { - // Only unfocus editor if a new widget has been focused - this.focusedEditor = undefined; - this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true); - this.onDidChangeFocusedEditorEmitter.fire(undefined); - } + this.notebookEditorFocusChanged(event.newValue as NotebookEditorWidget, event.newValue instanceof NotebookEditorWidget); }); } @@ -92,4 +81,18 @@ export class NotebookEditorWidgetService { return Array.from(this.notebookEditors.values()); } + notebookEditorFocusChanged(editor: NotebookEditorWidget, focus: boolean): void { + if (focus) { + if (editor !== this.focusedEditor) { + this.focusedEditor = editor; + this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, true); + this.onDidChangeFocusedEditorEmitter.fire(this.focusedEditor); + } + } else if (this.focusedEditor) { + this.focusedEditor = undefined; + this.contextKeyService.setContext(NOTEBOOK_EDITOR_FOCUSED, false); + this.onDidChangeFocusedEditorEmitter.fire(undefined); + } + } + } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts index a35aaf3b30453..93dde74a3a473 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts @@ -27,7 +27,6 @@ import { CellExecution, NotebookEditorWidgetService, NotebookExecutionStateService, NotebookKernelChangeEvent, NotebookKernelService, NotebookService } from '@theia/notebook/lib/browser'; -import { combinedDisposable } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; import { interfaces } from '@theia/core/shared/inversify'; import { NotebookKernelSourceAction } from '@theia/notebook/lib/common'; import { NotebookDto } from './notebook-dto'; @@ -153,6 +152,20 @@ export class NotebookKernelsMainImpl implements NotebookKernelsMain { } }); }); + this.notebookKernelService.onDidChangeSelectedKernel(e => { + if (e.newKernel) { + const newKernelHandle = Array.from(this.kernels.entries()).find(([_, [kernel]]) => kernel.id === e.newKernel)?.[0]; + if (newKernelHandle !== undefined) { + this.proxy.$acceptNotebookAssociation(newKernelHandle, e.notebook.toComponents(), true); + } + } else { + const oldKernelHandle = Array.from(this.kernels.entries()).find(([_, [kernel]]) => kernel.id === e.oldKernel)?.[0]; + if (oldKernelHandle !== undefined) { + this.proxy.$acceptNotebookAssociation(oldKernelHandle, e.notebook.toComponents(), false); + } + + } + }); } async $postMessage(handle: number, editorId: string | undefined, message: unknown): Promise { @@ -196,16 +209,8 @@ export class NotebookKernelsMainImpl implements NotebookKernelsMain { } }(handle, data, this.languageService); - const listener = this.notebookKernelService.onDidChangeSelectedKernel(e => { - if (e.oldKernel === kernel.id) { - this.proxy.$acceptNotebookAssociation(handle, e.notebook.toComponents(), false); - } else if (e.newKernel === kernel.id) { - this.proxy.$acceptNotebookAssociation(handle, e.notebook.toComponents(), true); - } - }); - const registration = this.notebookKernelService.registerKernel(kernel); - this.kernels.set(handle, [kernel, combinedDisposable(listener, registration)]); + this.kernels.set(handle, [kernel, registration]); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 934cc61308f38..0d4c48e4f8a87 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -99,6 +99,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { if (this.editor) { this.toDispose.push(this.editor.onDidPostKernelMessage(message => { + // console.log('from extension customKernelMessage ', JSON.stringify(message)); this.webviewWidget.sendMessage({ type: 'customKernelMessage', message @@ -106,6 +107,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { })); this.toDispose.push(this.editor.onPostRendererMessage(messageObj => { + // console.log('from extension customRendererMessage ', JSON.stringify(messageObj)); this.webviewWidget.sendMessage({ type: 'customRendererMessage', ...messageObj @@ -188,6 +190,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { this.updateOutput({ newOutputs: this.cell.outputs, start: 0, deleteCount: 0 }); break; case 'customRendererMessage': + // console.log('from webview customRendererMessage ', message.rendererId, '', JSON.stringify(message.message)); this.messagingService.getScoped(this.editor.id).postMessage(message.rendererId, message.message); break; case 'didRenderOutput': @@ -197,6 +200,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { this.editor.node.getElementsByClassName('theia-notebook-viewport')[0].children[0].scrollBy(message.deltaX, message.deltaY); break; case 'customKernelMessage': + // console.log('from webview customKernelMessage ', JSON.stringify(message.message)); this.editor.recieveKernelMessage(message.message); break; } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 5eb60f62ab5a0..6f38f53abb358 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -573,5 +573,13 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { }); window.addEventListener('wheel', handleWheel); + (document.head as HTMLHeadElement & { originalAppendChild: typeof document.head.appendChild }).originalAppendChild = document.head.appendChild; + (document.head as HTMLHeadElement & { originalAppendChild: typeof document.head.appendChild }).appendChild = function appendChild(node: T): T { + if (node instanceof HTMLScriptElement && node.src.includes('webviewuuid')) { + node.src = node.src.replace('webviewuuid', location.hostname.split('.')[0]); + } + return this.originalAppendChild(node); + }; + theia.postMessage({ type: 'initialized' }); } diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts index 363d9d8657d42..e09d8fff69e22 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-kernels.ts @@ -20,11 +20,11 @@ import { CellExecuteUpdateDto, NotebookKernelDto, NotebookKernelsExt, NotebookKernelsMain, - NotebookKernelSourceActionDto, NotebookOutputDto, PluginModel, PluginPackage, PLUGIN_RPC_CONTEXT + NotebookKernelSourceActionDto, NotebookOutputDto, PluginModel, PLUGIN_RPC_CONTEXT } from '../../common'; import { RPCProtocol } from '../../common/rpc-protocol'; import { UriComponents } from '../../common/uri-components'; -import { CancellationTokenSource, Disposable, DisposableCollection, Emitter, Path } from '@theia/core'; +import { CancellationTokenSource, Disposable, DisposableCollection, Emitter } from '@theia/core'; import { Cell } from './notebook-document'; import { NotebooksExtImpl } from './notebooks'; import { NotebookCellOutputConverter, NotebookCellOutputItem, NotebookKernelSourceAction } from '../type-converters'; @@ -34,6 +34,8 @@ import { CommandRegistryImpl } from '../command-registry'; import { NotebookCellOutput, NotebookRendererScript, URI } from '../types-impl'; import { toUriComponents } from '../../main/browser/hierarchy/hierarchy-types-converters'; import type * as theia from '@theia/plugin'; +import { WebviewsExtImpl } from '../webviews'; +import { WorkspaceExtImpl } from '../workspace'; interface KernelData { extensionId: string; @@ -64,8 +66,21 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { rpc: RPCProtocol, private readonly notebooks: NotebooksExtImpl, private readonly commands: CommandRegistryImpl, + private readonly webviews: WebviewsExtImpl, + workspace: WorkspaceExtImpl ) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN); + + // call onDidChangeSelection for all kernels after trust is granted to inform extensions they can set the kernel as assoiciated + // the jupyter extension for example does not set kernel association after trust is granted + workspace.onDidGrantWorkspaceTrust(() => { + this.kernelData.forEach(kernel => { + kernel.associatedNotebooks.forEach(async (_, uri) => { + const notebook = await this.notebooks.waitForNotebookDocument(URI.parse(uri)); + kernel.onDidChangeSelection.fire({ selected: true, notebook: notebook.apiNotebook }); + }); + }); + }); } private currentHandle = 0; @@ -216,8 +231,7 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { return that.proxy.$postMessage(handle, 'notebook:' + editor?.notebook.uri.toString(), message); }, asWebviewUri(localResource: theia.Uri): theia.Uri { - const basePath = PluginPackage.toPluginUrl(extension, ''); - return URI.from({ path: new Path(basePath).join(localResource.path).toString(), scheme: 'https' }); + return that.webviews.toGeneralWebviewResource(extension, localResource); } }; @@ -294,20 +308,20 @@ export class NotebookKernelsExtImpl implements NotebookKernelsExt { }; } - async $acceptNotebookAssociation(handle: number, uri: UriComponents, value: boolean): Promise { + async $acceptNotebookAssociation(handle: number, uri: UriComponents, selected: boolean): Promise { const obj = this.kernelData.get(handle); if (obj) { // update data structure const notebook = await this.notebooks.waitForNotebookDocument(URI.from(uri)); - if (value) { + if (selected) { obj.associatedNotebooks.set(notebook.uri.toString(), true); } else { obj.associatedNotebooks.delete(notebook.uri.toString()); } - console.debug(`NotebookController[${handle}] ASSOCIATE notebook`, notebook.uri.toString(), value); + console.debug(`NotebookController[${handle}] ASSOCIATE notebook`, notebook.uri.toString(), selected); // send event obj.onDidChangeSelection.fire({ - selected: value, + selected: selected, notebook: notebook.apiNotebook }); } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 1114cc2e7244c..83c3f80a5c312 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -276,7 +276,7 @@ export function createAPIFactory( const notebooksExt = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT, new NotebooksExtImpl(rpc, commandRegistry, editorsAndDocumentsExt, documents)); const notebookEditors = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_EDITORS_EXT, new NotebookEditorsExtImpl(notebooksExt)); const notebookRenderers = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_EXT, new NotebookRenderersExtImpl(rpc, notebooksExt)); - const notebookKernels = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_KERNELS_EXT, new NotebookKernelsExtImpl(rpc, notebooksExt, commandRegistry)); + const notebookKernels = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_KERNELS_EXT, new NotebookKernelsExtImpl(rpc, notebooksExt, commandRegistry, webviewExt, workspaceExt)); const notebookDocuments = rpc.set(MAIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_EXT, new NotebookDocumentsExtImpl(notebooksExt)); const statusBarMessageRegistryExt = new StatusBarMessageRegistryExt(rpc); const terminalExt = rpc.set(MAIN_RPC_CONTEXT.TERMINAL_EXT, new TerminalServiceExtImpl(rpc)); @@ -744,7 +744,7 @@ export function createAPIFactory( registerTextDocumentContentProvider(scheme: string, provider: theia.TextDocumentContentProvider): theia.Disposable { return workspaceExt.registerTextDocumentContentProvider(scheme, provider); }, - registerFileSystemProvider(scheme: string, provider: theia.FileSystemProvider, options?: { isCaseSensitive?: boolean, isReadonly?: boolean | MarkdownString}): + registerFileSystemProvider(scheme: string, provider: theia.FileSystemProvider, options?: { isCaseSensitive?: boolean, isReadonly?: boolean | MarkdownString }): theia.Disposable { return fileSystemExt.registerFileSystemProvider(scheme, provider, options); }, diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index d80fe9d873e2d..e9aff70fb9bb8 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -24,6 +24,7 @@ import { fromViewColumn, toViewColumn, toWebviewPanelShowOptions } from './type- import { Disposable, WebviewPanelTargetArea, URI } from './types-impl'; import { WorkspaceExtImpl } from './workspace'; import { PluginIconPath } from './plugin-icon-path'; +import { PluginModel, PluginPackage } from '../common'; @injectable() export class WebviewsExtImpl implements WebviewsExt { @@ -196,6 +197,14 @@ export class WebviewsExtImpl implements WebviewsExt { return undefined; } + toGeneralWebviewResource(extension: PluginModel, resource: theia.Uri): theia.Uri { + const extensionUri = URI.parse(extension.packageUri); + const relativeResourcePath = resource.path.replace(extensionUri.path, ''); + const basePath = PluginPackage.toPluginUrl(extension, '') + relativeResourcePath; + + return URI.parse(this.initData!.webviewResourceRoot.replace('{{uuid}}', 'webviewUUID')).with({ path: basePath }); + } + public deleteWebview(handle: string): void { this.webviews.delete(handle); } From 8ea18465e4aa7214e9713931308398aa9b0374f0 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 26 Mar 2024 09:55:46 +0100 Subject: [PATCH 147/441] Bump API version to 1.87.2 (#13446) (#13514) Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + dev-packages/application-package/src/api.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21929257f8b64..6c2d3bb4353fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## not yet released +- [application-package] bumped the default supported API from `1.86.2` to `1.87.2` [#13514](https://github.com/eclipse-theia/theia/pull/13514) - contributed on behalf of STMicroelectronics - [core] Fix quickpick problems found in IDE testing [#13451](https://github.com/eclipse-theia/theia/pull/13451) - contributed on behalf of STMicroelectronics - [plugin] Extend TextEditorLineNumbersStyle with Interval [#13458](https://github.com/eclipse-theia/theia/pull/13458) - contributed on behalf of STMicroelectronics diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index e011224f06b2c..d92a85ce42d43 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.86.2'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.87.2'; From ddb91b901bd1973efaedc13c7de9edd30a78b364 Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Tue, 26 Mar 2024 15:53:04 +0100 Subject: [PATCH 148/441] Resolve links to workspace files in terminal (#13498) * Resolve links to workspace files in terminal by searching for matching files * Match files without a starting separator, e.g. 'yarn.lock:25:2' Contributed on behalf of STMicroelectronics --- packages/terminal/package.json | 1 + .../browser/terminal-file-link-provider.ts | 73 +++++++++++++++++-- .../src/browser/terminal-frontend-module.ts | 4 +- packages/terminal/tsconfig.json | 3 + 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 46a4131b43112..8ddf3640ecdda 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -9,6 +9,7 @@ "@theia/process": "1.47.0", "@theia/variable-resolver": "1.47.0", "@theia/workspace": "1.47.0", + "@theia/file-search": "1.47.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", diff --git a/packages/terminal/src/browser/terminal-file-link-provider.ts b/packages/terminal/src/browser/terminal-file-link-provider.ts index 5ee6feefe7ffa..d067a83a2a2fb 100644 --- a/packages/terminal/src/browser/terminal-file-link-provider.ts +++ b/packages/terminal/src/browser/terminal-file-link-provider.ts @@ -23,12 +23,15 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { TerminalWidget } from './base/terminal-widget'; import { TerminalLink, TerminalLinkProvider } from './terminal-link-provider'; import { TerminalWidgetImpl } from './terminal-widget-impl'; - +import { FileSearchService } from '@theia/file-search/lib/common/file-search-service'; +import { WorkspaceService } from '@theia/workspace/lib/browser'; @injectable() export class FileLinkProvider implements TerminalLinkProvider { @inject(OpenerService) protected readonly openerService: OpenerService; @inject(FileService) protected fileService: FileService; + @inject(FileSearchService) protected searchService: FileSearchService; + @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; async provideLinks(line: string, terminal: TerminalWidget): Promise { const links: TerminalLink[] = []; @@ -42,11 +45,48 @@ export class FileLinkProvider implements TerminalLinkProvider { length: match.length, handle: () => this.open(match, terminal) }); + } else { + const searchTerm = await this.extractPath(match); + const fileUri = await this.isValidWorkspaceFile(searchTerm, terminal); + if (fileUri) { + const position = await this.extractPosition(match); + links.push({ + startIndex: regExp.lastIndex - match.length, + length: match.length, + handle: () => this.openURI(fileUri, position) + }); + } } } return links; } + protected async isValidWorkspaceFile(searchTerm: string | undefined, terminal: TerminalWidget): Promise { + if (!searchTerm) { + return undefined; + } + const cwd = await this.getCwd(terminal); + // remove any leading ./, ../ etc. as they can't be searched + searchTerm = searchTerm.replace(/^(\.+[\\/])+/, ''); + const workspaceRoots = this.workspaceService.tryGetRoots().map(root => root.resource.toString()); + // try and find a matching file in the workspace + const files = (await this.searchService.find(searchTerm, { + rootUris: [cwd.toString(), ...workspaceRoots], + fuzzyMatch: true, + limit: 1 + })); + // checks if the string end in a separator + searchTerm + const regex = new RegExp(`[\\\\|\\/]${searchTerm}$`); + if (files.length && regex.test(files[0])) { + const fileUri = new URI(files[0]); + const valid = await this.isValidFileURI(fileUri); + if (valid) { + return fileUri; + } + + } + } + protected async createRegExp(): Promise { const baseLocalLinkClause = OS.backend.isWindows ? winLocalLinkClause : unixLocalLinkClause; return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); @@ -57,10 +97,7 @@ export class FileLinkProvider implements TerminalLinkProvider { const toOpen = await this.toURI(match, await this.getCwd(terminal)); if (toOpen) { // TODO: would be better to ask the opener service, but it returns positively even for unknown files. - try { - const stat = await this.fileService.resolve(toOpen); - return !stat.isDirectory; - } catch { } + return this.isValidFileURI(toOpen); } } catch (err) { console.trace('Error validating ' + match, err); @@ -68,6 +105,14 @@ export class FileLinkProvider implements TerminalLinkProvider { return false; } + protected async isValidFileURI(uri: URI): Promise { + try { + const stat = await this.fileService.resolve(uri); + return !stat.isDirectory; + } catch { } + return false; + } + protected async toURI(match: string, cwd: URI): Promise { const path = await this.extractPath(match); if (!path) { @@ -97,8 +142,11 @@ export class FileLinkProvider implements TerminalLinkProvider { if (!toOpen) { return; } - const position = await this.extractPosition(match); + return this.openURI(toOpen, position); + } + + async openURI(toOpen: URI, position: Position): Promise { let options = {}; if (position) { options = { selection: { start: position } }; @@ -108,7 +156,7 @@ export class FileLinkProvider implements TerminalLinkProvider { const opener = await this.openerService.getOpener(toOpen, options); opener.open(toOpen, options); } catch (err) { - console.error('Cannot open link ' + match, err); + console.error('Cannot open link ' + toOpen, err); } } @@ -153,6 +201,17 @@ export class FileDiffPostLinkProvider extends FileLinkProvider { } } +@injectable() +export class LocalFileLinkProvider extends FileLinkProvider { + override async createRegExp(): Promise { + // match links that might not start with a separator, e.g. 'foo.bar' + const baseLocalLinkClause = OS.backend.isWindows ? + '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)*)' + : '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)*)'; + return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); + } +} + // The following regular expressions are taken from: // https://github.com/microsoft/vscode/blob/b118105bf28d773fbbce683f7230d058be2f89a7/src/vs/workbench/contrib/terminal/browser/links/terminalLocalLinkDetector.ts#L34-L58 diff --git a/packages/terminal/src/browser/terminal-frontend-module.ts b/packages/terminal/src/browser/terminal-frontend-module.ts index e5547eb1d64ad..5b105f527dbd9 100644 --- a/packages/terminal/src/browser/terminal-frontend-module.ts +++ b/packages/terminal/src/browser/terminal-frontend-module.ts @@ -41,7 +41,7 @@ import { TerminalThemeService } from './terminal-theme-service'; import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access'; import { createXtermLinkFactory, TerminalLinkProvider, TerminalLinkProviderContribution, XtermLinkFactory } from './terminal-link-provider'; import { UrlLinkProvider } from './terminal-url-link-provider'; -import { FileDiffPostLinkProvider, FileDiffPreLinkProvider, FileLinkProvider } from './terminal-file-link-provider'; +import { FileDiffPostLinkProvider, FileDiffPreLinkProvider, FileLinkProvider, LocalFileLinkProvider } from './terminal-file-link-provider'; import { ContributedTerminalProfileStore, DefaultProfileStore, DefaultTerminalProfileService, TerminalProfileService, TerminalProfileStore, UserTerminalProfileStore @@ -123,6 +123,8 @@ export default new ContainerModule(bind => { bind(TerminalLinkProvider).toService(FileDiffPreLinkProvider); bind(FileDiffPostLinkProvider).toSelf().inSingletonScope(); bind(TerminalLinkProvider).toService(FileDiffPostLinkProvider); + bind(LocalFileLinkProvider).toSelf().inSingletonScope(); + bind(TerminalLinkProvider).toService(LocalFileLinkProvider); bind(ContributedTerminalProfileStore).to(DefaultProfileStore).inSingletonScope(); bind(UserTerminalProfileStore).to(DefaultProfileStore).inSingletonScope(); diff --git a/packages/terminal/tsconfig.json b/packages/terminal/tsconfig.json index adb72fecde9d5..e612a846eb047 100644 --- a/packages/terminal/tsconfig.json +++ b/packages/terminal/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../editor" }, + { + "path": "../file-search" + }, { "path": "../filesystem" }, From a47ddeb51a506a41ff65f6b74807b93b290e8e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 27 Mar 2024 10:33:43 +0100 Subject: [PATCH 149/441] Add secondary window support for text editors (#13493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - moves hard-coded support for webviews in secondary windows into the webviews support - introduces patch-package as a way to manage Phosphor patches - fixes the context menu support in secondary windows fixes #13163, #12722 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 2 +- dev-packages/application-manager/package.json | 1 - .../src/generator/frontend-generator.ts | 9 - .../src/generator/webpack-generator.ts | 22 +-- dev-packages/cli/bin/theia-patch.js | 38 +++++ dev-packages/cli/package.json | 4 +- .../cli/patches/@phosphor+widgets+1.9.3.patch | 157 ++++++++++++++++++ .../@theia+monaco-editor-core+1.83.101.patch | 32 ++++ package.json | 1 + packages/core/package.json | 2 +- .../browser/color-application-contribution.ts | 13 +- .../menu/browser-context-menu-renderer.ts | 3 +- .../src/browser/menu/browser-menu-plugin.ts | 4 +- .../src/browser/secondary-window-handler.ts | 43 +++-- .../src/browser/shell/application-shell.ts | 43 +++-- packages/core/src/browser/styling-service.ts | 15 +- .../browser/window/default-window-service.ts | 4 + .../window/test/mock-window-service.ts | 1 + .../core/src/browser/window/window-service.ts | 5 + .../menu/electron-context-menu-renderer.ts | 4 +- packages/core/src/electron-browser/preload.ts | 4 +- .../window/electron-window-service.ts | 3 + .../core/src/electron-common/electron-api.ts | 2 +- .../src/electron-main/electron-api-main.ts | 9 +- packages/debug/package.json | 3 +- packages/editor/package.json | 3 +- packages/editor/src/browser/editor-widget.ts | 6 +- packages/monaco/package.json | 3 +- packages/monaco/src/browser/monaco-command.ts | 10 +- .../monaco/src/browser/monaco-context-menu.ts | 12 ++ .../src/browser/monaco-editor-service.ts | 3 + ...onaco-frontend-application-contribution.ts | 17 ++ packages/monaco/src/browser/monaco-init.ts | 3 + .../monaco-standalone-theme-service.ts | 50 ++++++ .../browser/plugin-ext-frontend-module.ts | 3 + .../webview-secondary-window-support.ts | 47 ++++++ yarn.lock | 86 ++++++++-- 37 files changed, 560 insertions(+), 107 deletions(-) create mode 100755 dev-packages/cli/bin/theia-patch.js create mode 100644 dev-packages/cli/patches/@phosphor+widgets+1.9.3.patch create mode 100644 dev-packages/cli/patches/@theia+monaco-editor-core+1.83.101.patch create mode 100644 packages/monaco/src/browser/monaco-standalone-theme-service.ts create mode 100644 packages/plugin-ext/src/main/browser/webview/webview-secondary-window-support.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c2d3bb4353fe..c8e3d90033637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ - [plugin] Extend TextEditorLineNumbersStyle with Interval [#13458](https://github.com/eclipse-theia/theia/pull/13458) - contributed on behalf of STMicroelectronics
    [Breaking Changes:](#breaking_changes_not_yet_released) +- [core] Add secondary windows support for text editors. [#13493](https://github.com/eclipse-theia/theia/pull/13493 ). The changes in require more extensive patches for our dependencies than before. For this purpose, we are using the `patch-package` library. However, this change requires adopters to add the line `"postinstall": "theia-patch"` to the `package.json` at the root of their monorepo (where the `node_modules` folder is located). - contributed on behalf of STMicroelectronics -- [component] add here ## v1.47.0 - 02/29/2024 diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 7b9e46b97e031..f08b8a849f11c 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -57,7 +57,6 @@ "source-map": "^0.6.1", "source-map-loader": "^2.0.1", "source-map-support": "^0.5.19", - "string-replace-loader": "^3.1.0", "style-loader": "^2.0.0", "tslib": "^2.6.2", "umd-compat-loader": "^2.1.2", diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index ee304fec409a7..bf586df8db3e5 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -188,15 +188,6 @@ ${Array.from(frontendModules.values(), jsModulePath => `\ } - diff --git a/dev-packages/application-manager/src/generator/webpack-generator.ts b/dev-packages/application-manager/src/generator/webpack-generator.ts index 9fe4f05e7e143..c02fae28273e5 100644 --- a/dev-packages/application-manager/src/generator/webpack-generator.ts +++ b/dev-packages/application-manager/src/generator/webpack-generator.ts @@ -128,24 +128,6 @@ module.exports = [{ cache: staticCompression, module: { rules: [ - { - // Removes the host check in PhosphorJS to enable moving widgets to secondary windows. - test: /widget\\.js$/, - loader: 'string-replace-loader', - include: /node_modules[\\\\/]@phosphor[\\\\/]widgets[\\\\/]lib/, - options: { - multiple: [ - { - search: /document\\.body\\.contains\\(widget.node\\)/gm, - replace: 'widget.node.ownerDocument.body.contains(widget.node)' - }, - { - search: /\\!document\\.body\\.contains\\(host\\)/gm, - replace: ' !host.ownerDocument.body.contains(host)' - } - ] - } - }, { test: /\\.css$/, exclude: /materialcolors\\.css$|\\.useable\\.css$/, @@ -280,6 +262,10 @@ module.exports = [{ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, "css-loader"] + }, + { + test: /\.wasm$/, + type: 'asset/resource' } ] }, diff --git a/dev-packages/cli/bin/theia-patch.js b/dev-packages/cli/bin/theia-patch.js new file mode 100755 index 0000000000000..5c9e7ad2ad828 --- /dev/null +++ b/dev-packages/cli/bin/theia-patch.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +// @ts-check +const path = require('path'); +const cp = require('child_process'); + +const patchPackage= require.resolve('patch-package'); +console.log(`patch-package = ${patchPackage}`); + +const patchesDir = path.join('.', 'node_modules', '@theia', 'cli', 'patches'); + +console.log(`patchesdir = ${patchesDir}`); + +const env = Object.assign({}, process.env); + +const scriptProcess = cp.exec(`node ${patchPackage} --patch-dir "${patchesDir}"`, { + cwd: process.cwd(), + env, + +}); + +scriptProcess.stdout.pipe(process.stdout); +scriptProcess.stderr.pipe(process.stderr); diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index b882060db672d..b8137f9dfa1b0 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -20,7 +20,8 @@ "src" ], "bin": { - "theia": "./bin/theia" + "theia": "./bin/theia", + "theia-patch": "./bin/theia-patch.js" }, "scripts": { "prepare": "tsc -b", @@ -30,6 +31,7 @@ "clean": "theiaext clean" }, "dependencies": { + "patch-package": "^8.0.0", "@theia/application-manager": "1.47.0", "@theia/application-package": "1.47.0", "@theia/ffmpeg": "1.47.0", diff --git a/dev-packages/cli/patches/@phosphor+widgets+1.9.3.patch b/dev-packages/cli/patches/@phosphor+widgets+1.9.3.patch new file mode 100644 index 0000000000000..40f582ab14dcf --- /dev/null +++ b/dev-packages/cli/patches/@phosphor+widgets+1.9.3.patch @@ -0,0 +1,157 @@ +diff --git a/node_modules/@phosphor/widgets/lib/menu.d.ts b/node_modules/@phosphor/widgets/lib/menu.d.ts +index 5d5053c..7802167 100644 +--- a/node_modules/@phosphor/widgets/lib/menu.d.ts ++++ b/node_modules/@phosphor/widgets/lib/menu.d.ts +@@ -195,7 +195,7 @@ export declare class Menu extends Widget { + * + * This is a no-op if the menu is already attached to the DOM. + */ +- open(x: number, y: number, options?: Menu.IOpenOptions): void; ++ open(x: number, y: number, options?: Menu.IOpenOptions, anchor?: HTMLElement): void; + /** + * Handle the DOM events for the menu. + * +diff --git a/node_modules/@phosphor/widgets/lib/menu.js b/node_modules/@phosphor/widgets/lib/menu.js +index de23022..a8b15b1 100644 +--- a/node_modules/@phosphor/widgets/lib/menu.js ++++ b/node_modules/@phosphor/widgets/lib/menu.js +@@ -13,7 +13,7 @@ var __extends = (this && this.__extends) || (function () { + }; + })(); + var __assign = (this && this.__assign) || function () { +- __assign = Object.assign || function(t) { ++ __assign = Object.assign || function (t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) +@@ -424,7 +424,7 @@ var Menu = /** @class */ (function (_super) { + * + * This is a no-op if the menu is already attached to the DOM. + */ +- Menu.prototype.open = function (x, y, options) { ++ Menu.prototype.open = function (x, y, options, node) { + if (options === void 0) { options = {}; } + // Bail early if the menu is already attached. + if (this.isAttached) { +@@ -434,7 +434,7 @@ var Menu = /** @class */ (function (_super) { + var forceX = options.forceX || false; + var forceY = options.forceY || false; + // Open the menu as a root menu. +- Private.openRootMenu(this, x, y, forceX, forceY); ++ Private.openRootMenu(this, x, y, forceX, forceY, node); + // Activate the menu to accept keyboard input. + this.activate(); + }; +@@ -484,8 +484,16 @@ var Menu = /** @class */ (function (_super) { + this.node.addEventListener('mouseenter', this); + this.node.addEventListener('mouseleave', this); + this.node.addEventListener('contextmenu', this); +- document.addEventListener('mousedown', this, true); + }; ++ ++ Menu.prototype.onAfterAttach = function (msg) { ++ this.node.ownerDocument.addEventListener('mousedown', this, true); ++ } ++ ++ Menu.prototype.onBeforeDetach = function (msg) { ++ this.node.ownerDocument.removeEventListener('mousedown', this, true); ++ } ++ + /** + * A message handler invoked on an `'after-detach'` message. + */ +@@ -496,7 +504,6 @@ var Menu = /** @class */ (function (_super) { + this.node.removeEventListener('mouseenter', this); + this.node.removeEventListener('mouseleave', this); + this.node.removeEventListener('contextmenu', this); +- document.removeEventListener('mousedown', this, true); + }; + /** + * A message handler invoked on an `'activate-request'` message. +@@ -1124,14 +1131,15 @@ var Private; + /** + * Open a menu as a root menu at the target location. + */ +- function openRootMenu(menu, x, y, forceX, forceY) { ++ function openRootMenu(menu, x, y, forceX, forceY, element) { + // Ensure the menu is updated before attaching and measuring. + messaging_1.MessageLoop.sendMessage(menu, widget_1.Widget.Msg.UpdateRequest); + // Get the current position and size of the main viewport. ++ var doc = element ? element.ownerDocument : document; + var px = window.pageXOffset; + var py = window.pageYOffset; +- var cw = document.documentElement.clientWidth; +- var ch = document.documentElement.clientHeight; ++ var cw = doc.documentElement.clientWidth; ++ var ch = doc.documentElement.clientHeight; + // Compute the maximum allowed height for the menu. + var maxHeight = ch - (forceY ? y : 0); + // Fetch common variables. +@@ -1145,7 +1153,7 @@ var Private; + style.visibility = 'hidden'; + style.maxHeight = maxHeight + "px"; + // Attach the menu to the document. +- widget_1.Widget.attach(menu, document.body); ++ widget_1.Widget.attach(menu, doc.body); + // Measure the size of the menu. + var _a = node.getBoundingClientRect(), width = _a.width, height = _a.height; + // Adjust the X position of the menu to fit on-screen. +@@ -1177,8 +1185,8 @@ var Private; + // Get the current position and size of the main viewport. + var px = window.pageXOffset; + var py = window.pageYOffset; +- var cw = document.documentElement.clientWidth; +- var ch = document.documentElement.clientHeight; ++ var cw = itemNode.ownerDocument.documentElement.clientWidth; ++ var ch = itemNode.ownerDocument.documentElement.clientHeight; + // Compute the maximum allowed height for the menu. + var maxHeight = ch; + // Fetch common variables. +@@ -1192,7 +1200,7 @@ var Private; + style.visibility = 'hidden'; + style.maxHeight = maxHeight + "px"; + // Attach the menu to the document. +- widget_1.Widget.attach(submenu, document.body); ++ widget_1.Widget.attach(submenu, itemNode.ownerDocument.body); + // Measure the size of the menu. + var _a = node.getBoundingClientRect(), width = _a.width, height = _a.height; + // Compute the box sizing for the menu. +diff --git a/node_modules/@phosphor/widgets/lib/menubar.js b/node_modules/@phosphor/widgets/lib/menubar.js +index a8e10f4..da2ee82 100644 +--- a/node_modules/@phosphor/widgets/lib/menubar.js ++++ b/node_modules/@phosphor/widgets/lib/menubar.js +@@ -521,7 +521,7 @@ var MenuBar = /** @class */ (function (_super) { + // Get the positioning data for the new menu. + var _a = itemNode.getBoundingClientRect(), left = _a.left, bottom = _a.bottom; + // Open the new menu at the computed location. +- newMenu.open(left, bottom, { forceX: true, forceY: true }); ++ newMenu.open(left, bottom, { forceX: true, forceY: true }, this.node); + }; + /** + * Close the child menu immediately. +diff --git a/node_modules/@phosphor/widgets/lib/widget.js b/node_modules/@phosphor/widgets/lib/widget.js +index 01241fa..62da27c 100644 +--- a/node_modules/@phosphor/widgets/lib/widget.js ++++ b/node_modules/@phosphor/widgets/lib/widget.js +@@ -906,10 +906,10 @@ exports.Widget = Widget; + if (widget.parent) { + throw new Error('Cannot attach a child widget.'); + } +- if (widget.isAttached || document.body.contains(widget.node)) { ++ if (widget.isAttached || widget.node.ownerDocument.body.contains(widget.node)) { + throw new Error('Widget is already attached.'); + } +- if (!document.body.contains(host)) { ++ if (!host.ownerDocument.body.contains(host)) { + throw new Error('Host is not attached.'); + } + messaging_1.MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach); +@@ -930,7 +930,7 @@ exports.Widget = Widget; + if (widget.parent) { + throw new Error('Cannot detach a child widget.'); + } +- if (!widget.isAttached || !document.body.contains(widget.node)) { ++ if (!widget.isAttached || !widget.node.ownerDocument.body.contains(widget.node)) { + throw new Error('Widget is not attached.'); + } + messaging_1.MessageLoop.sendMessage(widget, Widget.Msg.BeforeDetach); diff --git a/dev-packages/cli/patches/@theia+monaco-editor-core+1.83.101.patch b/dev-packages/cli/patches/@theia+monaco-editor-core+1.83.101.patch new file mode 100644 index 0000000000000..da1e72d06ac90 --- /dev/null +++ b/dev-packages/cli/patches/@theia+monaco-editor-core+1.83.101.patch @@ -0,0 +1,32 @@ +diff --git a/node_modules/@theia/monaco-editor-core/esm/vs/base/browser/ui/sash/sash.js b/node_modules/@theia/monaco-editor-core/esm/vs/base/browser/ui/sash/sash.js +index 111dec4..b196066 100644 +--- a/node_modules/@theia/monaco-editor-core/esm/vs/base/browser/ui/sash/sash.js ++++ b/node_modules/@theia/monaco-editor-core/esm/vs/base/browser/ui/sash/sash.js +@@ -47,14 +47,15 @@ function setGlobalHoverDelay(size) { + } + exports.setGlobalHoverDelay = setGlobalHoverDelay; + class MouseEventFactory { +- constructor() { ++ constructor(el) { ++ this.el = el; + this.disposables = new lifecycle_1.DisposableStore(); + } + get onPointerMove() { +- return this.disposables.add(new event_1.DomEmitter(window, 'mousemove')).event; ++ return this.disposables.add(new event_1.DomEmitter(this.el.ownerDocument.defaultView, 'mousemove')).event; + } + get onPointerUp() { +- return this.disposables.add(new event_1.DomEmitter(window, 'mouseup')).event; ++ return this.disposables.add(new event_1.DomEmitter(this.el.ownerDocument.defaultView, 'mouseup')).event; + } + dispose() { + this.disposables.dispose(); +@@ -243,7 +244,7 @@ class Sash extends lifecycle_1.Disposable { + this.el.classList.add('mac'); + } + const onMouseDown = this._register(new event_1.DomEmitter(this.el, 'mousedown')).event; +- this._register(onMouseDown(e => this.onPointerStart(e, new MouseEventFactory()), this)); ++ this._register(onMouseDown(e => this.onPointerStart(e, new MouseEventFactory(this.el)), this)); + const onMouseDoubleClick = this._register(new event_1.DomEmitter(this.el, 'dblclick')).event; + this._register(onMouseDoubleClick(this.onPointerDoublePress, this)); + const onMouseEnter = this._register(new event_1.DomEmitter(this.el, 'mouseenter')).event; diff --git a/package.json b/package.json index a7df25c10403e..d82581281ddd4 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "lint:clean": "rimraf .eslintcache", "lint:oneshot": "node --max-old-space-size=4096 node_modules/eslint/bin/eslint.js --cache=true \"{dev-packages,packages,examples}/**/*.{ts,tsx}\"", "preinstall": "node-gyp install", + "postinstall": "theia-patch", "prepare": "yarn -s compile:references && lerna run prepare && yarn -s compile", "publish:latest": "lerna publish --exact --yes --no-push", "publish:next": "lerna publish preminor --exact --canary --preid next --dist-tag next --no-git-reset --no-git-tag-version --no-push --yes && yarn -s publish:check", diff --git a/packages/core/package.json b/packages/core/package.json index 8b63571a3d75a..a001804f9bbd0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -213,4 +213,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} +} \ No newline at end of file diff --git a/packages/core/src/browser/color-application-contribution.ts b/packages/core/src/browser/color-application-contribution.ts index 4ad35b3de44b5..91cb42774fbdd 100644 --- a/packages/core/src/browser/color-application-contribution.ts +++ b/packages/core/src/browser/color-application-contribution.ts @@ -22,6 +22,7 @@ import { FrontendApplicationContribution } from './frontend-application-contribu import { ContributionProvider } from '../common/contribution-provider'; import { Disposable, DisposableCollection } from '../common/disposable'; import { DEFAULT_BACKGROUND_COLOR_STORAGE_KEY } from './frontend-application-config-provider'; +import { SecondaryWindowHandler } from './secondary-window-handler'; export const ColorContribution = Symbol('ColorContribution'); export interface ColorContribution { @@ -43,6 +44,9 @@ export class ColorApplicationContribution implements FrontendApplicationContribu @inject(ThemeService) protected readonly themeService: ThemeService; + @inject(SecondaryWindowHandler) + protected readonly secondaryWindowHandler: SecondaryWindowHandler; + onStart(): void { for (const contribution of this.colorContributions.getContributions()) { contribution.registerColors(this.colors); @@ -55,13 +59,18 @@ export class ColorApplicationContribution implements FrontendApplicationContribu this.colors.onDidChange(() => this.update()); this.registerWindow(window); + this.secondaryWindowHandler.onWillAddWidget(([widget, window]) => { + this.registerWindow(window); + }); + this.secondaryWindowHandler.onWillRemoveWidget(([widget, window]) => { + this.windows.delete(window); + }); } - registerWindow(win: Window): Disposable { + registerWindow(win: Window): void { this.windows.add(win); this.updateWindow(win); this.onDidChangeEmitter.fire(); - return Disposable.create(() => this.windows.delete(win)); } protected readonly toUpdate = new DisposableCollection(); diff --git a/packages/core/src/browser/menu/browser-context-menu-renderer.ts b/packages/core/src/browser/menu/browser-context-menu-renderer.ts index 775efaec0e28f..1ae5f1f826878 100644 --- a/packages/core/src/browser/menu/browser-context-menu-renderer.ts +++ b/packages/core/src/browser/menu/browser-context-menu-renderer.ts @@ -40,7 +40,8 @@ export class BrowserContextMenuRenderer extends ContextMenuRenderer { if (onHide) { contextMenu.aboutToClose.connect(() => onHide!()); } - contextMenu.open(x, y); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + contextMenu.open(x, y, undefined, context); return new BrowserContextMenuAccess(contextMenu); } diff --git a/packages/core/src/browser/menu/browser-menu-plugin.ts b/packages/core/src/browser/menu/browser-menu-plugin.ts index 8145d9d58e84f..931b0719d931e 100644 --- a/packages/core/src/browser/menu/browser-menu-plugin.ts +++ b/packages/core/src/browser/menu/browser-menu-plugin.ts @@ -267,14 +267,14 @@ export class DynamicMenuWidget extends MenuWidget { }); } - public override open(x: number, y: number, options?: MenuWidget.IOpenOptions): void { + public override open(x: number, y: number, options?: MenuWidget.IOpenOptions, anchor?: HTMLElement): void { const cb = () => { this.restoreFocusedElement(); this.aboutToClose.disconnect(cb); }; this.aboutToClose.connect(cb); this.preserveFocusedElement(); - super.open(x, y, options); + super.open(x, y, options, anchor); } protected updateSubMenus(parent: MenuWidget, menu: CompoundMenuNode, commands: MenuCommandRegistry): void { diff --git a/packages/core/src/browser/secondary-window-handler.ts b/packages/core/src/browser/secondary-window-handler.ts index d5aaef2c30a17..e967da357b1e5 100644 --- a/packages/core/src/browser/secondary-window-handler.ts +++ b/packages/core/src/browser/secondary-window-handler.ts @@ -22,8 +22,6 @@ import { ApplicationShell } from './shell/application-shell'; import { Emitter } from '../common/event'; import { SecondaryWindowService } from './window/secondary-window-service'; import { KeybindingRegistry } from './keybinding'; -import { ColorApplicationContribution } from './color-application-contribution'; -import { StylingService } from './styling-service'; /** Widget to be contained directly in a secondary window. */ class SecondaryWindowRootWidget extends Widget { @@ -47,7 +45,6 @@ class SecondaryWindowRootWidget extends Widget { * This handler manages the opened secondary windows and sets up messaging between them and the Theia main window. * In addition, it provides access to the extracted widgets and provides notifications when widgets are added to or removed from this handler. * - * @experimental The functionality provided by this handler is experimental and has known issues in Electron apps. */ @injectable() export class SecondaryWindowHandler { @@ -59,17 +56,19 @@ export class SecondaryWindowHandler { @inject(KeybindingRegistry) protected keybindings: KeybindingRegistry; - @inject(ColorApplicationContribution) - protected colorAppContribution: ColorApplicationContribution; - - @inject(StylingService) - protected stylingService: StylingService; + protected readonly onWillAddWidgetEmitter = new Emitter<[Widget, Window]>(); + /** Subscribe to get notified when a widget is added to this handler, i.e. the widget was moved to an secondary window . */ + readonly onWillAddWidget = this.onWillAddWidgetEmitter.event; - protected readonly onDidAddWidgetEmitter = new Emitter(); + protected readonly onDidAddWidgetEmitter = new Emitter<[Widget, Window]>(); /** Subscribe to get notified when a widget is added to this handler, i.e. the widget was moved to an secondary window . */ readonly onDidAddWidget = this.onDidAddWidgetEmitter.event; - protected readonly onDidRemoveWidgetEmitter = new Emitter(); + protected readonly onWillRemoveWidgetEmitter = new Emitter<[Widget, Window]>(); + /** Subscribe to get notified when a widget is removed from this handler, i.e. the widget's window was closed or the widget was disposed. */ + readonly onWillRemoveWidget = this.onWillRemoveWidgetEmitter.event; + + protected readonly onDidRemoveWidgetEmitter = new Emitter<[Widget, Window]>(); /** Subscribe to get notified when a widget is removed from this handler, i.e. the widget's window was closed or the widget was disposed. */ readonly onDidRemoveWidget = this.onDidRemoveWidgetEmitter.event; @@ -122,7 +121,8 @@ export class SecondaryWindowHandler { } const mainWindowTitle = document.title; - newWindow.onload = () => { + + newWindow.addEventListener('load', () => { this.keybindings.registerEventListeners(newWindow); // Use the widget's title as the window title // Even if the widget's label were malicious, this should be safe against XSS because the HTML standard defines this is inserted via a text node. @@ -134,8 +134,8 @@ export class SecondaryWindowHandler { console.error('Could not find dom element to attach to in secondary window'); return; } - const unregisterWithColorContribution = this.colorAppContribution.registerWindow(newWindow); - const unregisterWithStylingService = this.stylingService.registerWindow(newWindow); + + this.onWillAddWidgetEmitter.fire([widget, newWindow]); widget.secondaryWindow = newWindow; const rootWidget = new SecondaryWindowRootWidget(); @@ -145,13 +145,12 @@ export class SecondaryWindowHandler { widget.show(); widget.update(); - this.addWidget(widget); + this.addWidget(widget, newWindow); // Close the window if the widget is disposed, e.g. by a command closing all widgets. widget.disposed.connect(() => { - unregisterWithColorContribution.dispose(); - unregisterWithStylingService.dispose(); - this.removeWidget(widget); + this.onWillRemoveWidgetEmitter.fire([widget, newWindow]); + this.removeWidget(widget, newWindow); if (!newWindow.closed) { newWindow.close(); } @@ -165,7 +164,7 @@ export class SecondaryWindowHandler { updateWidget(); }); widget.activate(); - }; + }); } /** @@ -195,18 +194,18 @@ export class SecondaryWindowHandler { return undefined; } - protected addWidget(widget: ExtractableWidget): void { + protected addWidget(widget: ExtractableWidget, win: Window): void { if (!this._widgets.includes(widget)) { this._widgets.push(widget); - this.onDidAddWidgetEmitter.fire(widget); + this.onDidAddWidgetEmitter.fire([widget, win]); } } - protected removeWidget(widget: ExtractableWidget): void { + protected removeWidget(widget: ExtractableWidget, win: Window): void { const index = this._widgets.indexOf(widget); if (index > -1) { this._widgets.splice(index, 1); - this.onDidRemoveWidgetEmitter.fire(widget); + this.onDidRemoveWidgetEmitter.fire([widget, win]); } } } diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index da6e9e99f256a..424c0bf43dcb5 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -44,6 +44,7 @@ import { SecondaryWindowHandler } from '../secondary-window-handler'; import URI from '../../common/uri'; import { OpenerService } from '../opener-service'; import { PreviewableWidget } from '../widgets/previewable-widget'; +import { WindowService } from '../window/window-service'; /** The class name added to ApplicationShell instances. */ const APPLICATION_SHELL_CLASS = 'theia-ApplicationShell'; @@ -273,6 +274,7 @@ export class ApplicationShell extends Widget { @inject(CorePreferences) protected readonly corePreferences: CorePreferences, @inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService, @inject(SecondaryWindowHandler) protected readonly secondaryWindowHandler: SecondaryWindowHandler, + @inject(WindowService) protected readonly windowService: WindowService ) { super(options as Widget.IOptions); @@ -338,8 +340,8 @@ export class ApplicationShell extends Widget { this.rightPanelHandler.dockPanel.widgetRemoved.connect((_, widget) => this.fireDidRemoveWidget(widget)); this.secondaryWindowHandler.init(this); - this.secondaryWindowHandler.onDidAddWidget(widget => this.fireDidAddWidget(widget)); - this.secondaryWindowHandler.onDidRemoveWidget(widget => this.fireDidRemoveWidget(widget)); + this.secondaryWindowHandler.onDidAddWidget(([widget, window]) => this.fireDidAddWidget(widget)); + this.secondaryWindowHandler.onDidRemoveWidget(([widget, window]) => this.fireDidRemoveWidget(widget)); this.layout = this.createLayout(); @@ -1323,20 +1325,23 @@ export class ApplicationShell extends Widget { let widget = find(this.mainPanel.widgets(), w => w.id === id); if (widget) { this.mainPanel.activateWidget(widget); - return widget; } - widget = find(this.bottomPanel.widgets(), w => w.id === id); - if (widget) { - this.expandBottomPanel(); - this.bottomPanel.activateWidget(widget); - return widget; + if (!widget) { + widget = find(this.bottomPanel.widgets(), w => w.id === id); + if (widget) { + this.expandBottomPanel(); + this.bottomPanel.activateWidget(widget); + } } - widget = this.leftPanelHandler.activate(id); - if (widget) { - return widget; + if (!widget) { + widget = this.leftPanelHandler.activate(id); + } + + if (!widget) { + widget = this.rightPanelHandler.activate(id); } - widget = this.rightPanelHandler.activate(id); if (widget) { + this.windowService.focus(); return widget; } return this.secondaryWindowHandler.activateWidget(id); @@ -1433,17 +1438,19 @@ export class ApplicationShell extends Widget { if (tabBar) { tabBar.currentTitle = widget.title; } - return widget; } - widget = this.leftPanelHandler.expand(id); - if (widget) { - return widget; + if (!widget) { + widget = this.leftPanelHandler.expand(id); + } + if (!widget) { + widget = this.rightPanelHandler.expand(id); } - widget = this.rightPanelHandler.expand(id); if (widget) { + this.windowService.focus(); return widget; + } else { + return this.secondaryWindowHandler.revealWidget(id); } - return this.secondaryWindowHandler.revealWidget(id); } /** diff --git a/packages/core/src/browser/styling-service.ts b/packages/core/src/browser/styling-service.ts index 8acac1b1372be..221577711a09d 100644 --- a/packages/core/src/browser/styling-service.ts +++ b/packages/core/src/browser/styling-service.ts @@ -21,7 +21,7 @@ import { ColorRegistry } from './color-registry'; import { DecorationStyle } from './decoration-style'; import { FrontendApplicationContribution } from './frontend-application-contribution'; import { ThemeService } from './theming'; -import { Disposable } from '../common'; +import { SecondaryWindowHandler } from './secondary-window-handler'; export const StylingParticipant = Symbol('StylingParticipant'); @@ -52,16 +52,25 @@ export class StylingService implements FrontendApplicationContribution { @inject(ContributionProvider) @named(StylingParticipant) protected readonly themingParticipants: ContributionProvider; + @inject(SecondaryWindowHandler) + protected readonly secondaryWindowHandler: SecondaryWindowHandler; + onStart(): void { this.registerWindow(window); + this.secondaryWindowHandler.onWillAddWidget(([widget, window]) => { + this.registerWindow(window); + }); + this.secondaryWindowHandler.onWillRemoveWidget(([widget, window]) => { + this.cssElements.delete(window); + }); + this.themeService.onDidColorThemeChange(e => this.applyStylingToWindows(e.newTheme)); } - registerWindow(win: Window): Disposable { + registerWindow(win: Window): void { const cssElement = DecorationStyle.createStyleElement('contributedColorTheme', win.document.head); this.cssElements.set(win, cssElement); this.applyStyling(this.themeService.getCurrentTheme(), cssElement); - return Disposable.create(() => this.cssElements.delete(win)); } protected applyStylingToWindows(theme: Theme): void { diff --git a/packages/core/src/browser/window/default-window-service.ts b/packages/core/src/browser/window/default-window-service.ts index bee19fa4a0408..f2d83ca5858fb 100644 --- a/packages/core/src/browser/window/default-window-service.ts +++ b/packages/core/src/browser/window/default-window-service.ts @@ -57,6 +57,10 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC this.openNewWindow(`#${DEFAULT_WINDOW_HASH}`); } + focus(): void { + window.focus(); + } + /** * Returns a list of actions that {@link FrontendApplicationContribution}s would like to take before shutdown * It is expected that this will succeed - i.e. return an empty array - at most once per session. If no vetoes are received diff --git a/packages/core/src/browser/window/test/mock-window-service.ts b/packages/core/src/browser/window/test/mock-window-service.ts index 3d924337b04a5..245c0c691acfd 100644 --- a/packages/core/src/browser/window/test/mock-window-service.ts +++ b/packages/core/src/browser/window/test/mock-window-service.ts @@ -21,6 +21,7 @@ import { WindowService } from '../window-service'; export class MockWindowService implements WindowService { openNewWindow(): undefined { return undefined; } openNewDefaultWindow(): void { } + focus(): void { } reload(): void { } isSafeToShutDown(): Promise { return Promise.resolve(true); } setSafeToShutDown(): void { } diff --git a/packages/core/src/browser/window/window-service.ts b/packages/core/src/browser/window/window-service.ts index 694046fe910a2..6f1a0fc7bb40e 100644 --- a/packages/core/src/browser/window/window-service.ts +++ b/packages/core/src/browser/window/window-service.ts @@ -42,6 +42,11 @@ export interface WindowService { */ openNewDefaultWindow(params?: WindowReloadOptions): void; + /** + * Reveal and focuses the current window + */ + focus(): void; + /** * Fires when the `window` unloads. The unload event is inevitable. On this event, the frontend application can save its state and release resource. * Saving the state and releasing any resources must be a synchronous call. Any asynchronous calls invoked after emitting this event might be ignored. diff --git a/packages/core/src/electron-browser/menu/electron-context-menu-renderer.ts b/packages/core/src/electron-browser/menu/electron-context-menu-renderer.ts index 8ed8b1cebd1a8..7633930144c03 100644 --- a/packages/core/src/electron-browser/menu/electron-context-menu-renderer.ts +++ b/packages/core/src/electron-browser/menu/electron-context-menu-renderer.ts @@ -104,11 +104,13 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer { const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode); const { x, y } = coordinateFromAnchor(anchor); + const windowName = options.context?.ownerDocument.defaultView?.Window.name; + const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => { if (onHide) { onHide(); } - }); + }, windowName); // native context menu stops the event loop, so there is no keyboard events this.context.resetAltPressed(); return new ElectronContextMenuAccess(menuHandle); diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index c78043cb1fc9c..4018bdb0936c0 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -81,11 +81,11 @@ const api: TheiaCoreAPI = { }, attachSecurityToken: (endpoint: string) => ipcRenderer.invoke(CHANNEL_ATTACH_SECURITY_TOKEN, endpoint), - popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise { + popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise { const menuId = nextMenuId++; const handlers = new Map void>(); commandHandlers.set(menuId, handlers); - const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y); + const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y, windowName); const closeListener = () => { ipcRenderer.removeListener(CHANNEL_ON_CLOSE_POPUP, closeListener); commandHandlers.delete(menuId); diff --git a/packages/core/src/electron-browser/window/electron-window-service.ts b/packages/core/src/electron-browser/window/electron-window-service.ts index f2ec5d6ae2edc..f9248cb54806e 100644 --- a/packages/core/src/electron-browser/window/electron-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-window-service.ts @@ -57,6 +57,9 @@ export class ElectronWindowService extends DefaultWindowService { this.delegate.openNewDefaultWindow(params); } + override focus(): void { + window.electronTheiaCore.focusWindow(window.name); + } @postConstruct() protected init(): void { // Update the default zoom level on startup when the preferences event is fired. diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 5bff4341c68ab..77ffa0d5f3704 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -50,7 +50,7 @@ export interface TheiaCoreAPI { setMenuBarVisible(visible: boolean, windowName?: string): void; setMenu(menu: MenuDto[] | undefined): void; - popup(menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise; + popup(menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise; closePopup(handle: number): void; focusWindow(name: string): void; diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index add9606bbdad1..6b3cb9d8fd46b 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -115,7 +115,7 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { }); // popup menu - ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y) => { + ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y, windowName?: string) => { const zoom = event.sender.getZoomFactor(); // TODO: Remove the offset once Electron fixes https://github.com/electron/electron/issues/31641 const offset = process.platform === 'win32' ? 0 : 2; @@ -124,7 +124,14 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { y = Math.round(y * zoom) + offset; const popup = Menu.buildFromTemplate(this.fromMenuDto(event.sender, menuId, menu)); this.openPopups.set(menuId, popup); + let electronWindow: BrowserWindow | undefined; + if (windowName) { + electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName); + } else { + electronWindow = BrowserWindow.fromWebContents(event.sender) || undefined; + } popup.popup({ + window: electronWindow, callback: () => { this.openPopups.delete(menuId); event.sender.send(CHANNEL_ON_CLOSE_POPUP, menuId); diff --git a/packages/debug/package.json b/packages/debug/package.json index ff5aa34b473bc..d23b9564552b9 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -28,6 +28,7 @@ "theiaExtensions": [ { "frontend": "lib/browser/debug-frontend-module", + "secondaryWindow": "lib/browser/debug-frontend-module", "backend": "lib/node/debug-backend-module" } ], @@ -62,4 +63,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/editor/package.json b/packages/editor/package.json index 0a23dbe92e228..6521ff71a9601 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -12,7 +12,8 @@ }, "theiaExtensions": [ { - "frontend": "lib/browser/editor-frontend-module" + "frontend": "lib/browser/editor-frontend-module", + "secondaryWindow": "lib/browser/editor-frontend-module" } ], "keywords": [ diff --git a/packages/editor/src/browser/editor-widget.ts b/packages/editor/src/browser/editor-widget.ts index d31e089b09843..bba055996c5a4 100644 --- a/packages/editor/src/browser/editor-widget.ts +++ b/packages/editor/src/browser/editor-widget.ts @@ -15,12 +15,12 @@ // ***************************************************************************** import { Disposable, SelectionService, Event, UNTITLED_SCHEME, DisposableCollection } from '@theia/core/lib/common'; -import { Widget, BaseWidget, Message, Saveable, SaveableSource, Navigatable, StatefulWidget, lock, TabBar, DockPanel, unlock } from '@theia/core/lib/browser'; +import { Widget, BaseWidget, Message, Saveable, SaveableSource, Navigatable, StatefulWidget, lock, TabBar, DockPanel, unlock, ExtractableWidget } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { find } from '@theia/core/shared/@phosphor/algorithm'; import { TextEditor } from './editor'; -export class EditorWidget extends BaseWidget implements SaveableSource, Navigatable, StatefulWidget { +export class EditorWidget extends BaseWidget implements SaveableSource, Navigatable, StatefulWidget, ExtractableWidget { protected toDisposeOnTabbarChange = new DisposableCollection(); protected currentTabbar: TabBar | undefined; @@ -51,6 +51,8 @@ export class EditorWidget extends BaseWidget implements SaveableSource, Navigata } })); } + isExtractable: boolean = true; + secondaryWindow: Window | undefined; setSelection(): void { if (this.editor.isFocused() && this.selectionService.selection !== this.editor) { diff --git a/packages/monaco/package.json b/packages/monaco/package.json index e9e5ef83c38a4..c728b911ff6a3 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -22,7 +22,8 @@ }, "theiaExtensions": [ { - "frontend": "lib/browser/monaco-frontend-module" + "frontend": "lib/browser/monaco-frontend-module", + "secondaryWindow": "lib/browser/monaco-frontend-module" } ], "keywords": [ diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index cc2983784bacd..fef9775b2d779 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -38,16 +38,16 @@ export namespace MonacoCommands { ['redo', CommonCommands.REDO.id], ['editor.action.selectAll', CommonCommands.SELECT_ALL.id], ['actions.find', CommonCommands.FIND.id], - ['editor.action.startFindReplaceAction', CommonCommands.REPLACE.id] + ['editor.action.startFindReplaceAction', CommonCommands.REPLACE.id], + ['editor.action.clipboardCutAction', CommonCommands.CUT.id], + ['editor.action.clipboardCopyAction', CommonCommands.COPY.id], + ['editor.action.clipboardPasteAction', CommonCommands.PASTE.id] ]); export const GO_TO_DEFINITION = 'editor.action.revealDefinition'; export const EXCLUDE_ACTIONS = new Set([ - 'editor.action.quickCommand', - 'editor.action.clipboardCutAction', - 'editor.action.clipboardCopyAction', - 'editor.action.clipboardPasteAction' + 'editor.action.quickCommand' ]); } diff --git a/packages/monaco/src/browser/monaco-context-menu.ts b/packages/monaco/src/browser/monaco-context-menu.ts index 681909b0a4a0b..ad4f7239de0d2 100644 --- a/packages/monaco/src/browser/monaco-context-menu.ts +++ b/packages/monaco/src/browser/monaco-context-menu.ts @@ -51,9 +51,20 @@ export class MonacoContextMenuService implements IContextMenuService { } } + private getContext(delegate: IContextMenuDelegate): HTMLElement | undefined { + const anchor = delegate.getAnchor(); + if (anchor instanceof HTMLElement) { + return anchor; + } else if (anchor instanceof StandardMouseEvent) { + return anchor.target; + } else { + return undefined; + } + } showContextMenu(delegate: IContextMenuDelegate): void { const anchor = this.toAnchor(delegate.getAnchor()); const actions = delegate.getActions(); + const context = this.getContext(delegate); const onHide = () => { delegate.onHide?.(false); this.onDidHideContextMenuEmitter.fire(); @@ -63,6 +74,7 @@ export class MonacoContextMenuService implements IContextMenuService { // In case of 'Quick Fix' actions come as 'CodeActionAction' items if (actions.length > 0 && actions[0] instanceof MenuItemAction) { this.contextMenuRenderer.render({ + context: context, menuPath: this.menuPath(), anchor, onHide diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts index 85c57658b2e1b..0138443dabcf3 100644 --- a/packages/monaco/src/browser/monaco-editor-service.ts +++ b/packages/monaco/src/browser/monaco-editor-service.ts @@ -143,6 +143,9 @@ export class MonacoEditorService extends StandaloneCodeEditorService { } const area = (ref && this.shell.getAreaFor(ref)) || 'main'; const mode = ref && sideBySide ? 'split-right' : undefined; + if (area === 'secondaryWindow') { + return { area: 'main', mode }; + } return { area, mode, ref }; } diff --git a/packages/monaco/src/browser/monaco-frontend-application-contribution.ts b/packages/monaco/src/browser/monaco-frontend-application-contribution.ts index 6d9b4251d5b41..e93e9700b8295 100644 --- a/packages/monaco/src/browser/monaco-frontend-application-contribution.ts +++ b/packages/monaco/src/browser/monaco-frontend-application-contribution.ts @@ -27,6 +27,12 @@ import { editorOptionsRegistry, IEditorOption } from '@theia/monaco-editor-core/ import { MAX_SAFE_INTEGER } from '@theia/core'; import { editorGeneratedPreferenceProperties } from '@theia/editor/lib/browser/editor-generated-preference-schema'; import { WorkspaceFileService } from '@theia/workspace/lib/common/workspace-file-service'; +import { SecondaryWindowHandler } from '@theia/core/lib/browser/secondary-window-handler'; +import { EditorWidget } from '@theia/editor/lib/browser'; +import { MonacoEditor } from './monaco-editor'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { StandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneThemeService'; +import { IStandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/common/standaloneTheme'; @injectable() export class MonacoFrontendApplicationContribution implements FrontendApplicationContribution, StylingParticipant { @@ -47,6 +53,9 @@ export class MonacoFrontendApplicationContribution implements FrontendApplicatio @inject(WorkspaceFileService) protected readonly workspaceFileService: WorkspaceFileService; + @inject(SecondaryWindowHandler) + protected readonly secondaryWindowHandler: SecondaryWindowHandler; + @postConstruct() protected init(): void { this.addAdditionalPreferenceValidations(); @@ -82,6 +91,14 @@ export class MonacoFrontendApplicationContribution implements FrontendApplicatio 'extensions': workspaceExtensions.map(ext => `.${ext}`) }); } + onStart(): void { + this.secondaryWindowHandler.onDidAddWidget(([widget, window]) => { + if (widget instanceof EditorWidget && widget.editor instanceof MonacoEditor) { + const themeService = StandaloneServices.get(IStandaloneThemeService) as StandaloneThemeService; + themeService.registerEditorContainer(widget.node); + } + }); + } registerThemeStyle(theme: ColorTheme, collector: CssStyleCollector): void { if (isHighContrast(theme.type)) { diff --git a/packages/monaco/src/browser/monaco-init.ts b/packages/monaco/src/browser/monaco-init.ts index 1fa6c367b0f40..80ec1fd2d8338 100644 --- a/packages/monaco/src/browser/monaco-init.ts +++ b/packages/monaco/src/browser/monaco-init.ts @@ -46,6 +46,8 @@ import { IBulkEditService } from '@theia/monaco-editor-core/esm/vs/editor/browse import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; import { MonacoQuickInputImplementation } from './monaco-quick-input-service'; import { IQuickInputService } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput'; +import { IStandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/common/standaloneTheme'; +import { MonacoStandaloneThemeService } from './monaco-standalone-theme-service'; class MonacoEditorServiceConstructor { /** @@ -109,6 +111,7 @@ export namespace MonacoInit { [IBulkEditService.toString()]: new SyncDescriptor(MonacoBulkEditServiceConstructor, [container]), [ICommandService.toString()]: new SyncDescriptor(MonacoCommandServiceConstructor, [container]), [IQuickInputService.toString()]: new SyncDescriptor(MonacoQuickInputImplementationConstructor, [container]), + [IStandaloneThemeService.toString()]: new MonacoStandaloneThemeService() }); } } diff --git a/packages/monaco/src/browser/monaco-standalone-theme-service.ts b/packages/monaco/src/browser/monaco-standalone-theme-service.ts new file mode 100644 index 0000000000000..f36d7083d6b48 --- /dev/null +++ b/packages/monaco/src/browser/monaco-standalone-theme-service.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { IDisposable } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; +import { StandaloneThemeService } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneThemeService'; + +export class MonacoStandaloneThemeService extends StandaloneThemeService { + protected get styleElements(): HTMLStyleElement[] { + // access private style element array + return (this as any)._styleElements; + } + + protected get allCSS(): string { + return (this as any)._allCSS; + } + + override registerEditorContainer(domNode: HTMLElement): IDisposable { + const style = domNode.ownerDocument.createElement('style'); + style.type = 'text/css'; + style.media = 'screen'; + style.className = 'monaco-colors'; + style.textContent = this.allCSS; + domNode.ownerDocument.head.appendChild(style); + this.styleElements.push(style); + return { + dispose: () => { + for (let i = 0; i < this.styleElements.length; i++) { + if (this.styleElements[i] === style) { + this.styleElements.splice(i, 1); + return; + } + } + } + }; + } +} diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 18168ac9d4d1e..10c4a6f29b79c 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -90,6 +90,7 @@ import { CellOutputWebviewImpl, createCellOutputWebviewContainer } from './noteb import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { ArgumentProcessorContribution } from './command-registry-main'; +import { WebviewSecondaryWindowSupport } from './webview/webview-secondary-window-support'; export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -187,6 +188,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(WebviewWidgetFactory).toDynamicValue(ctx => new WebviewWidgetFactory(ctx.container)).inSingletonScope(); bind(WidgetFactory).toService(WebviewWidgetFactory); bind(WebviewContextKeys).toSelf().inSingletonScope(); + bind(WebviewSecondaryWindowSupport).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(WebviewSecondaryWindowSupport); bind(FrontendApplicationContribution).toService(WebviewContextKeys); bind(CustomEditorContribution).toSelf().inSingletonScope(); diff --git a/packages/plugin-ext/src/main/browser/webview/webview-secondary-window-support.ts b/packages/plugin-ext/src/main/browser/webview/webview-secondary-window-support.ts new file mode 100644 index 0000000000000..3a756e871fd60 --- /dev/null +++ b/packages/plugin-ext/src/main/browser/webview/webview-secondary-window-support.ts @@ -0,0 +1,47 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { MaybePromise } from '@theia/core/lib/common'; +import { FrontendApplication, FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { SecondaryWindowHandler } from '@theia/core/lib/browser/secondary-window-handler'; +import { WebviewWidget } from './webview'; + +@injectable() +export class WebviewSecondaryWindowSupport implements FrontendApplicationContribution { + @inject(SecondaryWindowHandler) + protected readonly secondaryWindowHandler: SecondaryWindowHandler; + + onStart(app: FrontendApplication): MaybePromise { + this.secondaryWindowHandler.onDidAddWidget(([widget, win]) => { + if (widget instanceof WebviewWidget) { + const script = win.document.createElement('script'); + script.text = ` + window.addEventListener('message', e => { + // Only process messages from Theia main window + if (e.source === window.opener) { + // Delegate message to iframe + const frame = window.document.getElementsByTagName('iframe').item(0); + if (frame) { + frame.contentWindow?.postMessage({ ...e.data }, '*'); + } + } + }); `; + win.document.head.append(script); + } + }); + } +} diff --git a/yarn.lock b/yarn.lock index 7ffee9d042681..489221288bada 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3900,7 +3900,7 @@ chromium-bidi@0.4.4: dependencies: mitt "3.0.0" -ci-info@^3.2.0, ci-info@^3.6.1: +ci-info@^3.2.0, ci-info@^3.6.1, ci-info@^3.7.0: version "3.9.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== @@ -5784,6 +5784,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + fix-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/fix-path/-/fix-path-3.0.0.tgz#c6b82fd5f5928e520b392a63565ebfef0ddf037e" @@ -5913,7 +5920,7 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.8: +fs-extra@^9.0.0, fs-extra@^9.0.8: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -7069,7 +7076,7 @@ is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -7322,6 +7329,16 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +json-stable-stringify@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== + dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" + jsonify "^0.0.1" + object-keys "^1.1.1" + json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -7370,6 +7387,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== + jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -7423,6 +7445,13 @@ kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + lazystream@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" @@ -8001,7 +8030,7 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -8991,6 +9020,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -9282,6 +9319,27 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +patch-package@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61" + integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^4.1.2" + ci-info "^3.7.0" + cross-spawn "^7.0.3" + find-yarn-workspace-root "^2.0.0" + fs-extra "^9.0.0" + json-stable-stringify "^1.0.2" + klaw-sync "^6.0.0" + minimist "^1.2.6" + open "^7.4.2" + rimraf "^2.6.3" + semver "^7.5.3" + slash "^2.0.0" + tmp "^0.0.33" + yaml "^2.2.2" + path-browserify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -10205,7 +10263,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2, rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@2, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -10634,6 +10692,11 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -10896,14 +10959,6 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -string-replace-loader@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-3.1.0.tgz#11ac6ee76bab80316a86af358ab773193dd57a4f" - integrity sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - "string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -12377,6 +12432,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^2.2.2: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.1.tgz#2e57e0b5e995292c25c75d2658f0664765210eed" + integrity sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" From a828e582715e2b181ebe9ca58c7c2ff189f1290f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 27 Mar 2024 12:15:38 +0100 Subject: [PATCH 150/441] Clone values returned from WorkspaceConfiguration.inspect() (#13527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13087 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/plugin-ext/src/plugin/preference-registry.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/plugin/preference-registry.ts b/packages/plugin-ext/src/plugin/preference-registry.ts index 29863cfb265b6..6d676309d1889 100644 --- a/packages/plugin-ext/src/plugin/preference-registry.ts +++ b/packages/plugin-ext/src/plugin/preference-registry.ts @@ -198,10 +198,10 @@ export class PreferenceRegistryExtImpl implements PreferenceRegistryExt { } const configInspect: ConfigurationInspect = { key }; - configInspect.defaultValue = result.default?.value; - configInspect.globalValue = result.user?.value; - configInspect.workspaceValue = result.workspace?.value; - configInspect.workspaceFolderValue = result.workspaceFolder?.value; + configInspect.defaultValue = cloneDeep(result.default?.value); + configInspect.globalValue = cloneDeep(result.user?.value); + configInspect.workspaceValue = cloneDeep(result.workspace?.value); + configInspect.workspaceFolderValue = cloneDeep(result.workspaceFolder?.value); return configInspect; } }; From 02acd2ed6dff27ea4bd6866142b2c44943851aa3 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 28 Mar 2024 08:38:38 +0100 Subject: [PATCH 151/441] fixed notebook document metadata edit and added execute cells above/below commands (#13528) * notebook workspace edits, document and cell metadata edits Signed-off-by: Jonah Iden * fixed metadata edit and added execute above/below commands Signed-off-by: Jonah Iden * removed notebook debug functionality because its not yet supported Signed-off-by: Jonah Iden * fixed cell and below start index Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 58 +++++++++++++++--- .../notebook/src/browser/notebook-types.ts | 14 ++++- .../src/browser/service/notebook-service.ts | 21 +++++++ .../browser/view-model/notebook-cell-model.ts | 5 ++ .../src/browser/view-model/notebook-model.ts | 59 ++++++++++++++++++- .../view/notebook-cell-toolbar-factory.tsx | 3 +- .../notebook/src/common/notebook-common.ts | 13 ++++ .../plugin-ext/src/common/plugin-api-rpc.ts | 14 +++++ .../src/main/browser/languages-main.ts | 1 - .../src/main/browser/main-context.ts | 4 +- .../notebooks/notebook-documents-main.ts | 6 ++ .../main/browser/notebooks/notebooks-main.ts | 24 ++++++-- .../src/main/browser/text-editors-main.ts | 31 ++++++++-- .../src/plugin/notebook/notebook-document.ts | 3 +- .../src/plugin/notebook/notebooks.ts | 8 ++- 15 files changed, 234 insertions(+), 30 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 1aefe02a12ff3..0481b4bd6f6ba 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -62,6 +62,18 @@ export namespace NotebookCellCommands { export const EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell-and-focus-next', }); + + export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({ + id: 'notebookActions.executeAbove', + label: 'Execute Above Cells', + iconClass: codicon('run-above') + }); + + export const EXECUTE_CELL_AND_BELOW_COMMAND = Command.toDefaultLocalizedCommand({ + id: 'notebookActions.executeBelow', + label: 'Execute Cell and Below', + iconClass: codicon('run-below') + }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const STOP_CELL_EXECUTION_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.stop-cell-execution', @@ -140,20 +152,30 @@ export class NotebookCellActionContribution implements MenuContribution, Command label: nls.localizeByDefault('Stop Editing Cell'), order: '10' }); + menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { - commandId: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, - icon: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.iconClass, + commandId: NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND.id, + icon: NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND.iconClass, when: `${NOTEBOOK_CELL_TYPE} == 'code'`, - label: nls.localizeByDefault('Execute Cell'), + label: nls.localizeByDefault('Execute Above Cells'), order: '10' }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { - commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id, - icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass, - label: nls.localizeByDefault('Split Cell'), + commandId: NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND.id, + icon: NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND.iconClass, + when: `${NOTEBOOK_CELL_TYPE} == 'code'`, + label: nls.localizeByDefault('Execute Cell and Below'), order: '20' }); + + // menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { + // commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id, + // icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass, + // label: nls.localizeByDefault('Split Cell'), + // order: '20' + // }); + menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.DELETE_COMMAND.id, icon: NotebookCellCommands.DELETE_COMMAND.iconClass, @@ -190,9 +212,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command }); // Notebook Cell extra execution options - // menus.registerIndependentSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU, - // nls.localizeByDefault('More...'), - // { role: CompoundMenuNodeRole.Flat, icon: codicon('chevron-down') }); + menus.registerIndependentSubmenu(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU, + nls.localizeByDefault('More...'), + { role: CompoundMenuNodeRole.Flat, icon: codicon('chevron-down') }); // menus.getMenu(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU).addNode(menus.getMenuNode(NotebookCellActionContribution.CONTRIBUTED_CELL_EXECUTION_MENU)); // code cell output sidebar menu @@ -247,6 +269,24 @@ export class NotebookCellActionContribution implements MenuContribution, Command }) ); + commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler( + (notebookModel, cell) => { + const index = notebookModel.cells.indexOf(cell); + if (index > 0) { + this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells.slice(0, index).filter(c => c.cellKind === CellKind.Code)); + } + }) + ); + + commands.registerCommand(NotebookCellCommands.EXECUTE_CELL_AND_BELOW_COMMAND, this.editableCellCommandHandler( + (notebookModel, cell) => { + const index = notebookModel.cells.indexOf(cell); + if (index < notebookModel.cells.length - 1) { + this.notebookExecutionService.executeNotebookCells(notebookModel, notebookModel.cells.slice(index).filter(c => c.cellKind === CellKind.Code)); + } + }) + ); + commands.registerCommand(NotebookCellCommands.STOP_CELL_EXECUTION_COMMAND, { execute: (notebookModel: NotebookModel, cell: NotebookCellModel) => { notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; diff --git a/packages/notebook/src/browser/notebook-types.ts b/packages/notebook/src/browser/notebook-types.ts index a1ac52ae52370..1f82f071755e7 100644 --- a/packages/notebook/src/browser/notebook-types.ts +++ b/packages/notebook/src/browser/notebook-types.ts @@ -17,6 +17,7 @@ import { CellData, CellEditType, CellMetadataEdit, CellOutput, CellOutputItem, CellRange, NotebookCellContentChangeEvent, NotebookCellInternalMetadata, + NotebookCellMetadata, NotebookCellsChangeInternalMetadataEvent, NotebookCellsChangeLanguageEvent, NotebookCellsChangeMetadataEvent, @@ -152,13 +153,24 @@ export interface CellReplaceEdit { cells: CellData[]; } +export interface CellPartialMetadataEdit { + editType: CellEditType.PartialMetadata; + index: number; + metadata: NullablePartialNotebookCellMetadata; +} + export type ImmediateCellEditOperation = CellOutputEditByHandle | CellOutputItemEdit | CellPartialInternalMetadataEditByHandle; // add more later on export type CellEditOperation = ImmediateCellEditOperation | CellReplaceEdit | CellOutputEdit | - CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit; // add more later on + CellMetadataEdit | CellLanguageEdit | DocumentMetadataEdit | CellMoveEdit | CellPartialMetadataEdit; // add more later on export type NullablePartialNotebookCellInternalMetadata = { [Key in keyof Partial]: NotebookCellInternalMetadata[Key] | null }; + +export type NullablePartialNotebookCellMetadata = { + [Key in keyof Partial]: NotebookCellMetadata[Key] | null +}; + export interface CellPartialInternalMetadataEditByHandle { editType: CellEditType.PartialInternalMetadata; handle: number; diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index 583dac957a0f5..ee6cd86fa599a 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -23,6 +23,7 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from '../view-model/notebook-cell-model'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { CellEditOperation } from '../notebook-types'; export const NotebookProvider = Symbol('notebook provider'); @@ -37,6 +38,13 @@ export interface NotebookSerializer { fromNotebook(data: NotebookData): Promise; } +export interface NotebookWorkspaceEdit { + edits: { + resource: URI; + edit: CellEditOperation + }[] +} + @injectable() export class NotebookService implements Disposable { @@ -178,4 +186,17 @@ export class NotebookService implements Disposable { listNotebookDocuments(): NotebookModel[] { return [...this.notebookModels.values()]; } + + applyWorkspaceEdit(workspaceEdit: NotebookWorkspaceEdit): boolean { + try { + workspaceEdit.edits.forEach(edit => { + const notebook = this.getNotebookEditorModel(edit.resource); + notebook?.applyEdits([edit.edit], true); + }); + return true; + } catch (e) { + console.error(e); + return false; + } + } } diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 8020c320c3f53..ead96aea7de09 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -118,6 +118,11 @@ export class NotebookCellModel implements NotebookCell, Disposable { return this._metadata; } + set metadata(newMetadata: NotebookCellMetadata) { + this._metadata = newMetadata; + this.onDidChangeMetadataEmitter.fire(); + } + protected _metadata: NotebookCellMetadata; protected toDispose = new DisposableCollection(); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 803cbc202e98b..41df43d0fab82 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -18,10 +18,15 @@ import { Disposable, Emitter, Event, Resource, URI } from '@theia/core'; import { Saveable, SaveOptions } from '@theia/core/lib/browser'; import { CellData, CellEditType, CellUri, NotebookCellInternalMetadata, + NotebookCellMetadata, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookData, NotebookDocumentMetadata, } from '../../common'; -import { NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, CellEditOperation, NullablePartialNotebookCellInternalMetadata } from '../notebook-types'; +import { + NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, + CellEditOperation, NullablePartialNotebookCellInternalMetadata, + NullablePartialNotebookCellMetadata +} from '../notebook-types'; import { NotebookSerializer } from '../service/notebook-service'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model'; @@ -285,7 +290,10 @@ export class NotebookModel implements Saveable, Disposable { cell.changeOutputItems(edit.outputId, !!edit.append, edit.items); break; case CellEditType.Metadata: - this.updateNotebookMetadata(edit.metadata, computeUndoRedo); + this.changeCellMetadata(this.cells[cellIndex], edit.metadata, computeUndoRedo); + break; + case CellEditType.PartialMetadata: + this.changeCellMetadataPartial(this.cells[cellIndex], edit.metadata, computeUndoRedo); break; case CellEditType.PartialInternalMetadata: this.changeCellInternalMetadataPartial(this.cells[cellIndex], edit.internalMetadata); @@ -294,6 +302,7 @@ export class NotebookModel implements Saveable, Disposable { this.changeCellLanguage(this.cells[cellIndex], edit.language, computeUndoRedo); break; case CellEditType.DocumentMetadata: + this.updateNotebookMetadata(edit.metadata, computeUndoRedo); break; case CellEditType.Move: this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo); @@ -379,6 +388,38 @@ export class NotebookModel implements Saveable, Disposable { this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]); } + protected changeCellMetadataPartial(cell: NotebookCellModel, metadata: NullablePartialNotebookCellMetadata, computeUndoRedo: boolean): void { + const newMetadata: NotebookCellMetadata = { + ...cell.metadata + }; + let k: keyof NullablePartialNotebookCellMetadata; + // eslint-disable-next-line guard-for-in + for (k in metadata) { + const value = metadata[k] ?? undefined; + newMetadata[k] = value as unknown; + } + + this.changeCellMetadata(cell, newMetadata, computeUndoRedo); + } + + protected changeCellMetadata(cell: NotebookCellModel, metadata: NotebookCellMetadata, computeUndoRedo: boolean): void { + const triggerDirtyChange = this.isCellMetadataChanged(cell.metadata, metadata); + + if (triggerDirtyChange) { + if (computeUndoRedo) { + const oldMetadata = cell.metadata; + cell.metadata = metadata; + this.undoRedoService.pushElement(this.uri, + async () => { cell.metadata = oldMetadata; }, + async () => { cell.metadata = metadata; } + ); + } + } + + cell.metadata = metadata; + this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata }]); + } + protected changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void { if (cell.language === languageId) { return; @@ -407,4 +448,18 @@ export class NotebookModel implements Saveable, Disposable { protected getCellIndexByHandle(handle: number): number { return this.cells.findIndex(c => c.handle === handle); } + + protected isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata): boolean { + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + for (const key of keys) { + if ( + (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata]) + ) { + return true; + } + } + + return false; + } + } diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx index f6798745bcd65..b57fb6bf326cc 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx @@ -89,7 +89,8 @@ export class NotebookCellToolbarFactory { anchor: e.nativeEvent, menuPath, includeAnchorArg: false, - args: [notebookModel, cell, output] + args: [cell], + context: this.notebookContextManager.context }) : () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output), isVisible: () => menuPath ? true : Boolean(this.commandRegistry.getVisibleHandler(menuNode.command!, notebookModel, cell, output)), diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index af1b1b4a2905a..c7db67d8f9b67 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -179,6 +179,19 @@ export namespace NotebookModelResource { } } +export interface NotebookCellModelResource { + notebookCellModelUri: URI; +} + +export namespace NotebookCellModelResource { + export function is(item: unknown): item is NotebookCellModelResource { + return isObject(item) && item.notebookCellModelUri instanceof URI; + } + export function create(uri: URI): NotebookCellModelResource { + return { notebookCellModelUri: uri }; + } +} + export enum NotebookCellExecutionState { Unconfirmed = 1, Pending = 2, diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index bf40c9d1e7c3f..33d84951c1cdc 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1563,6 +1563,16 @@ export interface WorkspaceNotebookCellEditDto { cellEdit: CellEditOperationDto; } +export namespace WorkspaceNotebookCellEditDto { + export function is(arg: WorkspaceNotebookCellEditDto | WorkspaceFileEditDto | WorkspaceTextEditDto): arg is WorkspaceNotebookCellEditDto { + return !!arg + && 'resource' in arg + && 'cellEdit' in arg + && arg.cellEdit !== null + && typeof arg.cellEdit === 'object'; + } +} + export interface WorkspaceEditDto { edits: Array; } @@ -2464,6 +2474,10 @@ export type NotebookRawContentEventDto = readonly outputItems: NotebookOutputItemDto[]; readonly append: boolean; } + | { + readonly kind: notebookCommon.NotebookCellsChangeType.ChangeDocumentMetadata + readonly metadata: notebookCommon.NotebookDocumentMetadata; + } | notebookCommon.NotebookCellsChangeLanguageEvent | notebookCommon.NotebookCellsChangeMetadataEvent | notebookCommon.NotebookCellsChangeInternalMetadataEvent diff --git a/packages/plugin-ext/src/main/browser/languages-main.ts b/packages/plugin-ext/src/main/browser/languages-main.ts index 99347fd5356d5..4051d8cc9f91e 100644 --- a/packages/plugin-ext/src/main/browser/languages-main.ts +++ b/packages/plugin-ext/src/main/browser/languages-main.ts @@ -1434,7 +1434,6 @@ export function toMonacoWorkspaceEdit(data: WorkspaceEditDto | undefined): monac metadata: fileEdit.metadata }; } - // TODO implement WorkspaceNotebookCellEditDto }) }; } diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index 95cef28663854..ba820d4f335d9 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -44,7 +44,6 @@ import { EditorManager } from '@theia/editor/lib/browser'; import { EditorModelService } from './text-editor-model-service'; import { OpenerService } from '@theia/core/lib/browser/opener-service'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; -import { MonacoBulkEditService } from '@theia/monaco/lib/browser/monaco-bulk-edit-service'; import { MainFileSystemEventService } from './main-file-system-event-service'; import { LabelServiceMainImpl } from './label-service-main'; import { TimelineMainImpl } from './timeline-main'; @@ -109,8 +108,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, notebookDocumentsMain, notebookEditorsMain)); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, new NotebookKernelsMainImpl(rpc, container)); - const bulkEditService = container.get(MonacoBulkEditService); - const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, bulkEditService); + const editorsMain = new TextEditorsMainImpl(editorsAndDocuments, documentsMain, rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.TEXT_EDITORS_MAIN, editorsMain); // start listening only after all clients are subscribed to events diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index 04fe85dbcaacc..e7361bf569ed5 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -102,6 +102,12 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { case NotebookCellsChangeType.ChangeCellInternalMetadata: eventDto.rawEvents.push(e); break; + case NotebookCellsChangeType.ChangeDocumentMetadata: + eventDto.rawEvents.push({ + kind: e.kind, + metadata: e.metadata + }); + break; } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts index 7263af2d788a6..87d1027ebf6d5 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts @@ -14,17 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { CancellationToken, DisposableCollection, Emitter, Event } from '@theia/core'; +import { CancellationToken, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { NotebookCellStatusBarItem, NotebookData, TransientOptions } from '@theia/notebook/lib/common'; -import { NotebookService } from '@theia/notebook/lib/browser'; +import { CellEditType, NotebookCellModelResource, NotebookCellStatusBarItem, NotebookData, NotebookModelResource, TransientOptions } from '@theia/notebook/lib/common'; +import { NotebookService, NotebookWorkspaceEdit } from '@theia/notebook/lib/browser'; import { Disposable } from '@theia/plugin'; -import { CommandRegistryMain, MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain } from '../../../common'; +import { CommandRegistryMain, MAIN_RPC_CONTEXT, NotebooksExt, NotebooksMain, WorkspaceEditDto, WorkspaceNotebookCellEditDto } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; import { UriComponents } from '@theia/core/lib/common/uri'; import { HostedPluginSupport } from '../../../hosted/browser/hosted-plugin'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; +import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import { interfaces } from '@theia/core/shared/inversify'; export interface NotebookCellStatusBarItemList { @@ -62,7 +63,9 @@ export class NotebooksMainImpl implements NotebooksMain { commands.registerArgumentProcessor({ processArgument: arg => { if (arg instanceof NotebookModel) { - return arg.uri; + return NotebookModelResource.create(arg.uri); + } else if (arg instanceof NotebookCellModel) { + return NotebookCellModelResource.create(arg.uri); } return arg; } @@ -148,3 +151,14 @@ export class NotebooksMainImpl implements NotebooksMain { } } +export function toNotebookWorspaceEdit(dto: WorkspaceEditDto): NotebookWorkspaceEdit { + return { + edits: dto.edits.map((edit: WorkspaceNotebookCellEditDto) => ({ + resource: URI.fromComponents(edit.resource), + edit: edit.cellEdit.editType === CellEditType.Replace ? { + ...edit.cellEdit, + cells: edit.cellEdit.cells.map(cell => NotebookDto.fromNotebookCellDataDto(cell)) + } : edit.cellEdit + })) + }; +} diff --git a/packages/plugin-ext/src/main/browser/text-editors-main.ts b/packages/plugin-ext/src/main/browser/text-editors-main.ts index 3786b211e26db..93c2d820c54ef 100644 --- a/packages/plugin-ext/src/main/browser/text-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/text-editors-main.ts @@ -28,6 +28,7 @@ import { ThemeDecorationInstanceRenderOptions, DecorationOptions, WorkspaceEditDto, + WorkspaceNotebookCellEditDto, DocumentsMain, WorkspaceEditMetadataDto, } from '../../common/plugin-api-rpc'; @@ -46,7 +47,10 @@ import { ResourceEdit } from '@theia/monaco-editor-core/esm/vs/editor/browser/se import { IDecorationRenderOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; -import { URI } from '@theia/core'; +import { ArrayUtils, URI } from '@theia/core'; +import { toNotebookWorspaceEdit } from './notebooks/notebooks-main'; +import { interfaces } from '@theia/core/shared/inversify'; +import { NotebookService } from '@theia/notebook/lib/browser'; export class TextEditorsMainImpl implements TextEditorsMain, Disposable { @@ -55,13 +59,20 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { private readonly editorsToDispose = new Map(); private readonly fileEndpoint = new Endpoint({ path: 'file' }).getRestUrl(); + private readonly bulkEditService: MonacoBulkEditService; + private readonly notebookService: NotebookService; + constructor( private readonly editorsAndDocuments: EditorsAndDocumentsMain, private readonly documents: DocumentsMain, rpc: RPCProtocol, - private readonly bulkEditService: MonacoBulkEditService, + container: interfaces.Container ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TEXT_EDITORS_EXT); + + this.bulkEditService = container.get(MonacoBulkEditService); + this.notebookService = container.get(NotebookService); + this.toDispose.push(editorsAndDocuments); this.toDispose.push(editorsAndDocuments.onTextEditorAdd(editors => editors.forEach(this.onTextEditorAdd, this))); this.toDispose.push(editorsAndDocuments.onTextEditorRemove(editors => editors.forEach(this.onTextEditorRemove, this))); @@ -128,11 +139,19 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable { } async $tryApplyWorkspaceEdit(dto: WorkspaceEditDto, metadata?: WorkspaceEditMetadataDto): Promise { - const workspaceEdit = toMonacoWorkspaceEdit(dto); + const [notebookEdits, monacoEdits] = ArrayUtils.partition(dto.edits, edit => WorkspaceNotebookCellEditDto.is(edit)); try { - const edits = ResourceEdit.convert(workspaceEdit); - const { isApplied } = await this.bulkEditService.apply(edits, { respectAutoSaveConfig: metadata?.isRefactoring }); - return isApplied; + if (notebookEdits.length > 0) { + const workspaceEdit = toNotebookWorspaceEdit({ edits: notebookEdits }); + return this.notebookService.applyWorkspaceEdit(workspaceEdit); + } + if (monacoEdits.length > 0) { + const workspaceEdit = toMonacoWorkspaceEdit({ edits: monacoEdits }); + const edits = ResourceEdit.convert(workspaceEdit); + const { isApplied } = await this.bulkEditService.apply(edits, { respectAutoSaveConfig: metadata?.isRefactoring }); + return isApplied; + } + return false; } catch { return false; } diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts index 66b63479be6fb..645d8bdbe1dac 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts @@ -287,7 +287,8 @@ export class NotebookDocument implements Disposable { } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.Output) { this.setCellOutputs(rawEvent.index, rawEvent.outputs); relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, outputs: this.cells[rawEvent.index].apiCell.outputs }); - + } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.ChangeDocumentMetadata) { + this.metadata = result.metadata ?? {}; // } else if (rawEvent.kind === notebookCommon.NotebookCellsChangeType.OutputItem) { // this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems); // relaxedCellChanges.push({ cell: this.cells[rawEvent.index].apiCell, outputs: this.cells[rawEvent.index].apiCell.outputs }); diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 01034fd711fe8..d76dec7ba71d8 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -36,7 +36,7 @@ import { NotebookDocument } from './notebook-document'; import { NotebookEditor } from './notebook-editor'; import { EditorsAndDocumentsExtImpl } from '../editors-and-documents'; import { DocumentsExtImpl } from '../documents'; -import { NotebookModelResource } from '@theia/notebook/lib/common'; +import { CellUri, NotebookCellModelResource, NotebookModelResource } from '@theia/notebook/lib/common'; export class NotebooksExtImpl implements NotebooksExt { @@ -86,6 +86,12 @@ export class NotebooksExtImpl implements NotebooksExt { processArgument: arg => { if (NotebookModelResource.is(arg)) { return this.documents.get(arg.notebookModelUri.toString())?.apiNotebook; + } else if (NotebookCellModelResource.is(arg)) { + const cellUri = CellUri.parse(arg.notebookCellModelUri); + if (cellUri) { + return this.documents.get(cellUri?.notebook.toString())?.getCell(cellUri.handle)?.apiCell; + } + return undefined; } else { return arg; } From 6d45de3238b5983b354e9a3f3df160b75e280cc7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:13:15 +0100 Subject: [PATCH 152/441] Translation update for version 1.48.0 (#13540) Co-authored-by: jfaltermeier --- packages/core/i18n/nls.cs.json | 4 ++++ packages/core/i18n/nls.de.json | 4 ++++ packages/core/i18n/nls.es.json | 4 ++++ packages/core/i18n/nls.fr.json | 4 ++++ packages/core/i18n/nls.hu.json | 4 ++++ packages/core/i18n/nls.it.json | 4 ++++ packages/core/i18n/nls.ja.json | 4 ++++ packages/core/i18n/nls.json | 4 ++++ packages/core/i18n/nls.pl.json | 4 ++++ packages/core/i18n/nls.pt-br.json | 4 ++++ packages/core/i18n/nls.pt-pt.json | 4 ++++ packages/core/i18n/nls.ru.json | 4 ++++ packages/core/i18n/nls.zh-cn.json | 4 ++++ 13 files changed, 52 insertions(+) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 41599892ed569..992275d56b70a 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Upravit stav...", + "notebook.cell.insertMarkdownCellAbove": "Vložení buňky Markdown nad", + "notebook.cell.insertMarkdownCellBelow": "Vložení buňky Markdown níže", + "notebook.cell.to-code-cell": "Změna buňky na kód", + "notebook.cell.to-markdown-cell": "Změna buňky na Mardown", "terminal:new:profile": "Vytvoření nového integrovaného terminálu z profilu", "terminal:profile:default": "Zvolte výchozí profil terminálu", "theia": { diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index 29f9a5e223706..da230756de3f8 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Bedingung...", + "notebook.cell.insertMarkdownCellAbove": "Markdown-Zelle oben einfügen", + "notebook.cell.insertMarkdownCellBelow": "Markdown-Zelle unten einfügen", + "notebook.cell.to-code-cell": "Zelle in Code ändern", + "notebook.cell.to-markdown-cell": "Zelle in Mardown ändern", "terminal:new:profile": "Neues integriertes Terminal aus einem Profil erstellen", "terminal:profile:default": "Wählen Sie das Standard-Terminalprofil", "theia": { diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index caf454bfaf59b..26ef471361279 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condición...", + "notebook.cell.insertMarkdownCellAbove": "Insertar celda Markdown arriba", + "notebook.cell.insertMarkdownCellBelow": "Insertar celda Markdown abajo", + "notebook.cell.to-code-cell": "Cambiar celda por código", + "notebook.cell.to-markdown-cell": "Cambiar Celda a Mardown", "terminal:new:profile": "Crear un nuevo terminal integrado a partir de un perfil", "terminal:profile:default": "Elija el perfil de terminal por defecto", "theia": { diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index fa6c41d96e358..29e091d0c35e7 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Condition...", + "notebook.cell.insertMarkdownCellAbove": "Insérer une cellule Markdown au-dessus", + "notebook.cell.insertMarkdownCellBelow": "Insérer une cellule Markdown en dessous", + "notebook.cell.to-code-cell": "Changer la cellule en code", + "notebook.cell.to-markdown-cell": "Changer la cellule en Mardown", "terminal:new:profile": "Créer un nouveau terminal intégré à partir d'un profil", "terminal:profile:default": "Choisissez le profil du terminal par défaut", "theia": { diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index 8abac541f7298..41c447d17dcc4 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Szerkesztési feltétel...", + "notebook.cell.insertMarkdownCellAbove": "Markdown-cella beszúrása fent", + "notebook.cell.insertMarkdownCellBelow": "Markdown-cella beszúrása az alábbiakban", + "notebook.cell.to-code-cell": "Cellát kódra váltani", + "notebook.cell.to-markdown-cell": "Cellát átváltoztatni Mardown-ra", "terminal:new:profile": "Új integrált terminál létrehozása profilból", "terminal:profile:default": "Válassza ki az alapértelmezett terminálprofilt", "theia": { diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 73277214b9be2..0bc23922e3044 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Modifica della condizione...", + "notebook.cell.insertMarkdownCellAbove": "Inserire la cella Markdown sopra", + "notebook.cell.insertMarkdownCellBelow": "Inserire la cella Markdown in basso", + "notebook.cell.to-code-cell": "Cambia cella in codice", + "notebook.cell.to-markdown-cell": "Cambiare la cella in Mardown", "terminal:new:profile": "Creare un nuovo terminale integrato da un profilo", "terminal:profile:default": "Scegliere il profilo del terminale predefinito", "theia": { diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 9a354d931577d..e194152794a6b 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "編集条件...", + "notebook.cell.insertMarkdownCellAbove": "マークダウン・セルを上に挿入する", + "notebook.cell.insertMarkdownCellBelow": "下にマークダウン・セルを挿入する", + "notebook.cell.to-code-cell": "セルをコードに変更", + "notebook.cell.to-markdown-cell": "セルをマーダウンに変更", "terminal:new:profile": "プロファイルから新しい統合端末を作成する", "terminal:profile:default": "デフォルトの端末プロファイルを選択", "theia": { diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index d41273de44661..0552f9f7b2b01 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Condition...", + "notebook.cell.insertMarkdownCellAbove": "Insert Markdown Cell Above", + "notebook.cell.insertMarkdownCellBelow": "Insert Markdown Cell Below", + "notebook.cell.to-code-cell": "Change Cell to Code", + "notebook.cell.to-markdown-cell": "Change Cell to Mardown", "terminal:new:profile": "Create New Integrated Terminal from a Profile", "terminal:profile:default": "Choose the default Terminal Profile", "theia": { diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 02364f173186b..e8e79a9c7c28c 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Warunek edycji...", + "notebook.cell.insertMarkdownCellAbove": "Wstaw komórkę Markdown powyżej", + "notebook.cell.insertMarkdownCellBelow": "Wstaw komórkę Markdown poniżej", + "notebook.cell.to-code-cell": "Zmień komórkę na kod", + "notebook.cell.to-markdown-cell": "Zmień komórkę na Mardown", "terminal:new:profile": "Tworzenie nowego zintegrowanego terminalu z profilu", "terminal:profile:default": "Wybierz domyślny profil terminala", "theia": { diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index 68f4a1145c3c9..d5f3a783a4b27 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condição...", + "notebook.cell.insertMarkdownCellAbove": "Inserir célula Markdown acima", + "notebook.cell.insertMarkdownCellBelow": "Inserir célula Markdown abaixo", + "notebook.cell.to-code-cell": "Alterar célula para código", + "notebook.cell.to-markdown-cell": "Mudança de célula para Mardown", "terminal:new:profile": "Criar um novo terminal integrado a partir de um perfil", "terminal:profile:default": "Escolha o Perfil do Terminal padrão", "theia": { diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 7fd649020c687..1ef5bb8b4e7c5 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condição...", + "notebook.cell.insertMarkdownCellAbove": "Inserir célula Markdown acima", + "notebook.cell.insertMarkdownCellBelow": "Inserir célula Markdown abaixo", + "notebook.cell.to-code-cell": "Alterar célula para código", + "notebook.cell.to-markdown-cell": "Alterar Célula para Mardown", "terminal:new:profile": "Criar Novo Terminal Integrado a partir de um Perfil", "terminal:profile:default": "Escolha o Perfil Terminal padrão", "theia": { diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index d6acaa3d110bd..d12889b106ac0 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "Изменить состояние...", + "notebook.cell.insertMarkdownCellAbove": "Вставить ячейку для уценки выше", + "notebook.cell.insertMarkdownCellBelow": "Вставить ячейку для уценки ниже", + "notebook.cell.to-code-cell": "Измените ячейку на код", + "notebook.cell.to-markdown-cell": "Измените ячейку на Мардаун", "terminal:new:profile": "Создание нового интегрированного терминала из профиля", "terminal:profile:default": "Выберите профиль терминала по умолчанию", "theia": { diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 8acf13b6a9869..15124a791a7ff 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -1,5 +1,9 @@ { "debug.breakpoint.editCondition": "编辑条件...", + "notebook.cell.insertMarkdownCellAbove": "在上方插入 Markdown 单元格", + "notebook.cell.insertMarkdownCellBelow": "在下面插入 Markdown 单元格", + "notebook.cell.to-code-cell": "将单元格更改为代码", + "notebook.cell.to-markdown-cell": "将 Cell 改为 Mardown", "terminal:new:profile": "从配置文件中创建新的集成终端", "terminal:profile:default": "选择默认的终端配置文件", "theia": { From f41a7641500acd6bb9f8a4501e2444904b02004f Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 28 Mar 2024 10:48:32 +0100 Subject: [PATCH 153/441] Refer to other repositories in Readme (#13534) fixed #13533 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d656aeab01a14..33d246b3b561a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Eclipse Theia is an extensible framework to develop full-fledged multi-language
    - [Website](#website) +- [Repositories](#repositories) - [Releases](#releases) - [Scope](#scope) - [Roadmap](#roadmap) @@ -39,6 +40,9 @@ Eclipse Theia is an extensible framework to develop full-fledged multi-language [Visit the Eclipse Theia website](http://www.theia-ide.org) for more information and [the Theia documentation](http://www.theia-ide.org/doc). +## Repositories +This is the main repository for the Eclipse Theia project, containing the sources of the Theia Platform. Please open generic discussions, bug reports and feature requests about Theia on this repository. The Theia project also includes additional repositories, e.g. for the [artifacts building the Theia IDE](https://github.com/eclipse-theia/theia-blueprint) and the [Theia website](https://github.com/eclipse-theia/theia-website). Please also see the [overview of all Theia project repositories](https://github.com/eclipse-theia). + ## Releases - [All available releases](https://github.com/eclipse-theia/theia/releases) are available on GitHub including changelogs. From 2c61f6cf137c1a5fda2284b629f534842750bc91 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 28 Mar 2024 11:01:34 +0100 Subject: [PATCH 154/441] notebook open optimizations (#13488) * only initialize notebbok cell editor when in viewport Signed-off-by: Jonah Iden * optimzied notebook loading time by bulk creating notebook cell editor text models Signed-off-by: Jonah Iden * remove measurement Signed-off-by: Jonah Iden * add document for cell when creating new one Signed-off-by: Jonah Iden * review changes * rebase build fixe Signed-off-by: Jonah Iden * create unmaged models for monacto text model service Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../src/browser/monaco-text-model-service.ts | 10 +++- .../src/browser/notebook-frontend-module.ts | 5 +- .../notebook-monaco-text-model-service.ts | 48 +++++++++++++++++++ .../src/browser/service/notebook-service.ts | 10 ++-- .../browser/view-model/notebook-cell-model.ts | 9 ++-- .../src/browser/view/notebook-cell-editor.tsx | 2 +- .../src/main/browser/documents-main.ts | 4 ++ .../src/main/browser/main-context.ts | 8 ++-- .../notebooks/notebook-documents-main.ts | 19 ++++++-- .../src/plugin/notebook/notebook-document.ts | 12 +++++ .../src/plugin/notebook/notebooks.ts | 13 ++++- 11 files changed, 119 insertions(+), 21 deletions(-) create mode 100644 packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index bd1ecf07c5a97..318de5575ba9e 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -109,7 +109,15 @@ export class MonacoTextModelService implements ITextModelService { return this._models.acquire(raw.toString()); } - protected async loadModel(uri: URI): Promise { + /** + * creates a model which is not saved by the model service. + * this will therefore also not be created on backend side. + */ + createUnmangedModel(raw: monaco.Uri | URI): Promise { + return this.loadModel(new URI(raw.toString())); + } + + async loadModel(uri: URI): Promise { await this.editorPreferences.ready; const resource = await this.resourceProvider(uri); const model = await (await this.createModel(resource)).load(); diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index cb6814b219bd6..f74e129578d67 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -40,8 +40,9 @@ import { NotebookKernelHistoryService } from './service/notebook-kernel-history- import { NotebookEditorWidgetService } from './service/notebook-editor-widget-service'; import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service'; import { NotebookColorContribution } from './contributions/notebook-color-contribution'; +import { NotebookMonacoTextModelService } from './service/notebook-monaco-text-model-service'; -export default new ContainerModule(bind => { +export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); bind(ColorContribution).toService(NotebookColorContribution); @@ -86,4 +87,6 @@ export default new ContainerModule(bind => { bind(NotebookCellModelFactory).toFactory(ctx => (props: NotebookCellModelProps) => createNotebookCellModelContainer(ctx.container, props).get(NotebookCellModel) ); + + bind(NotebookMonacoTextModelService).toSelf().inSingletonScope(); }); diff --git a/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts b/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts new file mode 100644 index 0000000000000..6b0c5e8576206 --- /dev/null +++ b/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ReferenceCollection, URI, Reference, Event } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; +import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; +import { NotebookModel } from '../view-model/notebook-model'; + +/** + * special service for creating monaco textmodels for notebook cells. + * Its for optimization purposes since there is alot of overhead otherwise with calling the backend to create a document for each cell and other smaller things. + */ +@injectable() +export class NotebookMonacoTextModelService { + + @inject(MonacoTextModelService) + protected readonly monacoTextModelService: MonacoTextModelService; + + protected readonly cellmodels = new ReferenceCollection( + uri => this.monacoTextModelService.createUnmangedModel(new URI(uri)) + ); + + getOrCreateNotebookCellModelReference(uri: URI): Promise> { + return this.cellmodels.acquire(uri.toString()); + } + + async createTextModelsForNotebook(notebook: NotebookModel): Promise { + await Promise.all(notebook.cells.map(cell => this.getOrCreateNotebookCellModelReference(cell.uri))); + } + + get onDidCreateNotebookCellModel(): Event { + return this.cellmodels.onDidCreate; + } +} diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index ee6cd86fa599a..75f5a64791030 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -20,9 +20,9 @@ import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { NotebookData, TransientOptions } from '../../common'; import { NotebookModel, NotebookModelFactory, NotebookModelProps } from '../view-model/notebook-model'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from '../view-model/notebook-cell-model'; import { Deferred } from '@theia/core/lib/common/promise-util'; +import { NotebookMonacoTextModelService } from './notebook-monaco-text-model-service'; import { CellEditOperation } from '../notebook-types'; export const NotebookProvider = Symbol('notebook provider'); @@ -51,15 +51,15 @@ export class NotebookService implements Disposable { @inject(FileService) protected fileService: FileService; - @inject(MonacoTextModelService) - protected modelService: MonacoTextModelService; - @inject(NotebookModelFactory) protected notebookModelFactory: (props: NotebookModelProps) => NotebookModel; @inject(NotebookCellModelFactory) protected notebookCellModelFactory: (props: NotebookCellModelProps) => NotebookCellModel; + @inject(NotebookMonacoTextModelService) + protected textModelService: NotebookMonacoTextModelService; + protected willUseNotebookSerializerEmitter = new Emitter(); readonly onWillUseNotebookSerializer = this.willUseNotebookSerializerEmitter.event; @@ -119,7 +119,7 @@ export class NotebookService implements Disposable { this.notebookModels.set(resource.uri.toString(), model); // Resolve cell text models right after creating the notebook model // This ensures that all text models are available in the plugin host - await Promise.all(model.cells.map(e => e.resolveTextModel())); + this.textModelService.createTextModelsForNotebook(model); this.didAddNotebookDocumentEmitter.fire(model); return model; } diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index ead96aea7de09..1f20849de19ff 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -21,12 +21,12 @@ import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; -import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; import { CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata, NotebookCellMetadata, CellOutput, CellData, CellOutputItem } from '../../common'; import { NotebookCellOutputsSplice } from '../notebook-types'; +import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service'; import { NotebookCellOutputModel } from './notebook-cell-output-model'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); @@ -105,8 +105,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; - @inject(MonacoTextModelService) - protected readonly textModelService: MonacoTextModelService; + + @inject(NotebookMonacoTextModelService) + protected readonly textModelService: NotebookMonacoTextModelService; get outputs(): NotebookCellOutputModel[] { return this._outputs; @@ -290,7 +291,7 @@ export class NotebookCellModel implements NotebookCell, Disposable { return this.textModel; } - const ref = await this.textModelService.createModelReference(this.uri); + const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri); this.textModel = ref.object; this.textModel.onDidChangeContent(e => { this.props.source = e.model.getText(); diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 0e5fb95c13b31..d4b951678a1eb 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -135,7 +135,7 @@ export class CellEditor extends React.Component { override render(): React.ReactNode { return
    this.setContainer(container)} style={{ height: this.editor ? undefined : this.estimateHeight() }}> -
    ; +
    ; } } diff --git a/packages/plugin-ext/src/main/browser/documents-main.ts b/packages/plugin-ext/src/main/browser/documents-main.ts index 798738759d998..deed64a60d2d5 100644 --- a/packages/plugin-ext/src/main/browser/documents-main.ts +++ b/packages/plugin-ext/src/main/browser/documents-main.ts @@ -32,6 +32,7 @@ import { dispose } from '../../common/disposable-util'; import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages'; import * as monaco from '@theia/monaco-editor-core'; import { TextDocumentChangeReason } from '../../plugin/types-impl'; +import { NotebookDocumentsMainImpl } from './notebooks/notebook-documents-main'; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. @@ -90,6 +91,7 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { constructor( editorsAndDocuments: EditorsAndDocumentsMain, + notebookDocuments: NotebookDocumentsMainImpl, private readonly modelService: EditorModelService, rpc: RPCProtocol, private editorManager: EditorManager, @@ -105,6 +107,8 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { this.toDispose.push(editorsAndDocuments.onDocumentRemove(documents => documents.forEach(this.onModelRemoved, this))); this.toDispose.push(modelService.onModelModeChanged(this.onModelChanged, this)); + this.toDispose.push(notebookDocuments.onDidAddNotebookCellModel(this.onModelAdded, this)); + this.toDispose.push(modelService.onModelSaved(m => { this.proxy.$acceptModelSaved(m.textEditorModel.uri); })); diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index ba820d4f335d9..e0bdf7d826df5 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -90,21 +90,23 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container const editorsAndDocuments = new EditorsAndDocumentsMain(rpc, container); + const notebookDocumentsMain = new NotebookDocumentsMainImpl(rpc, container); + rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain); + const modelService = container.get(EditorModelService); const editorManager = container.get(EditorManager); const openerService = container.get(OpenerService); const shell = container.get(ApplicationShell); const untitledResourceResolver = container.get(UntitledResourceResolver); const languageService = container.get(MonacoLanguages); - const documentsMain = new DocumentsMainImpl(editorsAndDocuments, modelService, rpc, editorManager, openerService, shell, untitledResourceResolver, languageService); + const documentsMain = new DocumentsMainImpl(editorsAndDocuments, notebookDocumentsMain, modelService, rpc, + editorManager, openerService, shell, untitledResourceResolver, languageService); rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, new NotebooksMainImpl(rpc, container, commandRegistryMain)); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_RENDERERS_MAIN, new NotebookRenderersMainImpl(rpc, container)); const notebookEditorsMain = new NotebookEditorsMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN, notebookEditorsMain); - const notebookDocumentsMain = new NotebookDocumentsMainImpl(rpc, container); - rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_AND_EDITORS_MAIN, new NotebooksAndEditorsMain(rpc, container, notebookDocumentsMain, notebookEditorsMain)); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_KERNELS_MAIN, new NotebookKernelsMainImpl(rpc, container)); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index e7361bf569ed5..761c63bf1c738 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -14,24 +14,28 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { DisposableCollection } from '@theia/core'; +import { DisposableCollection, Event } from '@theia/core'; import { URI, UriComponents } from '@theia/core/lib/common/uri'; import { interfaces } from '@theia/core/shared/inversify'; import { NotebookModelResolverService } from '@theia/notebook/lib/browser'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { NotebookCellsChangeType } from '@theia/notebook/lib/common'; +import { NotebookMonacoTextModelService } from '@theia/notebook/lib/browser/service/notebook-monaco-text-model-service'; import { MAIN_RPC_CONTEXT, NotebookCellsChangedEventDto, NotebookDataDto, NotebookDocumentsExt, NotebookDocumentsMain } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; +import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { - private readonly disposables = new DisposableCollection(); + protected readonly disposables = new DisposableCollection(); - private readonly proxy: NotebookDocumentsExt; - private readonly documentEventListenersMapping = new Map(); + protected readonly proxy: NotebookDocumentsExt; + protected readonly documentEventListenersMapping = new Map(); - private readonly notebookModelResolverService: NotebookModelResolverService; + protected readonly notebookModelResolverService: NotebookModelResolverService; + + protected notebookMonacoTextModelService: NotebookMonacoTextModelService; constructor( rpc: RPCProtocol, @@ -44,6 +48,11 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { this.disposables.push(this.notebookModelResolverService.onDidChangeDirty(model => this.proxy.$acceptDirtyStateChanged(model.uri.toComponents(), model.isDirty()))); this.disposables.push(this.notebookModelResolverService.onDidSaveNotebook(e => this.proxy.$acceptModelSaved(e))); + this.notebookMonacoTextModelService = container.get(NotebookMonacoTextModelService) as NotebookMonacoTextModelService; + } + + get onDidAddNotebookCellModel(): Event { + return this.notebookMonacoTextModelService.onDidCreateNotebookCellModel; } dispose(): void { diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts index 645d8bdbe1dac..9ca4db6f35ee1 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts @@ -358,6 +358,18 @@ export class NotebookDocument implements Disposable { const extCell = new Cell(this, this.editorsAndDocuments, cell); if (!initialization) { addedCellDocuments.push(Cell.asModelAddData(this.apiNotebook, cell)); + this.editorsAndDocuments.$acceptEditorsAndDocumentsDelta({ + addedDocuments: [ + { + uri: cell.uri, + versionId: 1, + lines: cell.source, + EOL: cell.eol, + modeId: '', + isDirty: false + } + ] + }); } return extCell; }); diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index d76dec7ba71d8..d21a99588636a 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -76,7 +76,7 @@ export class NotebooksExtImpl implements NotebooksExt { rpc: RPCProtocol, commands: CommandRegistryExt, private textDocumentsAndEditors: EditorsAndDocumentsExtImpl, - private textDocuments: DocumentsExtImpl + private textDocuments: DocumentsExtImpl, ) { this.notebookProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN); this.notebookDocumentsProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN); @@ -242,6 +242,17 @@ export class NotebooksExtImpl implements NotebooksExt { this.documents.get(uri.toString())?.dispose(); this.documents.set(uri.toString(), document); + this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ + addedDocuments: modelData.cells.map(cell => ({ + uri: cell.uri, + versionId: 1, + lines: cell.source, + EOL: cell.eol, + modeId: '', + isDirty: false + })) + }); + this.onDidOpenNotebookDocumentEmitter.fire(document.apiNotebook); } } From ffae6a0739f7924a7cacafeb5ceb1140d93fe010 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 28 Mar 2024 19:29:30 +0900 Subject: [PATCH 155/441] Make `acquireVsCodeApi` available on global objects (#13411) --- packages/plugin-ext/src/main/browser/webview/pre/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/webview/pre/main.js b/packages/plugin-ext/src/main/browser/webview/pre/main.js index 004f20920b4fe..284e1c461bd44 100644 --- a/packages/plugin-ext/src/main/browser/webview/pre/main.js +++ b/packages/plugin-ext/src/main/browser/webview/pre/main.js @@ -146,7 +146,7 @@ */ function getDefaultScript(state) { return ` -const acquireVsCodeApi = (function() { +globalThis.acquireVsCodeApi = (function() { const originalPostMessage = window.parent.postMessage.bind(window.parent); const originalConsole = {...console}; const targetOrigin = '*'; @@ -198,7 +198,7 @@ const acquireVsCodeApi = (function() { }); }; })(); -const acquireTheiaApi = acquireVsCodeApi; +globalThis.acquireTheiaApi = acquireVsCodeApi; delete window.parent; delete window.top; delete window.frameElement; From b212f9d9e1b73a7d6233693fd126fbe539cfa15c Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 28 Mar 2024 13:08:33 +0100 Subject: [PATCH 156/441] core: update re-exports for 1.48.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 257376145bd82..6ebe084fce222 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.47.0`](https://www.npmjs.com/package/@theia/application-package/v/1.47.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.47.0`](https://www.npmjs.com/package/@theia/application-package/v/1.47.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.47.0`](https://www.npmjs.com/package/@theia/application-package/v/1.47.0)) - - `@theia/request` (from [`@theia/request@1.47.0`](https://www.npmjs.com/package/@theia/request/v/1.47.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.47.0`](https://www.npmjs.com/package/@theia/request/v/1.47.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.47.0`](https://www.npmjs.com/package/@theia/request/v/1.47.0)) + - `@theia/application-package` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) + - `@theia/request` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From ea2cf0220436a38f7735aba47ad4a1e075ed5876 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 28 Mar 2024 13:10:23 +0100 Subject: [PATCH 157/441] v1.48.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 90 ++++++++-------- examples/browser/package.json | 102 +++++++++--------- examples/electron/package.json | 100 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 12 +-- packages/debug/package.json | 28 ++--- packages/dev-container/package.json | 12 +-- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 20 ++-- packages/keymaps/package.json | 14 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 18 ++-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 12 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 12 +-- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 +++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 14 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 18 ++-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 10 +- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 26 ++--- packages/terminal/package.json | 18 ++-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 20 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 72 files changed, 523 insertions(+), 523 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index f08b8a849f11c..d5783d1051d8a 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.47.0", - "@theia/ffmpeg": "1.47.0", - "@theia/native-webpack-plugin": "1.47.0", + "@theia/application-package": "1.48.0", + "@theia/ffmpeg": "1.48.0", + "@theia/native-webpack-plugin": "1.48.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index ed07b3e191eed..25bee70100b5f 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.47.0", + "@theia/request": "1.48.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index b8137f9dfa1b0..aaced7d725372 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,12 +32,12 @@ }, "dependencies": { "patch-package": "^8.0.0", - "@theia/application-manager": "1.47.0", - "@theia/application-package": "1.47.0", - "@theia/ffmpeg": "1.47.0", - "@theia/localization-manager": "1.47.0", - "@theia/ovsx-client": "1.47.0", - "@theia/request": "1.47.0", + "@theia/application-manager": "1.48.0", + "@theia/application-package": "1.48.0", + "@theia/ffmpeg": "1.48.0", + "@theia/localization-manager": "1.48.0", + "@theia/ovsx-client": "1.48.0", + "@theia/request": "1.48.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index ef8a7de6a3d86..adebabdf71a11 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index 24af3ee2dd9c1..42819cde08eb2 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~4.5.5" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index c2d0d4e6bb69f..dd6d3b3bc8e0b 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.47.0", + "version": "1.48.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index 0f2789e2de3c5..bfce1cb9154e3 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.47.0", + "@theia/request": "1.48.0", "semver": "^7.5.4", "tslib": "^2.6.2" } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index ed069a81a7595..bc80e10a6a5c5 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.47.0", + "version": "1.48.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.47.0", - "@theia/ext-scripts": "1.47.0", - "@theia/re-exports": "1.47.0", + "@theia/core": "1.48.0", + "@theia/ext-scripts": "1.48.0", + "@theia/re-exports": "1.48.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index e13dc8f720e93..e2f4ca82fdcf1 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.47.0", + "version": "1.48.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index 86221d4b43e5a..7b8dffbe1e572 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index a0bfe3bde6edd..0b25cf1bc7068 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index 3290ccb9a3b51..aef5d1a762cfb 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/plugin-ext-headless": "1.47.0" + "@theia/core": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/plugin-ext-headless": "1.48.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 6cf5794609179..2aac38f2a1459 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.47.0", - "@theia/ovsx-client": "1.47.0", - "@theia/search-in-workspace": "1.47.0", - "@theia/test": "1.47.0", - "@theia/toolbar": "1.47.0", - "@theia/vsx-registry": "1.47.0", - "@theia/workspace": "1.47.0" + "@theia/output": "1.48.0", + "@theia/ovsx-client": "1.48.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/test": "1.48.0", + "@theia/toolbar": "1.48.0", + "@theia/vsx-registry": "1.48.0", + "@theia/workspace": "1.48.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 95f8f20593bc8..03ec3d04f74c8 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.47.0" + "@theia/core": "1.48.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 02c2818f062eb..fdf75ae2fe9fd 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.47.0", + "version": "1.48.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,49 +15,49 @@ } }, "dependencies": { - "@theia/api-samples": "1.47.0", - "@theia/bulk-edit": "1.47.0", - "@theia/callhierarchy": "1.47.0", - "@theia/console": "1.47.0", - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/editor-preview": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/getting-started": "1.47.0", - "@theia/git": "1.47.0", - "@theia/keymaps": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/memory-inspector": "1.47.0", - "@theia/messages": "1.47.0", - "@theia/metrics": "1.47.0", - "@theia/mini-browser": "1.47.0", - "@theia/monaco": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/outline-view": "1.47.0", - "@theia/output": "1.47.0", - "@theia/plugin-dev": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/plugin-ext-vscode": "1.47.0", - "@theia/plugin-metrics": "1.47.0", - "@theia/preferences": "1.47.0", - "@theia/preview": "1.47.0", - "@theia/process": "1.47.0", - "@theia/property-view": "1.47.0", - "@theia/scm": "1.47.0", - "@theia/scm-extra": "1.47.0", - "@theia/search-in-workspace": "1.47.0", - "@theia/secondary-window": "1.47.0", - "@theia/task": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/timeline": "1.47.0", - "@theia/toolbar": "1.47.0", - "@theia/typehierarchy": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/vsx-registry": "1.47.0", - "@theia/workspace": "1.47.0" + "@theia/api-samples": "1.48.0", + "@theia/bulk-edit": "1.48.0", + "@theia/callhierarchy": "1.48.0", + "@theia/console": "1.48.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/editor-preview": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/getting-started": "1.48.0", + "@theia/git": "1.48.0", + "@theia/keymaps": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/memory-inspector": "1.48.0", + "@theia/messages": "1.48.0", + "@theia/metrics": "1.48.0", + "@theia/mini-browser": "1.48.0", + "@theia/monaco": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/outline-view": "1.48.0", + "@theia/output": "1.48.0", + "@theia/plugin-dev": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/plugin-ext-vscode": "1.48.0", + "@theia/plugin-metrics": "1.48.0", + "@theia/preferences": "1.48.0", + "@theia/preview": "1.48.0", + "@theia/process": "1.48.0", + "@theia/property-view": "1.48.0", + "@theia/scm": "1.48.0", + "@theia/scm-extra": "1.48.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/secondary-window": "1.48.0", + "@theia/task": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/timeline": "1.48.0", + "@theia/toolbar": "1.48.0", + "@theia/typehierarchy": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/vsx-registry": "1.48.0", + "@theia/workspace": "1.48.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -73,6 +73,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.47.0" + "@theia/cli": "1.48.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index daa632e7b1e47..4c026ac437477 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.47.0", + "version": "1.48.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,55 +20,55 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.47.0", - "@theia/api-samples": "1.47.0", - "@theia/bulk-edit": "1.47.0", - "@theia/callhierarchy": "1.47.0", - "@theia/console": "1.47.0", - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", - "@theia/dev-container": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/editor-preview": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/getting-started": "1.47.0", - "@theia/git": "1.47.0", - "@theia/keymaps": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/memory-inspector": "1.47.0", - "@theia/messages": "1.47.0", - "@theia/metrics": "1.47.0", - "@theia/mini-browser": "1.47.0", - "@theia/monaco": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/notebook": "1.47.0", - "@theia/outline-view": "1.47.0", - "@theia/output": "1.47.0", - "@theia/plugin-dev": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/plugin-ext-headless": "1.47.0", - "@theia/plugin-ext-vscode": "1.47.0", - "@theia/plugin-metrics": "1.47.0", - "@theia/preferences": "1.47.0", - "@theia/preview": "1.47.0", - "@theia/process": "1.47.0", - "@theia/property-view": "1.47.0", - "@theia/remote": "1.47.0", - "@theia/scm": "1.47.0", - "@theia/scm-extra": "1.47.0", - "@theia/search-in-workspace": "1.47.0", - "@theia/secondary-window": "1.47.0", - "@theia/task": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/test": "1.47.0", - "@theia/timeline": "1.47.0", - "@theia/toolbar": "1.47.0", - "@theia/typehierarchy": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/vsx-registry": "1.47.0", - "@theia/workspace": "1.47.0" + "@theia/api-provider-sample": "1.48.0", + "@theia/api-samples": "1.48.0", + "@theia/bulk-edit": "1.48.0", + "@theia/callhierarchy": "1.48.0", + "@theia/console": "1.48.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", + "@theia/dev-container": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/editor-preview": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/getting-started": "1.48.0", + "@theia/git": "1.48.0", + "@theia/keymaps": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/memory-inspector": "1.48.0", + "@theia/messages": "1.48.0", + "@theia/metrics": "1.48.0", + "@theia/mini-browser": "1.48.0", + "@theia/monaco": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/notebook": "1.48.0", + "@theia/outline-view": "1.48.0", + "@theia/output": "1.48.0", + "@theia/plugin-dev": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/plugin-ext-headless": "1.48.0", + "@theia/plugin-ext-vscode": "1.48.0", + "@theia/plugin-metrics": "1.48.0", + "@theia/preferences": "1.48.0", + "@theia/preview": "1.48.0", + "@theia/process": "1.48.0", + "@theia/property-view": "1.48.0", + "@theia/remote": "1.48.0", + "@theia/scm": "1.48.0", + "@theia/scm-extra": "1.48.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/secondary-window": "1.48.0", + "@theia/task": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/test": "1.48.0", + "@theia/timeline": "1.48.0", + "@theia/toolbar": "1.48.0", + "@theia/typehierarchy": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/vsx-registry": "1.48.0", + "@theia/workspace": "1.48.0" }, "scripts": { "clean": "theia clean", @@ -91,6 +91,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.47.0" + "@theia/cli": "1.48.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 2e04a9ffcf28b..bb3f4cfc40ffd 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.47.0", + "version": "1.48.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -20,54 +20,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.47.0", - "@theia/api-samples": "1.47.0", - "@theia/bulk-edit": "1.47.0", - "@theia/callhierarchy": "1.47.0", - "@theia/console": "1.47.0", - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", - "@theia/dev-container": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/editor-preview": "1.47.0", - "@theia/electron": "1.47.0", - "@theia/external-terminal": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/getting-started": "1.47.0", - "@theia/git": "1.47.0", - "@theia/keymaps": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/memory-inspector": "1.47.0", - "@theia/messages": "1.47.0", - "@theia/metrics": "1.47.0", - "@theia/mini-browser": "1.47.0", - "@theia/monaco": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/outline-view": "1.47.0", - "@theia/output": "1.47.0", - "@theia/plugin-dev": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/plugin-ext-headless": "1.47.0", - "@theia/plugin-ext-vscode": "1.47.0", - "@theia/preferences": "1.47.0", - "@theia/preview": "1.47.0", - "@theia/process": "1.47.0", - "@theia/property-view": "1.47.0", - "@theia/remote": "1.47.0", - "@theia/scm": "1.47.0", - "@theia/scm-extra": "1.47.0", - "@theia/search-in-workspace": "1.47.0", - "@theia/secondary-window": "1.47.0", - "@theia/task": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/timeline": "1.47.0", - "@theia/toolbar": "1.47.0", - "@theia/typehierarchy": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/vsx-registry": "1.47.0", - "@theia/workspace": "1.47.0" + "@theia/api-provider-sample": "1.48.0", + "@theia/api-samples": "1.48.0", + "@theia/bulk-edit": "1.48.0", + "@theia/callhierarchy": "1.48.0", + "@theia/console": "1.48.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", + "@theia/dev-container": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/editor-preview": "1.48.0", + "@theia/electron": "1.48.0", + "@theia/external-terminal": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/getting-started": "1.48.0", + "@theia/git": "1.48.0", + "@theia/keymaps": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/memory-inspector": "1.48.0", + "@theia/messages": "1.48.0", + "@theia/metrics": "1.48.0", + "@theia/mini-browser": "1.48.0", + "@theia/monaco": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/outline-view": "1.48.0", + "@theia/output": "1.48.0", + "@theia/plugin-dev": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/plugin-ext-headless": "1.48.0", + "@theia/plugin-ext-vscode": "1.48.0", + "@theia/preferences": "1.48.0", + "@theia/preview": "1.48.0", + "@theia/process": "1.48.0", + "@theia/property-view": "1.48.0", + "@theia/remote": "1.48.0", + "@theia/scm": "1.48.0", + "@theia/scm-extra": "1.48.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/secondary-window": "1.48.0", + "@theia/task": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/timeline": "1.48.0", + "@theia/toolbar": "1.48.0", + "@theia/typehierarchy": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/vsx-registry": "1.48.0", + "@theia/workspace": "1.48.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -85,7 +85,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.47.0", + "@theia/cli": "1.48.0", "electron": "^23.2.4" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index d9514d939e966..c1c53206a572a 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.47.0", + "version": "1.48.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index 28594e68c798a..f2f9f7afd0002 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.47.0", + "version": "1.48.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 787142a50916b..35ccebc2fae66 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.47.0", + "@theia/workspace": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index deccbdda0fe8f..6bc112ef41042 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 2abaa9735799d..16f3b873acc76 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index a001804f9bbd0..d692c66fc789b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.47.0", - "@theia/request": "1.47.0", + "@theia/application-package": "1.48.0", + "@theia/request": "1.48.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -206,11 +206,11 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", - "@theia/re-exports": "1.47.0", + "@theia/ext-scripts": "1.48.0", + "@theia/re-exports": "1.48.0", "minimist": "^1.2.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/debug/package.json b/packages/debug/package.json index d23b9564552b9..f3ab2c9bd9ac6 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,21 +1,21 @@ { "name": "@theia/debug", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.47.0", - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/console": "1.48.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.47.0", - "@theia/process": "1.47.0", - "@theia/task": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/output": "1.48.0", + "@theia/process": "1.48.0", + "@theia/task": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/workspace": "1.48.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -58,7 +58,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index 246fa87cc3add..72c816c517e54 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,12 +1,12 @@ { "name": "@theia/dev-container", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/output": "1.47.0", - "@theia/remote": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/output": "1.48.0", + "@theia/remote": "1.48.0", + "@theia/workspace": "1.48.0", "dockerode": "^4.0.2", "uuid": "^8.0.0", "jsonc-parser": "^2.2.0" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index 877841419bc1f..8f4314adb39cd 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/navigator": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/navigator": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index 6521ff71a9601..b65a872605af6 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/variable-resolver": "1.47.0", + "@theia/core": "1.48.0", + "@theia/variable-resolver": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index 73965e3f3c8a4..f259a338c9f2b 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", - "@theia/re-exports": "1.47.0" + "@theia/ext-scripts": "1.48.0", + "@theia/re-exports": "1.48.0" }, "peerDependencies": { "electron": "^23.2.4" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index 2cb3e6e8a4b53..ca2b90a94d2b3 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/workspace": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 05eec9ec2f8e8..0f6d4f1dcb2b8 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/process": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/process": "1.48.0", + "@theia/workspace": "1.48.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 46cb56b5fd498..d07a2453577cf 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -73,7 +73,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 0e386747bc801..fd99b9f9d23ce 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/keymaps": "1.47.0", - "@theia/preview": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/keymaps": "1.48.0", + "@theia/preview": "1.48.0", + "@theia/workspace": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 249ac3c100db4..d7e9ddf1c8038 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.47.0", - "@theia/scm": "1.47.0", - "@theia/scm-extra": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/navigator": "1.48.0", + "@theia/scm": "1.48.0", + "@theia/scm-extra": "1.48.0", + "@theia/workspace": "1.48.0", "@types/diff": "^3.2.2", "@types/p-queue": "^2.3.1", "diff": "^3.4.0", @@ -67,11 +67,11 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index d0e7106baf667..855cd1da96bd2 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.47.0", - "@theia/userstorage": "1.47.0", + "@theia/preferences": "1.48.0", + "@theia/userstorage": "1.48.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "publishConfig": { "access": "public" @@ -49,4 +49,4 @@ "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/markers/package.json b/packages/markers/package.json index 526e1f33148e4..d885ffb474502 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/workspace": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 3c60d7391b818..97f4a002bc94c 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index 6fe6e6bb26944..1d74195290d3e 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 03da14040a9bd..296a00aa7783d 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index bf754f5c70580..1c78b7ff7695b 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index c728b911ff6a3..fd67ee2d6cef1 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/markers": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/markers": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/outline-view": "1.48.0", + "@theia/workspace": "1.48.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,9 +52,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/navigator/package.json b/packages/navigator/package.json index 8fceebddee2c1..5b846f477b597 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/workspace": "1.48.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index d50da607b4c9a..fd1e14a2bf514 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,12 +1,12 @@ { "name": "@theia/notebook", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/vscode-notebook-renderer": "^1.72.0" }, "nyc": { diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index a08858d61adc9..54934ab695de8 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index a1f3926397239..aae39f23c0454 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,9 +44,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index 32a9cbf6141ff..c51e7180b6e9c 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/output": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/output": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/workspace": "1.48.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index feac07ff832f9..68792a0be9560 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/terminal": "1.47.0", + "@theia/core": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/terminal": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 0a61db587f2cc..b7fc61a1e8df8 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,21 +1,21 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.47.0", - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/callhierarchy": "1.48.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.47.0", - "@theia/plugin": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/typehierarchy": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/navigator": "1.48.0", + "@theia/plugin": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/typehierarchy": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/workspace": "1.48.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -54,9 +54,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 7ee894ba685cf..69bfae2bf80a1 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.47.0", - "@theia/callhierarchy": "1.47.0", - "@theia/console": "1.47.0", - "@theia/core": "1.47.0", - "@theia/debug": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/editor-preview": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/messages": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/bulk-edit": "1.48.0", + "@theia/callhierarchy": "1.48.0", + "@theia/console": "1.48.0", + "@theia/core": "1.48.0", + "@theia/debug": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/editor-preview": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/messages": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.47.0", - "@theia/notebook": "1.47.0", - "@theia/output": "1.47.0", - "@theia/plugin": "1.47.0", - "@theia/preferences": "1.47.0", - "@theia/scm": "1.47.0", - "@theia/search-in-workspace": "1.47.0", - "@theia/task": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/timeline": "1.47.0", - "@theia/typehierarchy": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/workspace": "1.47.0", - "@theia/test": "1.47.0", + "@theia/navigator": "1.48.0", + "@theia/notebook": "1.48.0", + "@theia/output": "1.48.0", + "@theia/plugin": "1.48.0", + "@theia/preferences": "1.48.0", + "@theia/scm": "1.48.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/task": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/timeline": "1.48.0", + "@theia/typehierarchy": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/workspace": "1.48.0", + "@theia/test": "1.48.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 66d4283157775..54d9c74c58a6a 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.47.0", - "@theia/metrics": "1.47.0", + "@theia/core": "1.48.0", + "@theia/metrics": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.47.0", - "@theia/plugin-ext": "1.47.0", + "@theia/plugin": "1.48.0", + "@theia/plugin-ext": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,9 +44,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 48d733099629e..41e305a2c5ce0 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 4c6bbd902154e..4752a78a050e9 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/userstorage": "1.48.0", + "@theia/workspace": "1.48.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -49,9 +49,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/preview/package.json b/packages/preview/package.json index f5f95f870bdbb..760bbdd78d620 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/mini-browser": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/mini-browser": "1.48.0", + "@theia/monaco": "1.48.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index 309fdab380aab..736f1b4b13ae0 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "node-pty": "0.11.0-beta17", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index 8fd0384c9b17d..e2c6576078bb3 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 5a287e315834f..f371be6ffa48c 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index 3b3a7ef19dcdc..66ae1944bdf76 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/scm": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/scm": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index 54c48b429e8a4..0971567f6e2b4 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,11 +1,11 @@ { "name": "@theia/scm", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", "@types/diff": "^3.2.2", "diff": "^3.4.0", "p-debounce": "^2.1.0", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index f4a65d1ec56aa..4f89297ab7453 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/process": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/process": "1.48.0", + "@theia/workspace": "1.48.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index c628a9d80f279..4729fffd71d4e 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index 52347ddc348be..0409517f2ffb7 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/markers": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/markers": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.47.0", - "@theia/terminal": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/process": "1.48.0", + "@theia/terminal": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/workspace": "1.48.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,9 +53,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" } -} \ No newline at end of file +} diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 8ddf3640ecdda..a149bfb1bf49e 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/process": "1.47.0", - "@theia/variable-resolver": "1.47.0", - "@theia/workspace": "1.47.0", - "@theia/file-search": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/process": "1.48.0", + "@theia/variable-resolver": "1.48.0", + "@theia/workspace": "1.48.0", + "@theia/file-search": "1.48.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index de8024d03b50f..1936586573a7e 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/terminal": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/terminal": "1.48.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index f2bab6238b371..eb77860b006a8 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/navigator": "1.47.0", + "@theia/core": "1.48.0", + "@theia/navigator": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index e9d96b04e1090..e3ba668a727bf 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", - "@theia/file-search": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/monaco": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", + "@theia/file-search": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.47.0", - "@theia/userstorage": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/search-in-workspace": "1.48.0", + "@theia/userstorage": "1.48.0", + "@theia/workspace": "1.48.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", @@ -49,4 +49,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index edbb0a943d444..8d42c6b7f134c 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/editor": "1.47.0", + "@theia/core": "1.48.0", + "@theia/editor": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index abb871edb8833..00a85d008faf8 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index ec4d72a7ef43a..0a4b2bf56c714 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.47.0", + "@theia/core": "1.48.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index faa2bda1385ba..06a027303262c 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/navigator": "1.47.0", - "@theia/ovsx-client": "1.47.0", - "@theia/plugin-ext": "1.47.0", - "@theia/plugin-ext-vscode": "1.47.0", - "@theia/preferences": "1.47.0", - "@theia/workspace": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/navigator": "1.48.0", + "@theia/ovsx-client": "1.48.0", + "@theia/plugin-ext": "1.48.0", + "@theia/plugin-ext-vscode": "1.48.0", + "@theia/preferences": "1.48.0", + "@theia/workspace": "1.48.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -54,7 +54,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0", + "@theia/ext-scripts": "1.48.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 36d977553b203..7970e5d19145e 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.47.0", + "version": "1.48.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.47.0", - "@theia/filesystem": "1.47.0", - "@theia/variable-resolver": "1.47.0", + "@theia/core": "1.48.0", + "@theia/filesystem": "1.48.0", + "@theia/variable-resolver": "1.48.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.47.0" + "@theia/ext-scripts": "1.48.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 5fe5e5e24cd92..834fdd36b495f 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.47.0", + "version": "1.48.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 4751de72e2de1..8aee6a6efd846 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.47.0", + "version": "1.48.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index f94bcb110975f..b025d0f0def04 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.47.0", + "version": "1.48.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.47.0" + "@theia/api-provider-sample": "1.48.0" }, "scripts": { "prepare": "yarn -s package", From 52ac2a4855874fbf4661f591c3c72f8b28e24e99 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 28 Mar 2024 11:02:51 +0100 Subject: [PATCH 158/441] docs: updated changelog for 1.48.0 Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e3d90033637..dd737cd843def 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,50 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## not yet released + -[Breaking Changes:](#breaking_changes_not_yet_released) -- [core] Add secondary windows support for text editors. [#13493](https://github.com/eclipse-theia/theia/pull/13493 ). The changes in require more extensive patches for our dependencies than before. For this purpose, we are using the `patch-package` library. However, this change requires adopters to add the line `"postinstall": "theia-patch"` to the `package.json` at the root of their monorepo (where the `node_modules` folder is located). - contributed on behalf of STMicroelectronics +## v1.48.0 - 03/28/2024 +- [application-package] bumped the default supported API from `1.86.2` to `1.87.2` [#13514](https://github.com/eclipse-theia/theia/pull/13514) - contributed on behalf of STMicroelectronics +- [core] added "New File" default implementation [#13344](https://github.com/eclipse-theia/theia/pull/13344) +- [core] added logic to check for disposed before sending update message in toolbars [#13454](https://github.com/eclipse-theia/theia/pull/13454) - contributed on behalf of STMicroelectronics +- [core] fixed default translation of Close Editor command [#13412](https://github.com/eclipse-theia/theia/pull/13412) +- [core] fixed logic to allow reopening secondary windows [#13509](https://github.com/eclipse-theia/theia/pull/13509) - contributed on behalf of STMicroelectronics +- [core] fixed rending of quickpick buttons [#13342](https://github.com/eclipse-theia/theia/pull/13342) - contributed on behalf of STMicroelectronics +- [core] updated logic to remove unneeded URI conversion [#13415](https://github.com/eclipse-theia/theia/pull/13415) +- [dev-container] added first version of dev-container support [#13372](https://github.com/eclipse-theia/theia/pull/13372) +- [editor] added secondary window support for text editors [#13493](https://github.com/eclipse-theia/theia/pull/13493) - contributed on behalf of STMicroelectronics +- [git] fixed detecting changes after git init [#13487](https://github.com/eclipse-theia/theia/pull/13487) +- [metrics] allowed accessing the metrics endpoint for performance analysis in electron [#13380](https://github.com/eclipse-theia/theia/pull/13380) - contributed on behalf of STMicroelectronics +- [monaco] fixed monaco quickpick [#13451](https://github.com/eclipse-theia/theia/pull/13451) - contributed on behalf of STMicroelectronics +- [monaco] fixed rending of quickpick buttons [#13342](https://github.com/eclipse-theia/theia/pull/13342) - contributed on behalf of STMicroelectronics +- [notebook] added execute cells above/below commands [#13528](https://github.com/eclipse-theia/theia/pull/13528) +- [notebook] added execution order display to code cells [#13502](https://github.com/eclipse-theia/theia/pull/13502) +- [notebook] added keybindings to notebook editor [#13497](https://github.com/eclipse-theia/theia/pull/13497) +- [notebook] added support for custom widget types for notebook outputs [#13517](https://github.com/eclipse-theia/theia/pull/13517) +- [notebook] fixed cell execution height styling [#13515](https://github.com/eclipse-theia/theia/pull/13515) +- [notebook] fixed context keys for notebook editor context [#13448](https://github.com/eclipse-theia/theia/pull/13448) +- [notebook] fixed keybindings triggers when cell editor is focused [#13500](https://github.com/eclipse-theia/theia/pull/13500) +- [notebook] fixed notebook document metadata edit [#13528](https://github.com/eclipse-theia/theia/pull/13528) +- [notebook] fixed renaming and moving of open notebooks [#13467](https://github.com/eclipse-theia/theia/pull/13467) +- [notebook] fixed undo redo keybindings for notebook editor [#13518](https://github.com/eclipse-theia/theia/pull/13518) +- [notebook] improved focusing of the notebook cell editors [#13516](https://github.com/eclipse-theia/theia/pull/13516) +- [notebook] improved performance when opening notebooks [#13488](https://github.com/eclipse-theia/theia/pull/13488) +- [notebook] updated logic to only initialize notebook cell editor when in viewport [#13476](https://github.com/eclipse-theia/theia/pull/13476) +- [plugin] added `Interval` `TextEditorLineNumbersStyle` [#13458](https://github.com/eclipse-theia/theia/pull/13458) - contributed on behalf of STMicroelectronics +- [plugin] added terminal observer API [#13402](https://github.com/eclipse-theia/theia/pull/13402) +- [plugin] changed logic to ensure that showOpenDialog returns correct file URI [#13208](https://github.com/eclipse-theia/theia/pull/13208) - contributed on behalf of STMicroelectronics +- [plugin] fixed quickpick [#13451](https://github.com/eclipse-theia/theia/pull/13451) - contributed on behalf of STMicroelectronics +- [plugin] made `acquireVsCodeApi` function available on global objects [#13411](https://github.com/eclipse-theia/theia/pull/13411) +- [plugin] updated logic to avoid disposal of `QuickInputExt` on hide [#13485](https://github.com/eclipse-theia/theia/pull/13485) - contributed on behalf of STMicroelectronics +- [remote] added logic to support remote port forwarding [#13439](https://github.com/eclipse-theia/theia/pull/13439) +- [terminal] added logic to resolve links to workspace files in terminal [#13498](https://github.com/eclipse-theia/theia/pull/13498) - contributed on behalf of STMicroelectronics +- [terminal] added terminal observer API [#13402](https://github.com/eclipse-theia/theia/pull/13402) + +[Breaking Changes:](#breaking_changes_1.48.0) +- [core] Add secondary windows support for text editors. [#13493](https://github.com/eclipse-theia/theia/pull/13493 ). The changes in require more extensive patches for our dependencies than before. For this purpose, we are using the `patch-package` library. However, this change requires adopters to add the line `"postinstall": "theia-patch"` to the `package.json` at the root of their monorepo (where the `node_modules` folder is located). - contributed on behalf of STMicroelectronics ## v1.47.0 - 02/29/2024 From 86bac3e4c153344c1c6d2a9463d05f123dfe454b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 3 Apr 2024 09:21:10 +0200 Subject: [PATCH 159/441] Add "patches" folder to package.json "files" field. (#13554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder Contribute on behalf of STMicroelectronics --- dev-packages/cli/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index aaced7d725372..4bd2ad9878d22 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -17,7 +17,8 @@ "files": [ "bin", "lib", - "src" + "src", + "patches" ], "bin": { "theia": "./bin/theia", @@ -63,4 +64,4 @@ "@types/node-fetch": "^2.5.7", "@types/proxy-from-env": "^1.0.1" } -} +} \ No newline at end of file From 5d653b20c85c4e57c287ba78dfffc4df8968cfaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 4 Apr 2024 09:40:26 +0200 Subject: [PATCH 160/441] Fix window revealing when navigating with multiple windows (#13561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: #13559 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/electron-browser/preload.ts | 2 +- .../src/electron-browser/window/electron-window-service.ts | 2 +- packages/core/src/electron-common/electron-api.ts | 2 +- packages/core/src/electron-main/electron-api-main.ts | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index 4018bdb0936c0..d7f6f30aa4ee0 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -75,7 +75,7 @@ const api: TheiaCoreAPI = { ipcRenderer.send(CHANNEL_SET_MENU, mainMenuId, convertMenu(menu, handlers)); }, getSecurityToken: () => ipcRenderer.sendSync(CHANNEL_GET_SECURITY_TOKEN), - focusWindow: (name: string) => ipcRenderer.send(CHANNEL_FOCUS_WINDOW, name), + focusWindow: (name?: string) => ipcRenderer.send(CHANNEL_FOCUS_WINDOW, name), showItemInFolder: fsPath => { ipcRenderer.send(CHANNEL_SHOW_ITEM_IN_FOLDER, fsPath); }, diff --git a/packages/core/src/electron-browser/window/electron-window-service.ts b/packages/core/src/electron-browser/window/electron-window-service.ts index f9248cb54806e..518dd1127b2e1 100644 --- a/packages/core/src/electron-browser/window/electron-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-window-service.ts @@ -58,7 +58,7 @@ export class ElectronWindowService extends DefaultWindowService { } override focus(): void { - window.electronTheiaCore.focusWindow(window.name); + window.electronTheiaCore.focusWindow(); } @postConstruct() protected init(): void { diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 77ffa0d5f3704..6abe167e8a784 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -53,7 +53,7 @@ export interface TheiaCoreAPI { popup(menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise; closePopup(handle: number): void; - focusWindow(name: string): void; + focusWindow(name?: string): void; showItemInFolder(fsPath: string): void; diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index 6b3cb9d8fd46b..485d917c9a288 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -147,7 +147,9 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { // focus windows for secondary window support ipcMain.on(CHANNEL_FOCUS_WINDOW, (event, windowName) => { - const electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName); + const electronWindow = windowName + ? BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName) + : BrowserWindow.fromWebContents(event.sender); if (electronWindow) { if (electronWindow.isMinimized()) { electronWindow.restore(); From 35a340ba73b9a43b3f8f1a6d6070eed2330af6bc Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 4 Apr 2024 13:13:20 +0200 Subject: [PATCH 161/441] add shift enter also for markdown cells (#13563) * add shift enter also for markdown cells Signed-off-by: Jonah Iden * stop edit on shift+enter Signed-off-by: Jonah Iden * shift+enter insert cell of same kind below Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 0481b4bd6f6ba..37766a87c0bb5 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -259,12 +259,18 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { - commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); + if (cell.cellKind === CellKind.Code) { + commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); + } else { + commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell); + } const index = notebookModel.cells.indexOf(cell); if (index < notebookModel.cells.length - 1) { notebookModel.setSelectedCell(notebookModel.cells[index + 1]); + } else if (cell.cellKind === CellKind.Code) { + commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id); } else { - commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id, notebookModel, CellKind.Code, 'below'); + commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id); } }) ); @@ -353,7 +359,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(), - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id, From a77ba542b720523d58d566bd11ab483fefa1e028 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 4 Apr 2024 14:25:51 +0200 Subject: [PATCH 162/441] Support truncated notebook output commands (#13555) * made notebook truncated output commands working (open as texteditor, as scrollable element, open settings) Signed-off-by: Jonah Iden * review changes Signed-off-by: Jonah Iden * fixed type Co-authored-by: Mark Sujew --------- Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- .../core/src/browser/command-open-handler.ts | 2 +- .../notebook-output-action-contribution.ts | 82 +++++++++++++++++++ .../notebook-cell-resource-resolver.ts | 40 ++++++++- .../src/browser/notebook-frontend-module.ts | 8 +- .../browser/view-model/notebook-cell-model.ts | 7 ++ .../notebook/src/common/notebook-common.ts | 33 +++++++- .../renderers/cell-output-webview.tsx | 15 +++- 7 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/notebook-output-action-contribution.ts diff --git a/packages/core/src/browser/command-open-handler.ts b/packages/core/src/browser/command-open-handler.ts index f3e7323b04c07..2692ae5347765 100644 --- a/packages/core/src/browser/command-open-handler.ts +++ b/packages/core/src/browser/command-open-handler.ts @@ -41,7 +41,7 @@ export class CommandOpenHandler implements OpenHandler { try { args = JSON.parse(uri.query); } catch { - // ignore error + args = uri.query; } } if (!Array.isArray(args)) { diff --git a/packages/notebook/src/browser/contributions/notebook-output-action-contribution.ts b/packages/notebook/src/browser/contributions/notebook-output-action-contribution.ts new file mode 100644 index 0000000000000..c7756fe507f43 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-output-action-contribution.ts @@ -0,0 +1,82 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Command, CommandContribution, CommandRegistry } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; +import { CellOutput, CellUri } from '../../common'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { EditorManager } from '@theia/editor/lib/browser'; + +export namespace NotebookOutputCommands { + export const ENABLE_SCROLLING = Command.toDefaultLocalizedCommand({ + id: 'cellOutput.enableScrolling', + }); + + export const OPEN_LARGE_OUTPUT = Command.toDefaultLocalizedCommand({ + id: 'workbench.action.openLargeOutput', + label: 'Open Large Output' + }); +} + +@injectable() +export class NotebookOutputActionContribution implements CommandContribution { + + @inject(NotebookEditorWidgetService) + protected readonly notebookEditorService: NotebookEditorWidgetService; + + @inject(EditorManager) + protected readonly editorManager: EditorManager; + + registerCommands(commands: CommandRegistry): void { + commands.registerCommand(NotebookOutputCommands.ENABLE_SCROLLING, { + execute: outputId => { + const [cell, output] = this.findOutputAndCell(outputId) ?? []; + if (cell && output?.metadata) { + output.metadata['scrollable'] = true; + cell.restartOutputRenderer(output.outputId); + } + } + }); + + commands.registerCommand(NotebookOutputCommands.OPEN_LARGE_OUTPUT, { + execute: outputId => { + const [cell, output] = this.findOutputAndCell(outputId) ?? []; + if (cell && output) { + this.editorManager.open(CellUri.generateCellOutputUri(CellUri.parse(cell.uri)!.notebook, output.outputId)); + } + } + }); + } + + protected findOutputAndCell(output: string): [NotebookCellModel, CellOutput] | undefined { + const model = this.notebookEditorService.focusedEditor?.model; + if (!model) { + return undefined; + } + + const outputId = output.slice(0, output.lastIndexOf('-')); + + for (const cell of model.cells) { + for (const outputModel of cell.outputs) { + if (outputModel.outputId === outputId) { + return [cell, outputModel]; + } + } + } + } + +} diff --git a/packages/notebook/src/browser/notebook-cell-resource-resolver.ts b/packages/notebook/src/browser/notebook-cell-resource-resolver.ts index 1d6af928cc9b8..bfe3b3e606917 100644 --- a/packages/notebook/src/browser/notebook-cell-resource-resolver.ts +++ b/packages/notebook/src/browser/notebook-cell-resource-resolver.ts @@ -65,7 +65,7 @@ export class NotebookCellResourceResolver implements ResourceResolver { protected readonly notebookService: NotebookService; async resolve(uri: URI): Promise { - if (uri.scheme !== CellUri.scheme) { + if (uri.scheme !== CellUri.cellUriScheme) { throw new Error(`Cannot resolve cell uri with scheme '${uri.scheme}'`); } @@ -90,3 +90,41 @@ export class NotebookCellResourceResolver implements ResourceResolver { } } + +@injectable() +export class NotebookOutputResourceResolver implements ResourceResolver { + + @inject(NotebookService) + protected readonly notebookService: NotebookService; + + async resolve(uri: URI): Promise { + if (uri.scheme !== CellUri.outputUriScheme) { + throw new Error(`Cannot resolve output uri with scheme '${uri.scheme}'`); + } + + const parsedUri = CellUri.parseCellOutputUri(uri); + if (!parsedUri) { + throw new Error(`Cannot parse uri '${uri.toString()}'`); + } + + const notebookModel = this.notebookService.getNotebookEditorModel(parsedUri.notebook); + + if (!notebookModel) { + throw new Error(`No notebook found for uri '${parsedUri.notebook}'`); + } + + const ouputModel = notebookModel.cells.flatMap(cell => cell.outputs).find(output => output.outputId === parsedUri.outputId); + + if (!ouputModel) { + throw new Error(`No output found with id '${parsedUri.outputId}' in '${parsedUri.notebook}'`); + } + + return { + uri: uri, + dispose: () => { }, + readContents: async () => ouputModel.outputs[0].data.toString(), + readOnly: true, + }; + } + +} diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index f74e129578d67..8f5a3c50c8e85 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -24,7 +24,7 @@ import { NotebookTypeRegistry } from './notebook-type-registry'; import { NotebookRendererRegistry } from './notebook-renderer-registry'; import { NotebookService } from './service/notebook-service'; import { NotebookEditorWidgetFactory } from './notebook-editor-widget-factory'; -import { NotebookCellResourceResolver } from './notebook-cell-resource-resolver'; +import { NotebookCellResourceResolver, NotebookOutputResourceResolver } from './notebook-cell-resource-resolver'; import { NotebookModelResolverService } from './service/notebook-model-resolver-service'; import { NotebookCellActionContribution } from './contributions/notebook-cell-actions-contribution'; import { NotebookCellToolbarFactory } from './view/notebook-cell-toolbar-factory'; @@ -41,6 +41,7 @@ import { NotebookEditorWidgetService } from './service/notebook-editor-widget-se import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service'; import { NotebookColorContribution } from './contributions/notebook-color-contribution'; import { NotebookMonacoTextModelService } from './service/notebook-monaco-text-model-service'; +import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -67,6 +68,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); bind(NotebookModelResolverService).toSelf().inSingletonScope(); + bind(NotebookOutputResourceResolver).toSelf().inSingletonScope(); + bind(ResourceResolver).toService(NotebookOutputResourceResolver); bind(NotebookCellActionContribution).toSelf().inSingletonScope(); bind(MenuContribution).toService(NotebookCellActionContribution); @@ -78,6 +81,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MenuContribution).toService(NotebookActionsContribution); bind(KeybindingContribution).toService(NotebookActionsContribution); + bind(NotebookOutputActionContribution).toSelf().inSingletonScope(); + bind(CommandContribution).toService(NotebookOutputActionContribution); + bind(NotebookEditorWidgetContainerFactory).toFactory(ctx => (props: NotebookEditorProps) => createNotebookEditorWidgetContainer(ctx.container, props).get(NotebookEditorWidget) ); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 1f20849de19ff..eb2ad2a126ed6 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -298,6 +298,13 @@ export class NotebookCellModel implements NotebookCell, Disposable { }); return ref.object; } + + restartOutputRenderer(outputId: string): void { + const output = this.outputs.find(out => out.outputId === outputId); + if (output) { + this.onDidChangeOutputItemsEmitter.fire(output); + } + } } function computeRunStartTimeAdjustment(oldMetadata: NotebookCellInternalMetadata, newMetadata: NotebookCellInternalMetadata): number | undefined { diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index c7db67d8f9b67..bf7355035c796 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -261,7 +261,8 @@ export function isTextStreamMime(mimeType: string): boolean { export namespace CellUri { - export const scheme = 'vscode-notebook-cell'; + export const cellUriScheme = 'vscode-notebook-cell'; + export const outputUriScheme = 'vscode-notebook-cell-output'; const _lengths = ['W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f']; const _padRegexp = new RegExp(`^[${_lengths.join('')}]+`); @@ -273,11 +274,11 @@ export namespace CellUri { const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z'; const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')} `; - return notebook.withScheme(scheme).withFragment(fragment); + return notebook.withScheme(cellUriScheme).withFragment(fragment); } export function parse(cell: URI): { notebook: URI; handle: number } | undefined { - if (cell.scheme !== scheme) { + if (cell.scheme !== cellUriScheme) { return undefined; } @@ -298,6 +299,30 @@ export namespace CellUri { }; } + export function generateCellOutputUri(notebook: URI, outputId?: string): URI { + return notebook + .withScheme(outputUriScheme) + .withQuery(`op${outputId ?? ''},${notebook.scheme !== 'file' ? notebook.scheme : ''}`); + }; + + export function parseCellOutputUri(uri: URI): { notebook: URI; outputId?: string } | undefined { + if (uri.scheme !== outputUriScheme) { + return; + } + + const match = /^op([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.query); + if (!match) { + return undefined; + } + + const outputId = match[1] || undefined; + const scheme = match[2]; + return { + outputId, + notebook: uri.withScheme(scheme || 'file').withoutQuery() + }; + } + export function generateCellPropertyUri(notebook: URI, handle: number, cellScheme: string): URI { return CellUri.generate(notebook, handle).withScheme(cellScheme); } @@ -307,6 +332,6 @@ export namespace CellUri { return undefined; } - return CellUri.parse(uri.withScheme(scheme)); + return CellUri.parse(uri.withScheme(cellUriScheme)); } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 0d4c48e4f8a87..2e8b91a5a0c24 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -117,7 +117,20 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); - this.webviewWidget.setContentOptions({ allowScripts: true }); + this.webviewWidget.setContentOptions({ + allowScripts: true, + // eslint-disable-next-line max-len + // list taken from https://github.com/microsoft/vscode/blob/a27099233b956dddc2536d4a0d714ab36266d897/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts#L762-L774 + enableCommandUris: [ + 'github-issues.authNow', + 'workbench.extensions.search', + 'workbench.action.openSettings', + '_notebook.selectKernel', + 'jupyter.viewOutput', + 'workbench.action.openLargeOutput', + 'cellOutput.enableScrolling', + ] + }); this.webviewWidget.setHTML(await this.createWebviewContent()); this.webviewWidget.onMessage((message: FromWebviewMessage) => { From 740fae9954b348e98b9b208c16cfb24196bd531a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 4 Apr 2024 14:57:28 +0200 Subject: [PATCH 163/441] Always consider the "passthrough" commmand enabled for keybindings (#13564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13560 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/browser/keybinding.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/browser/keybinding.ts b/packages/core/src/browser/keybinding.ts index a88eba3057be2..6871f5381ba62 100644 --- a/packages/core/src/browser/keybinding.ts +++ b/packages/core/src/browser/keybinding.ts @@ -497,7 +497,7 @@ export class KeybindingRegistry { isEnabledInScope(binding: common.Keybinding, target: HTMLElement | undefined): boolean { const context = binding.context && this.contexts[binding.context]; - if (binding.command && !this.commandRegistry.isEnabled(binding.command, binding.args)) { + if (binding.command && (!this.isPseudoCommand(binding.command) && !this.commandRegistry.isEnabled(binding.command, binding.args))) { return false; } if (context && !context.isEnabled(binding)) { From 9b0e47f23661070e15602c90dd68a298aced2902 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 4 Apr 2024 15:28:53 +0200 Subject: [PATCH 164/441] fix context for multiple notebooks and focus first cell in new (empty) notebook (#13566) Signed-off-by: Jonah Iden --- packages/notebook/src/browser/notebook-editor-widget.tsx | 8 ++++++++ packages/notebook/src/browser/notebook-frontend-module.ts | 2 -- .../notebook/src/browser/view-model/notebook-model.ts | 6 ++++-- .../notebook/src/browser/view/notebook-cell-list-view.tsx | 2 +- .../notebook/src/browser/view/notebook-cell-toolbar.tsx | 5 +++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 19a1fce82c1d2..3f8d53c51db6c 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -32,6 +32,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import { NotebookContextManager } from './service/notebook-context-manager'; import { NotebookViewportService } from './view/notebook-viewport-service'; +import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution'; const PerfectScrollbar = require('react-perfect-scrollbar'); export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory'); @@ -43,6 +44,7 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container child.bind(NotebookContextManager).toSelf().inSingletonScope(); child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope(); + child.bind(NotebookCellToolbarFactory).toSelf().inSingletonScope(); child.bind(NotebookCodeCellRenderer).toSelf().inSingletonScope(); child.bind(NotebookMarkdownCellRenderer).toSelf().inSingletonScope(); child.bind(NotebookViewportService).toSelf().inSingletonScope(); @@ -152,6 +154,12 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.renderers.set(CellKind.Markup, this.markdownCellRenderer); this.renderers.set(CellKind.Code, this.codeCellRenderer); this._ready.resolve(this.waitForData()); + this.ready.then(model => { + if (model.cells.length === 1 && model.cells[0].source === '') { + this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]); + model.setSelectedCell(model.cells[0]); + } + }); } protected async waitForData(): Promise { diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 8f5a3c50c8e85..962b4cf4811bc 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -27,7 +27,6 @@ import { NotebookEditorWidgetFactory } from './notebook-editor-widget-factory'; import { NotebookCellResourceResolver, NotebookOutputResourceResolver } from './notebook-cell-resource-resolver'; import { NotebookModelResolverService } from './service/notebook-model-resolver-service'; import { NotebookCellActionContribution } from './contributions/notebook-cell-actions-contribution'; -import { NotebookCellToolbarFactory } from './view/notebook-cell-toolbar-factory'; import { createNotebookModelContainer, NotebookModel, NotebookModelFactory, NotebookModelProps } from './view-model/notebook-model'; import { createNotebookCellModelContainer, NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from './view-model/notebook-cell-model'; import { createNotebookEditorWidgetContainer, NotebookEditorWidgetContainerFactory, NotebookEditorProps, NotebookEditorWidget } from './notebook-editor-widget'; @@ -54,7 +53,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookRendererRegistry).toSelf().inSingletonScope(); bind(WidgetFactory).to(NotebookEditorWidgetFactory).inSingletonScope(); - bind(NotebookCellToolbarFactory).toSelf().inSingletonScope(); bind(NotebookService).toSelf().inSingletonScope(); bind(NotebookEditorWidgetService).toSelf().inSingletonScope(); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 41df43d0fab82..e25c866446240 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -235,8 +235,10 @@ export class NotebookModel implements Saveable, Disposable { } setSelectedCell(cell: NotebookCellModel): void { - this.selectedCell = cell; - this.onDidChangeSelectedCellEmitter.fire(cell); + if (this.selectedCell !== cell) { + this.selectedCell = cell; + this.onDidChangeSelectedCellEmitter.fire(cell); + } } private addCellOutputListeners(cells: NotebookCellModel[]): void { diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 6ad3465386d8a..a695aacef252c 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -45,7 +45,7 @@ export class NotebookCellListView extends React.Component { if (e.newCellIds && e.newCellIds.length > 0) { this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) }); diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx index 548d42062ea42..12520d4199954 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx @@ -35,8 +35,9 @@ abstract class NotebookCellActionBar extends React.Component { - if (this.props.getMenuItems().some(item => item.contextKeys ? e.affects(item.contextKeys) : false)) { - this.setState({ inlineItems: this.props.getMenuItems() }); + const menuItems = this.props.getMenuItems(); + if (menuItems.some(item => item.contextKeys ? e.affects(item.contextKeys) : false)) { + this.setState({ inlineItems: menuItems }); } })); this.state = { inlineItems: this.props.getMenuItems() }; From 09051c419afb34dc0b2b10d4cb4f0e19962d593f Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 4 Apr 2024 16:17:53 +0200 Subject: [PATCH 165/441] reenable initial reading of execution summary (#13567) Signed-off-by: Jonah Iden --- packages/plugin-ext/src/plugin/type-converters.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 22f679de04007..86e70d7258510 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -749,9 +749,9 @@ export function isUriComponents(arg: unknown): arg is UriComponents { return isObject(arg) && typeof arg.scheme === 'string' && (arg['$mid'] === 1 || ( - typeof arg.path === 'string' && - typeof arg.query === 'string' && - typeof arg.fragment === 'string')); + typeof arg.path === 'string' && + typeof arg.query === 'string' && + typeof arg.fragment === 'string')); } export function isModelCallHierarchyItem(arg: unknown): arg is model.CallHierarchyItem { @@ -1522,8 +1522,8 @@ export namespace NotebookCellData { cellKind: NotebookCellKind.from(data.kind), language: data.languageId, source: data.value, - // metadata: data.metadata, - // internalMetadata: NotebookCellExecutionSummary.from(data.executionSummary ?? {}), + metadata: data.metadata, + internalMetadata: NotebookCellExecutionSummary.from(data.executionSummary ?? {}), outputs: data.outputs ? data.outputs.map(NotebookCellOutputConverter.from) : [] }; } From 52ab029070d4f37562bfef577d4af9dc75d550de Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 5 Apr 2024 11:48:56 +0200 Subject: [PATCH 166/441] Notebook Outline-View and Breadcrumbs. (#13562) * notbook outline and breadcrumbs + jupyter show Table of contents menu item working Signed-off-by: Jonah Iden * some basic review comment changes Signed-off-by: Jonah Iden * parse markdown cell content as plain text Signed-off-by: Jonah Iden * fixed lint Signed-off-by: Jonah Iden * import type instead of class Co-authored-by: Mark Sujew --------- Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- packages/notebook/package.json | 4 +- .../notebook-label-provider-contribution.ts | 63 ++++++++++ .../notebook-outline-contribution.ts | 112 ++++++++++++++++++ .../src/browser/notebook-frontend-module.ts | 9 +- .../src/browser/view-model/notebook-model.ts | 1 + packages/notebook/tsconfig.json | 3 + .../outline-breadcrumbs-contribution.tsx | 4 + .../src/browser/outline-view-service.ts | 9 ++ packages/plugin-ext-vscode/package.json | 1 + .../plugin-vscode-commands-contribution.ts | 8 ++ packages/plugin-ext-vscode/tsconfig.json | 3 + 11 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts create mode 100644 packages/notebook/src/browser/contributions/notebook-outline-contribution.ts diff --git a/packages/notebook/package.json b/packages/notebook/package.json index fd1e14a2bf514..f96708192efc9 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -7,6 +7,7 @@ "@theia/editor": "1.48.0", "@theia/filesystem": "1.48.0", "@theia/monaco": "1.48.0", + "@theia/outline-view": "1.48.0", "@theia/monaco-editor-core": "1.83.101", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" @@ -45,7 +46,8 @@ }, "devDependencies": { "@theia/ext-scripts": "1.48.0", - "@types/vscode-notebook-renderer": "^1.72.0" + "@types/vscode-notebook-renderer": "^1.72.0", + "@types/markdown-it": "^12.2.3" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts b/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts new file mode 100644 index 0000000000000..ec75fcf2cc5c5 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts @@ -0,0 +1,63 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { codicon, LabelProvider, LabelProviderContribution } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CellKind } from '../../common'; +import { NotebookService } from '../service/notebook-service'; +import { NotebookCellOutlineNode } from './notebook-outline-contribution'; +import type Token = require('markdown-it/lib/token'); +import markdownit = require('@theia/core/shared/markdown-it'); + +@injectable() +export class NotebookLabelProviderContribution implements LabelProviderContribution { + + @inject(NotebookService) + protected readonly notebookService: NotebookService; + + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; + + protected markdownIt = markdownit(); + + canHandle(element: object): number { + if (NotebookCellOutlineNode.is(element)) { + return 200; + } + return 0; + } + + getIcon(element: NotebookCellOutlineNode): string { + return element.notebookCell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code'); + } + + getName(element: NotebookCellOutlineNode): string { + return element.notebookCell.cellKind === CellKind.Code ? + element.notebookCell.text.split('\n')[0] : + this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {})); + } + + getLongName(element: NotebookCellOutlineNode): string { + return element.notebookCell.cellKind === CellKind.Code ? + element.notebookCell.text.split('\n')[0] : + this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {})); + } + + extractPlaintext(parsedMarkdown: Token[]): string { + return parsedMarkdown.map(token => token.children ? this.extractPlaintext(token.children) : token.content).join(''); + } + +} diff --git a/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts b/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts new file mode 100644 index 0000000000000..e1423dbc457a7 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts @@ -0,0 +1,112 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { codicon, FrontendApplicationContribution, LabelProvider, TreeNode } from '@theia/core/lib/browser'; +import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; +import { OutlineViewService } from '@theia/outline-view/lib/browser/outline-view-service'; +import { NotebookModel } from '../view-model/notebook-model'; +import { OutlineSymbolInformationNode } from '@theia/outline-view/lib/browser/outline-view-widget'; +import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { DisposableCollection, URI } from '@theia/core'; +import { CellKind, CellUri } from '../../common'; +import { NotebookService } from '../service/notebook-service'; +export interface NotebookCellOutlineNode extends OutlineSymbolInformationNode { + notebookCell: NotebookCellModel; + uri: URI; +} + +export namespace NotebookCellOutlineNode { + export function is(element: object): element is NotebookCellOutlineNode { + return TreeNode.is(element) && OutlineSymbolInformationNode.is(element) && 'notebookCell' in element; + } +} + +@injectable() +export class NotebookOutlineContribution implements FrontendApplicationContribution { + + @inject(NotebookEditorWidgetService) + protected readonly notebookEditorWidgetService: NotebookEditorWidgetService; + + @inject(OutlineViewService) + protected readonly outlineViewService: OutlineViewService; + + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; + + @inject(NotebookService) + protected readonly notebookService: NotebookService; + + protected currentEditor?: NotebookEditorWidget; + + protected editorListeners: DisposableCollection = new DisposableCollection(); + protected editorModelListeners: DisposableCollection = new DisposableCollection(); + + onStart(): void { + this.notebookEditorWidgetService.onDidChangeFocusedEditor(editor => this.updateOutline(editor)); + + this.outlineViewService.onDidSelect(node => this.selectCell(node)); + this.outlineViewService.onDidTapNode(node => this.selectCell(node)); + } + + protected async updateOutline(editor: NotebookEditorWidget | undefined): Promise { + if (editor && !editor.isDisposed) { + await editor.ready; + this.currentEditor = editor; + this.editorListeners.dispose(); + this.editorListeners.push(editor.onDidChangeVisibility(() => { + if (this.currentEditor === editor && !editor.isVisible) { + this.outlineViewService.publish([]); + } + })); + if (editor.model) { + this.editorModelListeners.dispose(); + this.editorModelListeners.push(editor.model.onDidChangeSelectedCell(() => { + if (editor === this.currentEditor) { + this.updateOutline(editor); + } + })); + const roots = editor && editor.model && await this.createRoots(editor.model); + this.outlineViewService.publish(roots || []); + } + } + } + + protected async createRoots(model: NotebookModel): Promise { + return model.cells.map(cell => ({ + id: cell.uri.toString(), + iconClass: cell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code'), + parent: undefined, + children: [], + selected: model.selectedCell === cell, + expanded: false, + notebookCell: cell, + uri: model.uri, + } as NotebookCellOutlineNode)); + } + + selectCell(node: object): void { + if (NotebookCellOutlineNode.is(node)) { + const parsed = CellUri.parse(node.notebookCell.uri); + const model = parsed && this.notebookService.getNotebookEditorModel(parsed.notebook); + if (model) { + model.setSelectedCell(node.notebookCell); + } + } + } + +} diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 962b4cf4811bc..6255d227283f2 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { KeybindingContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -40,6 +40,8 @@ import { NotebookEditorWidgetService } from './service/notebook-editor-widget-se import { NotebookRendererMessagingService } from './service/notebook-renderer-messaging-service'; import { NotebookColorContribution } from './contributions/notebook-color-contribution'; import { NotebookMonacoTextModelService } from './service/notebook-monaco-text-model-service'; +import { NotebookOutlineContribution } from './contributions/notebook-outline-contribution'; +import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution'; import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution'; export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -93,4 +95,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { ); bind(NotebookMonacoTextModelService).toSelf().inSingletonScope(); + + bind(NotebookOutlineContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(NotebookOutlineContribution); + bind(NotebookLabelProviderContribution).toSelf().inSingletonScope(); + bind(LabelProviderContribution).toService(NotebookLabelProviderContribution); }); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index e25c866446240..f6762feaa4f53 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -148,6 +148,7 @@ export class NotebookModel implements Saveable, Disposable { this.onDidSaveNotebookEmitter.dispose(); this.onDidAddOrRemoveCellEmitter.dispose(); this.onDidChangeContentEmitter.dispose(); + this.onDidChangeSelectedCellEmitter.dispose(); this.cells.forEach(cell => cell.dispose()); } diff --git a/packages/notebook/tsconfig.json b/packages/notebook/tsconfig.json index 8f53c0fe2dd53..6c992299de5cc 100644 --- a/packages/notebook/tsconfig.json +++ b/packages/notebook/tsconfig.json @@ -20,6 +20,9 @@ }, { "path": "../monaco" + }, + { + "path": "../outline-view" } ] } diff --git a/packages/outline-view/src/browser/outline-breadcrumbs-contribution.tsx b/packages/outline-view/src/browser/outline-breadcrumbs-contribution.tsx index 285d76cc928c3..530e194a49e08 100644 --- a/packages/outline-view/src/browser/outline-breadcrumbs-contribution.tsx +++ b/packages/outline-view/src/browser/outline-breadcrumbs-contribution.tsx @@ -30,10 +30,14 @@ export interface BreadcrumbPopupOutlineViewFactory { export class BreadcrumbPopupOutlineView extends OutlineViewWidget { @inject(OpenerService) protected readonly openerService: OpenerService; + @inject(OutlineViewService) + protected readonly outlineViewService: OutlineViewService; + protected override tapNode(node?: TreeNode): void { if (UriSelection.is(node) && OutlineSymbolInformationNode.hasRange(node)) { open(this.openerService, node.uri, { selection: node.range }); } else { + this.outlineViewService.didTapNode(node as OutlineSymbolInformationNode); super.tapNode(node); } } diff --git a/packages/outline-view/src/browser/outline-view-service.ts b/packages/outline-view/src/browser/outline-view-service.ts index 278813371f661..dd2f58465a0f7 100644 --- a/packages/outline-view/src/browser/outline-view-service.ts +++ b/packages/outline-view/src/browser/outline-view-service.ts @@ -30,6 +30,7 @@ export class OutlineViewService implements WidgetFactory { protected readonly onDidChangeOpenStateEmitter = new Emitter(); protected readonly onDidSelectEmitter = new Emitter(); protected readonly onDidOpenEmitter = new Emitter(); + protected readonly onDidTapNodeEmitter = new Emitter(); constructor(@inject(OutlineViewWidgetFactory) protected factory: OutlineViewWidgetFactory) { } @@ -49,10 +50,18 @@ export class OutlineViewService implements WidgetFactory { return this.onDidChangeOpenStateEmitter.event; } + get onDidTapNode(): Event { + return this.onDidTapNodeEmitter.event; + } + get open(): boolean { return this.widget !== undefined && this.widget.isVisible; } + didTapNode(node: OutlineSymbolInformationNode): void { + this.onDidTapNodeEmitter.fire(node); + } + /** * Publish the collection of outline view symbols. * - Publishing includes setting the `OutlineViewWidget` tree with symbol information. diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index b7fc61a1e8df8..a753581864f20 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -16,6 +16,7 @@ "@theia/typehierarchy": "1.48.0", "@theia/userstorage": "1.48.0", "@theia/workspace": "1.48.0", + "@theia/outline-view": "1.48.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index d7bce9e0a203a..092f71e93d16a 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -79,6 +79,7 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service'; import * as monaco from '@theia/monaco-editor-core'; import { VSCodeExtensionUri } from '../common/plugin-vscode-uri'; import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings'; +import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution'; export namespace VscodeCommands { export const OPEN: Command = { @@ -180,6 +181,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly windowService: WindowService; @inject(MessageService) protected readonly messageService: MessageService; + @inject(OutlineViewContribution) + protected outlineViewContribution: OutlineViewContribution; private async openWith(commandId: string, resource: URI, columnOrOptions?: ViewColumn | TextDocumentShowOptions, openerId?: string): Promise { if (!resource) { @@ -912,6 +915,11 @@ export class PluginVscodeCommandsContribution implements CommandContribution { }; } }); + + // required by Jupyter for the show table of contents action + commands.registerCommand({ id: 'outline.focus' }, { + execute: () => this.outlineViewContribution.openView({ activate: true }) + }); } private async resolveLanguageId(resource: URI): Promise { diff --git a/packages/plugin-ext-vscode/tsconfig.json b/packages/plugin-ext-vscode/tsconfig.json index deb8724d9036a..c1cb4d0e2f0d2 100644 --- a/packages/plugin-ext-vscode/tsconfig.json +++ b/packages/plugin-ext-vscode/tsconfig.json @@ -32,6 +32,9 @@ { "path": "../navigator" }, + { + "path": "../outline-view" + }, { "path": "../plugin" }, From bce99c299d7a01573330482ce26563eb6b00a6e9 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 5 Apr 2024 14:44:13 +0200 Subject: [PATCH 167/441] Disable clear all outputs in notebook main toolbar (#13569) * reenable initial reading of execution summary Signed-off-by: Jonah Iden * disabling clear all outputs command in notebook toolbar when no outputs are available Signed-off-by: Jonah Iden * fixed build Signed-off-by: Jonah Iden * formatting Co-authored-by: Mark Sujew --------- Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- packages/core/src/common/event.ts | 18 +++++++++++ .../notebook-actions-contribution.ts | 10 +++++-- .../notebook-cell-actions-contribution.ts | 8 ++++- .../notebook/src/browser/notebook-types.ts | 2 ++ .../service/notebook-context-manager.ts | 13 ++++++-- packages/notebook/src/browser/style/index.css | 5 ++-- .../src/browser/view-model/notebook-model.ts | 30 +++++++++++-------- .../browser/view/notebook-main-toolbar.tsx | 23 +++++++++----- 8 files changed, 81 insertions(+), 28 deletions(-) diff --git a/packages/core/src/common/event.ts b/packages/core/src/common/event.ts index 99fdef2ea5fde..00d6e0d00eab5 100644 --- a/packages/core/src/common/event.ts +++ b/packages/core/src/common/event.ts @@ -467,3 +467,21 @@ export class AsyncEmitter extends Emitter { } } + +export class QueueableEmitter extends Emitter { + + currentQueue?: T[]; + + queue(...arg: T[]): void { + if (!this.currentQueue) { + this.currentQueue = []; + } + this.currentQueue.push(...arg); + } + + override fire(): void { + super.fire(this.currentQueue || []); + this.currentQueue = undefined; + } + +} diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 50f3ebf73a9ef..c0fc2fb10f2d0 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -24,7 +24,7 @@ import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; -import { NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from './notebook-context-keys'; +import { NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -141,7 +141,10 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon )); commands.registerCommand(NotebookCommands.CLEAR_ALL_OUTPUTS_COMMAND, this.editableCommandHandler( - notebookModel => notebookModel.cells.forEach(cell => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: [] })) + notebookModel => notebookModel.applyEdits(notebookModel.cells.map(cell => ({ + editType: CellEditType.Output, + handle: cell.handle, deleteCount: cell.outputs.length, outputs: [] + })), false) )); commands.registerCommand(NotebookCommands.CHANGE_SELECTED_CELL, @@ -217,7 +220,8 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon commandId: NotebookCommands.CLEAR_ALL_OUTPUTS_COMMAND.id, label: nls.localizeByDefault('Clear All Outputs'), icon: codicon('clear-all'), - order: '30' + order: '30', + when: NOTEBOOK_HAS_OUTPUTS }); // other items } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 37766a87c0bb5..1364bb9effdea 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -301,7 +301,13 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }); commands.registerCommand(NotebookCellCommands.CLEAR_OUTPUTS_COMMAND, this.editableCellCommandHandler( - (_, cell) => cell.spliceNotebookCellOutputs({ start: 0, deleteCount: (cell ?? this.getSelectedCell()).outputs.length, newOutputs: [] }) + (notebook, cell) => notebook.applyEdits([{ + editType: CellEditType.Output, + handle: cell.handle, + outputs: [], + deleteCount: cell.outputs.length, + append: false + }], true) )); commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( (_, __, output) => output?.requestOutputPresentationUpdate() diff --git a/packages/notebook/src/browser/notebook-types.ts b/packages/notebook/src/browser/notebook-types.ts index 1f82f071755e7..f1bb70b5eab11 100644 --- a/packages/notebook/src/browser/notebook-types.ts +++ b/packages/notebook/src/browser/notebook-types.ts @@ -111,6 +111,7 @@ export interface CellOutputEdit { editType: CellEditType.Output; index: number; outputs: CellOutput[]; + deleteCount?: number; append?: boolean; } @@ -118,6 +119,7 @@ export interface CellOutputEditByHandle { editType: CellEditType.Output; handle: number; outputs: CellOutput[]; + deleteCount?: number; append?: boolean; } diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 7980fcfa82090..e9ece94ae316a 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -22,12 +22,12 @@ import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, - NOTEBOOK_CELL_TYPE, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, + NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_VIEW_TYPE } from '../contributions/notebook-context-keys'; import { NotebookEditorWidget } from '../notebook-editor-widget'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; -import { CellKind } from '../../common'; +import { CellKind, NotebookCellsChangeType } from '../../common'; import { NotebookExecutionStateService } from './notebook-execution-state-service'; @injectable() @@ -73,6 +73,15 @@ export class NotebookContextManager { } })); + widget.model?.onDidChangeContent(events => { + if (events.some(e => e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Output)) { + this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, widget.model?.cells.some(cell => cell.outputs.length > 0)); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_HAS_OUTPUTS])); + } + }); + + this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, !!widget.model?.cells.find(cell => cell.outputs.length > 0)); + // Cell Selection realted keys this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); widget.model?.onDidChangeSelectedCell(e => { diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 9f1733a3fb3b6..a106da20e730c 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -213,8 +213,9 @@ cursor: pointer; } -.theia-notebook-main-toolbar-item:hover { - background-color: var(--theia-toolbar-hoverBackground); +.theia-notebook-main-toolbar-item.theia-mod-disabled:hover { + background-color: transparent; + cursor: default; } .theia-notebook-main-toolbar-item-text { diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index f6762feaa4f53..c636cb51f16d4 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable, Emitter, Event, Resource, URI } from '@theia/core'; +import { Disposable, Emitter, Event, QueueableEmitter, Resource, URI } from '@theia/core'; import { Saveable, SaveOptions } from '@theia/core/lib/browser'; import { CellData, CellEditType, CellUri, NotebookCellInternalMetadata, @@ -65,7 +65,7 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidAddOrRemoveCellEmitter = new Emitter(); readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event; - protected readonly onDidChangeContentEmitter = new Emitter(); + protected readonly onDidChangeContentEmitter = new QueueableEmitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; protected readonly onDidChangeSelectedCellEmitter = new Emitter(); @@ -284,13 +284,18 @@ export class NotebookModel implements Saveable, Disposable { } else { // could definitely be more efficient. See vscode __spliceNotebookCellOutputs2 // For now, just replace the whole existing output with the new output - cell.spliceNotebookCellOutputs({ start: 0, deleteCount: cell.outputs.length, newOutputs: edit.outputs }); + cell.spliceNotebookCellOutputs({ start: 0, deleteCount: edit.deleteCount ?? cell.outputs.length, newOutputs: edit.outputs }); } - + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.Output, index: cellIndex, outputs: cell.outputs, append: edit.append ?? false }); break; } case CellEditType.OutputItems: cell.changeOutputItems(edit.outputId, !!edit.append, edit.items); + this.onDidChangeContentEmitter.queue({ + kind: NotebookCellsChangeType.OutputItem, index: cellIndex, outputItems: edit.items, + outputId: edit.outputId, append: edit.append ?? false + }); + break; case CellEditType.Metadata: this.changeCellMetadata(this.cells[cellIndex], edit.metadata, computeUndoRedo); @@ -311,12 +316,15 @@ export class NotebookModel implements Saveable, Disposable { this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo); break; } + // if selected cell is affected update it because it can potentially have been replaced if (cell === this.selectedCell) { this.setSelectedCell(this.cells[cellIndex]); } } + this.onDidChangeContentEmitter.fire(); + } protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise { @@ -355,7 +363,7 @@ export class NotebookModel implements Saveable, Disposable { await Promise.all(cells.map(cell => cell.resolveTextModel())); this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); - this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes }); if (cells.length > 0) { this.setSelectedCell(cells[cells.length - 1]); cells[cells.length - 1].requestEdit(); @@ -373,9 +381,7 @@ export class NotebookModel implements Saveable, Disposable { } cell.internalMetadata = newInternalMetadata; - this.onDidChangeContentEmitter.fire([ - { kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata } - ]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellInternalMetadata, index: this.cells.indexOf(cell), internalMetadata: newInternalMetadata }); } protected updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean): void { @@ -388,7 +394,7 @@ export class NotebookModel implements Saveable, Disposable { } this.metadata = metadata; - this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }); } protected changeCellMetadataPartial(cell: NotebookCellModel, metadata: NullablePartialNotebookCellMetadata, computeUndoRedo: boolean): void { @@ -420,7 +426,7 @@ export class NotebookModel implements Saveable, Disposable { } cell.metadata = metadata; - this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata }]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata }); } protected changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void { @@ -430,7 +436,7 @@ export class NotebookModel implements Saveable, Disposable { cell.language = languageId; - this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ChangeCellLanguage, index: this.cells.indexOf(cell), language: languageId }); } protected moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean { @@ -443,7 +449,7 @@ export class NotebookModel implements Saveable, Disposable { const cells = this.cells.splice(fromIndex, length); this.cells.splice(toIndex, 0, ...cells); - this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }]); + this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.Move, index: fromIndex, length, newIdx: toIndex, cells }); return true; } diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index 12dff11891552..56ee76d0b850c 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -57,6 +57,10 @@ export class NotebookMainToolbar extends React.Component; } - protected renderMenuItem(item: MenuNode): React.ReactNode { + protected renderMenuItem(item: MenuNode, submenu?: string): React.ReactNode { if (item.role === CompoundMenuNodeRole.Group) { - const itemNodes = ArrayUtils.coalesce(item.children?.map(child => this.renderMenuItem(child)) ?? []); + const itemNodes = ArrayUtils.coalesce(item.children?.map(child => this.renderMenuItem(child, item.id)) ?? []); return {itemNodes} {itemNodes && itemNodes.length > 0 && } ; - } else if (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode)) { + } else if ((this.nativeSubmenus.includes(submenu ?? '')) || !item.when || this.props.contextKeyService.match(item.when, this.props.editorNode)) { const visibleCommand = Boolean(this.props.commandRegistry.getVisibleHandler(item.command ?? '', this.props.notebookModel)); if (!visibleCommand) { return undefined; } const title = (this.props.commandRegistry.getCommand(item.command ?? '') as NotebookCommand)?.tooltip ?? item.label; - return
    { - if (item.command) { + if (item.command && (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode))) { this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel); } }}> @@ -133,15 +137,18 @@ export class NotebookMainToolbar extends React.Component): void { + protected getAdditionalClasses(item: MenuNode): string { + return !item.when || this.props.contextKeyService.match(item.when, this.props.editorNode) ? '' : ' theia-mod-disabled'; + } + + protected getAllContextKeys(menus: readonly MenuNode[], keySet: Set): void { menus.filter(item => item.when) .forEach(item => this.props.contextKeyService.parseKeys(item.when!)?.forEach(key => keySet.add(key))); From 9cadc9d4e9476408fd74c966fa37abc9886de314 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 8 Apr 2024 16:17:24 +0200 Subject: [PATCH 168/441] Fix notebook cell EOL splitting (#13574) --- .../main/browser/notebooks/notebook-dto.ts | 2 +- .../src/plugin/notebook/notebook-document.ts | 19 ++----------------- .../src/plugin/notebook/notebooks.ts | 11 ++--------- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts index 491613ff55267..b80f7b05db948 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-dto.ts @@ -94,7 +94,7 @@ export namespace NotebookDto { return { handle: cell.handle, uri: cell.uri.toComponents(), - source: cell.text.split(eol), + source: cell.text.split(/\r?\n/g), eol, language: cell.language, cellKind: cell.cellKind, diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts index 9ca4db6f35ee1..456e218b3d250 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts @@ -26,7 +26,6 @@ import { Disposable, URI } from '@theia/core'; import * as typeConverters from '../type-converters'; import { ModelAddedData, NotebookCellDto, NotebookCellsChangedEventDto, NotebookModelAddedData, NotebookOutputDto } from '../../common'; import { NotebookRange } from '../types-impl'; -import { UriComponents } from '../../common/uri-components'; import { DocumentsExtImpl } from '../documents'; class RawContentChangeEvent { @@ -49,7 +48,7 @@ class RawContentChangeEvent { export class Cell { - static asModelAddData(notebook: theia.NotebookDocument, cell: NotebookCellDto): ModelAddedData & { notebook: theia.NotebookDocument } { + static asModelAddData(cell: NotebookCellDto): ModelAddedData { return { EOL: cell.eol, lines: cell.source, @@ -57,7 +56,6 @@ export class Cell { uri: cell.uri, isDirty: false, versionId: 1, - notebook, modeId: '' }; } @@ -348,8 +346,6 @@ export class NotebookDocument implements Disposable { } const contentChangeEvents: RawContentChangeEvent[] = []; - const addedCellDocuments: ModelAddedData[] = []; - const removedCellDocuments: UriComponents[] = []; splices.reverse().forEach(splice => { const cellDtos = splice.newItems; @@ -357,18 +353,8 @@ export class NotebookDocument implements Disposable { const extCell = new Cell(this, this.editorsAndDocuments, cell); if (!initialization) { - addedCellDocuments.push(Cell.asModelAddData(this.apiNotebook, cell)); this.editorsAndDocuments.$acceptEditorsAndDocumentsDelta({ - addedDocuments: [ - { - uri: cell.uri, - versionId: 1, - lines: cell.source, - EOL: cell.eol, - modeId: '', - isDirty: false - } - ] + addedDocuments: [Cell.asModelAddData(cell)] }); } return extCell; @@ -377,7 +363,6 @@ export class NotebookDocument implements Disposable { const changeEvent = new RawContentChangeEvent(splice.start, splice.deleteCount, [], newCells); const deletedItems = this.cells.splice(splice.start, splice.deleteCount, ...newCells); for (const cell of deletedItems) { - removedCellDocuments.push(cell.uri.toComponents()); changeEvent.deletedItems.push(cell.apiCell); } contentChangeEvents.push(changeEvent); diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index d21a99588636a..975dbd752e42e 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -32,7 +32,7 @@ import { UriComponents } from '../../common/uri-components'; import { CommandsConverter } from '../command-registry'; import * as typeConverters from '../type-converters'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { NotebookDocument } from './notebook-document'; +import { Cell, NotebookDocument } from './notebook-document'; import { NotebookEditor } from './notebook-editor'; import { EditorsAndDocumentsExtImpl } from '../editors-and-documents'; import { DocumentsExtImpl } from '../documents'; @@ -243,14 +243,7 @@ export class NotebooksExtImpl implements NotebooksExt { this.documents.set(uri.toString(), document); this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ - addedDocuments: modelData.cells.map(cell => ({ - uri: cell.uri, - versionId: 1, - lines: cell.source, - EOL: cell.eol, - modeId: '', - isDirty: false - })) + addedDocuments: modelData.cells.map(cell => Cell.asModelAddData(cell)) }); this.onDidOpenNotebookDocumentEmitter.fire(document.apiNotebook); From 3c05963dbd959941f48a36752ed7b9cacf15bec5 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 8 Apr 2024 16:22:51 +0200 Subject: [PATCH 169/441] Update "Open With..." command UX (#13573) --- .../browser/frontend-application-module.ts | 3 + packages/core/src/browser/index.ts | 1 + .../core/src/browser/open-with-service.ts | 93 +++++++++++++++++++ packages/editor/src/browser/editor-manager.ts | 16 +++- .../src/browser/file-navigator-commands.ts | 9 +- .../src/browser/navigator-contribution.ts | 35 ++++--- .../src/browser/notebook-open-handler.ts | 68 ++++++++++---- .../src/browser/notebook-type-registry.ts | 32 +++++-- .../plugin-custom-editor-registry.ts | 50 +++------- .../browser/plugin-contribution-handler.ts | 4 +- .../src/browser/workspace-commands.ts | 20 +--- 11 files changed, 227 insertions(+), 104 deletions(-) create mode 100644 packages/core/src/browser/open-with-service.ts diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index ebb8966a4caa3..b343a8b4125c8 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -140,6 +140,7 @@ import { HoverService } from './hover-service'; import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './shell/additional-views-menu-widget'; import { LanguageIconLabelProvider } from './language-icon-provider'; import { bindTreePreferences } from './tree'; +import { OpenWithService } from './open-with-service'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -224,6 +225,8 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(CommandOpenHandler).toSelf().inSingletonScope(); bind(OpenHandler).toService(CommandOpenHandler); + bind(OpenWithService).toSelf().inSingletonScope(); + bind(TooltipServiceImpl).toSelf().inSingletonScope(); bind(TooltipService).toService(TooltipServiceImpl); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index eecaa565401dc..fe5906c3be729 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -19,6 +19,7 @@ export * from './frontend-application'; export * from './frontend-application-contribution'; export * from './keyboard'; export * from './opener-service'; +export * from './open-with-service'; export * from './browser'; export * from './context-menu-renderer'; export * from './widgets'; diff --git a/packages/core/src/browser/open-with-service.ts b/packages/core/src/browser/open-with-service.ts new file mode 100644 index 0000000000000..c186feb34d559 --- /dev/null +++ b/packages/core/src/browser/open-with-service.ts @@ -0,0 +1,93 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from 'inversify'; +import { Disposable } from '../common/disposable'; +import { nls } from '../common/nls'; +import { MaybePromise } from '../common/types'; +import { URI } from '../common/uri'; +import { QuickInputService } from './quick-input'; + +export interface OpenWithHandler { + /** + * A unique id of this handler. + */ + readonly id: string; + /** + * A human-readable name of this handler. + */ + readonly label?: string; + /** + * A human-readable provider name of this handler. + */ + readonly providerName?: string; + /** + * A css icon class of this handler. + */ + readonly iconClass?: string; + /** + * Test whether this handler can open the given URI for given options. + * Return a nonzero number if this handler can open; otherwise it cannot. + * Never reject. + * + * A returned value indicating a priority of this handler. + */ + canHandle(uri: URI): number; + /** + * Open a widget for the given URI and options. + * Resolve to an opened widget or undefined, e.g. if a page is opened. + * Never reject if `canHandle` return a positive number; otherwise should reject. + */ + open(uri: URI): MaybePromise; +} + +@injectable() +export class OpenWithService { + + @inject(QuickInputService) + protected readonly quickInputService: QuickInputService; + + protected readonly handlers: OpenWithHandler[] = []; + + registerHandler(handler: OpenWithHandler): Disposable { + this.handlers.push(handler); + return Disposable.create(() => { + const index = this.handlers.indexOf(handler); + if (index !== -1) { + this.handlers.splice(index, 1); + } + }); + } + + async openWith(uri: URI): Promise { + const handlers = this.getHandlers(uri); + const result = await this.quickInputService.pick(handlers.map(handler => ({ + handler: handler, + label: handler.label ?? handler.id, + detail: handler.providerName + })), { + placeHolder: nls.localizeByDefault("Select editor for '{0}'", uri.path.base) + }); + if (result) { + return result.handler.open(uri); + } + } + + getHandlers(uri: URI): OpenWithHandler[] { + const map = new Map(this.handlers.map(handler => [handler, handler.canHandle(uri)])); + return this.handlers.filter(handler => map.get(handler)! > 0).sort((a, b) => map.get(b)! - map.get(a)!); + } +} diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 6b1b2bd8ad7d7..1da41c8a31246 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -16,8 +16,8 @@ import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; -import { RecursivePartial, Emitter, Event, MaybePromise, CommandService } from '@theia/core/lib/common'; -import { WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands } from '@theia/core/lib/browser'; +import { RecursivePartial, Emitter, Event, MaybePromise, CommandService, nls } from '@theia/core/lib/common'; +import { WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands, OpenWithService } from '@theia/core/lib/browser'; import { EditorWidget } from './editor-widget'; import { Range, Position, Location, TextEditor } from './editor'; import { EditorWidgetFactory } from './editor-widget-factory'; @@ -38,7 +38,7 @@ export class EditorManager extends NavigatableWidgetOpenHandler { readonly id = EditorWidgetFactory.ID; - readonly label = 'Code Editor'; + readonly label = nls.localizeByDefault('Text Editor'); protected readonly editorCounters = new Map(); @@ -56,6 +56,7 @@ export class EditorManager extends NavigatableWidgetOpenHandler { @inject(CommandService) protected readonly commands: CommandService; @inject(PreferenceService) protected readonly preferenceService: PreferenceService; + @inject(OpenWithService) protected readonly openWithService: OpenWithService; @postConstruct() protected override init(): void { @@ -84,6 +85,15 @@ export class EditorManager extends NavigatableWidgetOpenHandler { this.addRecentlyVisible(widget); } } + this.openWithService.registerHandler({ + id: this.id, + label: this.label, + providerName: nls.localizeByDefault('Built-in'), + // Higher priority than any other handler + // so that the text editor always appears first in the quick pick + canHandle: uri => this.canHandle(uri) * 100, + open: uri => this.open(uri) + }); this.updateCurrentEditor(); } diff --git a/packages/navigator/src/browser/file-navigator-commands.ts b/packages/navigator/src/browser/file-navigator-commands.ts index fa1c7c3b07edc..aefda94eb0fb2 100644 --- a/packages/navigator/src/browser/file-navigator-commands.ts +++ b/packages/navigator/src/browser/file-navigator-commands.ts @@ -52,11 +52,12 @@ export namespace FileNavigatorCommands { category: CommonCommands.FILE_CATEGORY, label: 'Focus on Files Explorer' }); - export const OPEN = Command.toDefaultLocalizedCommand({ + export const OPEN: Command = { id: 'navigator.open', - category: CommonCommands.FILE_CATEGORY, - label: 'Open' - }); + }; + export const OPEN_WITH: Command = { + id: 'navigator.openWith', + }; export const NEW_FILE_TOOLBAR: Command = { id: `${WorkspaceCommands.NEW_FILE.id}.toolbar`, iconClass: codicon('new-file') diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts index 2cf9ab6fa55a4..7a914a2d9ea1a 100644 --- a/packages/navigator/src/browser/navigator-contribution.ts +++ b/packages/navigator/src/browser/navigator-contribution.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; import { CommonCommands, @@ -31,7 +31,8 @@ import { ApplicationShell, TabBar, Title, - SHELL_TABBAR_CONTEXT_MENU + SHELL_TABBAR_CONTEXT_MENU, + OpenWithService } from '@theia/core/lib/browser'; import { FileDownloadCommands } from '@theia/filesystem/lib/browser/download/file-download-command-contribution'; import { @@ -40,6 +41,7 @@ import { MenuModelRegistry, MenuPath, Mutable, + QuickInputService, } from '@theia/core/lib/common'; import { DidCreateNewResourceEvent, @@ -113,6 +115,7 @@ export namespace NavigatorContextMenu { /** @deprecated use MODIFICATION */ export const ACTIONS = MODIFICATION; + /** @deprecated use the `FileNavigatorCommands.OPEN_WITH` command */ export const OPEN_WITH = [...NAVIGATION, 'open_with']; } @@ -148,6 +151,12 @@ export class FileNavigatorContribution extends AbstractViewContribution this.openWithService.getHandlers(uri).length > 0, + isVisible: uri => this.openWithService.getHandlers(uri).length > 0, + execute: uri => this.openWithService.openWith(uri) + })); registry.registerCommand(OpenEditorsCommands.CLOSE_ALL_TABS_FROM_TOOLBAR, { execute: widget => this.withOpenEditorsWidget(widget, () => this.shell.closeMany(this.editorWidgets)), isEnabled: widget => this.withOpenEditorsWidget(widget, () => true), @@ -366,18 +380,11 @@ export class FileNavigatorContribution extends AbstractViewContribution { - for (const opener of openers) { - const openWithCommand = WorkspaceCommands.FILE_OPEN_WITH(opener); - registry.registerMenuAction(NavigatorContextMenu.OPEN_WITH, { - commandId: openWithCommand.id, - label: opener.label, - icon: opener.iconClass - }); - } + label: nls.localizeByDefault('Open') + }); + registry.registerMenuAction(NavigatorContextMenu.NAVIGATION, { + commandId: FileNavigatorCommands.OPEN_WITH.id, + label: nls.localizeByDefault('Open With...') }); registry.registerMenuAction(NavigatorContextMenu.CLIPBOARD, { diff --git a/packages/notebook/src/browser/notebook-open-handler.ts b/packages/notebook/src/browser/notebook-open-handler.ts index b260e24c13ac3..d1f1ab307ef1a 100644 --- a/packages/notebook/src/browser/notebook-open-handler.ts +++ b/packages/notebook/src/browser/notebook-open-handler.ts @@ -14,32 +14,56 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { URI, MaybePromise } from '@theia/core'; +import { URI, MaybePromise, Disposable } from '@theia/core'; import { NavigatableWidgetOpenHandler, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { NotebookFileSelector, NotebookTypeDescriptor } from '../common/notebook-protocol'; -import { NotebookTypeRegistry } from './notebook-type-registry'; import { NotebookEditorWidget } from './notebook-editor-widget'; import { match } from '@theia/core/lib/common/glob'; import { NotebookEditorWidgetOptions } from './notebook-editor-widget-factory'; +export interface NotebookWidgetOpenerOptions extends WidgetOpenerOptions { + notebookType?: string; +} + @injectable() export class NotebookOpenHandler extends NavigatableWidgetOpenHandler { - id: string = 'notebook'; + readonly id = NotebookEditorWidget.ID; + + protected notebookTypes: NotebookTypeDescriptor[] = []; - @inject(NotebookTypeRegistry) - protected notebookTypeRegistry: NotebookTypeRegistry; + registerNotebookType(notebookType: NotebookTypeDescriptor): Disposable { + this.notebookTypes.push(notebookType); + return Disposable.create(() => { + this.notebookTypes.splice(this.notebookTypes.indexOf(notebookType), 1); + }); + } + + canHandle(uri: URI, options?: NotebookWidgetOpenerOptions): MaybePromise { + if (options?.notebookType) { + return this.canHandleType(uri, this.notebookTypes.find(type => type.type === options.notebookType)); + } + return Math.max(...this.notebookTypes.map(type => this.canHandleType(uri, type))); + } + + canHandleType(uri: URI, notebookType?: NotebookTypeDescriptor): number { + if (notebookType?.selector && this.matches(notebookType.selector, uri)) { + return this.calculatePriority(notebookType); + } else { + return 0; + } + } - canHandle(uri: URI, options?: WidgetOpenerOptions | undefined): MaybePromise { - const priorities = this.notebookTypeRegistry.notebookTypes - .filter(notebook => notebook.selector && this.matches(notebook.selector, uri)) - .map(notebook => this.calculatePriority(notebook)); - return Math.max(...priorities); + protected calculatePriority(notebookType: NotebookTypeDescriptor | undefined): number { + if (!notebookType) { + return 0; + } + return notebookType.priority === 'option' ? 100 : 200; } protected findHighestPriorityType(uri: URI): NotebookTypeDescriptor | undefined { - const matchingTypes = this.notebookTypeRegistry.notebookTypes + const matchingTypes = this.notebookTypes .filter(notebookType => notebookType.selector && this.matches(notebookType.selector, uri)) .map(notebookType => ({ descriptor: notebookType, priority: this.calculatePriority(notebookType) })); @@ -56,15 +80,19 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler { + return super.open(uri, options); } - protected override createWidgetOptions(uri: URI, options?: WidgetOpenerOptions | undefined): NotebookEditorWidgetOptions { + protected override createWidgetOptions(uri: URI, options?: NotebookWidgetOpenerOptions): NotebookEditorWidgetOptions { const widgetOptions = super.createWidgetOptions(uri, options); + if (options?.notebookType) { + return { + notebookType: options.notebookType, + ...widgetOptions + }; + } const notebookType = this.findHighestPriorityType(uri); if (!notebookType) { throw new Error('No notebook types registered for uri: ' + uri.toString()); @@ -75,11 +103,11 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler this.selectorMatches(selector, resource)); } - selectorMatches(selector: NotebookFileSelector, resource: URI): boolean { + protected selectorMatches(selector: NotebookFileSelector, resource: URI): boolean { return !!selector.filenamePattern && match(selector.filenamePattern, resource.path.name + resource.path.ext); } diff --git a/packages/notebook/src/browser/notebook-type-registry.ts b/packages/notebook/src/browser/notebook-type-registry.ts index 923d982c22cb9..1911e5bb9b3b4 100644 --- a/packages/notebook/src/browser/notebook-type-registry.ts +++ b/packages/notebook/src/browser/notebook-type-registry.ts @@ -13,22 +13,42 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable } from '@theia/core'; -import { injectable } from '@theia/core/shared/inversify'; + +import { Disposable, DisposableCollection } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { OpenWithService } from '@theia/core/lib/browser'; import { NotebookTypeDescriptor } from '../common/notebook-protocol'; +import { NotebookOpenHandler } from './notebook-open-handler'; @injectable() export class NotebookTypeRegistry { + + @inject(OpenWithService) + protected readonly openWithService: OpenWithService; + + @inject(NotebookOpenHandler) + protected readonly notebookOpenHandler: NotebookOpenHandler; + private readonly _notebookTypes: NotebookTypeDescriptor[] = []; get notebookTypes(): readonly NotebookTypeDescriptor[] { return this._notebookTypes; } - registerNotebookType(type: NotebookTypeDescriptor): Disposable { - this._notebookTypes.push(type); - return Disposable.create(() => { + registerNotebookType(type: NotebookTypeDescriptor, providerName: string): Disposable { + const toDispose = new DisposableCollection(); + toDispose.push(Disposable.create(() => { this._notebookTypes.splice(this._notebookTypes.indexOf(type), 1); - }); + })); + this._notebookTypes.push(type); + toDispose.push(this.notebookOpenHandler.registerNotebookType(type)); + toDispose.push(this.openWithService.registerHandler({ + id: type.type, + label: type.displayName, + providerName, + canHandle: uri => this.notebookOpenHandler.canHandleType(uri, type), + open: uri => this.notebookOpenHandler.open(uri, { notebookType: type.type }) + })); + return toDispose; } } diff --git a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts index fbb58b10f0479..50bfdb287f8a0 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts @@ -15,15 +15,11 @@ // ***************************************************************************** import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; -import { CustomEditor } from '../../../common'; +import { CustomEditor, DeployedPlugin } from '../../../common'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { CustomEditorOpener } from './custom-editor-opener'; -import { WorkspaceCommands } from '@theia/workspace/lib/browser'; -import { CommandRegistry, Emitter, MenuModelRegistry } from '@theia/core'; -import { SelectionService } from '@theia/core/lib/common'; -import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler'; -import { NavigatorContextMenu } from '@theia/navigator/lib//browser/navigator-contribution'; -import { ApplicationShell, DefaultOpenerService, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; +import { Emitter } from '@theia/core'; +import { ApplicationShell, DefaultOpenerService, OpenWithService, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; import { CustomEditorWidget } from './custom-editor-widget'; @injectable() @@ -38,21 +34,15 @@ export class PluginCustomEditorRegistry { @inject(DefaultOpenerService) protected readonly defaultOpenerService: DefaultOpenerService; - @inject(MenuModelRegistry) - protected readonly menuModelRegistry: MenuModelRegistry; - - @inject(CommandRegistry) - protected readonly commandRegistry: CommandRegistry; - - @inject(SelectionService) - protected readonly selectionService: SelectionService; - @inject(WidgetManager) protected readonly widgetManager: WidgetManager; @inject(ApplicationShell) protected readonly shell: ApplicationShell; + @inject(OpenWithService) + protected readonly openWithService: OpenWithService; + @postConstruct() protected init(): void { this.widgetManager.onDidCreateWidget(({ factoryId, widget }) => { @@ -71,7 +61,7 @@ export class PluginCustomEditorRegistry { }); } - registerCustomEditor(editor: CustomEditor): Disposable { + registerCustomEditor(editor: CustomEditor, plugin: DeployedPlugin): Disposable { if (this.editors.has(editor.viewType)) { console.warn('editor with such id already registered: ', JSON.stringify(editor)); return Disposable.NULL; @@ -87,26 +77,14 @@ export class PluginCustomEditorRegistry { this.widgetManager ); toDispose.push(this.defaultOpenerService.addHandler(editorOpenHandler)); - - const openWithCommand = WorkspaceCommands.FILE_OPEN_WITH(editorOpenHandler); - toDispose.push( - this.menuModelRegistry.registerMenuAction( - NavigatorContextMenu.OPEN_WITH, - { - commandId: openWithCommand.id, - label: editorOpenHandler.label - } - ) - ); toDispose.push( - this.commandRegistry.registerCommand( - openWithCommand, - UriAwareCommandHandler.MonoSelect(this.selectionService, { - execute: uri => editorOpenHandler.open(uri), - isEnabled: uri => editorOpenHandler.canHandle(uri) > 0, - isVisible: uri => editorOpenHandler.canHandle(uri) > 0 - }) - ) + this.openWithService.registerHandler({ + id: editor.viewType, + label: editorOpenHandler.label, + providerName: plugin.metadata.model.displayName, + canHandle: uri => editorOpenHandler.canHandle(uri), + open: uri => editorOpenHandler.open(uri) + }) ); toDispose.push( editorOpenHandler.onDidOpenCustomEditor(event => this.resolveWidget(event[0], event[1])) diff --git a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts index cf5aa974efb7c..b6e8c58dc7c47 100644 --- a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts @@ -288,7 +288,7 @@ export class PluginContributionHandler { if (contributions.customEditors) { for (const customEditor of contributions.customEditors) { pushContribution(`customEditors.${customEditor.viewType}`, - () => this.customEditorRegistry.registerCustomEditor(customEditor) + () => this.customEditorRegistry.registerCustomEditor(customEditor, plugin) ); } } @@ -434,7 +434,7 @@ export class PluginContributionHandler { if (contributions.notebooks) { for (const notebook of contributions.notebooks) { pushContribution(`notebook.${notebook.type}`, - () => this.notebookTypeRegistry.registerNotebookType(notebook) + () => this.notebookTypeRegistry.registerNotebookType(notebook, plugin.metadata.model.displayName) ); } } diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index 2e7625f434485..1f9350d1d6bcb 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -98,6 +98,7 @@ export namespace WorkspaceCommands { category: FILE_CATEGORY, label: 'New Folder...' }); + /** @deprecated Use the `OpenWithService` instead */ export const FILE_OPEN_WITH = (opener: OpenHandler): Command => ({ id: `file.openWith.${opener.id}` }); @@ -224,7 +225,6 @@ export class WorkspaceCommandContribution implements CommandContribution { } registerCommands(registry: CommandRegistry): void { - this.registerOpenWith(registry); registry.registerCommand(WorkspaceCommands.NEW_FILE, this.newWorkspaceRootUriAwareCommandHandler({ execute: uri => this.getDirectory(uri).then(parent => { if (parent) { @@ -356,24 +356,6 @@ export class WorkspaceCommandContribution implements CommandContribution { })); } - openers: OpenHandler[]; - protected async registerOpenWith(registry: CommandRegistry): Promise { - if (this.openerService.onDidChangeOpeners) { - this.openerService.onDidChangeOpeners(async e => { - this.openers = await this.openerService.getOpeners(); - }); - } - this.openers = await this.openerService.getOpeners(); - for (const opener of this.openers) { - const openWithCommand = WorkspaceCommands.FILE_OPEN_WITH(opener); - registry.registerCommand(openWithCommand, this.newUriAwareCommandHandler({ - execute: uri => opener.open(uri), - isEnabled: uri => opener.canHandle(uri) > 0, - isVisible: uri => opener.canHandle(uri) > 0 && this.areMultipleOpenHandlersPresent(this.openers, uri) - })); - } - } - protected newUriAwareCommandHandler(handler: UriCommandHandler): UriAwareCommandHandler { return UriAwareCommandHandler.MonoSelect(this.selectionService, handler); } From cadb0201f8c48f03c43a29acdd8bb3845fc9b7c2 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 9 Apr 2024 10:43:39 +0200 Subject: [PATCH 170/441] Improve notebook markdown cell rendering (#13577) --- .../view/notebook-markdown-cell-view.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index 19b8148af05bb..e02c82c11db4d 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -61,18 +61,23 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n return () => listener.dispose(); }, [editMode]); - let markdownContent = React.useMemo(() => markdownRenderer.render(new MarkdownStringImpl(cell.source)).element.innerHTML, [cell, editMode]); - if (markdownContent.length === 0) { - markdownContent = `${nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.')}`; + let markdownContent: HTMLElement = React.useMemo(() => { + const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true }); + return markdownRenderer.render(markdownString).element; + }, [cell, editMode]); + + if (!markdownContent.hasChildNodes()) { + const italic = document.createElement('i'); + italic.className = 'theia-notebook-empty-markdown'; + italic.innerText = nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.'); + italic.style.pointerEvents = 'none'; + markdownContent = italic; } return editMode ? :
    cell.requestEdit()} - // This sets the non React HTML node from the markdown renderers output as a child node to this react component - // This is currently sadly the best way we have to combine React (Virtual Nodes) and normal dom nodes - // the HTML is already sanitized by the markdown renderer, so we don't need to sanitize it again - dangerouslySetInnerHTML={{ __html: markdownContent }} // eslint-disable-line react/no-danger + ref={node => node?.replaceChildren(markdownContent)} />; } From 84fdd756e874bfaa546857b5381db36366ebae9e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 10 Apr 2024 09:54:53 +0200 Subject: [PATCH 171/441] Force notebook scrollbar update after content change (#13575) --- packages/notebook/src/browser/notebook-editor-widget.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 3f8d53c51db6c..6680fcec0eac5 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -16,7 +16,7 @@ import * as React from '@theia/core/shared/react'; import { CommandRegistry, MenuModelRegistry, URI } from '@theia/core'; -import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock } from '@theia/core/lib/browser'; +import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock, animationFrame } from '@theia/core/lib/browser'; import { ReactNode } from '@theia/core/shared/react'; import { CellKind } from '../common'; import { CellRenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view'; @@ -123,6 +123,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected readonly renderers = new Map(); protected _model?: NotebookModel; protected _ready: Deferred = new Deferred(); + protected scrollBarRef = React.createRef<{ updateScroll(): void }>(); get notebookType(): string { return this.props.notebookType; @@ -166,6 +167,11 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this._model = await this.props.notebookData; this.saveable.delegate = this._model; this.toDispose.push(this._model); + this.toDispose.push(this._model.onDidChangeContent(() => { + // Update the scroll bar content after the content has changed + // Wait one frame to ensure that the content has been rendered + animationFrame().then(() => this.scrollBarRef.current?.updateScroll()); + })); this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => { if (readOnly) { lock(this.title); @@ -212,6 +218,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa {this.notebookMainToolbarRenderer.render(this._model, this.node)}
    this.viewportService.viewportElement = ref}> this.viewportService.onScroll(e)}> Date: Wed, 10 Apr 2024 12:25:56 +0200 Subject: [PATCH 172/441] Open editor on file upload (#13578) --- .../src/browser/file-upload-service.ts | 8 ++++++++ .../filesystem-frontend-contribution.ts | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/packages/filesystem/src/browser/file-upload-service.ts b/packages/filesystem/src/browser/file-upload-service.ts index 76db7ea89927b..9447880b7daea 100644 --- a/packages/filesystem/src/browser/file-upload-service.ts +++ b/packages/filesystem/src/browser/file-upload-service.ts @@ -30,6 +30,7 @@ import { FileSystemPreferences } from './filesystem-preferences'; import { FileService } from './file-service'; import { ConfirmDialog, Dialog } from '@theia/core/lib/browser'; import { nls } from '@theia/core/lib/common/nls'; +import { Emitter, Event } from '@theia/core/lib/common/event'; export const HTTP_UPLOAD_URL: string = new Endpoint({ path: HTTP_FILE_UPLOAD_PATH }).getRestUrl().toString(true); @@ -62,6 +63,12 @@ export class FileUploadService { static TARGET = 'target'; static UPLOAD = 'upload'; + protected readonly onDidUploadEmitter = new Emitter(); + + get onDidUpload(): Event { + return this.onDidUploadEmitter.event; + } + protected uploadForm: FileUploadService.Form; protected deferredUpload?: Deferred; @@ -250,6 +257,7 @@ export class FileUploadService { throw error; } } + this.onDidUploadEmitter.fire(result.uploaded); return result; } diff --git a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts index 78ba910633819..836ccd5a65731 100644 --- a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts +++ b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts @@ -127,6 +127,9 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri await this.runEach((uri, widget) => this.applyMove(uri, widget, event)); this.resolveUserOperation(event); })())); + this.uploadService.onDidUpload(files => { + this.doHandleUpload(files); + }); } onStart?(app: FrontendApplication): MaybePromise { @@ -178,6 +181,22 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri } } + protected async doHandleUpload(uploads: string[]): Promise { + // Only handle single file uploads + if (uploads.length === 1) { + const uri = new URI(uploads[0]); + // Close all existing widgets for this URI + const widgets = this.shell.widgets.filter(widget => NavigatableWidget.getUri(widget)?.isEqual(uri)); + await this.shell.closeMany(widgets, { + // Don't ask to save the file if it's dirty + // The user has already confirmed the file overwrite + save: false + }); + // Open a new editor for this URI + open(this.openerService, uri); + } + } + /** * Opens a save dialog to create a new file. * From 3da5b6b28f4cebb4ba242b62b432d2196f4ba49e Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 10 Apr 2024 12:45:22 +0200 Subject: [PATCH 173/441] Improve notebook cell context key handling (#13572) * fixed all play buttons changing when executing single cell Signed-off-by: Jonah Iden * fixed context not correctly inheriting Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../service/notebook-context-manager.ts | 33 ++++++++++++++----- .../view/notebook-cell-toolbar-factory.tsx | 3 +- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index e9ece94ae316a..7d5f47fca571a 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { inject, injectable } from '@theia/core/shared/inversify'; -import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service'; +import { ContextKeyChangeEvent, ContextKeyService, ContextMatcher, ScopedValueStore } from '@theia/core/lib/browser/context-key-service'; import { DisposableCollection, Emitter } from '@theia/core'; import { NotebookKernelService } from './notebook-kernel-service'; import { @@ -53,6 +53,8 @@ export class NotebookContextManager { return this._context; } + protected cellContexts: Map> = new Map(); + init(widget: NotebookEditorWidget): void { this._context = widget.node; this.scopedStore = this.contextKeyService.createScoped(widget.node); @@ -89,6 +91,14 @@ export class NotebookContextManager { this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED])); }); + this.toDispose.push(this.executionStateService.onDidChangeExecution(e => { + if (e.notebook.toString() === widget.model?.uri.toString()) { + this.setCellContext(e.cellHandle, NOTEBOOK_CELL_EXECUTING, !!e.changed); + this.setCellContext(e.cellHandle, NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE])); + } + })); + widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e)); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); @@ -109,19 +119,26 @@ export class NotebookContextManager { this.scopedStore.setContext(NOTEBOOK_CELL_EDITABLE, cell.cellKind === CellKind.Markup && !cellEdit); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_MARKDOWN_EDIT_MODE])); })); - this.cellDisposables.push(this.executionStateService.onDidChangeExecution(e => { - if (cell && e.affectsCell(cell.uri)) { - this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTING, !!e.changed); - this.scopedStore.setContext(NOTEBOOK_CELL_EXECUTION_STATE, e.changed?.state ?? 'idle'); - this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE])); - } - })); } this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_TYPE])); } + protected setCellContext(cellHandle: number, key: string, value: unknown): void { + let cellContext = this.cellContexts.get(cellHandle); + if (!cellContext) { + cellContext = {}; + this.cellContexts.set(cellHandle, cellContext); + } + + cellContext[key] = value; + } + + getCellContext(cellHandle: number): ContextMatcher { + return this.contextKeyService.createOverlay(Object.entries(this.cellContexts.get(cellHandle) ?? {})); + } + onDidEditorTextFocus(focus: boolean): void { this.scopedStore.setContext('inputFocus', focus); } diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx index b57fb6bf326cc..7c88778f4aaed 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx @@ -64,9 +64,8 @@ export class NotebookCellToolbarFactory { private getMenuItems(menuItemPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): NotebookCellToolbarItem[] { const inlineItems: NotebookCellToolbarItem[] = []; - for (const menuNode of this.menuRegistry.getMenu(menuItemPath).children) { - if (!menuNode.when || this.contextKeyService.match(menuNode.when, this.notebookContextManager.context)) { + if (!menuNode.when || this.notebookContextManager.getCellContext(cell.handle).match(menuNode.when, this.notebookContextManager.context)) { if (menuNode.role === CompoundMenuNodeRole.Flat) { inlineItems.push(...menuNode.children?.map(child => this.createToolbarItem(child, notebookModel, cell, output)) ?? []); } else { From 32f393a3385927ed79708b4e1a2faf78806f1fd3 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 10 Apr 2024 12:53:07 +0200 Subject: [PATCH 174/441] Hide empty plugin view containers from user (#13581) * Hide empty plugin view containers from user * Fix playwright tests --- .../src/tests/theia-quick-command.test.ts | 4 +- .../src/browser/shell/view-contribution.ts | 5 +- .../main/browser/view/plugin-view-registry.ts | 79 ++++++++++++------- 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/examples/playwright/src/tests/theia-quick-command.test.ts b/examples/playwright/src/tests/theia-quick-command.test.ts index 4f9b8352cc4b6..6a11a248cec6f 100644 --- a/examples/playwright/src/tests/theia-quick-command.test.ts +++ b/examples/playwright/src/tests/theia-quick-command.test.ts @@ -61,8 +61,8 @@ test.describe('Theia Quick Command', () => { }); test('should trigger \'Toggle Explorer View\' command after typing', async () => { - await quickCommand.type('Toggle Explorer'); - await quickCommand.trigger('Toggle Explorer View'); + await quickCommand.type('Toggle Exp'); + await quickCommand.trigger('View: Toggle Explorer'); expect(await quickCommand.isOpen()).toBe(false); const explorerView = new TheiaExplorerView(app); expect(await explorerView.isDisplayed()).toBe(true); diff --git a/packages/core/src/browser/shell/view-contribution.ts b/packages/core/src/browser/shell/view-contribution.ts index 7b5ba31579dac..bad02117f733d 100644 --- a/packages/core/src/browser/shell/view-contribution.ts +++ b/packages/core/src/browser/shell/view-contribution.ts @@ -18,7 +18,7 @@ import { injectable, inject, interfaces, optional } from 'inversify'; import { Widget } from '@phosphor/widgets'; import { MenuModelRegistry, Command, CommandContribution, - MenuContribution, CommandRegistry + MenuContribution, CommandRegistry, nls } from '../../common'; import { KeybindingContribution, KeybindingRegistry } from '../keybinding'; import { WidgetManager } from '../widget-manager'; @@ -69,7 +69,8 @@ export abstract class AbstractViewContribution implements Comm if (options.toggleCommandId) { this.toggleCommand = { id: options.toggleCommandId, - label: 'Toggle ' + this.viewLabel + ' View' + category: nls.localizeByDefault('View'), + label: nls.localizeByDefault('Toggle {0}', this.viewLabel) }; } } diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 4da0239b8ad4c..58dcd9457acd2 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -55,6 +55,13 @@ export const PLUGIN_VIEW_DATA_FACTORY_ID = 'plugin-view-data'; export type ViewDataProvider = (params: { state?: object, viewInfo: View }) => Promise; +export interface ViewContainerInfo { + id: string + location: string + options: ViewContainerTitleOptions + onViewAdded: () => void +} + @injectable() export class PluginViewRegistry implements FrontendApplicationContribution { @@ -96,7 +103,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { private readonly views = new Map(); private readonly viewsWelcome = new Map(); - private readonly viewContainers = new Map(); + private readonly viewContainers = new Map(); private readonly containerViews = new Map(); private readonly viewClauseContexts = new Map | undefined>(); @@ -324,34 +331,47 @@ export class PluginViewRegistry implements FrontendApplicationContribution { protected doRegisterViewContainer(id: string, location: string, options: ViewContainerTitleOptions): Disposable { const toDispose = new DisposableCollection(); - this.viewContainers.set(id, [location, options]); toDispose.push(Disposable.create(() => this.viewContainers.delete(id))); const toggleCommandId = `plugin.view-container.${id}.toggle`; - toDispose.push(this.commands.registerCommand({ - id: toggleCommandId, - label: 'Toggle ' + options.label + ' View' - }, { - execute: () => this.toggleViewContainer(id) - })); - toDispose.push(this.menus.registerMenuAction(CommonMenus.VIEW_VIEWS, { - commandId: toggleCommandId, - label: options.label - })); - toDispose.push(this.quickView?.registerItem({ - label: options.label, - open: async () => { - const widget = await this.openViewContainer(id); + // Some plugins may register empty view containers. + // We should not register commands for them immediately, as that leads to bad UX. + // Instead, we register commands the first time we add a view to them. + let activate = () => { + toDispose.push(this.commands.registerCommand({ + id: toggleCommandId, + category: nls.localizeByDefault('View'), + label: nls.localizeByDefault('Toggle {0}', options.label) + }, { + execute: () => this.toggleViewContainer(id) + })); + toDispose.push(this.menus.registerMenuAction(CommonMenus.VIEW_VIEWS, { + commandId: toggleCommandId, + label: options.label + })); + toDispose.push(this.quickView?.registerItem({ + label: options.label, + open: async () => { + const widget = await this.openViewContainer(id); + if (widget) { + this.shell.activateWidget(widget.id); + } + } + })); + toDispose.push(Disposable.create(async () => { + const widget = await this.getPluginViewContainer(id); if (widget) { - this.shell.activateWidget(widget.id); + widget.dispose(); } - } - })); - toDispose.push(Disposable.create(async () => { - const widget = await this.getPluginViewContainer(id); - if (widget) { - widget.dispose(); - } - })); + })); + // Ignore every subsequent activation call + activate = () => { }; + }; + this.viewContainers.set(id, { + id, + location, + options, + onViewAdded: () => activate() + }); return toDispose; } @@ -374,6 +394,11 @@ export class PluginViewRegistry implements FrontendApplicationContribution { this.views.set(view.id, [viewContainerId, view]); toDispose.push(Disposable.create(() => this.views.delete(view.id))); + const containerInfo = this.viewContainers.get(viewContainerId); + if (containerInfo) { + containerInfo.onViewAdded(); + } + const containerViews = this.getContainerViews(viewContainerId); containerViews.push(view.id); this.containerViews.set(viewContainerId, containerViews); @@ -634,7 +659,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { if (!data) { return undefined; } - const [location] = data; + const { location } = data; const containerWidget = await this.getOrCreateViewContainerWidget(containerId); if (!containerWidget.isAttached) { await this.shell.addWidget(containerWidget, { @@ -648,7 +673,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { protected async prepareViewContainer(viewContainerId: string, containerWidget: ViewContainerWidget): Promise { const data = this.viewContainers.get(viewContainerId); if (data) { - const [, options] = data; + const { options } = data; containerWidget.setTitleOptions(options); } for (const viewId of this.getContainerViews(viewContainerId)) { From e99e42b5a9d404cc769a5f83736919494b89426f Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 10 Apr 2024 21:13:34 +0200 Subject: [PATCH 175/441] Use notebook URI as context for toolbar commands (#13585) --- .../notebook-actions-contribution.ts | 21 ++++++++++++++----- .../browser/view/notebook-main-toolbar.tsx | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index c0fc2fb10f2d0..96a5f8ad625fc 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls } from '@theia/core'; +import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { ApplicationShell, codicon, CommonCommands, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; @@ -184,14 +184,25 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { return { - isEnabled: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), - isVisible: (notebookModel: NotebookModel) => !Boolean(notebookModel?.readOnly), - execute: (notebookModel: NotebookModel) => { - execute(notebookModel); + isEnabled: (item: URI | NotebookModel) => this.withModel(item, model => !Boolean(model?.readOnly), false), + isVisible: (item: URI | NotebookModel) => this.withModel(item, model => !Boolean(model?.readOnly), false), + execute: (uri: URI | NotebookModel) => { + this.withModel(uri, execute, undefined); } }; } + protected withModel(item: URI | NotebookModel, execute: (notebookModel: NotebookModel) => T, defaultValue: T): T { + if (item instanceof URI) { + const model = this.notebookService.getNotebookEditorModel(item); + if (!model) { + return defaultValue; + } + item = model; + } + return execute(item); + } + registerMenus(menus: MenuModelRegistry): void { // independent submenu for plugins to add commands menus.registerIndependentSubmenu(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR, 'Notebook Main Toolbar'); diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index 56ee76d0b850c..78160e64ec19b 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -127,7 +127,7 @@ export class NotebookMainToolbar extends React.Component { if (item.command && (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode))) { - this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel); + this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel.uri); } }}> From 635eb5bb94e2d800d1d2ef448f8cd5cbe3fbcb8a Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 10 Apr 2024 21:14:19 +0200 Subject: [PATCH 176/441] Show short title for notebook toolbar commands (#13586) --- packages/core/src/common/command.ts | 4 ++++ .../notebook/src/browser/view/notebook-main-toolbar.tsx | 6 ++++-- packages/plugin-ext/src/common/plugin-protocol.ts | 2 ++ .../plugin-ext/src/hosted/node/scanners/scanner-theia.ts | 4 ++-- .../src/main/browser/plugin-contribution-handler.ts | 4 ++-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/core/src/common/command.ts b/packages/core/src/common/command.ts index d0e3f6302095a..0e32824c8b20f 100644 --- a/packages/core/src/common/command.ts +++ b/packages/core/src/common/command.ts @@ -41,6 +41,10 @@ export interface Command { * An icon class of this command. */ iconClass?: string; + /** + * A short title used for display in menus. + */ + shortTitle?: string; /** * A category of this command. */ diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index 78160e64ec19b..696bb496232a5 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -123,7 +123,9 @@ export class NotebookMainToolbar extends React.Component { if (item.command && (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode))) { @@ -131,7 +133,7 @@ export class NotebookMainToolbar extends React.Component - {item.label} + {label}
    ; } return undefined; diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts index d2ce75860d212..61f457a9a572c 100644 --- a/packages/plugin-ext/src/common/plugin-protocol.ts +++ b/packages/plugin-ext/src/common/plugin-protocol.ts @@ -198,6 +198,7 @@ export interface PluginPackageViewWelcome { export interface PluginPackageCommand { command: string; title: string; + shortTitle?: string; original?: string; category?: string; icon?: string | { light: string; dark: string; }; @@ -858,6 +859,7 @@ export interface ViewWelcome { export interface PluginCommand { command: string; title: string; + shortTitle?: string; originalTitle?: string; category?: string; iconUrl?: IconUrl; diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 7c9661fc04db4..834aaee8bc35a 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -455,9 +455,9 @@ export class TheiaPluginScanner extends AbstractPluginScanner { return translation; } - protected readCommand({ command, title, original, category, icon, enablement }: PluginPackageCommand, pck: PluginPackage): PluginCommand { + protected readCommand({ command, title, shortTitle, original, category, icon, enablement }: PluginPackageCommand, pck: PluginPackage): PluginCommand { const { themeIcon, iconUrl } = this.transformIconUrl(pck, icon) ?? {}; - return { command, title, originalTitle: original, category, iconUrl, themeIcon, enablement }; + return { command, title, shortTitle, originalTitle: original, category, iconUrl, themeIcon, enablement }; } protected transformIconUrl(plugin: PluginPackage, original?: IconUrl): { iconUrl?: IconUrl; themeIcon?: string } | undefined { diff --git a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts index b6e8c58dc7c47..89cd8b4f22fe8 100644 --- a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts @@ -463,7 +463,7 @@ export class PluginContributionHandler { return Disposable.NULL; } const toDispose = new DisposableCollection(); - for (const { iconUrl, themeIcon, command, category, title, originalTitle, enablement } of contribution.commands) { + for (const { iconUrl, themeIcon, command, category, shortTitle, title, originalTitle, enablement } of contribution.commands) { const reference = iconUrl && this.style.toIconClass(iconUrl); const icon = themeIcon && ThemeIcon.fromString(themeIcon); let iconClass; @@ -473,7 +473,7 @@ export class PluginContributionHandler { } else if (icon) { iconClass = ThemeIcon.asClassName(icon); } - toDispose.push(this.registerCommand({ id: command, category, label: title, originalLabel: originalTitle, iconClass }, enablement)); + toDispose.push(this.registerCommand({ id: command, category, shortTitle, label: title, originalLabel: originalTitle, iconClass }, enablement)); } return toDispose; } From 16982d36782c3cba017510feffc322973462ebe5 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 11 Apr 2024 17:38:09 +0200 Subject: [PATCH 177/441] added alot more keybings to the notebook editor (#13594) * addded alot more keybings to the notebook editor Signed-off-by: Jonah Iden * added z undo keybding as well Signed-off-by: Jonah Iden * align more to vscode Signed-off-by: Jonah Iden * added output collapsed message Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../src/browser/monaco-editor-provider.ts | 2 +- .../notebook-actions-contribution.ts | 68 ++++++++++++++++++- .../notebook-cell-actions-contribution.ts | 51 ++++++++++++-- .../contributions/notebook-preferences.ts | 31 +++++++++ .../src/browser/notebook-editor-widget.tsx | 2 + .../src/browser/notebook-frontend-module.ts | 7 +- .../service/notebook-clipboard-service.ts | 43 ++++++++++++ packages/notebook/src/browser/style/index.css | 9 +++ .../browser/view-model/notebook-cell-model.ts | 54 +++++++++++++++ .../src/browser/view-model/notebook-model.ts | 2 +- .../src/browser/view/notebook-cell-editor.tsx | 10 ++- .../browser/view/notebook-code-cell-view.tsx | 39 +++++++---- 12 files changed, 292 insertions(+), 26 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/notebook-preferences.ts create mode 100644 packages/notebook/src/browser/service/notebook-clipboard-service.ts diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index ed5b457cd7bce..3391dcdb3c6ea 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -211,7 +211,7 @@ export class MonacoEditorProvider { return editor; } - protected updateReadOnlyMessage(options: MonacoEditor.IOptions, readOnly: boolean | MarkdownString ): void { + protected updateReadOnlyMessage(options: MonacoEditor.IOptions, readOnly: boolean | MarkdownString): void { options.readOnlyMessage = MarkdownString.is(readOnly) ? readOnly : undefined; } diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 96a5f8ad625fc..00ae102716ac7 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -25,6 +25,7 @@ import { NotebookExecutionService } from '../service/notebook-execution-service' import { NotebookEditorWidget } from '../notebook-editor-widget'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; +import { NotebookClipboardService } from '../service/notebook-clipboard-service'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -66,6 +67,21 @@ export namespace NotebookCommands { id: 'notebook.change-selected-cell', category: 'Notebook', }); + + export const CUT_SELECTED_CELL = Command.toDefaultLocalizedCommand({ + id: 'notebook.cut-selected-cell', + category: 'Notebook', + }); + + export const COPY_SELECTED_CELL = Command.toDefaultLocalizedCommand({ + id: 'notebook.copy-selected-cell', + category: 'Notebook', + }); + + export const PASTE_CELL = Command.toDefaultLocalizedCommand({ + id: 'notebook.paste-cell', + category: 'Notebook', + }); } export enum CellChangeDirection { @@ -91,6 +107,9 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon @inject(NotebookEditorWidgetService) protected notebookEditorWidgetService: NotebookEditorWidgetService; + @inject(NotebookClipboardService) + protected notebookClipboardService: NotebookClipboardService; + registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, { execute: (notebookModel: NotebookModel, cellKind: CellKind = CellKind.Markup, index?: number | 'above' | 'below') => { @@ -180,6 +199,39 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon execute: () => (this.shell.activeWidget as NotebookEditorWidget).redo() }); + commands.registerCommand(NotebookCommands.CUT_SELECTED_CELL, this.editableCommandHandler( + () => { + const model = this.notebookEditorWidgetService.focusedEditor?.model; + const selectedCell = model?.selectedCell; + if (selectedCell) { + model.applyEdits([{ editType: CellEditType.Replace, index: model.cells.indexOf(selectedCell), count: 1, cells: [] }], true); + this.notebookClipboardService.copyCell(selectedCell); + } + })); + + commands.registerCommand(NotebookCommands.COPY_SELECTED_CELL, { + execute: () => { + const model = this.notebookEditorWidgetService.focusedEditor?.model; + const selectedCell = model?.selectedCell; + if (selectedCell) { + this.notebookClipboardService.copyCell(selectedCell); + } + } + }); + + commands.registerCommand(NotebookCommands.PASTE_CELL, { + isEnabled: () => !Boolean(this.notebookEditorWidgetService.focusedEditor?.model?.readOnly), + isVisible: () => !Boolean(this.notebookEditorWidgetService.focusedEditor?.model?.readOnly), + execute: (position?: 'above') => { + const copiedCell = this.notebookClipboardService.getCell(); + if (copiedCell) { + const model = this.notebookEditorWidgetService.focusedEditor?.model; + const insertIndex = model?.selectedCell ? model.cells.indexOf(model.selectedCell) + (position === 'above' ? 0 : 1) : 0; + model?.applyEdits([{ editType: CellEditType.Replace, index: insertIndex, count: 0, cells: [copiedCell] }], true); + } + } + }); + } protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { @@ -234,7 +286,6 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon order: '30', when: NOTEBOOK_HAS_OUTPUTS }); - // other items } registerKeybindings(keybindings: KeybindingRegistry): void { @@ -251,6 +302,21 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon args: CellChangeDirection.Down, when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, + { + command: NotebookCommands.CUT_SELECTED_CELL.id, + keybinding: 'ctrlcmd+x', + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + }, + { + command: NotebookCommands.COPY_SELECTED_CELL.id, + keybinding: 'ctrlcmd+c', + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + }, + { + command: NotebookCommands.PASTE_CELL.id, + keybinding: 'ctrlcmd+v', + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}` + }, ); } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 1364bb9effdea..f44a88cd2a054 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -22,7 +22,7 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED, - NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_EDITABLE + NOTEBOOK_CELL_FOCUSED } from './notebook-context-keys'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; @@ -110,14 +110,27 @@ export namespace NotebookCellCommands { }); export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({ - id: 'notebook.cell.to-code-cell', + id: 'notebook.cell.changeToCode', label: 'Change Cell to Code' }); export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({ - id: 'notebook.cell.to-markdown-cell', + id: 'notebook.cell.changeToMarkdown', label: 'Change Cell to Mardown' }); + + export const COLLAPSE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.collapseCellOutput', + category: 'Notebook', + label: 'Collapse Cell Output', + }); + + export const EXPAND_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.expandCellOutput', + category: 'Notebook', + label: 'Expand Cell Output', + }); + } @injectable() @@ -237,8 +250,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command } registerCommands(commands: CommandRegistry): void { - commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => cell.requestEdit())); - commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => (cell ?? this.getSelectedCell()).requestStopEdit() }); + commands.registerCommand(NotebookCellCommands.EDIT_COMMAND, this.editableCellCommandHandler((_, cell) => cell.requestFocusEditor())); + commands.registerCommand(NotebookCellCommands.STOP_EDIT_COMMAND, { execute: (_, cell: NotebookCellModel) => (cell ?? this.getSelectedCell()).requestBlurEditor() }); commands.registerCommand(NotebookCellCommands.DELETE_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { notebookModel.applyEdits([{ @@ -327,6 +340,25 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { changeCellType(notebookModel, cell, CellKind.Markup); })); + + commands.registerCommand(NotebookCellCommands.COLLAPSE_CELL_OUTPUT, { + execute: () => { + const selectedCell = this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; + if (selectedCell) { + selectedCell.outputVisible = false; + } + } + }); + + commands.registerCommand(NotebookCellCommands.EXPAND_CELL_OUTPUT, { + execute: () => { + const selectedCell = this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; + if (selectedCell) { + selectedCell.outputVisible = true; + } + } + }); + } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { @@ -350,13 +382,18 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.EDIT_COMMAND.id, keybinding: 'Enter', - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_EDITABLE}`, + when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, }, + { + command: NotebookCellCommands.STOP_EDIT_COMMAND.id, + keybinding: 'esc', + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + }, { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), @@ -386,7 +423,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command command: NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND.id, keybinding: 'M', when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, - } + }, ); } } diff --git a/packages/notebook/src/browser/contributions/notebook-preferences.ts b/packages/notebook/src/browser/contributions/notebook-preferences.ts new file mode 100644 index 0000000000000..9fd02a08dc653 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-preferences.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import { PreferenceSchema } from '@theia/core/lib/browser'; + +export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers'; + +export const notebookPreferenceSchema: PreferenceSchema = { + properties: { + [NOTEBOOK_LINE_NUMBERS]: { + type: 'string', + enum: ['on', 'off'], + default: 'off', + description: nls.localizeByDefault('Controls the display of line numbers in the cell editor.') + }, + } +}; diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 6680fcec0eac5..ada7c2a67f313 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -160,6 +160,8 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]); model.setSelectedCell(model.cells[0]); } + model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus())); + model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus()))); }); } diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 6255d227283f2..ca0fd71b6ee4f 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, PreferenceContribution, WidgetFactory } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -43,6 +43,8 @@ import { NotebookMonacoTextModelService } from './service/notebook-monaco-text-m import { NotebookOutlineContribution } from './contributions/notebook-outline-contribution'; import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution'; import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution'; +import { NotebookClipboardService } from './service/notebook-clipboard-service'; +import { notebookPreferenceSchema } from './contributions/notebook-preferences'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -64,6 +66,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookRendererMessagingService).toSelf().inSingletonScope(); bind(NotebookKernelHistoryService).toSelf().inSingletonScope(); bind(NotebookKernelQuickPickService).toSelf().inSingletonScope(); + bind(NotebookClipboardService).toSelf().inSingletonScope(); bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); @@ -100,4 +103,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(FrontendApplicationContribution).toService(NotebookOutlineContribution); bind(NotebookLabelProviderContribution).toSelf().inSingletonScope(); bind(LabelProviderContribution).toService(NotebookLabelProviderContribution); + + bind(PreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema }); }); diff --git a/packages/notebook/src/browser/service/notebook-clipboard-service.ts b/packages/notebook/src/browser/service/notebook-clipboard-service.ts new file mode 100644 index 0000000000000..8698fe518cec8 --- /dev/null +++ b/packages/notebook/src/browser/service/notebook-clipboard-service.ts @@ -0,0 +1,43 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { environment } from '@theia/core'; +import { CellData } from '../../common'; + +@injectable() +export class NotebookClipboardService { + + protected copiedCell: CellData | undefined; + + @inject(ClipboardService) + protected readonly clipboardService: ClipboardService; + + copyCell(cell: NotebookCellModel): void { + this.copiedCell = cell.getData(); + + if (environment.electron.is()) { + this.clipboardService.writeText(cell.text); + } + } + + getCell(): CellData | undefined { + return this.copiedCell; + } + +} diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index a106da20e730c..520cc6d150b42 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -266,3 +266,12 @@ background-color: var(--theia-notebook-focusedCellBorder); width: 100%; } + +.theia-notebook-collapsed-output { + padding: 4px 8px; + color: var(--theia-foreground); + margin-left: 30px; + font-size: 14px; + line-height: 22px; + opacity: 0.7; +} diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index eb2ad2a126ed6..dc3f20b1988c3 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -21,6 +21,7 @@ import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; +import { type MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata, NotebookCellMetadata, CellOutput, CellData, CellOutputItem @@ -28,6 +29,8 @@ import { import { NotebookCellOutputsSplice } from '../notebook-types'; import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service'; import { NotebookCellOutputModel } from './notebook-cell-output-model'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { NOTEBOOK_LINE_NUMBERS } from '../contributions/notebook-preferences'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel; @@ -103,12 +106,24 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onWillFocusCellEditorEmitter = new Emitter(); readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event; + protected readonly onWillBlurCellEditorEmitter = new Emitter(); + readonly onWillBlurCellEditor = this.onWillBlurCellEditorEmitter.event; + + protected readonly onDidChangeEditorOptionsEmitter = new Emitter(); + readonly onDidChangeEditorOptions: Event = this.onDidChangeEditorOptionsEmitter.event; + + protected readonly outputVisibilityChangeEmitter = new Emitter(); + readonly onDidChangeOutputVisibility: Event = this.outputVisibilityChangeEmitter.event; + @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @inject(NotebookMonacoTextModelService) protected readonly textModelService: NotebookMonacoTextModelService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + get outputs(): NotebookCellOutputModel[] { return this._outputs; } @@ -192,11 +207,45 @@ export class NotebookCellModel implements NotebookCell, Disposable { return this._editing; } + protected _editorOptions: MonacoEditor.IOptions = {}; + get editorOptions(): Readonly { + return this._editorOptions; + } + + set editorOptions(options: MonacoEditor.IOptions) { + this._editorOptions = options; + this.onDidChangeEditorOptionsEmitter.fire(options); + } + + protected _outputVisible: boolean = true; + get outputVisible(): boolean { + return this._outputVisible; + } + + set outputVisible(visible: boolean) { + if (this._outputVisible !== visible) { + this._outputVisible = visible; + this.outputVisibilityChangeEmitter.fire(visible); + } + } + @postConstruct() protected init(): void { this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op)); this._metadata = this.props.metadata ?? {}; this._internalMetadata = this.props.internalMetadata ?? {}; + + this.editorOptions = { + lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS) + }; + this.toDispose.push(this.preferenceService.onPreferenceChanged(e => { + if (e.preferenceName === NOTEBOOK_LINE_NUMBERS) { + this.editorOptions = { + ...this.editorOptions, + lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS) + }; + } + })); } dispose(): void { @@ -227,6 +276,11 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onWillFocusCellEditorEmitter.fire(); } + requestBlurEditor(): void { + this.requestStopEdit(); + this.onWillBlurCellEditorEmitter.fire(); + } + spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void { if (splice.deleteCount > 0 && splice.newOutputs.length > 0) { const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index c636cb51f16d4..94b140fdb7f2e 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -1,5 +1,5 @@ // ***************************************************************************** -// Copyright (C) 20023 Typefox and others. +// Copyright (C) 2023 Typefox and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index d4b951678a1eb..46e362fbeb2b6 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -42,7 +42,8 @@ const DEFAULT_EDITOR_OPTIONS: MonacoEditor.IOptions = { scrollbar: { ...MonacoEditorProvider.inlineOptions.scrollbar, alwaysConsumeMouseWheel: false - } + }, + lineDecorationsWidth: 10, }; export class CellEditor extends React.Component { @@ -56,6 +57,11 @@ export class CellEditor extends React.Component { this.toDispose.push(this.props.cell.onWillFocusCellEditor(() => { this.editor?.getControl().focus(); })); + + this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { + this.editor?.getControl().updateOptions(options); + })); + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(() => { if (this.props.notebookModel.selectedCell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { if (document.activeElement && 'blur' in document.activeElement) { @@ -96,7 +102,7 @@ export class CellEditor extends React.Component { editorModel, editorNode, monacoServices, - DEFAULT_EDITOR_OPTIONS, + { ...DEFAULT_EDITOR_OPTIONS, ...cell.editorOptions }, [[IContextKeyService, this.props.notebookContextManager.scopedStore]]); this.toDispose.push(this.editor); this.editor.setLanguage(cell.language); diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index de66115a85b77..292ea421a1819 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -28,7 +28,7 @@ import { NotebookCellActionContribution } from '../contributions/notebook-cell-a import { CellExecution, NotebookExecutionStateService } from '../service/notebook-execution-state-service'; import { codicon } from '@theia/core/lib/browser'; import { NotebookCellExecutionState } from '../../common'; -import { DisposableCollection } from '@theia/core'; +import { DisposableCollection, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; import { NotebookViewportService } from './notebook-viewport-service'; import { EditorPreferences } from '@theia/editor/lib/browser'; @@ -223,19 +223,15 @@ export class NotebookCodeCellOutputs extends React.Component { const { cell, notebook, outputWebviewFactory } = this.props; - this.toDispose.push(cell.onDidChangeOutputs(async () => { - if (!this.outputsWebviewPromise && cell.outputs.length > 0) { - this.outputsWebviewPromise = outputWebviewFactory(cell, notebook).then(webview => { - this.outputsWebview = webview; - this.forceUpdate(); - return webview; - }); - this.forceUpdate(); - } else if (this.outputsWebviewPromise && cell.outputs.length === 0 && cell.internalMetadata.runEndTime) { - (await this.outputsWebviewPromise).dispose(); + this.toDispose.push(cell.onDidChangeOutputs(() => this.updateOutputs())); + this.toDispose.push(cell.onDidChangeOutputVisibility(visible => { + if (!visible && this.outputsWebview) { + this.outputsWebview?.dispose(); this.outputsWebview = undefined; this.outputsWebviewPromise = undefined; this.forceUpdate(); + } else { + this.updateOutputs(); } })); if (cell.outputs.length > 0) { @@ -247,6 +243,23 @@ export class NotebookCodeCellOutputs extends React.Component { + const { cell, notebook, outputWebviewFactory } = this.props; + if (!this.outputsWebviewPromise && cell.outputs.length > 0) { + this.outputsWebviewPromise = outputWebviewFactory(cell, notebook).then(webview => { + this.outputsWebview = webview; + this.forceUpdate(); + return webview; + }); + this.forceUpdate(); + } else if (this.outputsWebviewPromise && cell.outputs.length === 0 && cell.internalMetadata.runEndTime) { + (await this.outputsWebviewPromise).dispose(); + this.outputsWebview = undefined; + this.outputsWebviewPromise = undefined; + this.forceUpdate(); + } + } + override async componentDidUpdate(): Promise { if (!(await this.outputsWebviewPromise)?.isAttached()) { (await this.outputsWebviewPromise)?.attachWebview(); @@ -259,12 +272,12 @@ export class NotebookCodeCellOutputs extends React.Component {this.props.renderSidebar()} {this.outputsWebview.render()} : - <>; + this.props.cell.outputs?.length ? {nls.localizeByDefault('Outputs are collapsed')} : <>; } From fd5be308a0da5c1e9f660c3afa86ebe60fc5bf8e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Fri, 12 Apr 2024 16:07:31 +0200 Subject: [PATCH 178/441] Fix monaco localization (#13557) --- .../src/browser/monaco-frontend-module.ts | 16 ---------------- packages/monaco/src/browser/monaco-init.ts | 17 +++++++++++++++++ .../browser/vsx-language-quick-pick-service.ts | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 0bee7f59228b4..0fc9b9ffaf317 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -14,22 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import * as MonacoNls from '@theia/monaco-editor-core/esm/vs/nls'; -import { nls } from '@theia/core/lib/common/nls'; -import { FormatType, Localization } from '@theia/core/lib/common/i18n/localization'; - -Object.assign(MonacoNls, { - localize(_key: string, label: string, ...args: FormatType[]): string { - if (nls.locale) { - const defaultKey = nls.getDefaultKey(label); - if (defaultKey) { - return nls.localize(defaultKey, label, ...args); - } - } - return Localization.format(label, args); - } -}); - import '../../src/browser/style/index.css'; import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { MenuContribution, CommandContribution, quickInputServicePath } from '@theia/core/lib/common'; diff --git a/packages/monaco/src/browser/monaco-init.ts b/packages/monaco/src/browser/monaco-init.ts index 80ec1fd2d8338..d622e596c3df5 100644 --- a/packages/monaco/src/browser/monaco-init.ts +++ b/packages/monaco/src/browser/monaco-init.ts @@ -27,6 +27,23 @@ * is allowed. */ +// Before importing anything from monaco we need to override its localization function +import * as MonacoNls from '@theia/monaco-editor-core/esm/vs/nls'; +import { nls } from '@theia/core/lib/common/nls'; +import { FormatType, Localization } from '@theia/core/lib/common/i18n/localization'; + +Object.assign(MonacoNls, { + localize(_key: string, label: string, ...args: FormatType[]): string { + if (nls.locale) { + const defaultKey = nls.getDefaultKey(label); + if (defaultKey) { + return nls.localize(defaultKey, label, ...args); + } + } + return Localization.format(label, args); + } +}); + import { Container } from '@theia/core/shared/inversify'; import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; diff --git a/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts b/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts index 16d775e3e34e5..6fc219c9247a2 100644 --- a/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts +++ b/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts @@ -72,7 +72,7 @@ export class VSXLanguageQuickPickService extends LanguageQuickPickService { localizationContribution.localizedLanguageName ?? localizationContribution.languageName ?? localizationContribution.languageId), }); try { - const extensionUri = VSCodeExtensionUri.fromId(extension.extension.name, extension.extension.namespace).toString(); + const extensionUri = VSCodeExtensionUri.fromId(`${extension.extension.namespace}.${extension.extension.name}`).toString(); await this.pluginServer.deploy(extensionUri); } finally { progress.cancel(); From d99f81b384c1b89140ff9c9b06f3c60c49220eb9 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 15 Apr 2024 16:26:49 +0200 Subject: [PATCH 179/441] Fix notebook model/cell disposal (#13606) --- .../src/browser/notebook-editor-widget.tsx | 1 + .../src/browser/service/notebook-service.ts | 4 ++++ .../src/browser/view-model/notebook-model.ts | 4 ++++ .../src/plugin/notebook/notebooks.ts | 19 +++++++++++++++---- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index ada7c2a67f313..a9fc8c2514b4b 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -258,6 +258,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.onDidReceiveKernelMessageEmitter.dispose(); this.onPostRendererMessageEmitter.dispose(); this.viewportService.dispose(); + this._model?.dispose(); super.dispose(); } diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index 75f5a64791030..4b9b1ba3b5a2d 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -121,6 +121,10 @@ export class NotebookService implements Disposable { // This ensures that all text models are available in the plugin host this.textModelService.createTextModelsForNotebook(model); this.didAddNotebookDocumentEmitter.fire(model); + model.onDidDispose(() => { + this.notebookModels.delete(resource.uri.toString()); + this.didRemoveNotebookDocumentEmitter.fire(model); + }); return model; } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 94b140fdb7f2e..ea8e484ec8c01 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -71,6 +71,9 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidChangeSelectedCellEmitter = new Emitter(); readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event; + protected readonly onDidDisposeEmitter = new Emitter(); + readonly onDidDispose = this.onDidDisposeEmitter.event; + get onDidChangeReadOnly(): Event { return this.props.resource.onDidChangeReadOnly ?? Event.None; } @@ -150,6 +153,7 @@ export class NotebookModel implements Saveable, Disposable { this.onDidChangeContentEmitter.dispose(); this.onDidChangeSelectedCellEmitter.dispose(); this.cells.forEach(cell => cell.dispose()); + this.onDidDisposeEmitter.fire(); } async save(options: SaveOptions): Promise { diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 975dbd752e42e..58a920e76d992 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -22,7 +22,7 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR import { URI as TheiaURI } from '../types-impl'; import * as theia from '@theia/plugin'; import { - CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto, + CommandRegistryExt, ModelAddedData, NotebookCellStatusBarListDto, NotebookDataDto, NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin, PLUGIN_RPC_CONTEXT } from '../../common'; @@ -204,6 +204,8 @@ export class NotebooksExtImpl implements NotebooksExt { } async $acceptDocumentsAndEditorsDelta(delta: NotebookDocumentsAndEditorsDelta): Promise { + const removedCellDocuments: UriComponents[] = []; + const addedCellDocuments: ModelAddedData[] = []; if (delta.removedDocuments) { for (const uri of delta.removedDocuments) { const revivedUri = URI.fromComponents(uri); @@ -213,6 +215,7 @@ export class NotebooksExtImpl implements NotebooksExt { document.dispose(); this.documents.delete(revivedUri.toString()); this.onDidCloseNotebookDocumentEmitter.fire(document.apiNotebook); + removedCellDocuments.push(...document.apiNotebook.getCells().map(cell => cell.document.uri)); } for (const editor of this.editors.values()) { @@ -223,6 +226,11 @@ export class NotebooksExtImpl implements NotebooksExt { } } + // publish all removed cell documents first + await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ + removedDocuments: removedCellDocuments + }); + if (delta.addedDocuments) { for (const modelData of delta.addedDocuments) { const uri = TheiaURI.from(modelData.uri); @@ -242,14 +250,17 @@ export class NotebooksExtImpl implements NotebooksExt { this.documents.get(uri.toString())?.dispose(); this.documents.set(uri.toString(), document); - this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ - addedDocuments: modelData.cells.map(cell => Cell.asModelAddData(cell)) - }); + addedCellDocuments.push(...modelData.cells.map(cell => Cell.asModelAddData(cell))); this.onDidOpenNotebookDocumentEmitter.fire(document.apiNotebook); } } + // publish all added cell documents in a separate call + await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ + addedDocuments: addedCellDocuments + }); + if (delta.addedEditors) { for (const editorModelData of delta.addedEditors) { if (this.editors.has(editorModelData.id)) { From fea794fca67f6d32ad996b0bd2576b0afc607476 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 17 Apr 2024 13:02:55 +0200 Subject: [PATCH 180/441] fix kernel autobind for on startup opened notebooks (#13598) Signed-off-by: Jonah Iden --- .../src/main/browser/notebooks/notebook-kernels-main.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts index 93dde74a3a473..f15101052a421 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts @@ -209,9 +209,16 @@ export class NotebookKernelsMainImpl implements NotebookKernelsMain { } }(handle, data, this.languageService); + // this is for when a kernel is bound to a notebook while being registered + const autobindListener = this.notebookKernelService.onDidChangeSelectedKernel(e => { + if (e.newKernel === kernel.id) { + this.proxy.$acceptNotebookAssociation(handle, e.notebook.toComponents(), true); + } + }); + const registration = this.notebookKernelService.registerKernel(kernel); this.kernels.set(handle, [kernel, registration]); - + autobindListener.dispose(); } $updateKernel(handle: number, data: Partial): void { From 106da538c88e2d8d20111abc08a1414d69c3b689 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 17 Apr 2024 15:24:07 +0200 Subject: [PATCH 181/441] fix webview communication for safari (#13587) Signed-off-by: Jonah Iden --- packages/plugin-ext/src/main/browser/webview/pre/host.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/webview/pre/host.js b/packages/plugin-ext/src/main/browser/webview/pre/host.js index 7ef317406d393..c3182a1aa98d7 100644 --- a/packages/plugin-ext/src/main/browser/webview/pre/host.js +++ b/packages/plugin-ext/src/main/browser/webview/pre/host.js @@ -31,7 +31,7 @@ let sourceIsChildFrame = false; for (let i = 0; i < window.frames.length; i++) { const frame = window.frames[i]; - if (e.source === frame) { + if (e.origin === frame.origin) { sourceIsChildFrame = true; break; } From a87c46ae8352e14c518513e80d8eb2094f9b66ed Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 17 Apr 2024 15:31:52 +0200 Subject: [PATCH 182/441] Fix remote support in packaged apps (#13584) --- .../src/generator/backend-generator.ts | 7 +++-- .../electron-main-application.ts | 4 --- .../src/node/backend-application-module.ts | 7 ++--- packages/core/src/node/backend-application.ts | 28 +++++++------------ .../env-variables/env-variables-server.ts | 6 ++-- .../setup/remote-copy-contribution.ts | 3 +- 6 files changed, 20 insertions(+), 35 deletions(-) diff --git a/dev-packages/application-manager/src/generator/backend-generator.ts b/dev-packages/application-manager/src/generator/backend-generator.ts index a5df1bdb80b46..fd47708a0d3af 100644 --- a/dev-packages/application-manager/src/generator/backend-generator.ts +++ b/dev-packages/application-manager/src/generator/backend-generator.ts @@ -52,10 +52,12 @@ if (process.env.LC_ALL) { } process.env.LC_NUMERIC = 'C'; +const { resolve } = require('path'); +const theiaAppProjectPath = resolve(__dirname, '..', '..'); +process.env.THEIA_APP_PROJECT_PATH = theiaAppProjectPath; const { default: electronMainApplicationModule } = require('@theia/core/lib/electron-main/electron-main-application-module'); const { ElectronMainApplication, ElectronMainApplicationGlobals } = require('@theia/core/lib/electron-main/electron-main-application'); const { Container } = require('inversify'); -const { resolve } = require('path'); const { app } = require('electron'); const config = ${this.prettyStringify(this.pck.props.frontend.config)}; @@ -71,7 +73,7 @@ const isSingleInstance = ${this.pck.props.backend.config.singleInstance === true const container = new Container(); container.load(electronMainApplicationModule); container.bind(ElectronMainApplicationGlobals).toConstantValue({ - THEIA_APP_PROJECT_PATH: resolve(__dirname, '..', '..'), + THEIA_APP_PROJECT_PATH: theiaAppProjectPath, THEIA_BACKEND_MAIN_PATH: resolve(__dirname, 'main.js'), THEIA_FRONTEND_HTML_PATH: resolve(__dirname, '..', '..', 'lib', 'frontend', 'index.html'), }); @@ -119,6 +121,7 @@ if ('ELECTRON_RUN_AS_NODE' in process.env) { } const path = require('path'); +process.env.THEIA_APP_PROJECT_PATH = path.resolve(__dirname, '..', '..') const express = require('express'); const { Container } = require('inversify'); const { BackendApplication, BackendApplicationServer, CliManager } = require('@theia/core/lib/node'); diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 2efa9fce419e2..9c0f410fba9ba 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -548,10 +548,6 @@ export class ElectronMainApplication { protected async startBackend(): Promise { // Check if we should run everything as one process. const noBackendFork = process.argv.indexOf('--no-cluster') !== -1; - // We cannot use the `process.cwd()` as the application project path (the location of the `package.json` in other words) - // in a bundled electron application because it depends on the way we start it. For instance, on OS X, these are a differences: - // https://github.com/eclipse-theia/theia/issues/3297#issuecomment-439172274 - process.env.THEIA_APP_PROJECT_PATH = this.globals.THEIA_APP_PROJECT_PATH; // Set the electron version for both the dev and the production mode. (https://github.com/eclipse-theia/theia/issues/3254) // Otherwise, the forked backend processes will not know that they're serving the electron frontend. process.env.THEIA_ELECTRON_VERSION = process.versions.electron; diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index e3461c473b956..a89a7a8a52ea6 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -21,7 +21,7 @@ import { bindContributionProvider, MessageService, MessageClient, ConnectionHandler, RpcConnectionHandler, CommandService, commandServicePath, messageServicePath, OSBackendProvider, OSBackendProviderPath } from '../common'; -import { BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution, BackendApplicationServer } from './backend-application'; +import { BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution, BackendApplicationServer, BackendApplicationPath } from './backend-application'; import { CliManager, CliContribution } from './cli'; import { IPCConnectionProvider } from './messaging'; import { ApplicationServerImpl } from './application-server'; @@ -101,10 +101,7 @@ export const backendApplicationModule = new ContainerModule(bind => { }) ).inSingletonScope(); - bind(ApplicationPackage).toDynamicValue(({ container }) => { - const { projectPath } = container.get(BackendApplicationCliContribution); - return new ApplicationPackage({ projectPath }); - }).inSingletonScope(); + bind(ApplicationPackage).toConstantValue(new ApplicationPackage({ projectPath: BackendApplicationPath })); bind(WsRequestValidator).toSelf().inSingletonScope(); bindContributionProvider(bind, WsRequestValidatorContribution); diff --git a/packages/core/src/node/backend-application.ts b/packages/core/src/node/backend-application.ts index a93376955750b..31549abbc5f79 100644 --- a/packages/core/src/node/backend-application.ts +++ b/packages/core/src/node/backend-application.ts @@ -27,9 +27,14 @@ import { CliContribution } from './cli'; import { Deferred } from '../common/promise-util'; import { environment } from '../common/index'; import { AddressInfo } from 'net'; -import { ApplicationPackage } from '@theia/application-package'; import { ProcessUtils } from './process-utils'; +/** + * The path to the application project directory. This is the directory where the application code is located. + * Mostly contains the `package.json` file and the `lib` directory. + */ +export const BackendApplicationPath = process.env.THEIA_APP_PROJECT_PATH || process.cwd(); + export type DnsResultOrder = 'ipv4first' | 'verbatim' | 'nodeDefault'; const APP_PROJECT_PATH = 'app-project-path'; @@ -115,7 +120,8 @@ export class BackendApplicationCliContribution implements CliContribution { ssl: boolean | undefined; cert: string | undefined; certkey: string | undefined; - projectPath: string; + /** @deprecated Use the `BackendApplicationPath` constant or `process.env.THEIA_APP_PROJECT_PATH` environment variable instead */ + projectPath = BackendApplicationPath; configure(conf: yargs.Argv): void { conf.option('port', { alias: 'p', description: 'The port the backend server listens on.', type: 'number', default: DEFAULT_PORT }); @@ -123,7 +129,7 @@ export class BackendApplicationCliContribution implements CliContribution { conf.option('ssl', { description: 'Use SSL (HTTPS), cert and certkey must also be set', type: 'boolean', default: DEFAULT_SSL }); conf.option('cert', { description: 'Path to SSL certificate.', type: 'string' }); conf.option('certkey', { description: 'Path to SSL certificate key.', type: 'string' }); - conf.option(APP_PROJECT_PATH, { description: 'Sets the application project directory', default: this.appProjectPath() }); + conf.option(APP_PROJECT_PATH, { description: 'Sets the application project directory', deprecated: true }); conf.option('dnsDefaultResultOrder', { type: 'string', description: 'Configure Node\'s DNS resolver default behavior, see https://nodejs.org/docs/latest-v18.x/api/dns.html#dnssetdefaultresultorderorder', @@ -138,19 +144,8 @@ export class BackendApplicationCliContribution implements CliContribution { this.ssl = args.ssl as boolean; this.cert = args.cert as string; this.certkey = args.certkey as string; - this.projectPath = args[APP_PROJECT_PATH] as string; this.dnsDefaultResultOrder = args.dnsDefaultResultOrder as DnsResultOrder; } - - protected appProjectPath(): string { - if (environment.electron.is()) { - if (process.env.THEIA_APP_PROJECT_PATH) { - return process.env.THEIA_APP_PROJECT_PATH; - } - throw new Error('The \'THEIA_APP_PROJECT_PATH\' environment variable must be set when running in electron.'); - } - return process.cwd(); - } } /** @@ -161,9 +156,6 @@ export class BackendApplication { protected readonly app: express.Application = express(); - @inject(ApplicationPackage) - protected readonly applicationPackage: ApplicationPackage; - @inject(ProcessUtils) protected readonly processUtils: ProcessUtils; @@ -352,7 +344,7 @@ export class BackendApplication { const acceptedEncodings = req.acceptsEncodings(); const gzUrl = `${req.url}.gz`; - const gzPath = path.join(this.applicationPackage.projectPath, 'lib', 'frontend', gzUrl); + const gzPath = path.join(BackendApplicationPath, 'lib', 'frontend', gzUrl); if (acceptedEncodings.indexOf('gzip') === -1 || !(await fs.pathExists(gzPath))) { next(); return; diff --git a/packages/core/src/node/env-variables/env-variables-server.ts b/packages/core/src/node/env-variables/env-variables-server.ts index 7c337992ac90b..57cdf059756fd 100644 --- a/packages/core/src/node/env-variables/env-variables-server.ts +++ b/packages/core/src/node/env-variables/env-variables-server.ts @@ -22,6 +22,7 @@ import { pathExists, mkdir } from 'fs-extra'; import { EnvVariable, EnvVariablesServer } from '../../common/env-variables'; import { isWindows } from '../../common/os'; import { FileUri } from '../../common/file-uri'; +import { BackendApplicationPath } from '../backend-application'; @injectable() export class EnvVariablesServerImpl implements EnvVariablesServer { @@ -45,10 +46,7 @@ export class EnvVariablesServerImpl implements EnvVariablesServer { } protected async createConfigDirUri(): Promise { - let dataFolderPath: string = ''; - if (process.env.THEIA_APP_PROJECT_PATH) { - dataFolderPath = join(process.env.THEIA_APP_PROJECT_PATH, 'data'); - } + const dataFolderPath = join(BackendApplicationPath, 'data'); const userDataPath = join(dataFolderPath, 'user-data'); const dataFolderExists = this.pathExistenceCache[dataFolderPath] ??= await pathExists(dataFolderPath); if (dataFolderExists) { diff --git a/packages/remote/src/electron-node/setup/remote-copy-contribution.ts b/packages/remote/src/electron-node/setup/remote-copy-contribution.ts index b6db142310ea8..4aecf9e8f0746 100644 --- a/packages/remote/src/electron-node/setup/remote-copy-contribution.ts +++ b/packages/remote/src/electron-node/setup/remote-copy-contribution.ts @@ -61,8 +61,7 @@ export class RemoteCopyRegistry { const globResult = await promiseGlob(pattern, { cwd: projectPath }); - const relativeFiles = globResult.map(file => path.relative(projectPath, file)); - for (const file of relativeFiles) { + for (const file of globResult) { const targetFile = this.withTarget(file, target); this.files.push({ path: file, From cff0a9c255ee748de2a055972196850faf09277f Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 22 Apr 2024 10:59:17 +0200 Subject: [PATCH 183/441] working notebook cell language select (#13615) * working notebook cell language select Also fixing notebook enter in notebook cell editors Signed-off-by: Jonah Iden * Use language display name * review changes Signed-off-by: Jonah Iden * correctly save metadata in notebook model Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- packages/editor/src/browser/editor-command.ts | 40 ++--------- .../src/browser/editor-frontend-module.ts | 3 + .../editor-language-quick-pick-service.ts | 68 +++++++++++++++++++ .../notebook-cell-actions-contribution.ts | 30 +++++++- packages/notebook/src/browser/style/index.css | 9 ++- .../browser/view-model/notebook-cell-model.ts | 11 ++- .../src/browser/view-model/notebook-model.ts | 2 +- .../src/browser/view/notebook-cell-editor.tsx | 4 ++ .../browser/view/notebook-code-cell-view.tsx | 22 ++++-- 9 files changed, 146 insertions(+), 43 deletions(-) create mode 100644 packages/editor/src/browser/editor-language-quick-pick-service.ts diff --git a/packages/editor/src/browser/editor-command.ts b/packages/editor/src/browser/editor-command.ts index 81e0c1505d02a..0a721159659a1 100644 --- a/packages/editor/src/browser/editor-command.ts +++ b/packages/editor/src/browser/editor-command.ts @@ -16,15 +16,15 @@ import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common'; -import URI from '@theia/core/lib/common/uri'; -import { CommonCommands, PreferenceService, LabelProvider, ApplicationShell, QuickInputService, QuickPickValue, QuickPickItemOrSeparator } from '@theia/core/lib/browser'; +import { CommonCommands, PreferenceService, LabelProvider, ApplicationShell, QuickInputService, QuickPickValue } from '@theia/core/lib/browser'; import { EditorManager } from './editor-manager'; import { EditorPreferences } from './editor-preferences'; import { ResourceProvider, MessageService } from '@theia/core'; -import { LanguageService, Language } from '@theia/core/lib/browser/language-service'; +import { LanguageService } from '@theia/core/lib/browser/language-service'; import { SUPPORTED_ENCODINGS } from '@theia/core/lib/browser/supported-encodings'; import { EncodingMode } from './editor'; import { nls } from '@theia/core/lib/common/nls'; +import { EditorLanguageQuickPickService } from './editor-language-quick-pick-service'; export namespace EditorCommands { @@ -237,6 +237,9 @@ export class EditorCommandContribution implements CommandContribution { @inject(ResourceProvider) protected readonly resourceProvider: ResourceProvider; + @inject(EditorLanguageQuickPickService) + protected readonly codeLanguageQuickPickService: EditorLanguageQuickPickService; + @postConstruct() protected init(): void { this.editorPreferences.onPreferenceChanged(e => { @@ -293,12 +296,7 @@ export class EditorCommandContribution implements CommandContribution { return; } const current = editor.document.languageId; - const items: Array | QuickPickItemOrSeparator> = [ - { label: nls.localizeByDefault('Auto Detect'), value: 'autoDetect' }, - { type: 'separator', label: nls.localizeByDefault('languages (identifier)') }, - ... (this.languages.languages.map(language => this.toQuickPickLanguage(language, current))).sort((e, e2) => e.label.localeCompare(e2.label)) - ]; - const selectedMode = await this.quickInputService?.showQuickPick(items, { placeholder: nls.localizeByDefault('Select Language Mode') }); + const selectedMode = await this.codeLanguageQuickPickService.pickEditorLanguage(current); if (selectedMode && ('value' in selectedMode)) { if (selectedMode.value === 'autoDetect') { editor.detectLanguage(); @@ -379,30 +377,6 @@ export class EditorCommandContribution implements CommandContribution { } } - protected toQuickPickLanguage(value: Language, current: string): QuickPickValue { - const languageUri = this.toLanguageUri(value); - const icon = this.labelProvider.getIcon(languageUri); - const iconClasses = icon !== '' ? [icon + ' file-icon'] : undefined; - const configured = current === value.id; - return { - value, - label: value.name, - description: nls.localizeByDefault(`({0})${configured ? ' - Configured Language' : ''}`, value.id), - iconClasses - }; - } - protected toLanguageUri(language: Language): URI { - const extension = language.extensions.values().next(); - if (extension.value) { - return new URI('file:///' + extension.value); - } - const filename = language.filenames.values().next(); - if (filename.value) { - return new URI('file:///' + filename.value); - } - return new URI('file:///.txt'); - } - protected isAutoSaveOn(): boolean { const autoSave = this.preferencesService.get(EditorCommandContribution.AUTOSAVE_PREFERENCE); return autoSave !== 'off'; diff --git a/packages/editor/src/browser/editor-frontend-module.ts b/packages/editor/src/browser/editor-frontend-module.ts index a22365ea28e3c..e4a089ae90b2a 100644 --- a/packages/editor/src/browser/editor-frontend-module.ts +++ b/packages/editor/src/browser/editor-frontend-module.ts @@ -38,6 +38,7 @@ import { QuickEditorService } from './quick-editor-service'; import { EditorLanguageStatusService } from './language-status/editor-language-status-service'; import { EditorLineNumberContribution } from './editor-linenumber-contribution'; import { UndoRedoService } from './undo-redo-service'; +import { EditorLanguageQuickPickService } from './editor-language-quick-pick-service'; export default new ContainerModule(bind => { bindEditorPreferences(bind); @@ -84,4 +85,6 @@ export default new ContainerModule(bind => { bind(EditorAccess).to(ActiveEditorAccess).inSingletonScope().whenTargetNamed(EditorAccess.ACTIVE); bind(UndoRedoService).toSelf().inSingletonScope(); + + bind(EditorLanguageQuickPickService).toSelf().inSingletonScope(); }); diff --git a/packages/editor/src/browser/editor-language-quick-pick-service.ts b/packages/editor/src/browser/editor-language-quick-pick-service.ts new file mode 100644 index 0000000000000..081a75b6d0fed --- /dev/null +++ b/packages/editor/src/browser/editor-language-quick-pick-service.ts @@ -0,0 +1,68 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { Language, LanguageService } from '@theia/core/lib/browser/language-service'; +import { nls, QuickInputService, QuickPickItemOrSeparator, QuickPickValue, URI } from '@theia/core'; +import { LabelProvider } from '@theia/core/lib/browser'; + +@injectable() +export class EditorLanguageQuickPickService { + @inject(LanguageService) + protected readonly languages: LanguageService; + + @inject(QuickInputService) + protected readonly quickInputService: QuickInputService; + + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; + + async pickEditorLanguage(current: string): Promise | undefined> { + const items: Array | QuickPickItemOrSeparator> = [ + { label: nls.localizeByDefault('Auto Detect'), value: 'autoDetect' }, + { type: 'separator', label: nls.localizeByDefault('languages (identifier)') }, + ... (this.languages.languages.map(language => this.toQuickPickLanguage(language, current))).sort((e, e2) => e.label.localeCompare(e2.label)) + ]; + const selectedMode = await this.quickInputService?.showQuickPick(items, { placeholder: nls.localizeByDefault('Select Language Mode') }); + return (selectedMode && 'value' in selectedMode) ? selectedMode : undefined; + } + + protected toQuickPickLanguage(value: Language, current: string): QuickPickValue { + const languageUri = this.toLanguageUri(value); + const icon = this.labelProvider.getIcon(languageUri); + const iconClasses = icon !== '' ? [icon + ' file-icon'] : undefined; + const configured = current === value.id; + return { + value, + label: value.name, + description: nls.localizeByDefault(`({0})${configured ? ' - Configured Language' : ''}`, value.id), + iconClasses + }; + } + + protected toLanguageUri(language: Language): URI { + const extension = language.extensions.values().next(); + if (extension.value) { + return new URI('file:///' + extension.value); + } + const filename = language.filenames.values().next(); + if (filename.value) { + return new URI('file:///' + filename.value); + } + return new URI('file:///.txt'); + } + +} diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index f44a88cd2a054..0c9acfff28703 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -31,6 +31,7 @@ import { CellEditType, CellKind } from '../../common'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NotebookCommands } from './notebook-actions-contribution'; import { changeCellType } from './cell-operations'; +import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -131,6 +132,12 @@ export namespace NotebookCellCommands { label: 'Expand Cell Output', }); + export const CHANGE_CELL_LANGUAGE = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.changeLanguage', + category: 'Notebook', + label: 'Change Cell Language', + }); + } @injectable() @@ -145,6 +152,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command @inject(NotebookEditorWidgetService) protected notebookEditorWidgetService: NotebookEditorWidgetService; + @inject(EditorLanguageQuickPickService) + protected languageQuickPickService: EditorLanguageQuickPickService; + @postConstruct() protected init(): void { NotebookContextKeys.initNotebookContextKeys(this.contextKeyService); @@ -359,6 +369,24 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }); + commands.registerCommand(NotebookCellCommands.CHANGE_CELL_LANGUAGE, { + isVisible: () => !!this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell, + execute: async (notebook?: NotebookModel, cell?: NotebookCellModel) => { + const selectedCell = cell ?? this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; + const activeNotebook = notebook ?? this.notebookEditorWidgetService.focusedEditor?.model; + if (selectedCell && activeNotebook) { + const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); + if (language?.value && language.value !== 'autoDetect') { + this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ + editType: CellEditType.CellLanguage, + index: activeNotebook.cells.indexOf(selectedCell), + language: language.value.id + }], true); + } + } + } + }); + } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { @@ -382,7 +410,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.EDIT_COMMAND.id, keybinding: 'Enter', - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 520cc6d150b42..ce8afa1a658b4 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -109,8 +109,13 @@ flex-grow: 1; } -.notebook-cell-status-right { - margin: 0 5px; +.notebook-cell-language-label { + padding: 0 5px; +} + +.notebook-cell-language-label:hover { + cursor: pointer; + background-color: var(--theia-toolbar-hoverBackground); } .notebook-cell-status-item { diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index dc3f20b1988c3..ea637ae256fbb 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -31,6 +31,7 @@ import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text- import { NotebookCellOutputModel } from './notebook-cell-output-model'; import { PreferenceService } from '@theia/core/lib/browser'; import { NOTEBOOK_LINE_NUMBERS } from '../contributions/notebook-preferences'; +import { LanguageService } from '@theia/core/lib/browser/language-service'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel; @@ -121,6 +122,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { @inject(NotebookMonacoTextModelService) protected readonly textModelService: NotebookMonacoTextModelService; + @inject(LanguageService) + protected readonly languageService: LanguageService; + @inject(PreferenceService) protected readonly preferenceService: PreferenceService; @@ -182,16 +186,19 @@ export class NotebookCellModel implements NotebookCell, Disposable { return; } - this.props.language = newLanguage; if (this.textModel) { this.textModel.setLanguageId(newLanguage); } - this.language = newLanguage; + this.props.language = newLanguage; this.onDidChangeLanguageEmitter.fire(newLanguage); this.onDidChangeContentEmitter.fire('language'); } + get languageName(): string { + return this.languageService.getLanguage(this.language)?.name ?? this.language; + } + get uri(): URI { return this.props.uri; } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index ea8e484ec8c01..4230666db5e63 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -141,7 +141,7 @@ export class NotebookModel implements Saveable, Disposable { this.addCellOutputListeners(this.cells); - this.metadata = this.metadata; + this.metadata = this.props.data.metadata; this.nextHandle = this.cells.length; } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 46e362fbeb2b6..f0e8de320eed2 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -62,6 +62,10 @@ export class CellEditor extends React.Component { this.editor?.getControl().updateOptions(options); })); + this.toDispose.push(this.props.cell.onDidChangeLanguage(language => { + this.editor?.setLanguage(language); + })); + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(() => { if (this.props.notebookModel.selectedCell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { if (document.activeElement && 'blur' in document.activeElement) { diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 292ea421a1819..0cbaf6529f6c6 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -24,11 +24,11 @@ import { NotebookModel } from '../view-model/notebook-model'; import { CellEditor } from './notebook-cell-editor'; import { CellRenderer } from './notebook-cell-list-view'; import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory'; -import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution'; +import { NotebookCellActionContribution, NotebookCellCommands } from '../contributions/notebook-cell-actions-contribution'; import { CellExecution, NotebookExecutionStateService } from '../service/notebook-execution-state-service'; import { codicon } from '@theia/core/lib/browser'; import { NotebookCellExecutionState } from '../../common'; -import { DisposableCollection, nls } from '@theia/core'; +import { CommandRegistry, DisposableCollection, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; import { NotebookViewportService } from './notebook-viewport-service'; import { EditorPreferences } from '@theia/editor/lib/browser'; @@ -61,6 +61,9 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(EditorPreferences) protected readonly editorPreferences: EditorPreferences; + @inject(CommandRegistry) + protected readonly commandRegistry: CommandRegistry; + protected fontInfo: BareFontInfo | undefined; render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { @@ -76,7 +79,10 @@ export class NotebookCodeCellRenderer implements CellRenderer { notebookContextManager={this.notebookContextManager} notebookViewportService={this.notebookViewportService} fontInfo={this.getOrCreateMonacoFontInfo()} /> - cell.requestFocusEditor()}> + cell.requestFocusEditor()} />
    @@ -108,7 +114,9 @@ export class NotebookCodeCellRenderer implements CellRenderer { } export interface NotebookCodeCellStatusProps { + notebook: NotebookModel; cell: NotebookCellModel; + commandRegistry: CommandRegistry; executionStateService: NotebookExecutionStateService; onClick: () => void; } @@ -146,6 +154,10 @@ export class NotebookCodeCellStatus extends React.Component { + this.forceUpdate(); + })); } override componentWillUnmount(): void { @@ -158,7 +170,9 @@ export class NotebookCodeCellStatus extends React.Component
    - {this.props.cell.language} + { + this.props.commandRegistry.executeCommand(NotebookCellCommands.CHANGE_CELL_LANGUAGE.id, this.props.notebook, this.props.cell); + }}>{this.props.cell.languageName}
    ; } From 80ae4507d298d063ad5c8761750301db50179827 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Mon, 22 Apr 2024 16:16:20 +0200 Subject: [PATCH 184/441] Duplicate Clipboard actions in editor context menu #13619 (#13626) --- packages/monaco/src/browser/monaco-menu.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/monaco/src/browser/monaco-menu.ts b/packages/monaco/src/browser/monaco-menu.ts index 40ab614701f43..21cebc0d42af1 100644 --- a/packages/monaco/src/browser/monaco-menu.ts +++ b/packages/monaco/src/browser/monaco-menu.ts @@ -14,12 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; -import { MenuContribution, MenuModelRegistry, MAIN_MENU_BAR, MenuPath, MenuAction } from '@theia/core/lib/common'; -import { EditorMainMenu, EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser'; -import { MonacoCommandRegistry } from './monaco-command-registry'; +import { MAIN_MENU_BAR, MenuAction, MenuContribution, MenuModelRegistry, MenuPath } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common/nls'; -import { IMenuItem, isIMenuItem, MenuId, MenuRegistry } from '@theia/monaco-editor-core/esm/vs/platform/actions/common/actions'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { EDITOR_CONTEXT_MENU, EditorMainMenu } from '@theia/editor/lib/browser'; +import { IMenuItem, MenuId, MenuRegistry, isIMenuItem } from '@theia/monaco-editor-core/esm/vs/platform/actions/common/actions'; +import { MonacoCommands } from './monaco-command'; +import { MonacoCommandRegistry } from './monaco-command-registry'; export interface MonacoActionGroup { id: string; @@ -46,7 +47,11 @@ export class MonacoEditorMenuContribution implements MenuContribution { const commandId = this.commands.validate(item.command.id); if (commandId) { const menuPath = [...EDITOR_CONTEXT_MENU, (item.group || '')]; - registry.registerMenuAction(menuPath, this.buildMenuAction(commandId, item)); + const coreId = MonacoCommands.COMMON_ACTIONS.get(commandId); + if (!(coreId && registry.getMenu(menuPath).children.some(it => it.id === coreId))) { + // Don't add additional actions if the item is already registered with a core ID. + registry.registerMenuAction(menuPath, this.buildMenuAction(commandId, item)); + } } } From 796973487f7f6bf80a1e7c1d675bec026aa594b7 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Mon, 22 Apr 2024 16:17:02 +0200 Subject: [PATCH 185/441] Add a new built-in handler to open files with system application #13535 (#13601) --- packages/core/src/electron-browser/preload.ts | 5 +- .../core/src/electron-common/electron-api.ts | 2 + .../src/electron-main/electron-api-main.ts | 7 ++- .../electron-navigator-menu-contribution.ts | 47 +++++++++++++++---- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index d7f6f30aa4ee0..b0903d1a764af 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -26,7 +26,7 @@ import { CHANNEL_IS_FULL_SCREEN, CHANNEL_SET_MENU_BAR_VISIBLE, CHANNEL_REQUEST_CLOSE, CHANNEL_SET_TITLE_STYLE, CHANNEL_RESTART, CHANNEL_REQUEST_RELOAD, CHANNEL_APP_STATE_CHANGED, CHANNEL_SHOW_ITEM_IN_FOLDER, CHANNEL_READ_CLIPBOARD, CHANNEL_WRITE_CLIPBOARD, CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR, - CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE + CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP } from '../electron-common/electron-api'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -79,6 +79,9 @@ const api: TheiaCoreAPI = { showItemInFolder: fsPath => { ipcRenderer.send(CHANNEL_SHOW_ITEM_IN_FOLDER, fsPath); }, + openWithSystemApp: fsPath => { + ipcRenderer.send(CHANNEL_OPEN_WITH_SYSTEM_APP, fsPath); + }, attachSecurityToken: (endpoint: string) => ipcRenderer.invoke(CHANNEL_ATTACH_SECURITY_TOKEN, endpoint), popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise { diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 6abe167e8a784..6bcde6a4fb333 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -56,6 +56,7 @@ export interface TheiaCoreAPI { focusWindow(name?: string): void; showItemInFolder(fsPath: string): void; + openWithSystemApp(fsPath: string): void; getTitleBarStyleAtStartup(): Promise; setTitleBarStyle(style: string): void; @@ -112,6 +113,7 @@ export const CHANNEL_FOCUS_WINDOW = 'FocusWindow'; export const CHANNEL_SHOW_OPEN = 'ShowOpenDialog'; export const CHANNEL_SHOW_SAVE = 'ShowSaveDialog'; export const CHANNEL_SHOW_ITEM_IN_FOLDER = 'ShowItemInFolder'; +export const CHANNEL_OPEN_WITH_SYSTEM_APP = 'OpenWithSystemApp'; export const CHANNEL_ATTACH_SECURITY_TOKEN = 'AttachSecurityToken'; export const CHANNEL_GET_TITLE_STYLE_AT_STARTUP = 'GetTitleStyleAtStartup'; diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index 485d917c9a288..d5966536dd470 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -53,7 +53,8 @@ import { CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR, CHANNEL_WC_METADATA, - CHANNEL_ABOUT_TO_CLOSE + CHANNEL_ABOUT_TO_CLOSE, + CHANNEL_OPEN_WITH_SYSTEM_APP } from '../electron-common/electron-api'; import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application'; import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common'; @@ -164,6 +165,10 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { shell.showItemInFolder(fsPath); }); + ipcMain.on(CHANNEL_OPEN_WITH_SYSTEM_APP, (event, fsPath) => { + shell.openPath(fsPath); + }); + ipcMain.handle(CHANNEL_GET_TITLE_STYLE_AT_STARTUP, event => application.getTitleBarStyleAtStartup(event.sender)); ipcMain.on(CHANNEL_SET_TITLE_STYLE, (event, style) => application.setTitleBarStyle(event.sender, style)); diff --git a/packages/navigator/src/electron-browser/electron-navigator-menu-contribution.ts b/packages/navigator/src/electron-browser/electron-navigator-menu-contribution.ts index 6e12555784473..3020bd5f4c335 100644 --- a/packages/navigator/src/electron-browser/electron-navigator-menu-contribution.ts +++ b/packages/navigator/src/electron-browser/electron-navigator-menu-contribution.ts @@ -14,17 +14,19 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, SelectionService } from '@theia/core'; -import { CommonCommands, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; +import { Command, CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry, SelectionService, URI } from '@theia/core'; +import { CommonCommands, KeybindingContribution, KeybindingRegistry, OpenWithService } from '@theia/core/lib/browser'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; +import { nls } from '@theia/core/lib/common'; +import { FileUri } from '@theia/core/lib/common/file-uri'; +import { isOSX, isWindows } from '@theia/core/lib/common/os'; +import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler'; +import '@theia/core/lib/electron-common/electron-api'; import { inject, injectable } from '@theia/core/shared/inversify'; import { FileStatNode } from '@theia/filesystem/lib/browser'; -import { FileNavigatorWidget, FILE_NAVIGATOR_ID } from '../browser'; -import { NavigatorContextMenu, SHELL_TABBAR_CONTEXT_REVEAL } from '../browser/navigator-contribution'; -import { isWindows, isOSX } from '@theia/core/lib/common/os'; import { WorkspaceService } from '@theia/workspace/lib/browser'; -import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handler'; -import '@theia/core/lib/electron-common/electron-api'; +import { FILE_NAVIGATOR_ID, FileNavigatorWidget } from '../browser'; +import { NavigatorContextMenu, SHELL_TABBAR_CONTEXT_REVEAL } from '../browser/navigator-contribution'; export const OPEN_CONTAINING_FOLDER = Command.toDefaultLocalizedCommand({ id: 'revealFileInOS', @@ -34,6 +36,12 @@ export const OPEN_CONTAINING_FOLDER = Command.toDefaultLocalizedCommand({ /* linux */ 'Open Containing Folder' }); +export const OPEN_WITH_SYSTEM_APP = Command.toDefaultLocalizedCommand({ + id: 'openWithSystemApp', + category: CommonCommands.FILE_CATEGORY, + label: 'Open With System Editor' +}); + @injectable() export class ElectronNavigatorMenuContribution implements MenuContribution, CommandContribution, KeybindingContribution { @@ -46,14 +54,37 @@ export class ElectronNavigatorMenuContribution implements MenuContribution, Comm @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; + @inject(OpenWithService) + protected readonly openWithService: OpenWithService; + registerCommands(commands: CommandRegistry): void { commands.registerCommand(OPEN_CONTAINING_FOLDER, UriAwareCommandHandler.MonoSelect(this.selectionService, { execute: async uri => { - window.electronTheiaCore.showItemInFolder(uri['codeUri'].fsPath); + window.electronTheiaCore.showItemInFolder(FileUri.fsPath(uri)); }, isEnabled: uri => !!this.workspaceService.getWorkspaceRootUri(uri), isVisible: uri => !!this.workspaceService.getWorkspaceRootUri(uri), })); + commands.registerCommand(OPEN_WITH_SYSTEM_APP, UriAwareCommandHandler.MonoSelect(this.selectionService, { + execute: async uri => { + this.openWithSystemApplication(uri); + } + })); + this.openWithService.registerHandler({ + id: 'system-editor', + label: nls.localize('theia/navigator/systemEditor', 'System Editor'), + providerName: nls.localizeByDefault('Built-in'), + // Low priority to avoid conflicts with other open handlers. + canHandle: uri => (uri.scheme === 'file') ? 10 : 0, + open: uri => { + this.openWithSystemApplication(uri); + return {}; + } + }); + } + + protected openWithSystemApplication(uri: URI): void { + window.electronTheiaCore.openWithSystemApp(FileUri.fsPath(uri)); } registerMenus(menus: MenuModelRegistry): void { From 346822bb061b26f1b9e568da1dae2ce1f68b332d Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Mon, 22 Apr 2024 16:46:15 +0200 Subject: [PATCH 186/441] Fix onLanguage activation event (#13630) According to the the VSCode documentation `onLanguage` (without any language specified) is a valid activation event. This is used in the built-in emmet extension for example. Beforehand extensions with this event were not triggered. With this commit the extensions with `onLanguage` are activated, when the first language is loaded. --- packages/plugin-ext/src/hosted/browser/hosted-plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index c298d7df1195b..e972a1603f36a 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -409,6 +409,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport { + await this.activateByEvent('onLanguage'); await this.activateByEvent(`onLanguage:${languageId}`); } From 7ed5fcb500736d377c4c06feca5f01f40baa4073 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 22 Apr 2024 17:11:28 +0200 Subject: [PATCH 187/441] Fix notebook widget icon on reload (#13612) --- .../browser/notebook-editor-widget-factory.ts | 23 +++++++++++++++---- .../src/browser/notebook-editor-widget.tsx | 2 +- .../notebooks/notebook-kernels-main.ts | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget-factory.ts b/packages/notebook/src/browser/notebook-editor-widget-factory.ts index fc9d90d0ac109..3582f867e4beb 100644 --- a/packages/notebook/src/browser/notebook-editor-widget-factory.ts +++ b/packages/notebook/src/browser/notebook-editor-widget-factory.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { URI } from '@theia/core'; +import { nls, URI } from '@theia/core'; import { WidgetFactory, NavigatableWidgetOptions, LabelProvider } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; import { NotebookEditorWidget, NotebookEditorWidgetContainerFactory, NotebookEditorProps } from './notebook-editor-widget'; @@ -51,10 +51,13 @@ export class NotebookEditorWidgetFactory implements WidgetFactory { const editor = await this.createEditor(uri, options.notebookType); - const icon = this.labelProvider.getIcon(uri); - editor.title.label = this.labelProvider.getName(uri); - editor.title.iconClass = icon + ' file-icon'; - + this.setLabels(editor, uri); + const labelListener = this.labelProvider.onDidChange(event => { + if (event.affects(uri)) { + this.setLabels(editor, uri); + } + }); + editor.onDidDispose(() => labelListener.dispose()); return editor; } @@ -67,4 +70,14 @@ export class NotebookEditorWidgetFactory implements WidgetFactory { }); } + private setLabels(editor: NotebookEditorWidget, uri: URI): void { + editor.title.caption = uri.path.fsPath(); + if (editor.model?.readOnly) { + editor.title.caption += ` • ${nls.localizeByDefault('Read-only')}`; + } + const icon = this.labelProvider.getIcon(uri); + editor.title.label = this.labelProvider.getName(uri); + editor.title.iconClass = icon + ' file-icon'; + } + } diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index a9fc8c2514b4b..5c4cac3870f60 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -118,7 +118,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa readonly onPostRendererMessage = this.onPostRendererMessageEmitter.event; protected readonly onDidReceiveKernelMessageEmitter = new Emitter(); - readonly onDidRecieveKernelMessage = this.onDidReceiveKernelMessageEmitter.event; + readonly onDidReceiveKernelMessage = this.onDidReceiveKernelMessageEmitter.event; protected readonly renderers = new Map(); protected _model?: NotebookModel; diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts index f15101052a421..8572ebee91f17 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts @@ -145,7 +145,7 @@ export class NotebookKernelsMainImpl implements NotebookKernelsMain { this.notebookEditorWidgetService = container.get(NotebookEditorWidgetService); this.notebookEditorWidgetService.onDidAddNotebookEditor(editor => { - editor.onDidRecieveKernelMessage(async message => { + editor.onDidReceiveKernelMessage(async message => { const kernel = this.notebookKernelService.getSelectedOrSuggestedKernel(editor.model!); if (kernel) { this.proxy.$acceptKernelMessageFromRenderer(kernel.handle, editor.id, message); From 47ec23b81ece55b082ee38dbfdd51fac665aa096 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Tue, 23 Apr 2024 06:55:07 +0000 Subject: [PATCH 188/441] Bind Extension search bar within view container so it is re-created (#13623) Fixes https://github.com/eclipse-theia/theia/issues/13622 --- .../vsx-registry/src/browser/vsx-registry-frontend-module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts index fe3b8f4416b00..2bb0b34eec0c9 100644 --- a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts +++ b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts @@ -78,6 +78,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { progressLocationId: 'extensions' }); child.bind(VSXExtensionsViewContainer).toSelf(); + child.bind(VSXExtensionsSearchBar).toSelf().inSingletonScope(); const viewContainer = child.get(VSXExtensionsViewContainer); const widgetManager = child.get(WidgetManager); for (const id of [ @@ -96,7 +97,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { })).inSingletonScope(); bind(VSXExtensionsSearchModel).toSelf().inSingletonScope(); - bind(VSXExtensionsSearchBar).toSelf().inSingletonScope(); rebind(LanguageQuickPickService).to(VSXLanguageQuickPickService).inSingletonScope(); From 746f8ff1aeb0922dcb601dec7c9ca52ce0138118 Mon Sep 17 00:00:00 2001 From: Vladimir Piskarev Date: Tue, 23 Apr 2024 11:50:31 +0300 Subject: [PATCH 189/441] Add Dirty Diff Peek View (#13104) Closes #4544. --- CHANGELOG.md | 12 +- packages/editor/src/browser/editor.ts | 2 + .../dirty-diff/dirty-diff-contribution.ts | 8 +- .../browser/dirty-diff/dirty-diff-manager.ts | 45 ++- packages/git/src/browser/git-contribution.ts | 90 ++++- .../git/src/node/git-repository-watcher.ts | 2 + .../monaco/src/browser/monaco-diff-editor.ts | 55 ++- .../browser/monaco-diff-navigator-factory.ts | 4 +- .../browser/monaco-editor-peek-view-widget.ts | 233 +++++++++++ .../src/browser/monaco-editor-provider.ts | 37 ++ packages/monaco/src/browser/monaco-editor.ts | 72 +++- packages/monaco/src/browser/style/index.css | 1 - .../menus/plugin-menu-command-adapter.ts | 39 ++ .../menus/vscode-theia-menu-mappings.ts | 3 + packages/scm/package.json | 2 + .../decorations/scm-decorations-service.ts | 96 +++-- .../src/browser/dirty-diff/content-lines.ts | 9 + .../browser/dirty-diff/diff-computer.spec.ts | 172 ++++++--- .../src/browser/dirty-diff/diff-computer.ts | 128 ++++-- .../dirty-diff/dirty-diff-decorator.ts | 27 +- .../browser/dirty-diff/dirty-diff-module.ts | 9 + .../dirty-diff/dirty-diff-navigator.ts | 288 ++++++++++++++ .../browser/dirty-diff/dirty-diff-widget.ts | 364 ++++++++++++++++++ packages/scm/src/browser/scm-colors.ts | 21 + packages/scm/src/browser/scm-contribution.ts | 103 ++++- packages/scm/src/browser/scm-tree-widget.tsx | 2 +- .../browser/style/dirty-diff-decorator.css | 3 +- packages/scm/tsconfig.json | 3 + 28 files changed, 1649 insertions(+), 181 deletions(-) create mode 100644 packages/monaco/src/browser/monaco-editor-peek-view-widget.ts create mode 100644 packages/scm/src/browser/dirty-diff/dirty-diff-navigator.ts create mode 100644 packages/scm/src/browser/dirty-diff/dirty-diff-widget.ts create mode 100644 packages/scm/src/browser/scm-colors.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index dd737cd843def..2d3fcdfacf9bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,15 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) - +- [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) + +[Breaking Changes:](#breaking_changes_not_yet_released) +- [scm] revised some of the dirty diff related types [#13104](https://github.com/eclipse-theia/theia/pull/13104) + - replaced `DirtyDiff.added/removed/modified` with `changes`, which provides more detailed information about the changes + - changed the semantics of `LineRange` to represent a range that spans up to but not including the `end` line (previously, it included the `end` line) + - changed the signature of `DirtyDiffDecorator.toDeltaDecoration(LineRange | number, EditorDecorationOptions)` to `toDeltaDecoration(Change)` ## v1.48.0 - 03/28/2024 @@ -95,7 +101,7 @@ - Moved `ThemaIcon` and `ThemeColor` to the common folder - Minor typing adjustments in QuickPickService: in parti - FileUploadService: moved id field from data transfer item to the corresponding file info - - The way we instantiate monaco services has changed completely: if you touch monaco services in your code, please read the description in the + - The way we instantiate monaco services has changed completely: if you touch monaco services in your code, please read the description in the file comment in `monaco-init.ts`. ## v1.46.0 - 01/25/2024 diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index ce792c7a6929e..0613b7b60936f 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -293,6 +293,8 @@ export interface TextEditor extends Disposable, TextEditorSelection, Navigatable setEncoding(encoding: string, mode: EncodingMode): void; readonly onEncodingChanged: Event; + + shouldDisplayDirtyDiff(): boolean; } export interface Dimension { diff --git a/packages/git/src/browser/dirty-diff/dirty-diff-contribution.ts b/packages/git/src/browser/dirty-diff/dirty-diff-contribution.ts index 0ffc8a4148dfe..ced8be176d5bb 100644 --- a/packages/git/src/browser/dirty-diff/dirty-diff-contribution.ts +++ b/packages/git/src/browser/dirty-diff/dirty-diff-contribution.ts @@ -16,6 +16,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { DirtyDiffDecorator } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-decorator'; +import { DirtyDiffNavigator } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-navigator'; import { FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser'; import { DirtyDiffManager } from './dirty-diff-manager'; @@ -25,10 +26,13 @@ export class DirtyDiffContribution implements FrontendApplicationContribution { constructor( @inject(DirtyDiffManager) protected readonly dirtyDiffManager: DirtyDiffManager, @inject(DirtyDiffDecorator) protected readonly dirtyDiffDecorator: DirtyDiffDecorator, + @inject(DirtyDiffNavigator) protected readonly dirtyDiffNavigator: DirtyDiffNavigator, ) { } onStart(app: FrontendApplication): void { - this.dirtyDiffManager.onDirtyDiffUpdate(update => this.dirtyDiffDecorator.applyDecorations(update)); + this.dirtyDiffManager.onDirtyDiffUpdate(update => { + this.dirtyDiffDecorator.applyDecorations(update); + this.dirtyDiffNavigator.handleDirtyDiffUpdate(update); + }); } - } diff --git a/packages/git/src/browser/dirty-diff/dirty-diff-manager.ts b/packages/git/src/browser/dirty-diff/dirty-diff-manager.ts index 714ce0f12f943..31ede34ee21f0 100644 --- a/packages/git/src/browser/dirty-diff/dirty-diff-manager.ts +++ b/packages/git/src/browser/dirty-diff/dirty-diff-manager.ts @@ -71,13 +71,13 @@ export class DirtyDiffManager { protected async handleEditorCreated(editorWidget: EditorWidget): Promise { const editor = editorWidget.editor; - const uri = editor.uri.toString(); - if (editor.uri.scheme !== 'file') { + if (!this.supportsDirtyDiff(editor)) { return; } const toDispose = new DisposableCollection(); const model = this.createNewModel(editor); toDispose.push(model); + const uri = editor.uri.toString(); this.models.set(uri, model); toDispose.push(editor.onDocumentContentChanged(throttle((event: TextDocumentChangeEvent) => model.handleDocumentChanged(event.document), 1000))); editorWidget.disposed.connect(() => { @@ -93,6 +93,10 @@ export class DirtyDiffManager { model.handleDocumentChanged(editor.document); } + protected supportsDirtyDiff(editor: TextEditor): boolean { + return editor.uri.scheme === 'file' && editor.shouldDisplayDirtyDiff(); + } + protected createNewModel(editor: TextEditor): DirtyDiffModel { const previousRevision = this.createPreviousFileRevision(editor.uri); const model = new DirtyDiffModel(editor, this.preferences, previousRevision); @@ -101,11 +105,14 @@ export class DirtyDiffManager { } protected createPreviousFileRevision(fileUri: URI): DirtyDiffModel.PreviousFileRevision { + const getOriginalUri = (staged: boolean): URI => { + const query = staged ? '' : 'HEAD'; + return fileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery(query); + }; return { fileUri, getContents: async (staged: boolean) => { - const query = staged ? '' : 'HEAD'; - const uri = fileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery(query); + const uri = getOriginalUri(staged); const gitResource = await this.gitResourceResolver.getResource(uri); return gitResource.readContents(); }, @@ -115,7 +122,8 @@ export class DirtyDiffManager { return this.git.lsFiles(repository, fileUri.toString(), { errorUnmatch: true }); } return false; - } + }, + getOriginalUri }; } @@ -128,7 +136,6 @@ export class DirtyDiffManager { await model.handleGitStatusUpdate(repository, changes); } } - } export class DirtyDiffModel implements Disposable { @@ -137,7 +144,7 @@ export class DirtyDiffModel implements Disposable { protected enabled = true; protected staged: boolean; - protected previousContent: ContentLines | undefined; + protected previousContent: DirtyDiffModel.PreviousRevisionContent | undefined; protected currentContent: ContentLines | undefined; protected readonly onDirtyDiffUpdateEmitter = new Emitter(); @@ -181,7 +188,7 @@ export class DirtyDiffModel implements Disposable { update(): void { const editor = this.editor; if (!this.shouldRender()) { - this.onDirtyDiffUpdateEmitter.fire({ editor, added: [], removed: [], modified: [] }); + this.onDirtyDiffUpdateEmitter.fire({ editor, changes: [] }); return; } if (this.updateTimeout) { @@ -200,7 +207,7 @@ export class DirtyDiffModel implements Disposable { // a new update task should be scheduled anyway. return; } - const dirtyDiffUpdate = { editor, ...dirtyDiff }; + const dirtyDiffUpdate = { editor, previousRevisionUri: previous.uri, ...dirtyDiff }; this.onDirtyDiffUpdateEmitter.fire(dirtyDiffUpdate); }, 100); } @@ -251,9 +258,13 @@ export class DirtyDiffModel implements Disposable { return modelUri.startsWith(repoUri) && this.previousRevision.isVersionControlled(); } - protected async getPreviousRevisionContent(): Promise { - const contents = await this.previousRevision.getContents(this.staged); - return contents ? ContentLines.fromString(contents) : undefined; + protected async getPreviousRevisionContent(): Promise { + const { previousRevision, staged } = this; + const contents = await previousRevision.getContents(staged); + if (contents) { + const uri = previousRevision.getOriginalUri?.(staged); + return { ...ContentLines.fromString(contents), uri }; + } } dispose(): void { @@ -282,16 +293,18 @@ export namespace DirtyDiffModel { } export function documentContentLines(document: TextEditorDocument): ContentLines { - return { - length: document.lineCount, - getLineContent: line => document.getLineContent(line + 1), - }; + return ContentLines.fromTextEditorDocument(document); } export interface PreviousFileRevision { readonly fileUri: URI; getContents(staged: boolean): Promise; isVersionControlled(): Promise; + getOriginalUri?(staged: boolean): URI; + } + + export interface PreviousRevisionContent extends ContentLines { + readonly uri?: URI; } } diff --git a/packages/git/src/browser/git-contribution.ts b/packages/git/src/browser/git-contribution.ts index 8232ecf989c0d..de36bd4d7788a 100644 --- a/packages/git/src/browser/git-contribution.ts +++ b/packages/git/src/browser/git-contribution.ts @@ -32,7 +32,7 @@ import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { EditorContextMenu, EditorManager, EditorOpenerOptions, EditorWidget } from '@theia/editor/lib/browser'; -import { Git, GitFileChange, GitFileStatus } from '../common'; +import { Git, GitFileChange, GitFileStatus, GitWatcher, Repository } from '../common'; import { GitRepositoryTracker } from './git-repository-tracker'; import { GitAction, GitQuickOpenService } from './git-quick-open-service'; import { GitSyncService } from './git-sync-service'; @@ -42,6 +42,8 @@ import { GitErrorHandler } from '../browser/git-error-handler'; import { ScmWidget } from '@theia/scm/lib/browser/scm-widget'; import { ScmTreeWidget } from '@theia/scm/lib/browser/scm-tree-widget'; import { ScmCommand, ScmResource } from '@theia/scm/lib/browser/scm-provider'; +import { LineRange } from '@theia/scm/lib/browser/dirty-diff/diff-computer'; +import { DirtyDiffWidget, SCM_CHANGE_TITLE_MENU } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-widget'; import { ProgressService } from '@theia/core/lib/common/progress-service'; import { GitPreferences } from './git-preferences'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; @@ -166,6 +168,18 @@ export namespace GIT_COMMANDS { label: 'Stage All Changes', iconClass: codicon('add') }, 'vscode.git/package/command.stageAll', GIT_CATEGORY_KEY); + export const STAGE_CHANGE = Command.toLocalizedCommand({ + id: 'git.stage.change', + category: GIT_CATEGORY, + label: 'Stage Change', + iconClass: codicon('add') + }, 'vscode.git/package/command.stageChange', GIT_CATEGORY_KEY); + export const REVERT_CHANGE = Command.toLocalizedCommand({ + id: 'git.revert.change', + category: GIT_CATEGORY, + label: 'Revert Change', + iconClass: codicon('discard') + }, 'vscode.git/package/command.revertChange', GIT_CATEGORY_KEY); export const UNSTAGE = Command.toLocalizedCommand({ id: 'git.unstage', category: GIT_CATEGORY, @@ -280,6 +294,7 @@ export class GitContribution implements CommandContribution, MenuContribution, T @inject(GitPreferences) protected readonly gitPreferences: GitPreferences; @inject(DecorationsService) protected readonly decorationsService: DecorationsService; @inject(GitDecorationProvider) protected readonly gitDecorationProvider: GitDecorationProvider; + @inject(GitWatcher) protected readonly gitWatcher: GitWatcher; onStart(): void { this.updateStatusBar(); @@ -385,6 +400,15 @@ export class GitContribution implements CommandContribution, MenuContribution, T commandId: GIT_COMMANDS.DISCARD_ALL.id, when: 'scmProvider == git && scmResourceGroup == workingTree || scmProvider == git && scmResourceGroup == untrackedChanges', }); + + menus.registerMenuAction(SCM_CHANGE_TITLE_MENU, { + commandId: GIT_COMMANDS.STAGE_CHANGE.id, + when: 'scmProvider == git' + }); + menus.registerMenuAction(SCM_CHANGE_TITLE_MENU, { + commandId: GIT_COMMANDS.REVERT_CHANGE.id, + when: 'scmProvider == git' + }); } registerCommands(registry: CommandRegistry): void { @@ -573,6 +597,14 @@ export class GitContribution implements CommandContribution, MenuContribution, T isEnabled: widget => this.workspaceService.opened && (!widget || widget instanceof ScmWidget) && !this.repositoryProvider.selectedRepository, isVisible: widget => this.workspaceService.opened && (!widget || widget instanceof ScmWidget) && !this.repositoryProvider.selectedRepository }); + registry.registerCommand(GIT_COMMANDS.STAGE_CHANGE, { + execute: (widget: DirtyDiffWidget) => this.withProgress(() => this.stageChange(widget)), + isEnabled: widget => widget instanceof DirtyDiffWidget + }); + registry.registerCommand(GIT_COMMANDS.REVERT_CHANGE, { + execute: (widget: DirtyDiffWidget) => this.withProgress(() => this.revertChange(widget)), + isEnabled: widget => widget instanceof DirtyDiffWidget + }); } async amend(): Promise { { @@ -922,6 +954,62 @@ export class GitContribution implements CommandContribution, MenuContribution, T } + async stageChange(widget: DirtyDiffWidget): Promise { + const scmRepository = this.repositoryProvider.selectedScmRepository; + if (!scmRepository) { + return; + } + + const repository = scmRepository.provider.repository; + + const path = Repository.relativePath(repository, widget.uri)?.toString(); + if (!path) { + return; + } + + const { currentChange } = widget; + if (!currentChange) { + return; + } + + const dataToStage = await widget.getContentWithSelectedChanges(change => change === currentChange); + + try { + const hash = (await this.git.exec(repository, ['hash-object', '--stdin', '-w', '--path', path], { stdin: dataToStage, stdinEncoding: 'utf8' })).stdout.trim(); + + let mode = (await this.git.exec(repository, ['ls-files', '--format=%(objectmode)', '--', path])).stdout.split('\n').filter(line => !!line.trim())[0]; + if (!mode) { + mode = '100644'; // regular non-executable file + } + + await this.git.exec(repository, ['update-index', '--add', '--cacheinfo', mode, hash, path]); + + // enforce a notification as there would be no status update if the file had been staged already + this.gitWatcher.onGitChanged({ source: repository, status: await this.git.status(repository) }); + } catch (error) { + this.gitErrorHandler.handleError(error); + } + + widget.editor.cursor = LineRange.getStartPosition(currentChange.currentRange); + } + + async revertChange(widget: DirtyDiffWidget): Promise { + const { currentChange } = widget; + if (!currentChange) { + return; + } + + const editor = widget.editor.getControl(); + editor.pushUndoStop(); + editor.executeEdits('Revert Change', [{ + range: editor.getModel()!.getFullModelRange(), + text: await widget.getContentWithSelectedChanges(change => change !== currentChange) + }]); + editor.pushUndoStop(); + + widget.editor.cursor = LineRange.getStartPosition(currentChange.currentRange); + } + /** * It should be aligned with https://code.visualstudio.com/api/references/theme-color#git-colors */ diff --git a/packages/git/src/node/git-repository-watcher.ts b/packages/git/src/node/git-repository-watcher.ts index 3e1bc8a43e8b1..455842b8974b3 100644 --- a/packages/git/src/node/git-repository-watcher.ts +++ b/packages/git/src/node/git-repository-watcher.ts @@ -96,9 +96,11 @@ export class GitRepositoryWatcher implements Disposable { } else { const idleTimeout = this.watching ? 5000 : /* super long */ 1000 * 60 * 60 * 24; await new Promise(resolve => { + this.idle = true; const id = setTimeout(resolve, idleTimeout); this.interruptIdle = () => { clearTimeout(id); resolve(); }; }).then(() => { + this.idle = false; this.interruptIdle = undefined; }); } diff --git a/packages/monaco/src/browser/monaco-diff-editor.ts b/packages/monaco/src/browser/monaco-diff-editor.ts index b631ebb8f95da..22f6722e36230 100644 --- a/packages/monaco/src/browser/monaco-diff-editor.ts +++ b/packages/monaco/src/browser/monaco-diff-editor.ts @@ -22,8 +22,15 @@ import { EditorServiceOverrides, MonacoEditor, MonacoEditorServices } from './mo import { MonacoDiffNavigatorFactory } from './monaco-diff-navigator-factory'; import { DiffUris } from '@theia/core/lib/browser/diff-uris'; import * as monaco from '@theia/monaco-editor-core'; -import { IDiffEditorConstructionOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { StandaloneDiffEditor2 } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { ICodeEditor, IDiffEditorConstructionOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; +import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, StandaloneCodeEditor, StandaloneDiffEditor2 } + from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { IEditorConstructionOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/config/editorConfiguration'; +import { EmbeddedDiffEditorWidget } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; +import { ContextKeyValue, IContextKey } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IDisposable } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; +import { ICommandHandler } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; export namespace MonacoDiffEditor { export interface IOptions extends MonacoEditor.ICommonOptions, IDiffEditorConstructionOptions { @@ -31,7 +38,7 @@ export namespace MonacoDiffEditor { } export class MonacoDiffEditor extends MonacoEditor { - protected _diffEditor: StandaloneDiffEditor2; + protected _diffEditor: IStandaloneDiffEditor; protected _diffNavigator: DiffNavigator; constructor( @@ -42,9 +49,10 @@ export class MonacoDiffEditor extends MonacoEditor { services: MonacoEditorServices, protected readonly diffNavigatorFactory: MonacoDiffNavigatorFactory, options?: MonacoDiffEditor.IOptions, - override?: EditorServiceOverrides + override?: EditorServiceOverrides, + parentEditor?: MonacoEditor ) { - super(uri, modifiedModel, node, services, options, override); + super(uri, modifiedModel, node, services, options, override, parentEditor); this.documents.add(originalModel); const original = originalModel.textEditorModel; const modified = modifiedModel.textEditorModel; @@ -61,13 +69,15 @@ export class MonacoDiffEditor extends MonacoEditor { } protected override create(options?: IDiffEditorConstructionOptions, override?: EditorServiceOverrides): Disposable { + options = { ...options, fixedOverflowWidgets: true }; const instantiator = this.getInstantiatorWithOverrides(override); /** * @monaco-uplift. Should be guaranteed to work. * Incomparable enums prevent TypeScript from believing that public IStandaloneDiffEditor is satisfied by private StandaloneDiffEditor */ - this._diffEditor = instantiator - .createInstance(StandaloneDiffEditor2, this.node, { ...options, fixedOverflowWidgets: true }); + this._diffEditor = this.parentEditor ? + instantiator.createInstance(EmbeddedDiffEditor, this.node, options, {}, this.parentEditor.getControl() as unknown as ICodeEditor) : + instantiator.createInstance(StandaloneDiffEditor2, this.node, options); this.editor = this._diffEditor.getModifiedEditor() as unknown as monaco.editor.IStandaloneCodeEditor; return this._diffEditor; } @@ -97,4 +107,35 @@ export class MonacoDiffEditor extends MonacoEditor { return DiffUris.encode(left.withPath(resourceUri.path), right.withPath(resourceUri.path)); } + override shouldDisplayDirtyDiff(): boolean { + return false; + } +} + +class EmbeddedDiffEditor extends EmbeddedDiffEditorWidget implements IStandaloneDiffEditor { + + protected override _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, + options: Readonly): StandaloneCodeEditor { + return instantiationService.createInstance(StandaloneCodeEditor, container, options); + } + + override getOriginalEditor(): IStandaloneCodeEditor { + return super.getOriginalEditor() as IStandaloneCodeEditor; + } + + override getModifiedEditor(): IStandaloneCodeEditor { + return super.getModifiedEditor() as IStandaloneCodeEditor; + } + + addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null { + return this.getModifiedEditor().addCommand(keybinding, handler, context); + } + + createContextKey(key: string, defaultValue: T): IContextKey { + return this.getModifiedEditor().createContextKey(key, defaultValue); + } + + addAction(descriptor: IActionDescriptor): IDisposable { + return this.getModifiedEditor().addAction(descriptor); + } } diff --git a/packages/monaco/src/browser/monaco-diff-navigator-factory.ts b/packages/monaco/src/browser/monaco-diff-navigator-factory.ts index 639037c304800..897aaa611359d 100644 --- a/packages/monaco/src/browser/monaco-diff-navigator-factory.ts +++ b/packages/monaco/src/browser/monaco-diff-navigator-factory.ts @@ -16,7 +16,7 @@ import { injectable } from '@theia/core/shared/inversify'; import { DiffNavigator } from '@theia/editor/lib/browser'; -import { StandaloneDiffEditor2 } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { IDiffEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; @injectable() export class MonacoDiffNavigatorFactory { @@ -28,7 +28,7 @@ export class MonacoDiffNavigatorFactory { previous: () => { }, }; - createdDiffNavigator(editor: StandaloneDiffEditor2): DiffNavigator { + createdDiffNavigator(editor: IDiffEditor): DiffNavigator { return { hasNext: () => true, hasPrevious: () => true, diff --git a/packages/monaco/src/browser/monaco-editor-peek-view-widget.ts b/packages/monaco/src/browser/monaco-editor-peek-view-widget.ts new file mode 100644 index 0000000000000..4c5d90b455c34 --- /dev/null +++ b/packages/monaco/src/browser/monaco-editor-peek-view-widget.ts @@ -0,0 +1,233 @@ +// ***************************************************************************** +// Copyright (C) 2024 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Position, Range } from '@theia/core/shared/vscode-languageserver-protocol'; +import { DisposableCollection } from '@theia/core'; +import { MonacoEditor } from './monaco-editor'; +import * as monaco from '@theia/monaco-editor-core'; +import { PeekViewWidget, IPeekViewOptions, IPeekViewStyles } from '@theia/monaco-editor-core/esm/vs/editor/contrib/peekView/browser/peekView'; +import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; +import { ActionBar } from '@theia/monaco-editor-core/esm/vs/base/browser/ui/actionbar/actionbar'; +import { Action } from '@theia/monaco-editor-core/esm/vs/base/common/actions'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; +import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { Color } from '@theia/monaco-editor-core/esm/vs/base/common/color'; + +export { peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground } + from '@theia/monaco-editor-core/esm/vs/editor/contrib/peekView/browser/peekView'; + +export namespace MonacoEditorPeekViewWidget { + export interface Styles { + frameColor?: string; + arrowColor?: string; + headerBackgroundColor?: string; + primaryHeadingColor?: string; + secondaryHeadingColor?: string; + } + export interface Options { + showFrame?: boolean; + showArrow?: boolean; + frameWidth?: number; + className?: string; + isAccessible?: boolean; + isResizeable?: boolean; + keepEditorSelection?: boolean; + allowUnlimitedHeight?: boolean; + ordinal?: number; + showInHiddenAreas?: boolean; + supportOnTitleClick?: boolean; + } + export interface Action { + readonly id: string; + label: string; + tooltip: string; + class: string | undefined; + enabled: boolean; + checked?: boolean; + run(...args: unknown[]): unknown; + } + export interface ActionOptions { + icon?: boolean; + label?: boolean; + keybinding?: string; + index?: number; + } +} + +export class MonacoEditorPeekViewWidget { + + protected readonly toDispose = new DisposableCollection(); + + readonly onDidClose = this.toDispose.onDispose; + + private readonly themeService = StandaloneServices.get(IThemeService); + + private readonly delegate; + + constructor( + readonly editor: MonacoEditor, + options: MonacoEditorPeekViewWidget.Options = {}, + protected styles: MonacoEditorPeekViewWidget.Styles = {} + ) { + const that = this; + this.toDispose.push(this.delegate = new class extends PeekViewWidget { + + get actionBar(): ActionBar | undefined { + return this._actionbarWidget; + } + + fillHead(container: HTMLElement, noCloseAction?: boolean): void { + super._fillHead(container, noCloseAction); + } + + protected override _fillHead(container: HTMLElement, noCloseAction?: boolean): void { + that.fillHead(container, noCloseAction); + } + + fillBody(container: HTMLElement): void { + // super._fillBody is an abstract method + } + + protected override _fillBody(container: HTMLElement): void { + that.fillBody(container); + }; + + doLayoutHead(heightInPixel: number, widthInPixel: number): void { + super._doLayoutHead(heightInPixel, widthInPixel); + } + + protected override _doLayoutHead(heightInPixel: number, widthInPixel: number): void { + that.doLayoutHead(heightInPixel, widthInPixel); + } + + doLayoutBody(heightInPixel: number, widthInPixel: number): void { + super._doLayoutBody(heightInPixel, widthInPixel); + } + + protected override _doLayoutBody(heightInPixel: number, widthInPixel: number): void { + that.doLayoutBody(heightInPixel, widthInPixel); + } + + onWidth(widthInPixel: number): void { + super._onWidth(widthInPixel); + } + + protected override _onWidth(widthInPixel: number): void { + that.onWidth(widthInPixel); + } + + doRevealRange(range: monaco.Range, isLastLine: boolean): void { + super.revealRange(range, isLastLine); + } + + protected override revealRange(range: monaco.Range, isLastLine: boolean): void { + that.doRevealRange(that.editor['m2p'].asRange(range), isLastLine); + } + }( + editor.getControl() as unknown as ICodeEditor, + Object.assign({}, options, this.convertStyles(styles)), + StandaloneServices.get(IInstantiationService) + )); + this.toDispose.push(this.themeService.onDidColorThemeChange(() => this.style(this.styles))); + } + + dispose(): void { + this.toDispose.dispose(); + } + + create(): void { + this.delegate.create(); + } + + setTitle(primaryHeading: string, secondaryHeading?: string): void { + this.delegate.setTitle(primaryHeading, secondaryHeading); + } + + style(styles: MonacoEditorPeekViewWidget.Styles): void { + this.delegate.style(this.convertStyles(this.styles = styles)); + } + + show(rangeOrPos: Range | Position, heightInLines: number): void { + this.delegate.show(this.convertRangeOrPosition(rangeOrPos), heightInLines); + } + + hide(): void { + this.delegate.hide(); + } + + clearActions(): void { + this.delegate.actionBar?.clear(); + } + + addAction(id: string, label: string, cssClass: string | undefined, enabled: boolean, actionCallback: (arg: unknown) => unknown, + options?: MonacoEditorPeekViewWidget.ActionOptions): MonacoEditorPeekViewWidget.Action { + options = cssClass ? { icon: true, label: false, ...options } : { icon: false, label: true, ...options }; + const { actionBar } = this.delegate; + if (!actionBar) { + throw new Error('Action bar has not been created.'); + } + const action = new Action(id, label, cssClass, enabled, actionCallback); + actionBar.push(action, options); + return action; + } + + protected fillHead(container: HTMLElement, noCloseAction?: boolean): void { + this.delegate.fillHead(container, noCloseAction); + } + + protected fillBody(container: HTMLElement): void { + this.delegate.fillBody(container); + } + + protected doLayoutHead(heightInPixel: number, widthInPixel: number): void { + this.delegate.doLayoutHead(heightInPixel, widthInPixel); + } + + protected doLayoutBody(heightInPixel: number, widthInPixel: number): void { + this.delegate.doLayoutBody(heightInPixel, widthInPixel); + } + + protected onWidth(widthInPixel: number): void { + this.delegate.onWidth(widthInPixel); + } + + protected doRevealRange(range: Range, isLastLine: boolean): void { + this.delegate.doRevealRange(this.editor['p2m'].asRange(range), isLastLine); + } + + private convertStyles(styles: MonacoEditorPeekViewWidget.Styles): IPeekViewStyles { + return { + frameColor: this.convertColor(styles.frameColor), + arrowColor: this.convertColor(styles.arrowColor), + headerBackgroundColor: this.convertColor(styles.headerBackgroundColor), + primaryHeadingColor: this.convertColor(styles.primaryHeadingColor), + secondaryHeadingColor: this.convertColor(styles.secondaryHeadingColor), + }; + } + + private convertColor(color?: string): Color | undefined { + if (color === undefined) { + return undefined; + } + return this.themeService.getColorTheme().getColor(color) || Color.fromHex(color); + } + + private convertRangeOrPosition(arg: Range | Position): monaco.Range | monaco.Position { + const p2m = this.editor['p2m']; + return Range.is(arg) ? p2m.asRange(arg) : p2m.asPosition(arg); + } +} diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 3391dcdb3c6ea..75fa4dbcb049a 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -415,4 +415,41 @@ export class MonacoEditorProvider { } }; + async createEmbeddedDiffEditor(parentEditor: MonacoEditor, node: HTMLElement, originalUri: URI, modifiedUri: URI = parentEditor.uri, + options?: MonacoDiffEditor.IOptions): Promise { + options = { + scrollBeyondLastLine: true, + overviewRulerLanes: 2, + fixedOverflowWidgets: true, + minimap: { enabled: false }, + renderSideBySide: false, + readOnly: true, + renderIndicators: false, + diffAlgorithm: 'advanced', + stickyScroll: { enabled: false }, + ...options, + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + ...options?.scrollbar + } + }; + const uri = DiffUris.encode(originalUri, modifiedUri); + return await this.doCreateEditor(uri, async (override, toDispose) => + new MonacoDiffEditor( + uri, + node, + await this.getModel(originalUri, toDispose), + await this.getModel(modifiedUri, toDispose), + this.services, + this.diffNavigatorFactory, + options, + override, + parentEditor + ) + ) as MonacoDiffEditor; + } } diff --git a/packages/monaco/src/browser/monaco-editor.ts b/packages/monaco/src/browser/monaco-editor.ts index 350c6293e519f..5df77abe242ca 100644 --- a/packages/monaco/src/browser/monaco-editor.ts +++ b/packages/monaco/src/browser/monaco-editor.ts @@ -48,9 +48,20 @@ import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/stan import { ILanguageService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/language'; import { IInstantiationService, ServiceIdentifier } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; import { ICodeEditor, IMouseTargetMargin } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; -import { IStandaloneEditorConstructionOptions, StandaloneEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; +import { IStandaloneEditorConstructionOptions, StandaloneCodeEditor, StandaloneEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; import { ServiceCollection } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/serviceCollection'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; +import { ConfigurationChangedEvent, IEditorOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; +import { ICommandService } from '@theia/monaco-editor-core/esm/vs/platform/commands/common/commands'; +import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from '@theia/monaco-editor-core/esm/vs/platform/keybinding/common/keybinding'; +import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { INotificationService } from '@theia/monaco-editor-core/esm/vs/platform/notification/common/notification'; +import { IAccessibilityService } from '@theia/monaco-editor-core/esm/vs/platform/accessibility/common/accessibility'; +import { ILanguageConfigurationService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/languageConfigurationRegistry'; +import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures'; +import * as objects from '@theia/monaco-editor-core/esm/vs/base/common/objects'; export type ServicePair = [ServiceIdentifier, T]; @@ -103,7 +114,8 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { readonly node: HTMLElement, services: MonacoEditorServices, options?: MonacoEditor.IOptions, - override?: EditorServiceOverrides + override?: EditorServiceOverrides, + readonly parentEditor?: MonacoEditor ) { super(services); this.toDispose.pushAll([ @@ -153,7 +165,9 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { * @monaco-uplift. Should be guaranteed to work. * Incomparable enums prevent TypeScript from believing that public IStandaloneCodeEditor is satisfied by private StandaloneCodeEditor */ - return this.editor = instantiator.createInstance(StandaloneEditor, this.node, combinedOptions) as unknown as monaco.editor.IStandaloneCodeEditor; + return this.editor = (this.parentEditor ? + instantiator.createInstance(EmbeddedCodeEditor, this.node, combinedOptions, this.parentEditor.getControl() as unknown as ICodeEditor) : + instantiator.createInstance(StandaloneEditor, this.node, combinedOptions)) as unknown as monaco.editor.IStandaloneCodeEditor; } protected getInstantiatorWithOverrides(override?: EditorServiceOverrides): IInstantiationService { @@ -589,6 +603,9 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { return this.uri.withPath(resourceUri.path); } + shouldDisplayDirtyDiff(): boolean { + return true; + } } export namespace MonacoEditor { @@ -661,3 +678,52 @@ export namespace MonacoEditor { return {}; } } + +// adapted from https://github.com/microsoft/vscode/blob/0bd70d48ad8b3e2fb1922aa54f87c786ff2b4bd8/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts +// This class reproduces the logic in EmbeddedCodeEditorWidget but extends StandaloneCodeEditor rather than CodeEditorWidget. +class EmbeddedCodeEditor extends StandaloneCodeEditor { + + private readonly _parentEditor: ICodeEditor; + private readonly _overwriteOptions: IEditorOptions; + + constructor( + domElement: HTMLElement, + options: Readonly, + parentEditor: ICodeEditor, + @IInstantiationService instantiationService: IInstantiationService, + @ICodeEditorService codeEditorService: ICodeEditorService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService, + @IThemeService themeService: IThemeService, + @INotificationService notificationService: INotificationService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService, + @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + ) { + super(domElement, { ...parentEditor.getRawOptions(), overflowWidgetsDomNode: parentEditor.getOverflowWidgetsDomNode() }, instantiationService, codeEditorService, + commandService, contextKeyService, keybindingService, themeService, notificationService, accessibilityService, languageConfigurationService, languageFeaturesService); + + this._parentEditor = parentEditor; + this._overwriteOptions = options; + + // Overwrite parent's options + super.updateOptions(this._overwriteOptions); + + this._register(parentEditor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => this._onParentConfigurationChanged(e))); + } + + getParentEditor(): ICodeEditor { + return this._parentEditor; + } + + private _onParentConfigurationChanged(e: ConfigurationChangedEvent): void { + super.updateOptions(this._parentEditor.getRawOptions()); + super.updateOptions(this._overwriteOptions); + } + + override updateOptions(newOptions: IEditorOptions): void { + objects.mixin(this._overwriteOptions, newOptions, true); + super.updateOptions(this._overwriteOptions); + } +} diff --git a/packages/monaco/src/browser/style/index.css b/packages/monaco/src/browser/style/index.css index 577f853ef3d4e..7977874af744d 100644 --- a/packages/monaco/src/browser/style/index.css +++ b/packages/monaco/src/browser/style/index.css @@ -21,7 +21,6 @@ .monaco-editor .zone-widget { position: absolute; z-index: 10; - background-color: var(--theia-editorWidget-background); } .monaco-editor .zone-widget .zone-widget-container { diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index 0f7c41b34a4a9..cc8be6f6fe2b2 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -21,6 +21,9 @@ import { URI as CodeUri } from '@theia/core/shared/vscode-uri'; import { TreeWidgetSelection } from '@theia/core/lib/browser/tree/tree-widget-selection'; import { ScmRepository } from '@theia/scm/lib/browser/scm-repository'; import { ScmService } from '@theia/scm/lib/browser/scm-service'; +import { DirtyDiffWidget } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-widget'; +import { Change, LineRange } from '@theia/scm/lib/browser/dirty-diff/diff-computer'; +import { IChange } from '@theia/monaco-editor-core/esm/vs/editor/common/diff/legacyLinesDiffComputer'; import { TimelineItem } from '@theia/timeline/lib/common/timeline-model'; import { ScmCommandArg, TimelineCommandArg, TreeViewItemReference } from '../../../common'; import { TestItemReference, TestMessageArg } from '../../../common/test-types'; @@ -105,6 +108,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { ['scm/resourceState/context', toScmArgs], ['scm/title', () => [this.toScmArg(this.scmService.selectedRepository)]], ['testing/message/context', toTestMessageArgs], + ['scm/change/title', (...args) => this.toScmChangeArgs(...args)], ['timeline/item/context', (...args) => this.toTimelineArgs(...args)], ['view/item/context', (...args) => this.toTreeArgs(...args)], ['view/title', noArgs], @@ -229,6 +233,41 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { } } + protected toScmChangeArgs(...args: any[]): any[] { + const arg = args[0]; + if (arg instanceof DirtyDiffWidget) { + const toIChange = (change: Change): IChange => { + const convert = (range: LineRange): [number, number] => { + let startLineNumber; + let endLineNumber; + if (!LineRange.isEmpty(range)) { + startLineNumber = range.start + 1; + endLineNumber = range.end; + } else { + startLineNumber = range.start; + endLineNumber = 0; + } + return [startLineNumber, endLineNumber]; + }; + const { previousRange, currentRange } = change; + const [originalStartLineNumber, originalEndLineNumber] = convert(previousRange); + const [modifiedStartLineNumber, modifiedEndLineNumber] = convert(currentRange); + return { + originalStartLineNumber, + originalEndLineNumber, + modifiedStartLineNumber, + modifiedEndLineNumber + }; + }; + return [ + arg.uri['codeUri'], + arg.changes.map(toIChange), + arg.currentChangeIndex + ]; + } + return []; + } + protected toTimelineArgs(...args: any[]): any[] { const timelineArgs: any[] = []; const arg = args[0]; diff --git a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts index 0fc7f7b5925f3..6bbc91f01bfa0 100644 --- a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts +++ b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts @@ -26,6 +26,7 @@ import { DebugVariablesWidget } from '@theia/debug/lib/browser/view/debug-variab import { EditorWidget, EDITOR_CONTEXT_MENU } from '@theia/editor/lib/browser'; import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution'; import { ScmTreeWidget } from '@theia/scm/lib/browser/scm-tree-widget'; +import { PLUGIN_SCM_CHANGE_TITLE_MENU } from '@theia/scm/lib/browser/dirty-diff/dirty-diff-widget'; import { TIMELINE_ITEM_CONTEXT_MENU } from '@theia/timeline/lib/browser/timeline-tree-widget'; import { COMMENT_CONTEXT, COMMENT_THREAD_CONTEXT, COMMENT_TITLE } from '../comments/comment-thread-widget'; import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget'; @@ -53,6 +54,7 @@ export const implementedVSCodeContributionPoints = [ 'editor/title/run', 'editor/lineNumber/context', 'explorer/context', + 'scm/change/title', 'scm/resourceFolder/context', 'scm/resourceGroup/context', 'scm/resourceState/context', @@ -84,6 +86,7 @@ export const codeToTheiaMappings = new Map([ ['editor/title/run', [PLUGIN_EDITOR_TITLE_RUN_MENU]], ['editor/lineNumber/context', [EDITOR_LINENUMBER_CONTEXT_MENU]], ['explorer/context', [NAVIGATOR_CONTEXT_MENU]], + ['scm/change/title', [PLUGIN_SCM_CHANGE_TITLE_MENU]], ['scm/resourceFolder/context', [ScmTreeWidget.RESOURCE_FOLDER_CONTEXT_MENU]], ['scm/resourceGroup/context', [ScmTreeWidget.RESOURCE_GROUP_CONTEXT_MENU]], ['scm/resourceState/context', [ScmTreeWidget.RESOURCE_CONTEXT_MENU]], diff --git a/packages/scm/package.json b/packages/scm/package.json index 0971567f6e2b4..cad4914e6fe58 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -6,6 +6,8 @@ "@theia/core": "1.48.0", "@theia/editor": "1.48.0", "@theia/filesystem": "1.48.0", + "@theia/monaco": "1.48.0", + "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^3.2.2", "diff": "^3.4.0", "p-debounce": "^2.1.0", diff --git a/packages/scm/src/browser/decorations/scm-decorations-service.ts b/packages/scm/src/browser/decorations/scm-decorations-service.ts index 53dd72eb16341..c597dd91385dc 100644 --- a/packages/scm/src/browser/decorations/scm-decorations-service.ts +++ b/packages/scm/src/browser/decorations/scm-decorations-service.ts @@ -15,64 +15,88 @@ // ***************************************************************************** import { injectable, inject } from '@theia/core/shared/inversify'; -import { ResourceProvider } from '@theia/core'; -import { DirtyDiffDecorator } from '../dirty-diff/dirty-diff-decorator'; +import { DisposableCollection, Emitter, Event, ResourceProvider } from '@theia/core'; +import { DirtyDiffDecorator, DirtyDiffUpdate } from '../dirty-diff/dirty-diff-decorator'; import { DiffComputer } from '../dirty-diff/diff-computer'; import { ContentLines } from '../dirty-diff/content-lines'; -import { EditorManager, TextEditor } from '@theia/editor/lib/browser'; +import { EditorManager, EditorWidget, TextEditor } from '@theia/editor/lib/browser'; import { ScmService } from '../scm-service'; +import throttle = require('@theia/core/shared/lodash.throttle'); + @injectable() export class ScmDecorationsService { - private readonly diffComputer: DiffComputer; - private dirtyState: boolean = true; + private readonly diffComputer = new DiffComputer(); + + protected readonly onDirtyDiffUpdateEmitter = new Emitter(); + readonly onDirtyDiffUpdate: Event = this.onDirtyDiffUpdateEmitter.event; - constructor(@inject(DirtyDiffDecorator) protected readonly decorator: DirtyDiffDecorator, + constructor( + @inject(DirtyDiffDecorator) protected readonly decorator: DirtyDiffDecorator, @inject(ScmService) protected readonly scmService: ScmService, @inject(EditorManager) protected readonly editorManager: EditorManager, - @inject(ResourceProvider) protected readonly resourceProvider: ResourceProvider) { - this.diffComputer = new DiffComputer(); - this.editorManager.onCreated(async editor => this.applyEditorDecorations(editor.editor)); - this.scmService.onDidAddRepository(repository => repository.provider.onDidChange(() => { - const editor = this.editorManager.currentEditor; - if (editor) { - if (this.dirtyState) { - this.applyEditorDecorations(editor.editor); - this.dirtyState = false; - } else { - /** onDidChange event might be called several times one after another, so need to prevent repeated events. */ - setTimeout(() => { - this.dirtyState = true; - }, 500); - } + @inject(ResourceProvider) protected readonly resourceProvider: ResourceProvider + ) { + const updateTasks = new Map(); + this.editorManager.onCreated(editorWidget => { + const { editor } = editorWidget; + if (!this.supportsDirtyDiff(editor)) { + return; } - })); - this.scmService.onDidChangeSelectedRepository(() => { - const editor = this.editorManager.currentEditor; - if (editor) { - this.applyEditorDecorations(editor.editor); + const toDispose = new DisposableCollection(); + const updateTask = this.createUpdateTask(editor); + updateTasks.set(editorWidget, updateTask); + toDispose.push(editor.onDocumentContentChanged(() => updateTask())); + editorWidget.disposed.connect(() => { + updateTask.cancel(); + updateTasks.delete(editorWidget); + toDispose.dispose(); + }); + updateTask(); + }); + const runUpdateTasks = () => { + for (const updateTask of updateTasks.values()) { + updateTask(); } + }; + this.scmService.onDidAddRepository(({ provider }) => { + provider.onDidChange(runUpdateTasks); + provider.onDidChangeResources?.(runUpdateTasks); }); + this.scmService.onDidChangeSelectedRepository(runUpdateTasks); } async applyEditorDecorations(editor: TextEditor): Promise { const currentRepo = this.scmService.selectedRepository; if (currentRepo) { try { - const uri = editor.uri.withScheme(currentRepo.provider.id).withQuery(`{"ref":"", "path":"${editor.uri.path.toString()}"}`); + // Currently, the uri used here is specific to vscode.git; other SCM providers are thus not supported. + // See https://github.com/eclipse-theia/theia/pull/13104#discussion_r1494540628 for a detailed discussion. + const query = { path: editor.uri['codeUri'].fsPath, ref: '~' }; + const uri = editor.uri.withScheme(currentRepo.provider.id).withQuery(JSON.stringify(query)); const previousResource = await this.resourceProvider(uri); - const previousContent = await previousResource.readContents(); - const previousLines = ContentLines.fromString(previousContent); - const currentResource = await this.resourceProvider(editor.uri); - const currentContent = await currentResource.readContents(); - const currentLines = ContentLines.fromString(currentContent); - const { added, removed, modified } = this.diffComputer.computeDirtyDiff(ContentLines.arrayLike(previousLines), ContentLines.arrayLike(currentLines)); - this.decorator.applyDecorations({ editor: editor, added, removed, modified }); - currentResource.dispose(); - previousResource.dispose(); + try { + const previousContent = await previousResource.readContents(); + const previousLines = ContentLines.fromString(previousContent); + const currentLines = ContentLines.fromTextEditorDocument(editor.document); + const dirtyDiff = this.diffComputer.computeDirtyDiff(ContentLines.arrayLike(previousLines), ContentLines.arrayLike(currentLines)); + const update = { editor, previousRevisionUri: uri, ...dirtyDiff }; + this.decorator.applyDecorations(update); + this.onDirtyDiffUpdateEmitter.fire(update); + } finally { + previousResource.dispose(); + } } catch (e) { // Scm resource may not be found, do nothing. } } } + + protected supportsDirtyDiff(editor: TextEditor): boolean { + return editor.shouldDisplayDirtyDiff(); + } + + protected createUpdateTask(editor: TextEditor): { (): void; cancel(): void; } { + return throttle(() => this.applyEditorDecorations(editor), 500); + } } diff --git a/packages/scm/src/browser/dirty-diff/content-lines.ts b/packages/scm/src/browser/dirty-diff/content-lines.ts index d3c0a2207ec4d..2e0b40cfbb187 100644 --- a/packages/scm/src/browser/dirty-diff/content-lines.ts +++ b/packages/scm/src/browser/dirty-diff/content-lines.ts @@ -14,6 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +import { TextEditorDocument } from '@theia/editor/lib/browser'; + export interface ContentLines extends ArrayLike { readonly length: number, getLineContent: (line: number) => string, @@ -65,6 +67,13 @@ export namespace ContentLines { }; } + export function fromTextEditorDocument(document: TextEditorDocument): ContentLines { + return { + length: document.lineCount, + getLineContent: line => document.getLineContent(line + 1), + }; + } + export function arrayLike(lines: ContentLines): ContentLinesArrayLike { return new Proxy(lines as ContentLines, getProxyHandler()) as ContentLinesArrayLike; } diff --git a/packages/scm/src/browser/dirty-diff/diff-computer.spec.ts b/packages/scm/src/browser/dirty-diff/diff-computer.spec.ts index 34fa24e51a27c..62ebf18cd51f5 100644 --- a/packages/scm/src/browser/dirty-diff/diff-computer.spec.ts +++ b/packages/scm/src/browser/dirty-diff/diff-computer.spec.ts @@ -42,9 +42,12 @@ describe('dirty-diff-computer', () => { ], ); expect(dirtyDiff).to.be.deep.equal({ - added: [], - modified: [], - removed: [0], + changes: [ + { + previousRange: { start: 1, end: 2 }, + currentRange: { start: 1, end: 1 }, + }, + ], }); }); @@ -56,22 +59,29 @@ describe('dirty-diff-computer', () => { sequenceOfN(2), ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [1], - added: [], + changes: [ + { + previousRange: { start: 2, end: 2 + lines }, + currentRange: { start: 2, end: 2 }, + }, + ], }); }); }); it('remove all lines', () => { + const numberOfLines = 10; const dirtyDiff = computeDirtyDiff( - sequenceOfN(10, () => 'TO-BE-REMOVED'), + sequenceOfN(numberOfLines, () => 'TO-BE-REMOVED'), [''] ); expect(dirtyDiff).to.be.deep.equal({ - added: [], - modified: [], - removed: [0], + changes: [ + { + previousRange: { start: 0, end: numberOfLines }, + currentRange: { start: 0, end: 0 }, + }, + ], }); }); @@ -83,9 +93,12 @@ describe('dirty-diff-computer', () => { sequenceOfN(2), ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [0], - added: [], + changes: [ + { + previousRange: { start: 0, end: lines }, + currentRange: { start: 0, end: 0 }, + }, + ], }); }); }); @@ -96,9 +109,12 @@ describe('dirty-diff-computer', () => { const modified = insertIntoArray(previous, 2, ...sequenceOfN(lines, () => 'ADDED LINE')); const dirtyDiff = computeDirtyDiff(previous, modified); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 2, end: 2 + lines - 1 }], + changes: [ + { + previousRange: { start: 2, end: 2 }, + currentRange: { start: 2, end: 2 + lines }, + }, + ], }); }); }); @@ -111,9 +127,12 @@ describe('dirty-diff-computer', () => { .concat(sequenceOfN(2)) ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 0, end: lines - 1 }], + changes: [ + { + previousRange: { start: 0, end: 0 }, + currentRange: { start: 0, end: lines }, + }, + ], }); }); }); @@ -125,9 +144,12 @@ describe('dirty-diff-computer', () => { sequenceOfN(numberOfLines, () => 'ADDED LINE') ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 0, end: numberOfLines - 1 }], + changes: [ + { + previousRange: { start: 0, end: 0 }, + currentRange: { start: 0, end: numberOfLines }, + }, + ], }); }); @@ -145,9 +167,12 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 1, end: 2 }], + changes: [ + { + previousRange: { start: 1, end: 1 }, + currentRange: { start: 1, end: 3 }, + }, + ], }); }); @@ -162,9 +187,12 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 1, end: 1 }], + changes: [ + { + previousRange: { start: 1, end: 1 }, + currentRange: { start: 1, end: 2 }, + }, + ], }); }); @@ -176,9 +204,12 @@ describe('dirty-diff-computer', () => { .concat(new Array(lines).map(() => '')) ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 2, end: 1 + lines }], + changes: [ + { + previousRange: { start: 2, end: 2 }, + currentRange: { start: 2, end: 2 + lines }, + }, + ], }); }); }); @@ -200,9 +231,12 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 1, end: 5 }], + changes: [ + { + previousRange: { start: 1, end: 1 }, + currentRange: { start: 1, end: 6 }, + }, + ], }); }); @@ -213,9 +247,12 @@ describe('dirty-diff-computer', () => { ['0'].concat(sequenceOfN(lines, () => 'ADDED LINE')) ); expect(dirtyDiff).to.be.deep.equal({ - modified: [], - removed: [], - added: [{ start: 1, end: lines }], + changes: [ + { + previousRange: { start: 1, end: 1 }, + currentRange: { start: 1, end: lines + 1 }, + }, + ], }); }); }); @@ -234,9 +271,12 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - removed: [], - added: [], - modified: [{ start: 1, end: 1 }], + changes: [ + { + previousRange: { start: 1, end: 2 }, + currentRange: { start: 1, end: 2 }, + }, + ], }); }); @@ -247,9 +287,12 @@ describe('dirty-diff-computer', () => { sequenceOfN(numberOfLines, () => 'MODIFIED') ); expect(dirtyDiff).to.be.deep.equal({ - removed: [], - added: [], - modified: [{ start: 0, end: numberOfLines - 1 }], + changes: [ + { + previousRange: { start: 0, end: numberOfLines }, + currentRange: { start: 0, end: numberOfLines }, + }, + ], }); }); @@ -268,9 +311,12 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - removed: [], - added: [], - modified: [{ start: 1, end: 2 }], + changes: [ + { + previousRange: { start: 1, end: 4 }, + currentRange: { start: 1, end: 3 }, + }, + ], }); }); @@ -305,9 +351,20 @@ describe('dirty-diff-computer', () => { ] ); expect(dirtyDiff).to.be.deep.equal({ - removed: [3], - added: [{ start: 10, end: 11 }], - modified: [{ start: 0, end: 0 }], + changes: [ + { + previousRange: { start: 0, end: 1 }, + currentRange: { start: 0, end: 1 }, + }, + { + previousRange: { start: 4, end: 5 }, + currentRange: { start: 4, end: 4 }, + }, + { + previousRange: { start: 11, end: 11 }, + currentRange: { start: 10, end: 12 }, + }, + ], }); }); @@ -340,9 +397,20 @@ describe('dirty-diff-computer', () => { '' ]); expect(dirtyDiff).to.be.deep.equal({ - removed: [11], - added: [{ start: 5, end: 5 }, { start: 9, end: 9 }], - modified: [], + changes: [ + { + previousRange: { start: 5, end: 5 }, + currentRange: { start: 5, end: 6 }, + }, + { + previousRange: { start: 8, end: 8 }, + currentRange: { start: 9, end: 10 }, + }, + { + previousRange: { start: 9, end: 10 }, + currentRange: { start: 12, end: 12 }, + }, + ], }); }); diff --git a/packages/scm/src/browser/dirty-diff/diff-computer.ts b/packages/scm/src/browser/dirty-diff/diff-computer.ts index 5662cb993a54e..38b95e591798b 100644 --- a/packages/scm/src/browser/dirty-diff/diff-computer.ts +++ b/packages/scm/src/browser/dirty-diff/diff-computer.ts @@ -16,6 +16,7 @@ import * as jsdiff from 'diff'; import { ContentLinesArrayLike } from './content-lines'; +import { Position, Range, uinteger } from '@theia/core/shared/vscode-languageserver-protocol'; export class DiffComputer { @@ -25,52 +26,52 @@ export class DiffComputer { } computeDirtyDiff(previous: ContentLinesArrayLike, current: ContentLinesArrayLike): DirtyDiff { - const added: LineRange[] = []; - const removed: number[] = []; - const modified: LineRange[] = []; - const changes = this.computeDiff(previous, current); - let lastLine = -1; - for (let i = 0; i < changes.length; i++) { - const change = changes[i]; - const next = changes[i + 1]; + const changes: Change[] = []; + const diffResult = this.computeDiff(previous, current); + let currentRevisionLine = -1; + let previousRevisionLine = -1; + for (let i = 0; i < diffResult.length; i++) { + const change = diffResult[i]; + const next = diffResult[i + 1]; if (change.added) { // case: addition - const start = lastLine + 1; - const end = lastLine + change.count!; - added.push({ start, end }); - lastLine = end; + changes.push({ previousRange: LineRange.createEmptyLineRange(previousRevisionLine + 1), currentRange: toLineRange(change) }); + currentRevisionLine += change.count!; } else if (change.removed && next && next.added) { const isFirstChange = i === 0; - const isLastChange = i === changes.length - 2; + const isLastChange = i === diffResult.length - 2; const isNextEmptyLine = next.value.length > 0 && current[next.value[0]].length === 0; const isPrevEmptyLine = change.value.length > 0 && previous[change.value[0]].length === 0; if (isFirstChange && isNextEmptyLine) { // special case: removing at the beginning - removed.push(0); + changes.push({ previousRange: toLineRange(change), currentRange: LineRange.createEmptyLineRange(0) }); + previousRevisionLine += change.count!; } else if (isFirstChange && isPrevEmptyLine) { // special case: adding at the beginning - const start = 0; - const end = next.count! - 1; - added.push({ start, end }); - lastLine = end; + changes.push({ previousRange: LineRange.createEmptyLineRange(0), currentRange: toLineRange(next) }); + currentRevisionLine += next.count!; } else if (isLastChange && isNextEmptyLine) { - removed.push(lastLine + 1 /* = empty line */); + changes.push({ previousRange: toLineRange(change), currentRange: LineRange.createEmptyLineRange(currentRevisionLine + 2) }); + previousRevisionLine += change.count!; } else { // default case is a modification - const start = lastLine + 1; - const end = lastLine + next.count!; - modified.push({ start, end }); - lastLine = end; + changes.push({ previousRange: toLineRange(change), currentRange: toLineRange(next) }); + currentRevisionLine += next.count!; + previousRevisionLine += change.count!; } i++; // consume next eagerly } else if (change.removed && !(next && next.added)) { - removed.push(Math.max(0, lastLine)); + // case: removal + changes.push({ previousRange: toLineRange(change), currentRange: LineRange.createEmptyLineRange(currentRevisionLine + 1) }); + previousRevisionLine += change.count!; } else { - lastLine += change.count!; + // case: unchanged region + currentRevisionLine += change.count!; + previousRevisionLine += change.count!; } } - return { added, removed, modified }; + return { changes }; } } @@ -101,6 +102,11 @@ function diffArrays(oldArr: ContentLinesArrayLike, newArr: ContentLinesArrayLike return arrayDiff.diff(oldArr as any, newArr as any) as any; } +function toLineRange({ value }: DiffResult): LineRange { + const [start, end] = value; + return LineRange.create(start, end + 1); +} + export interface DiffResult { value: [number, number]; count?: number; @@ -109,21 +115,63 @@ export interface DiffResult { } export interface DirtyDiff { - /** - * Lines added by comparison to previous revision. - */ - readonly added: LineRange[]; - /** - * Lines, after which lines were removed by comparison to previous revision. - */ - readonly removed: number[]; - /** - * Lines modified by comparison to previous revision. - */ - readonly modified: LineRange[]; + readonly changes: readonly Change[]; +} + +export interface Change { + readonly previousRange: LineRange; + readonly currentRange: LineRange; +} + +export namespace Change { + export function isAddition(change: Change): boolean { + return LineRange.isEmpty(change.previousRange); + } + export function isRemoval(change: Change): boolean { + return LineRange.isEmpty(change.currentRange); + } + export function isModification(change: Change): boolean { + return !isAddition(change) && !isRemoval(change); + } } export interface LineRange { - start: number; - end: number; + readonly start: number; + readonly end: number; +} + +export namespace LineRange { + export function create(start: number, end: number): LineRange { + if (start < 0 || end < 0 || start > end) { + throw new Error(`Invalid line range: { start: ${start}, end: ${end} }`); + } + return { start, end }; + } + export function createSingleLineRange(line: number): LineRange { + return create(line, line + 1); + } + export function createEmptyLineRange(line: number): LineRange { + return create(line, line); + } + export function isEmpty(range: LineRange): boolean { + return range.start === range.end; + } + export function getStartPosition(range: LineRange): Position { + if (isEmpty(range)) { + return getEndPosition(range); + } + return Position.create(range.start, 0); + } + export function getEndPosition(range: LineRange): Position { + if (range.end < 1) { + return Position.create(0, 0); + } + return Position.create(range.end - 1, uinteger.MAX_VALUE); + } + export function toRange(range: LineRange): Range { + return Range.create(getStartPosition(range), getEndPosition(range)); + } + export function getLineCount(range: LineRange): number { + return range.end - range.start; + } } diff --git a/packages/scm/src/browser/dirty-diff/dirty-diff-decorator.ts b/packages/scm/src/browser/dirty-diff/dirty-diff-decorator.ts index a6ff31676d959..cf17bee272f27 100644 --- a/packages/scm/src/browser/dirty-diff/dirty-diff-decorator.ts +++ b/packages/scm/src/browser/dirty-diff/dirty-diff-decorator.ts @@ -16,8 +16,6 @@ import { injectable } from '@theia/core/shared/inversify'; import { - Range, - Position, EditorDecoration, EditorDecorationOptions, OverviewRulerLane, @@ -25,7 +23,8 @@ import { TextEditor, MinimapPosition } from '@theia/editor/lib/browser'; -import { DirtyDiff, LineRange } from './diff-computer'; +import { DirtyDiff, LineRange, Change } from './diff-computer'; +import { URI } from '@theia/core'; export enum DirtyDiffDecorationType { AddedLine = 'dirty-diff-added-line', @@ -84,24 +83,32 @@ const ModifiedLineDecoration = { isWholeLine: true }; +function getEditorDecorationOptions(change: Change): EditorDecorationOptions { + if (Change.isAddition(change)) { + return AddedLineDecoration; + } + if (Change.isRemoval(change)) { + return RemovedLineDecoration; + } + return ModifiedLineDecoration; +} + export interface DirtyDiffUpdate extends DirtyDiff { readonly editor: TextEditor; + readonly previousRevisionUri?: URI; } @injectable() export class DirtyDiffDecorator extends EditorDecorator { applyDecorations(update: DirtyDiffUpdate): void { - const modifications = update.modified.map(range => this.toDeltaDecoration(range, ModifiedLineDecoration)); - const additions = update.added.map(range => this.toDeltaDecoration(range, AddedLineDecoration)); - const removals = update.removed.map(line => this.toDeltaDecoration(line, RemovedLineDecoration)); - const decorations = [...modifications, ...additions, ...removals]; + const decorations = update.changes.map(change => this.toDeltaDecoration(change)); this.setDecorations(update.editor, decorations); } - protected toDeltaDecoration(from: LineRange | number, options: EditorDecorationOptions): EditorDecoration { - const [start, end] = (typeof from === 'number') ? [from, from] : [from.start, from.end]; - const range = Range.create(Position.create(start, 0), Position.create(end, 0)); + protected toDeltaDecoration(change: Change): EditorDecoration { + const range = LineRange.toRange(change.currentRange); + const options = getEditorDecorationOptions(change); return { range, options }; } } diff --git a/packages/scm/src/browser/dirty-diff/dirty-diff-module.ts b/packages/scm/src/browser/dirty-diff/dirty-diff-module.ts index 1982324afa773..3b2117f0f58f2 100644 --- a/packages/scm/src/browser/dirty-diff/dirty-diff-module.ts +++ b/packages/scm/src/browser/dirty-diff/dirty-diff-module.ts @@ -16,9 +16,18 @@ import { interfaces } from '@theia/core/shared/inversify'; import { DirtyDiffDecorator } from './dirty-diff-decorator'; +import { DirtyDiffNavigator } from './dirty-diff-navigator'; +import { DirtyDiffWidget, DirtyDiffWidgetFactory, DirtyDiffWidgetProps } from './dirty-diff-widget'; import '../../../src/browser/style/dirty-diff.css'; export function bindDirtyDiff(bind: interfaces.Bind): void { bind(DirtyDiffDecorator).toSelf().inSingletonScope(); + bind(DirtyDiffNavigator).toSelf().inSingletonScope(); + bind(DirtyDiffWidgetFactory).toFactory(({ container }) => props => { + const child = container.createChild(); + child.bind(DirtyDiffWidgetProps).toConstantValue(props); + child.bind(DirtyDiffWidget).toSelf(); + return child.get(DirtyDiffWidget); + }); } diff --git a/packages/scm/src/browser/dirty-diff/dirty-diff-navigator.ts b/packages/scm/src/browser/dirty-diff/dirty-diff-navigator.ts new file mode 100644 index 0000000000000..d765797337617 --- /dev/null +++ b/packages/scm/src/browser/dirty-diff/dirty-diff-navigator.ts @@ -0,0 +1,288 @@ +// ***************************************************************************** +// Copyright (C) 2023 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { Disposable, DisposableCollection, URI } from '@theia/core'; +import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { EditorManager, EditorMouseEvent, MouseTargetType, TextEditor } from '@theia/editor/lib/browser'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { Change, LineRange } from './diff-computer'; +import { DirtyDiffUpdate } from './dirty-diff-decorator'; +import { DirtyDiffWidget, DirtyDiffWidgetFactory } from './dirty-diff-widget'; + +@injectable() +export class DirtyDiffNavigator { + + protected readonly controllers = new Map(); + + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + + @inject(EditorManager) + protected readonly editorManager: EditorManager; + + @inject(DirtyDiffWidgetFactory) + protected readonly widgetFactory: DirtyDiffWidgetFactory; + + @postConstruct() + protected init(): void { + const dirtyDiffVisible: ContextKey = this.contextKeyService.createKey('dirtyDiffVisible', false); + this.editorManager.onActiveEditorChanged(editorWidget => { + dirtyDiffVisible.set(editorWidget && this.controllers.get(editorWidget.editor)?.isShowingChange()); + }); + this.editorManager.onCreated(editorWidget => { + const { editor } = editorWidget; + if (editor.uri.scheme !== 'file') { + return; + } + const controller = this.createController(editor); + controller.widgetFactory = props => { + const widget = this.widgetFactory(props); + if (widget.editor === this.editorManager.activeEditor?.editor) { + dirtyDiffVisible.set(true); + } + widget.onDidClose(() => { + if (widget.editor === this.editorManager.activeEditor?.editor) { + dirtyDiffVisible.set(false); + } + }); + return widget; + }; + this.controllers.set(editor, controller); + editorWidget.disposed.connect(() => { + this.controllers.delete(editor); + controller.dispose(); + }); + }); + } + + handleDirtyDiffUpdate(update: DirtyDiffUpdate): void { + const controller = this.controllers.get(update.editor); + controller?.handleDirtyDiffUpdate(update); + } + + canNavigate(): boolean { + return !!this.activeController?.canNavigate(); + } + + gotoNextChange(): void { + this.activeController?.gotoNextChange(); + } + + gotoPreviousChange(): void { + this.activeController?.gotoPreviousChange(); + } + + canShowChange(): boolean { + return !!this.activeController?.canShowChange(); + } + + showNextChange(): void { + this.activeController?.showNextChange(); + } + + showPreviousChange(): void { + this.activeController?.showPreviousChange(); + } + + isShowingChange(): boolean { + return !!this.activeController?.isShowingChange(); + } + + closeChangePeekView(): void { + this.activeController?.closeWidget(); + } + + protected get activeController(): DirtyDiffController | undefined { + const editor = this.editorManager.activeEditor?.editor; + return editor && this.controllers.get(editor); + } + + protected createController(editor: TextEditor): DirtyDiffController { + return new DirtyDiffController(editor); + } +} + +export class DirtyDiffController implements Disposable { + + protected readonly toDispose = new DisposableCollection(); + + widgetFactory?: DirtyDiffWidgetFactory; + protected widget?: DirtyDiffWidget; + protected dirtyDiff?: DirtyDiffUpdate; + + constructor(protected readonly editor: TextEditor) { + editor.onMouseDown(this.handleEditorMouseDown, this, this.toDispose); + } + + dispose(): void { + this.closeWidget(); + this.toDispose.dispose(); + } + + handleDirtyDiffUpdate(dirtyDiff: DirtyDiffUpdate): void { + if (dirtyDiff.editor === this.editor) { + this.closeWidget(); + this.dirtyDiff = dirtyDiff; + } + } + + canNavigate(): boolean { + return !!this.changes?.length; + } + + gotoNextChange(): void { + const { editor } = this; + const index = this.findNextClosestChange(editor.cursor.line, false); + const change = this.changes?.[index]; + if (change) { + const position = LineRange.getStartPosition(change.currentRange); + editor.cursor = position; + editor.revealPosition(position, { vertical: 'auto' }); + } + } + + gotoPreviousChange(): void { + const { editor } = this; + const index = this.findPreviousClosestChange(editor.cursor.line, false); + const change = this.changes?.[index]; + if (change) { + const position = LineRange.getStartPosition(change.currentRange); + editor.cursor = position; + editor.revealPosition(position, { vertical: 'auto' }); + } + } + + canShowChange(): boolean { + return !!(this.widget || this.widgetFactory && this.editor instanceof MonacoEditor && this.changes?.length && this.previousRevisionUri); + } + + showNextChange(): void { + if (this.widget) { + this.widget.showNextChange(); + } else { + (this.widget = this.createWidget())?.showChange( + this.findNextClosestChange(this.editor.cursor.line, true)); + } + } + + showPreviousChange(): void { + if (this.widget) { + this.widget.showPreviousChange(); + } else { + (this.widget = this.createWidget())?.showChange( + this.findPreviousClosestChange(this.editor.cursor.line, true)); + } + } + + isShowingChange(): boolean { + return !!this.widget; + } + + closeWidget(): void { + if (this.widget) { + this.widget.dispose(); + this.widget = undefined; + } + } + + protected get changes(): readonly Change[] | undefined { + return this.dirtyDiff?.changes; + } + + protected get previousRevisionUri(): URI | undefined { + return this.dirtyDiff?.previousRevisionUri; + } + + protected createWidget(): DirtyDiffWidget | undefined { + const { widgetFactory, editor, changes, previousRevisionUri } = this; + if (widgetFactory && editor instanceof MonacoEditor && changes?.length && previousRevisionUri) { + const widget = widgetFactory({ editor, previousRevisionUri, changes }); + widget.onDidClose(() => { + this.widget = undefined; + }); + return widget; + } + } + + protected findNextClosestChange(line: number, inclusive: boolean): number { + const length = this.changes?.length; + if (!length) { + return -1; + } + for (let i = 0; i < length; i++) { + const { currentRange } = this.changes![i]; + + if (inclusive) { + if (LineRange.getEndPosition(currentRange).line >= line) { + return i; + } + } else { + if (LineRange.getStartPosition(currentRange).line > line) { + return i; + } + } + } + return 0; + } + + protected findPreviousClosestChange(line: number, inclusive: boolean): number { + const length = this.changes?.length; + if (!length) { + return -1; + } + for (let i = length - 1; i >= 0; i--) { + const { currentRange } = this.changes![i]; + + if (inclusive) { + if (LineRange.getStartPosition(currentRange).line <= line) { + return i; + } + } else { + if (LineRange.getEndPosition(currentRange).line < line) { + return i; + } + } + } + return length - 1; + } + + protected handleEditorMouseDown({ event, target }: EditorMouseEvent): void { + if (event.button !== 0) { + return; + } + const { range, type, element } = target; + if (!range || type !== MouseTargetType.GUTTER_LINE_DECORATIONS || !element || element.className.indexOf('dirty-diff-glyph') < 0) { + return; + } + const gutterOffsetX = target.detail.offsetX - (element as HTMLElement).offsetLeft; + if (gutterOffsetX < -3 || gutterOffsetX > 3) { // dirty diff decoration on hover is 6px wide + return; // to avoid colliding with folding + } + const index = this.findNextClosestChange(range.start.line, true); + if (index < 0) { + return; + } + if (index === this.widget?.currentChangeIndex) { + this.closeWidget(); + return; + } + if (!this.widget) { + this.widget = this.createWidget(); + } + this.widget?.showChange(index); + } +} diff --git a/packages/scm/src/browser/dirty-diff/dirty-diff-widget.ts b/packages/scm/src/browser/dirty-diff/dirty-diff-widget.ts new file mode 100644 index 0000000000000..4fe5d5b5e58e9 --- /dev/null +++ b/packages/scm/src/browser/dirty-diff/dirty-diff-widget.ts @@ -0,0 +1,364 @@ +// ***************************************************************************** +// Copyright (C) 2023 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { Position, Range } from '@theia/core/shared/vscode-languageserver-protocol'; +import { ActionMenuNode, Disposable, Emitter, Event, MenuCommandExecutor, MenuModelRegistry, MenuPath, URI, nls } from '@theia/core'; +import { codicon } from '@theia/core/lib/browser'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { MonacoDiffEditor } from '@theia/monaco/lib/browser/monaco-diff-editor'; +import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider'; +import { MonacoEditorPeekViewWidget, peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground } + from '@theia/monaco/lib/browser/monaco-editor-peek-view-widget'; +import { Change, LineRange } from './diff-computer'; +import { ScmColors } from '../scm-colors'; +import * as monaco from '@theia/monaco-editor-core'; + +export const SCM_CHANGE_TITLE_MENU: MenuPath = ['scm-change-title-menu']; +/** Reserved for plugin contributions, corresponds to contribution point 'scm/change/title'. */ +export const PLUGIN_SCM_CHANGE_TITLE_MENU: MenuPath = ['plugin-scm-change-title-menu']; + +export const DirtyDiffWidgetProps = Symbol('DirtyDiffWidgetProps'); +export interface DirtyDiffWidgetProps { + readonly editor: MonacoEditor; + readonly previousRevisionUri: URI; + readonly changes: readonly Change[]; +} + +export const DirtyDiffWidgetFactory = Symbol('DirtyDiffWidgetFactory'); +export type DirtyDiffWidgetFactory = (props: DirtyDiffWidgetProps) => DirtyDiffWidget; + +@injectable() +export class DirtyDiffWidget implements Disposable { + + private readonly onDidCloseEmitter = new Emitter(); + readonly onDidClose: Event = this.onDidCloseEmitter.event; + protected index: number = -1; + private peekView?: DirtyDiffPeekView; + private diffEditorPromise?: Promise; + + constructor( + @inject(DirtyDiffWidgetProps) protected readonly props: DirtyDiffWidgetProps, + @inject(MonacoEditorProvider) readonly editorProvider: MonacoEditorProvider, + @inject(ContextKeyService) readonly contextKeyService: ContextKeyService, + @inject(MenuModelRegistry) readonly menuModelRegistry: MenuModelRegistry, + @inject(MenuCommandExecutor) readonly menuCommandExecutor: MenuCommandExecutor + ) { } + + @postConstruct() + create(): void { + this.peekView = new DirtyDiffPeekView(this); + this.peekView.onDidClose(e => this.onDidCloseEmitter.fire(e)); + this.diffEditorPromise = this.peekView.create(); + } + + get editor(): MonacoEditor { + return this.props.editor; + } + + get uri(): URI { + return this.editor.uri; + } + + get previousRevisionUri(): URI { + return this.props.previousRevisionUri; + } + + get changes(): readonly Change[] { + return this.props.changes; + } + + get currentChange(): Change | undefined { + return this.changes[this.index]; + } + + get currentChangeIndex(): number { + return this.index; + } + + showChange(index: number): void { + this.checkCreated(); + if (index >= 0 && index < this.changes.length) { + this.index = index; + this.showCurrentChange(); + } + } + + showNextChange(): void { + this.checkCreated(); + const index = this.index; + const length = this.changes.length; + if (length > 0 && (index < 0 || length > 1)) { + this.index = index < 0 ? 0 : cycle(index, 1, length); + this.showCurrentChange(); + } + } + + showPreviousChange(): void { + this.checkCreated(); + const index = this.index; + const length = this.changes.length; + if (length > 0 && (index < 0 || length > 1)) { + this.index = index < 0 ? length - 1 : cycle(index, -1, length); + this.showCurrentChange(); + } + } + + async getContentWithSelectedChanges(predicate: (change: Change, index: number, changes: readonly Change[]) => boolean): Promise { + this.checkCreated(); + const changes = this.changes.filter(predicate); + const { diffEditor } = await this.diffEditorPromise!; + const diffEditorModel = diffEditor.getModel()!; + return applyChanges(changes, diffEditorModel.original, diffEditorModel.modified); + } + + dispose(): void { + this.peekView?.dispose(); + this.onDidCloseEmitter.dispose(); + } + + protected showCurrentChange(): void { + this.peekView!.setTitle(this.computePrimaryHeading(), this.computeSecondaryHeading()); + const { previousRange, currentRange } = this.changes[this.index]; + this.peekView!.show(Position.create(LineRange.getEndPosition(currentRange).line, 0), + this.computeHeightInLines()); + this.diffEditorPromise!.then(({ diffEditor }) => { + let startLine = LineRange.getStartPosition(currentRange).line; + let endLine = LineRange.getEndPosition(currentRange).line; + if (LineRange.isEmpty(currentRange)) { // the change is a removal + ++endLine; + } else if (!LineRange.isEmpty(previousRange)) { // the change is a modification + --startLine; + ++endLine; + } + diffEditor.revealLinesInCenter(startLine + 1, endLine + 1, // monaco line numbers are 1-based + monaco.editor.ScrollType.Immediate); + }); + this.editor.focus(); + } + + protected computePrimaryHeading(): string { + return this.uri.path.base; + } + + protected computeSecondaryHeading(): string { + const index = this.index + 1; + const length = this.changes.length; + return length > 1 ? nls.localizeByDefault('{0} of {1} changes', index, length) : + nls.localizeByDefault('{0} of {1} change', index, length); + } + + protected computeHeightInLines(): number { + const editor = this.editor.getControl(); + const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight); + const editorHeight = editor.getLayoutInfo().height; + const editorHeightInLines = Math.floor(editorHeight / lineHeight); + + const { previousRange, currentRange } = this.changes[this.index]; + const changeHeightInLines = LineRange.getLineCount(currentRange) + LineRange.getLineCount(previousRange); + + return Math.min(changeHeightInLines + /* padding */ 8, Math.floor(editorHeightInLines / 3)); + } + + protected checkCreated(): void { + if (!this.peekView) { + throw new Error('create() method needs to be called first.'); + } + } +} + +function cycle(index: number, offset: -1 | 1, length: number): number { + return (index + offset + length) % length; +} + +// adapted from https://github.com/microsoft/vscode/blob/823d54f86ee13eb357bc6e8e562e89d793f3c43b/extensions/git/src/staging.ts +function applyChanges(changes: readonly Change[], original: monaco.editor.ITextModel, modified: monaco.editor.ITextModel): string { + const result: string[] = []; + let currentLine = 1; + + for (const change of changes) { + const { previousRange, currentRange } = change; + + const isInsertion = LineRange.isEmpty(previousRange); + const isDeletion = LineRange.isEmpty(currentRange); + + const convert = (range: LineRange): [number, number] => { + let startLineNumber; + let endLineNumber; + if (!LineRange.isEmpty(range)) { + startLineNumber = range.start + 1; + endLineNumber = range.end; + } else { + startLineNumber = range.start; + endLineNumber = 0; + } + return [startLineNumber, endLineNumber]; + }; + + const [originalStartLineNumber, originalEndLineNumber] = convert(previousRange); + const [modifiedStartLineNumber, modifiedEndLineNumber] = convert(currentRange); + + let toLine = isInsertion ? originalStartLineNumber + 1 : originalStartLineNumber; + let toCharacter = 1; + + // if this is a deletion at the very end of the document, + // we need to account for a newline at the end of the last line, + // which may have been deleted + if (isDeletion && originalEndLineNumber === original.getLineCount()) { + toLine--; + toCharacter = original.getLineMaxColumn(toLine); + } + + result.push(original.getValueInRange(new monaco.Range(currentLine, 1, toLine, toCharacter))); + + if (!isDeletion) { + let fromLine = modifiedStartLineNumber; + let fromCharacter = 1; + + // if this is an insertion at the very end of the document, + // we must start the next range after the last character of the previous line, + // in order to take the correct eol + if (isInsertion && originalStartLineNumber === original.getLineCount()) { + fromLine--; + fromCharacter = modified.getLineMaxColumn(fromLine); + } + + result.push(modified.getValueInRange(new monaco.Range(fromLine, fromCharacter, modifiedEndLineNumber + 1, 1))); + } + + currentLine = isInsertion ? originalStartLineNumber + 1 : originalEndLineNumber + 1; + } + + result.push(original.getValueInRange(new monaco.Range(currentLine, 1, original.getLineCount() + 1, 1))); + + return result.join(''); +} + +class DirtyDiffPeekView extends MonacoEditorPeekViewWidget { + + private diffEditorPromise?: Promise; + private height?: number; + + constructor(readonly widget: DirtyDiffWidget) { + super(widget.editor, { isResizeable: true, showArrow: true, frameWidth: 1, keepEditorSelection: true, className: 'dirty-diff' }); + } + + override async create(): Promise { + try { + super.create(); + const diffEditor = await this.diffEditorPromise!; + return new Promise(resolve => { + // setTimeout is needed here because the non-side-by-side diff editor might still not have created the view zones; + // otherwise, the first change shown might not be properly revealed in the diff editor. + // see also https://github.com/microsoft/vscode/blob/b30900b56c4b3ca6c65d7ab92032651f4cb23f15/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts#L248 + const disposable = diffEditor.diffEditor.onDidUpdateDiff(() => setTimeout(() => { + resolve(diffEditor); + disposable.dispose(); + })); + }); + } catch (e) { + this.dispose(); + throw e; + } + } + + override show(rangeOrPos: Range | Position, heightInLines: number): void { + const borderColor = this.getBorderColor(); + this.style({ + arrowColor: borderColor, + frameColor: borderColor, + headerBackgroundColor: peekViewTitleBackground, + primaryHeadingColor: peekViewTitleForeground, + secondaryHeadingColor: peekViewTitleInfoForeground + }); + this.updateActions(); + super.show(rangeOrPos, heightInLines); + } + + private getBorderColor(): string { + const { currentChange } = this.widget; + if (!currentChange) { + return peekViewBorder; + } + if (Change.isAddition(currentChange)) { + return ScmColors.editorGutterAddedBackground; + } else if (Change.isRemoval(currentChange)) { + return ScmColors.editorGutterDeletedBackground; + } else { + return ScmColors.editorGutterModifiedBackground; + } + } + + private updateActions(): void { + this.clearActions(); + const { contextKeyService, menuModelRegistry, menuCommandExecutor } = this.widget; + contextKeyService.with({ originalResourceScheme: this.widget.previousRevisionUri.scheme }, () => { + for (const menuPath of [SCM_CHANGE_TITLE_MENU, PLUGIN_SCM_CHANGE_TITLE_MENU]) { + const menu = menuModelRegistry.getMenu(menuPath); + for (const item of menu.children) { + if (item instanceof ActionMenuNode) { + const { command, id, label, icon, when } = item; + if (icon && menuCommandExecutor.isVisible(menuPath, command, this.widget) && (!when || contextKeyService.match(when))) { + this.addAction(id, label, icon, menuCommandExecutor.isEnabled(menuPath, command, this.widget), () => { + menuCommandExecutor.executeCommand(menuPath, command, this.widget); + }); + } + } + } + } + }); + this.addAction('dirtydiff.next', nls.localizeByDefault('Show Next Change'), codicon('arrow-down'), true, + () => this.widget.showNextChange()); + this.addAction('dirtydiff.previous', nls.localizeByDefault('Show Previous Change'), codicon('arrow-up'), true, + () => this.widget.showPreviousChange()); + this.addAction('peekview.close', nls.localizeByDefault('Close'), codicon('close'), true, + () => this.dispose()); + } + + protected override fillHead(container: HTMLElement): void { + super.fillHead(container, true); + } + + protected override fillBody(container: HTMLElement): void { + this.diffEditorPromise = this.widget.editorProvider.createEmbeddedDiffEditor(this.editor, container, this.widget.previousRevisionUri).then(diffEditor => { + this.toDispose.push(diffEditor); + return diffEditor; + }); + } + + protected override doLayoutBody(height: number, width: number): void { + super.doLayoutBody(height, width); + this.layout(height, width); + this.height = height; + } + + protected override onWidth(width: number): void { + super.onWidth(width); + const { height } = this; + if (height !== undefined) { + this.layout(height, width); + } + } + + private layout(height: number, width: number): void { + this.diffEditorPromise?.then(({ diffEditor }) => diffEditor.layout({ height, width })); + } + + protected override doRevealRange(range: Range): void { + this.editor.revealPosition(Position.create(range.end.line, 0), { vertical: 'centerIfOutsideViewport' }); + } +} diff --git a/packages/scm/src/browser/scm-colors.ts b/packages/scm/src/browser/scm-colors.ts new file mode 100644 index 0000000000000..853d218e679d8 --- /dev/null +++ b/packages/scm/src/browser/scm-colors.ts @@ -0,0 +1,21 @@ +// ***************************************************************************** +// Copyright (C) 2019 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export namespace ScmColors { + export const editorGutterModifiedBackground = 'editorGutter.modifiedBackground'; + export const editorGutterAddedBackground = 'editorGutter.addedBackground'; + export const editorGutterDeletedBackground = 'editorGutter.deletedBackground'; +} diff --git a/packages/scm/src/browser/scm-contribution.ts b/packages/scm/src/browser/scm-contribution.ts index 032079923632a..f896363f313de 100644 --- a/packages/scm/src/browser/scm-contribution.ts +++ b/packages/scm/src/browser/scm-contribution.ts @@ -29,7 +29,7 @@ import { CssStyleCollector } from '@theia/core/lib/browser'; import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { CommandRegistry, Command, Disposable, DisposableCollection, CommandService } from '@theia/core/lib/common'; +import { CommandRegistry, Command, Disposable, DisposableCollection, CommandService, MenuModelRegistry } from '@theia/core/lib/common'; import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service'; import { ScmService } from './scm-service'; import { ScmWidget } from '../browser/scm-widget'; @@ -38,10 +38,13 @@ import { ScmQuickOpenService } from './scm-quick-open-service'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; import { Color } from '@theia/core/lib/common/color'; +import { ScmColors } from './scm-colors'; import { ScmCommand } from './scm-provider'; import { ScmDecorationsService } from '../browser/decorations/scm-decorations-service'; import { nls } from '@theia/core/lib/common/nls'; import { isHighContrast } from '@theia/core/lib/common/theme'; +import { EditorMainMenu } from '@theia/editor/lib/browser'; +import { DirtyDiffNavigator } from './dirty-diff/dirty-diff-navigator'; export const SCM_WIDGET_FACTORY_ID = ScmWidget.ID; export const SCM_VIEW_CONTAINER_ID = 'scm-view-container'; @@ -51,6 +54,10 @@ export const SCM_VIEW_CONTAINER_TITLE_OPTIONS: ViewContainerTitleOptions = { closeable: true }; +export namespace ScmMenus { + export const CHANGES_GROUP = [...EditorMainMenu.GO, '6_changes_group']; +} + export namespace SCM_COMMANDS { export const CHANGE_REPOSITORY = { id: 'scm.change.repository', @@ -85,13 +92,36 @@ export namespace SCM_COMMANDS { label: nls.localizeByDefault('Collapse All'), originalLabel: 'Collapse All' }; + export const GOTO_NEXT_CHANGE = Command.toDefaultLocalizedCommand({ + id: 'workbench.action.editor.nextChange', + category: 'Source Control', + label: 'Go to Next Change' + }); + export const GOTO_PREVIOUS_CHANGE = Command.toDefaultLocalizedCommand({ + id: 'workbench.action.editor.previousChange', + category: 'Source Control', + label: 'Go to Previous Change' + }); + export const SHOW_NEXT_CHANGE = Command.toDefaultLocalizedCommand({ + id: 'editor.action.dirtydiff.next', + category: 'Source Control', + label: 'Show Next Change' + }); + export const SHOW_PREVIOUS_CHANGE = Command.toDefaultLocalizedCommand({ + id: 'editor.action.dirtydiff.previous', + category: 'Source Control', + label: 'Show Previous Change' + }); + export const CLOSE_CHANGE_PEEK_VIEW = { + id: 'editor.action.dirtydiff.close', + category: nls.localizeByDefault('Source Control'), + originalCategory: 'Source Control', + label: nls.localize('theia/scm/dirtyDiff/close', 'Close Change Peek View'), + originalLabel: 'Close Change Peek View' + }; } -export namespace ScmColors { - export const editorGutterModifiedBackground = 'editorGutter.modifiedBackground'; - export const editorGutterAddedBackground = 'editorGutter.addedBackground'; - export const editorGutterDeletedBackground = 'editorGutter.deletedBackground'; -} +export { ScmColors }; @injectable() export class ScmContribution extends AbstractViewContribution implements @@ -108,6 +138,7 @@ export class ScmContribution extends AbstractViewContribution impleme @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @inject(ContextKeyService) protected readonly contextKeys: ContextKeyService; @inject(ScmDecorationsService) protected readonly scmDecorationsService: ScmDecorationsService; + @inject(DirtyDiffNavigator) protected readonly dirtyDiffNavigator: DirtyDiffNavigator; protected scmFocus: ContextKey; @@ -144,6 +175,8 @@ export class ScmContribution extends AbstractViewContribution impleme this.updateContextKeys(); this.shell.onDidChangeCurrentWidget(() => this.updateContextKeys()); + + this.scmDecorationsService.onDirtyDiffUpdate(update => this.dirtyDiffNavigator.handleDirtyDiffUpdate(update)); } protected updateContextKeys(): void { @@ -160,6 +193,39 @@ export class ScmContribution extends AbstractViewContribution impleme execute: () => this.acceptInput(), isEnabled: () => !!this.scmFocus.get() && !!this.acceptInputCommand() }); + + // Note that commands for dirty diff navigation need to be always available. + // This is consistent with behavior in VS Code, and also with other similar commands (such as `Next Problem/Previous Problem`) in Theia. + // See https://github.com/eclipse-theia/theia/pull/13104#discussion_r1497316614 for a detailed discussion. + commandRegistry.registerCommand(SCM_COMMANDS.GOTO_NEXT_CHANGE, { + execute: () => this.dirtyDiffNavigator.gotoNextChange() + }); + commandRegistry.registerCommand(SCM_COMMANDS.GOTO_PREVIOUS_CHANGE, { + execute: () => this.dirtyDiffNavigator.gotoPreviousChange() + }); + commandRegistry.registerCommand(SCM_COMMANDS.SHOW_NEXT_CHANGE, { + execute: () => this.dirtyDiffNavigator.showNextChange() + }); + commandRegistry.registerCommand(SCM_COMMANDS.SHOW_PREVIOUS_CHANGE, { + execute: () => this.dirtyDiffNavigator.showPreviousChange() + }); + commandRegistry.registerCommand(SCM_COMMANDS.CLOSE_CHANGE_PEEK_VIEW, { + execute: () => this.dirtyDiffNavigator.closeChangePeekView() + }); + } + + override registerMenus(menus: MenuModelRegistry): void { + super.registerMenus(menus); + menus.registerMenuAction(ScmMenus.CHANGES_GROUP, { + commandId: SCM_COMMANDS.SHOW_NEXT_CHANGE.id, + label: nls.localizeByDefault('Next Change'), + order: '1' + }); + menus.registerMenuAction(ScmMenus.CHANGES_GROUP, { + commandId: SCM_COMMANDS.SHOW_PREVIOUS_CHANGE.id, + label: nls.localizeByDefault('Previous Change'), + order: '2' + }); } registerToolbarItems(registry: TabBarToolbarRegistry): void { @@ -219,6 +285,31 @@ export class ScmContribution extends AbstractViewContribution impleme keybinding: 'ctrlcmd+enter', when: 'scmFocus' }); + keybindings.registerKeybinding({ + command: SCM_COMMANDS.GOTO_NEXT_CHANGE.id, + keybinding: 'alt+f5', + when: 'editorTextFocus' + }); + keybindings.registerKeybinding({ + command: SCM_COMMANDS.GOTO_PREVIOUS_CHANGE.id, + keybinding: 'shift+alt+f5', + when: 'editorTextFocus' + }); + keybindings.registerKeybinding({ + command: SCM_COMMANDS.SHOW_NEXT_CHANGE.id, + keybinding: 'alt+f3', + when: 'editorTextFocus' + }); + keybindings.registerKeybinding({ + command: SCM_COMMANDS.SHOW_PREVIOUS_CHANGE.id, + keybinding: 'shift+alt+f3', + when: 'editorTextFocus' + }); + keybindings.registerKeybinding({ + command: SCM_COMMANDS.CLOSE_CHANGE_PEEK_VIEW.id, + keybinding: 'esc', + when: 'dirtyDiffVisible' + }); } protected async acceptInput(): Promise { diff --git a/packages/scm/src/browser/scm-tree-widget.tsx b/packages/scm/src/browser/scm-tree-widget.tsx index 105956cf85d4b..3dd0b346c9bc9 100644 --- a/packages/scm/src/browser/scm-tree-widget.tsx +++ b/packages/scm/src/browser/scm-tree-widget.tsx @@ -605,7 +605,7 @@ export class ScmResourceComponent extends ScmElement protected readonly contextMenuPath = ScmTreeWidget.RESOURCE_CONTEXT_MENU; protected get contextMenuArgs(): any[] { - if (!this.props.model.selectedNodes.some(node => ScmFileChangeNode.is(node) && node.sourceUri === this.props.sourceUri)) { + if (!this.props.model.selectedNodes.some(node => ScmFileChangeNode.is(node) && node === this.props.treeNode)) { // Clicked node is not in selection, so ignore selection and action on just clicked node return this.singleNodeArgs; } else { diff --git a/packages/scm/src/browser/style/dirty-diff-decorator.css b/packages/scm/src/browser/style/dirty-diff-decorator.css index 1b630c3ba07ac..f5f8beeb8c08e 100644 --- a/packages/scm/src/browser/style/dirty-diff-decorator.css +++ b/packages/scm/src/browser/style/dirty-diff-decorator.css @@ -19,6 +19,7 @@ border-top: 4px solid transparent; border-bottom: 4px solid transparent; transition: border-top-width 80ms linear, border-bottom-width 80ms linear, bottom 80ms linear; + pointer-events: none; } .dirty-diff-glyph:before { @@ -41,7 +42,7 @@ position: absolute; content: ''; height: 100%; - width: 9px; + width: 6px; left: -6px; } diff --git a/packages/scm/tsconfig.json b/packages/scm/tsconfig.json index 41c8ab00ce84c..8f53c0fe2dd53 100644 --- a/packages/scm/tsconfig.json +++ b/packages/scm/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../filesystem" + }, + { + "path": "../monaco" } ] } From 1578d2d32c7de3e4d0aa7e1bf4cffc458bb93664 Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Wed, 24 Apr 2024 10:57:48 +0200 Subject: [PATCH 190/441] plugin-ext-vscode: apply file handlers only for user plugins (#13435) This patch fixes an unexpected behavior where Theia would also pick up and deploy .vsix files from the local-plugins directory into the deployedPlugins directory, where they will be treated as user-installed extensions on the next start of theia. Instead, we now only apply the file handlers for .vsix files if they are user extensions. For system plugins, we print a warning message indicating that the plugin has to be unpacked manually. Fixes #13222 Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- .../src/main/node/plugin-deployer-impl.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts index 8c60671b78b88..e7a900e4290f8 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts @@ -225,8 +225,20 @@ export class PluginDeployerImpl implements PluginDeployer { } protected async resolveAndHandle(id: string, type: PluginType, options?: PluginDeployOptions): Promise { - const entries = await this.resolvePlugin(id, type, options); - await this.applyFileHandlers(entries); + let entries = await this.resolvePlugin(id, type, options); + if (type === PluginType.User) { + await this.applyFileHandlers(entries); + } else { + const filteredEntries: PluginDeployerEntry[] = []; + for (const entry of entries) { + if (await entry.isFile()) { + this.logger.warn(`Only user plugins will be handled by file handlers, please unpack the plugin '${entry.id()}' manually.`); + } else { + filteredEntries.push(entry); + } + } + entries = filteredEntries; + } await this.applyDirectoryFileHandlers(entries); return entries; } From 845b291b8cc5fec63b3274065eb487233ed49f54 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 24 Apr 2024 16:30:52 +0200 Subject: [PATCH 191/441] Stub TestCoverage API (#13631) contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + .../plugin-ext/src/plugin/plugin-context.ts | 14 +- packages/plugin-ext/src/plugin/tests.ts | 5 + packages/plugin-ext/src/plugin/types-impl.ts | 66 ++++++ packages/plugin/src/theia.d.ts | 196 ++++++++++++++++++ 5 files changed, 280 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d3fcdfacf9bc..853ba325270f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) +- [test] stub VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) [Breaking Changes:](#breaking_changes_not_yet_released) - [scm] revised some of the dirty diff related types [#13104](https://github.com/eclipse-theia/theia/pull/13104) diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 83c3f80a5c312..f405a67633964 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -203,7 +203,12 @@ import { TerminalOutputAnchor, TerminalQuickFixTerminalCommand, TerminalQuickFixOpener, - TestResultState + TestResultState, + BranchCoverage, + DeclarationCoverage, + FileCoverage, + StatementCoverage, + TestCoverageCount } from './types-impl'; import { AuthenticationExtImpl } from './authentication-ext'; import { SymbolKind } from '../common/plugin-api-rpc-model'; @@ -1402,7 +1407,12 @@ export function createAPIFactory( TerminalQuickFixTerminalCommand, TerminalQuickFixOpener, EditSessionIdentityMatch, - TestResultState + TestResultState, + BranchCoverage, + DeclarationCoverage, + FileCoverage, + StatementCoverage, + TestCoverageCount }; }; } diff --git a/packages/plugin-ext/src/plugin/tests.ts b/packages/plugin-ext/src/plugin/tests.ts index d6a45a7268aaa..70ae52f38a907 100644 --- a/packages/plugin-ext/src/plugin/tests.ts +++ b/packages/plugin-ext/src/plugin/tests.ts @@ -240,6 +240,8 @@ class TestRun implements theia.TestRun { onDidEnd: Event = this.onDidEndEmitter.event; private onWillFlushEmitter = new Emitter(); onWillFlush: Event = this.onWillFlushEmitter.event; + private onDidDisposeEmitter = new Emitter(); + onDidDispose: Event = this.onDidDisposeEmitter.event; readonly id: string; private testStateDeltas = new Map(); @@ -293,6 +295,9 @@ class TestRun implements theia.TestRun { this.proxy.$notifyTestRunEnded(this.controller.id, this.id); } + /** @stubbed */ + addCoverage(fileCoverage: theia.FileCoverage): void { } + private checkNotEnded(test: theia.TestItem): boolean { if (this.ended) { console.warn(`Setting the state of test "${test.id}" is a no - op after the run ends.`); diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 0a0e7c5611407..d46a05d63c4f4 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3330,6 +3330,72 @@ export class TestMessage implements theia.TestMessage { constructor(public message: string | theia.MarkdownString) { } } +@es5ClassCompat +export class TestCoverageCount { + constructor( public covered: number, public total: number) { } +} + +@es5ClassCompat +export class FileCoverage { + + detailedCoverage?: theia.FileCoverageDetail[]; + + static fromDetails(uri: theia.Uri, details: theia.FileCoverageDetail[]): FileCoverage { + const statements = new TestCoverageCount(0, 0); + const branches = new TestCoverageCount(0, 0); + const decl = new TestCoverageCount(0, 0); + + for (const detail of details) { + if (detail instanceof StatementCoverage) { + statements.total += 1; + statements.covered += detail.executed ? 1 : 0; + + for (const branch of detail.branches) { + branches.total += 1; + branches.covered += branch.executed ? 1 : 0; + } + } else { + decl.total += 1; + decl.covered += detail.executed ? 1 : 0; + } + } + + const coverage = new FileCoverage( + uri, + statements, + branches.total > 0 ? branches : undefined, + decl.total > 0 ? decl : undefined, + ); + + coverage.detailedCoverage = details; + + return coverage; + } + + constructor( + public uri: theia.Uri, + public statementCoverage: TestCoverageCount, + public branchCoverage?: TestCoverageCount, + public declarationCoverage?: TestCoverageCount, + ) { } +} + +@es5ClassCompat +export class StatementCoverage implements theia.StatementCoverage { + constructor(public executed: number | boolean, public location: Position | Range, public branches: BranchCoverage[] = []) { } +} + +export class BranchCoverage implements theia.BranchCoverage { + constructor(public executed: number | boolean, public location?: Position | Range, public label?: string) { } +} + +@es5ClassCompat +export class DeclarationCoverage implements theia.DeclarationCoverage { + constructor(public name: string, public executed: number | boolean, public location: Position | Range) { } +} + +export type FileCoverageDetail = StatementCoverage | DeclarationCoverage; + @es5ClassCompat export class TimelineItem { timestamp: number; diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index e8015f3e5cce4..3ffce743ddb84 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -16035,6 +16035,18 @@ export module '@theia/plugin' { */ runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void; + /** + * An extension-provided function that provides detailed statement and + * function-level coverage for a file. The editor will call this when more + * detail is needed for a file, such as when it's opened in an editor or + * expanded in the **Test Coverage** view. + * + * The {@link FileCoverage} object passed to this function is the same instance + * emitted on {@link TestRun.addCoverage} calls associated with this profile. + * @stubbed + */ + loadDetailedCoverage?: (testRun: TestRun, fileCoverage: FileCoverage, token: CancellationToken) => Thenable; + /** * Deletes the run profile. */ @@ -16314,11 +16326,23 @@ export module '@theia/plugin' { */ appendOutput(output: string, location?: Location, test?: TestItem): void; + /** + * Adds coverage for a file in the run. + * @stubbed + */ + addCoverage(fileCoverage: FileCoverage): void; + /** * Signals the end of the test run. Any tests included in the run whose * states have not been updated will have their state reset. */ end(): void; + + /** + * An event fired when the editor is no longer interested in data + * associated with the test run. + */ + onDidDispose: Event; } /** @@ -16527,6 +16551,178 @@ export module '@theia/plugin' { */ constructor(message: string | MarkdownString); } + + /** + * A class that contains information about a covered resource. A count can + * be give for lines, branches, and declarations in a file. + */ + export class TestCoverageCount { + /** + * Number of items covered in the file. + */ + covered: number; + /** + * Total number of covered items in the file. + */ + total: number; + + /** + * @param covered Value for {@link TestCoverageCount.covered} + * @param total Value for {@link TestCoverageCount.total} + */ + constructor(covered: number, total: number); + } + + /** + * Contains coverage metadata for a file. + */ + export class FileCoverage { + /** + * File URI. + */ + readonly uri: Uri; + + /** + * Statement coverage information. If the reporter does not provide statement + * coverage information, this can instead be used to represent line coverage. + */ + statementCoverage: TestCoverageCount; + + /** + * Branch coverage information. + */ + branchCoverage?: TestCoverageCount; + + /** + * Declaration coverage information. Depending on the reporter and + * language, this may be types such as functions, methods, or namespaces. + */ + declarationCoverage?: TestCoverageCount; + + /** + * Creates a {@link FileCoverage} instance with counts filled in from + * the coverage details. + * @param uri Covered file URI + * @param detailed Detailed coverage information + */ + static fromDetails(uri: Uri, details: readonly FileCoverageDetail[]): FileCoverage; + + /** + * @param uri Covered file URI + * @param statementCoverage Statement coverage information. If the reporter + * does not provide statement coverage information, this can instead be + * used to represent line coverage. + * @param branchCoverage Branch coverage information + * @param declarationCoverage Declaration coverage information + */ + constructor( + uri: Uri, + statementCoverage: TestCoverageCount, + branchCoverage?: TestCoverageCount, + declarationCoverage?: TestCoverageCount, + ); + } + + /** + * Contains coverage information for a single statement or line. + */ + export class StatementCoverage { + /** + * The number of times this statement was executed, or a boolean indicating + * whether it was executed if the exact count is unknown. If zero or false, + * the statement will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Statement location. + */ + location: Position | Range; + + /** + * Coverage from branches of this line or statement. If it's not a + * conditional, this will be empty. + */ + branches: BranchCoverage[]; + + /** + * @param location The statement position. + * @param executed The number of times this statement was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the statement will be marked as un-covered. + * @param branches Coverage from branches of this line. If it's not a + * conditional, this should be omitted. + */ + constructor(executed: number | boolean, location: Position | Range, branches?: BranchCoverage[]); + } + + /** + * Contains coverage information for a branch of a {@link StatementCoverage}. + */ + export class BranchCoverage { + /** + * The number of times this branch was executed, or a boolean indicating + * whether it was executed if the exact count is unknown. If zero or false, + * the branch will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Branch location. + */ + location?: Position | Range; + + /** + * Label for the branch, used in the context of "the ${label} branch was + * not taken," for example. + */ + label?: string; + + /** + * @param executed The number of times this branch was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the branch will be marked as un-covered. + * @param location The branch position. + */ + constructor(executed: number | boolean, location?: Position | Range, label?: string); + } + + /** + * Contains coverage information for a declaration. Depending on the reporter + * and language, this may be types such as functions, methods, or namespaces. + */ + export class DeclarationCoverage { + /** + * Name of the declaration. + */ + name: string; + + /** + * The number of times this declaration was executed, or a boolean + * indicating whether it was executed if the exact count is unknown. If + * zero or false, the declaration will be marked as un-covered. + */ + executed: number | boolean; + + /** + * Declaration location. + */ + location: Position | Range; + + /** + * @param executed The number of times this declaration was executed, or a + * boolean indicating whether it was executed if the exact count is + * unknown. If zero or false, the declaration will be marked as un-covered. + * @param location The declaration position. + */ + constructor(name: string, executed: number | boolean, location: Position | Range); + } + + /** + * Coverage details returned from {@link TestRunProfile.loadDetailedCoverage}. + */ + export type FileCoverageDetail = StatementCoverage | DeclarationCoverage; + /** * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, * and others. This API makes no assumption about what promise library is being used which From ae0b3e8547cd0d4a479fc59e9418fcf5cd1ba3c6 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 24 Apr 2024 16:34:21 +0200 Subject: [PATCH 192/441] Generate Extension Info in server application (#13590) fixes #13481 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 2 ++ .../application-manager/src/generator/backend-generator.ts | 2 ++ packages/core/src/node/application-server.ts | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 853ba325270f4..5a7b9d9a10cdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,12 @@ ## not yet released +- [application-manager] Generate Extension Info in server application to avoid empty About Dialog [#13590](https://github.com/eclipse-theia/theia/pull/13590) - contributed on behalf of STMicroelectronics - [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) - [test] stub VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) [Breaking Changes:](#breaking_changes_not_yet_released) + - [scm] revised some of the dirty diff related types [#13104](https://github.com/eclipse-theia/theia/pull/13104) - replaced `DirtyDiff.added/removed/modified` with `changes`, which provides more detailed information about the changes - changed the semantics of `LineRange` to represent a range that spans up to but not including the `end` line (previously, it included the `end` line) diff --git a/dev-packages/application-manager/src/generator/backend-generator.ts b/dev-packages/application-manager/src/generator/backend-generator.ts index fd47708a0d3af..1ad8680f9241e 100644 --- a/dev-packages/application-manager/src/generator/backend-generator.ts +++ b/dev-packages/application-manager/src/generator/backend-generator.ts @@ -185,6 +185,8 @@ const main = require('@theia/core/lib/node/main'); BackendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.backend.config)}); +globalThis.extensionInfo = ${this.prettyStringify(this.pck.extensionPackages.map(({ name, version }) => ({ name, version }))) }; + const serverModule = require('./server'); const serverAddress = main.start(serverModule()); diff --git a/packages/core/src/node/application-server.ts b/packages/core/src/node/application-server.ts index e80212248293d..df3996b6bb1b0 100644 --- a/packages/core/src/node/application-server.ts +++ b/packages/core/src/node/application-server.ts @@ -26,9 +26,9 @@ export class ApplicationServerImpl implements ApplicationServer { protected readonly applicationPackage: ApplicationPackage; getExtensionsInfos(): Promise { - const extensions = this.applicationPackage.extensionPackages; - const infos: ExtensionInfo[] = extensions.map(extension => ({ name: extension.name, version: extension.version })); - return Promise.resolve(infos); + // @ts-expect-error + const appInfo: ExtensionInfo[] = globalThis.extensionInfo; + return Promise.resolve(appInfo); } getApplicationInfo(): Promise { From c5c6a39ddc765899c5088f9f88f3595f4f11ca11 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 25 Apr 2024 16:49:36 +0200 Subject: [PATCH 193/441] fixes clear cell outputs command and improves toolbar creation (#13640) * fixes clear cell outputs command and improves toolbar creation Signed-off-by: Jonah Iden * fix output mime type change Signed-off-by: Jonah Iden * fix change presentation somtimes firing on old output Signed-off-by: Jonah Iden * removed console logs Signed-off-by: Jonah Iden * removed additional forgotten console.log Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 2 +- .../browser/view/notebook-cell-list-view.tsx | 5 +++- .../view/notebook-cell-toolbar-factory.tsx | 29 ++++++++++--------- .../browser/view/notebook-code-cell-view.tsx | 10 +++++-- .../renderers/output-webview-internal.ts | 10 +++---- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 0c9acfff28703..322a54a5c16d1 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -324,7 +324,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }); commands.registerCommand(NotebookCellCommands.CLEAR_OUTPUTS_COMMAND, this.editableCellCommandHandler( - (notebook, cell) => notebook.applyEdits([{ + (notebook, cell) => (notebook ?? this.notebookEditorWidgetService.focusedEditor?.model)?.applyEdits([{ editType: CellEditType.Output, handle: cell.handle, outputs: [], diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index a695aacef252c..88a6416b2333d 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -89,7 +89,10 @@ export class NotebookCellListView extends React.Component {this.state.selectedCell === cell && - this.props.toolbarRenderer.renderCellToolbar(NotebookCellActionContribution.ACTION_MENU, this.props.notebookModel, cell)} + this.props.toolbarRenderer.renderCellToolbar(NotebookCellActionContribution.ACTION_MENU, cell, { + contextMenuArgs: () => [cell], commandArgs: () => [this.props.notebookModel] + }) + } {this.shouldRenderDragOverIndicator(cell, 'bottom') && } diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx index 7c88778f4aaed..fe9f8bd4fac35 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar-factory.tsx @@ -20,9 +20,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookCellSidebar, NotebookCellToolbar } from './notebook-cell-toolbar'; import { ContextMenuRenderer } from '@theia/core/lib/browser'; -import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; -import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model'; import { NotebookContextManager } from '../service/notebook-context-manager'; export interface NotebookCellToolbarItem { @@ -34,6 +32,11 @@ export interface NotebookCellToolbarItem { contextKeys?: Set } +export interface toolbarItemOptions { + contextMenuArgs?: () => unknown[]; + commandArgs?: () => unknown[]; +} + @injectable() export class NotebookCellToolbarFactory { @@ -52,31 +55,31 @@ export class NotebookCellToolbarFactory { @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager; - renderCellToolbar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { - return this.getMenuItems(menuPath, notebookModel, cell)} + renderCellToolbar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode { + return this.getMenuItems(menuPath, cell, itemOptions)} onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />; } - renderSidebar(menuPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): React.ReactNode { - return this.getMenuItems(menuPath, notebookModel, cell, output)} + renderSidebar(menuPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): React.ReactNode { + return this.getMenuItems(menuPath, cell, itemOptions)} onContextKeysChanged={this.notebookContextManager.onDidChangeContext} />; } - private getMenuItems(menuItemPath: string[], notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): NotebookCellToolbarItem[] { + private getMenuItems(menuItemPath: string[], cell: NotebookCellModel, itemOptions: toolbarItemOptions): NotebookCellToolbarItem[] { const inlineItems: NotebookCellToolbarItem[] = []; for (const menuNode of this.menuRegistry.getMenu(menuItemPath).children) { if (!menuNode.when || this.notebookContextManager.getCellContext(cell.handle).match(menuNode.when, this.notebookContextManager.context)) { if (menuNode.role === CompoundMenuNodeRole.Flat) { - inlineItems.push(...menuNode.children?.map(child => this.createToolbarItem(child, notebookModel, cell, output)) ?? []); + inlineItems.push(...menuNode.children?.map(child => this.createToolbarItem(child, itemOptions)) ?? []); } else { - inlineItems.push(this.createToolbarItem(menuNode, notebookModel, cell, output)); + inlineItems.push(this.createToolbarItem(menuNode, itemOptions)); } } } return inlineItems; } - private createToolbarItem(menuNode: MenuNode, notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel): NotebookCellToolbarItem { + private createToolbarItem(menuNode: MenuNode, itemOptions: toolbarItemOptions): NotebookCellToolbarItem { const menuPath = menuNode.role === CompoundMenuNodeRole.Submenu ? this.menuRegistry.getPath(menuNode) : undefined; return { id: menuNode.id, @@ -88,11 +91,11 @@ export class NotebookCellToolbarFactory { anchor: e.nativeEvent, menuPath, includeAnchorArg: false, - args: [cell], + args: itemOptions.contextMenuArgs?.(), context: this.notebookContextManager.context }) : - () => this.commandRegistry.executeCommand(menuNode.command!, notebookModel, cell, output), - isVisible: () => menuPath ? true : Boolean(this.commandRegistry.getVisibleHandler(menuNode.command!, notebookModel, cell, output)), + () => this.commandRegistry.executeCommand(menuNode.command!, ...(itemOptions.commandArgs?.() ?? [])), + isVisible: () => menuPath ? true : Boolean(this.commandRegistry.getVisibleHandler(menuNode.command!, ...(itemOptions.commandArgs?.() ?? []))), contextKeys: menuNode.when ? this.contextKeyService.parseKeys(menuNode.when) : undefined }; } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 0cbaf6529f6c6..11f5ac626baf3 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -70,7 +70,10 @@ export class NotebookCodeCellRenderer implements CellRenderer { return
    - {this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, notebookModel, cell)} + {this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, cell, { + contextMenuArgs: () => [cell], commandArgs: () => [notebookModel, cell] + }) + }
    @@ -88,7 +91,10 @@ export class NotebookCodeCellRenderer implements CellRenderer {
    - this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.OUTPUT_SIDEBAR_MENU, notebookModel, cell, cell.outputs[0])} /> + this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.OUTPUT_SIDEBAR_MENU, cell, { + contextMenuArgs: () => [notebookModel, cell, cell.outputs[0]] + }) + } />
    ; } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 6f38f53abb358..887e523f02cb4 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -553,11 +553,11 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { renderers.getRenderer(event.data.rendererId)?.receiveMessage(event.data.message); break; case 'changePreferredMimetype': - const outputId = event.data.outputId; - const index = outputs.findIndex(output => output.outputId === outputId); - outputs.splice(index, 1); - clearOutput(outputs.splice(index, 1)[0]); - renderers.render(outputs[index], event.data.mimeType, undefined, new AbortController().signal); + const mimeType = event.data.mimeType; + outputs.forEach(output => { + output.element.innerHTML = ''; + renderers.render(output, mimeType, undefined, new AbortController().signal); + }); break; case 'customKernelMessage': onDidReceiveKernelMessage.fire(event.data.message); From cf7dc82bde98c91644672ba5a0939914aa3bd5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 25 Apr 2024 16:56:04 +0200 Subject: [PATCH 194/441] Reorder back-end plugin deployment to prevent early promise resolution (#13643) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workaround for #13638 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../src/main/node/plugin-deployer-impl.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts index e7a900e4290f8..4263a835cb4f9 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-impl.ts @@ -298,13 +298,12 @@ export class PluginDeployerImpl implements PluginDeployer { const pluginPaths = [...acceptedBackendPlugins, ...acceptedHeadlessPlugins].map(pluginEntry => pluginEntry.path()); this.logger.debug('local path to deploy on remote instance', pluginPaths); - const deployments = await Promise.all([ - // headless plugins are deployed like backend plugins - this.pluginDeployerHandler.deployBackendPlugins(acceptedHeadlessPlugins), - // start the backend plugins - this.pluginDeployerHandler.deployBackendPlugins(acceptedBackendPlugins), - this.pluginDeployerHandler.deployFrontendPlugins(acceptedFrontendPlugins) - ]); + const deployments = []; + // start the backend plugins + deployments.push(await this.pluginDeployerHandler.deployBackendPlugins(acceptedBackendPlugins)); + // headless plugins are deployed like backend plugins + deployments.push(await this.pluginDeployerHandler.deployBackendPlugins(acceptedHeadlessPlugins)); + deployments.push(await this.pluginDeployerHandler.deployFrontendPlugins(acceptedFrontendPlugins)); this.onDidDeployEmitter.fire(undefined); return deployments.reduce((accumulated, current) => accumulated += current ?? 0, 0); } From de86a44a40dfbaba8b93894d0e7ee46a9cd65fef Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 25 Apr 2024 17:23:15 +0200 Subject: [PATCH 195/441] Fix spawn calls for node LTS versions (#13614) --- .../application-manager/src/application-process.ts | 10 ++++++++-- dev-packages/cli/bin/theia-patch.js | 7 +++---- .../native-webpack-plugin/src/native-webpack-plugin.ts | 4 ---- .../private-re-exports/src/package-re-exports.ts | 3 ++- packages/process/package.json | 2 +- packages/process/src/node/pseudo-pty.ts | 2 ++ packages/terminal/src/node/shell-terminal-server.ts | 4 +++- yarn.lock | 10 +++++----- 8 files changed, 24 insertions(+), 18 deletions(-) diff --git a/dev-packages/application-manager/src/application-process.ts b/dev-packages/application-manager/src/application-process.ts index 47fda1c743ba1..2befd37bcfc50 100644 --- a/dev-packages/application-manager/src/application-process.ts +++ b/dev-packages/application-manager/src/application-process.ts @@ -32,7 +32,10 @@ export class ApplicationProcess { ) { } spawn(command: string, args?: string[], options?: cp.SpawnOptions): cp.ChildProcess { - return cp.spawn(command, args || [], Object.assign({}, this.defaultOptions, options)); + return cp.spawn(command, args || [], Object.assign({}, this.defaultOptions, { + ...options, + shell: true + })); } fork(modulePath: string, args?: string[], options?: cp.ForkOptions): cp.ChildProcess { @@ -50,7 +53,10 @@ export class ApplicationProcess { spawnBin(command: string, args: string[], options?: cp.SpawnOptions): cp.ChildProcess { const binPath = this.resolveBin(command); - return this.spawn(binPath, args, options); + return this.spawn(binPath, args, { + ...options, + shell: true + }); } protected resolveBin(command: string): string { diff --git a/dev-packages/cli/bin/theia-patch.js b/dev-packages/cli/bin/theia-patch.js index 5c9e7ad2ad828..a6a511fa9b2dd 100755 --- a/dev-packages/cli/bin/theia-patch.js +++ b/dev-packages/cli/bin/theia-patch.js @@ -19,7 +19,7 @@ const path = require('path'); const cp = require('child_process'); -const patchPackage= require.resolve('patch-package'); +const patchPackage = require.resolve('patch-package'); console.log(`patch-package = ${patchPackage}`); const patchesDir = path.join('.', 'node_modules', '@theia', 'cli', 'patches'); @@ -28,10 +28,9 @@ console.log(`patchesdir = ${patchesDir}`); const env = Object.assign({}, process.env); -const scriptProcess = cp.exec(`node ${patchPackage} --patch-dir "${patchesDir}"`, { +const scriptProcess = cp.exec(`node "${patchPackage}" --patch-dir "${patchesDir}"`, { cwd: process.cwd(), - env, - + env }); scriptProcess.stdout.pipe(process.stdout); diff --git a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts index eafa1a01ac386..7ff3c13e20fcc 100644 --- a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts +++ b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts @@ -124,10 +124,6 @@ export class NativeWebpackPlugin { const dllFile = require.resolve('node-pty/build/Release/winpty.dll'); const targetDllFile = path.join(targetDirectory, 'winpty.dll'); await this.copyExecutable(dllFile, targetDllFile); - } else { - const sourceFile = require.resolve('node-pty/build/Release/spawn-helper'); - const targetFile = path.join(targetDirectory, 'spawn-helper'); - await this.copyExecutable(sourceFile, targetFile); } } diff --git a/dev-packages/private-re-exports/src/package-re-exports.ts b/dev-packages/private-re-exports/src/package-re-exports.ts index 40aebc7a91db4..65f0fddfc4750 100644 --- a/dev-packages/private-re-exports/src/package-re-exports.ts +++ b/dev-packages/private-re-exports/src/package-re-exports.ts @@ -177,7 +177,8 @@ export class PackageReExports { ELECTRON_RUN_AS_NODE: '1' }, encoding: 'utf8', - stdio: ['ignore', 'pipe', 'inherit'] + stdio: ['ignore', 'pipe', 'inherit'], + shell: true }); const [packageRoot, reExports] = JSON.parse(stdout) as [string, ReExport[]]; return new PackageReExports(packageName, packageRoot, reExports); diff --git a/packages/process/package.json b/packages/process/package.json index 736f1b4b13ae0..ca4e0cd22b8c3 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -4,7 +4,7 @@ "description": "Theia process support.", "dependencies": { "@theia/core": "1.48.0", - "node-pty": "0.11.0-beta17", + "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" }, diff --git a/packages/process/src/node/pseudo-pty.ts b/packages/process/src/node/pseudo-pty.ts index 245a66dd2b211..b44cb5f7a126b 100644 --- a/packages/process/src/node/pseudo-pty.ts +++ b/packages/process/src/node/pseudo-pty.ts @@ -51,4 +51,6 @@ export class PseudoPty implements IPty { pause(): void { } resume(): void { } + + clear(): void { } } diff --git a/packages/terminal/src/node/shell-terminal-server.ts b/packages/terminal/src/node/shell-terminal-server.ts index 63eded6a30618..bb2270f162574 100644 --- a/packages/terminal/src/node/shell-terminal-server.ts +++ b/packages/terminal/src/node/shell-terminal-server.ts @@ -69,7 +69,9 @@ export class ShellTerminalServer extends BaseTerminalServer implements IShellTer private spawnAsPromised(command: string, args: string[]): Promise { return new Promise((resolve, reject) => { let stdout = ''; - const child = cp.spawn(command, args); + const child = cp.spawn(command, args, { + shell: true + }); if (child.pid) { child.stdout.on('data', (data: Buffer) => { stdout += data.toString(); diff --git a/yarn.lock b/yarn.lock index 489221288bada..8b45a3820e7f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8594,12 +8594,12 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-pty@0.11.0-beta17: - version "0.11.0-beta17" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta17.tgz#7df6a60dced6bf7a3a282b65cf51980c68954af6" - integrity sha512-JALo4LgYKmzmmXI23CIfS6DpCuno647YJpNg3RT6jCKTHWrt+RHeB6JAlb/pJG9dFNSeaiIAWD+0waEg2AzlfA== +node-pty@0.11.0-beta24: + version "0.11.0-beta24" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.11.0-beta24.tgz#084841017187656edaf14b459946c4a1d7cf8392" + integrity sha512-CzItw3hitX+wnpw9dHA/A+kcbV7ETNKrsyQJ+s0ZGzsu70+CSGuIGPLPfMnAc17vOrQktxjyRQfaqij75GVJFw== dependencies: - nan "^2.14.0" + nan "^2.17.0" node-releases@^2.0.14: version "2.0.14" From 59d0913d7aab5506cf02717b582571f61923e277 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Fri, 26 Apr 2024 16:06:30 +0200 Subject: [PATCH 196/441] Update DropMetada and documentPaste proposed API for 1.88 compatibility (#13632) contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- .../src/common/plugin-api-rpc-model.ts | 4 +- .../plugin-ext/src/plugin/plugin-context.ts | 4 + packages/plugin-ext/src/plugin/types-impl.ts | 62 ++++++-- .../src/theia.proposed.documentPaste.d.ts | 147 ++++++++++++++---- .../src/theia.proposed.dropMetadata.d.ts | 25 ++- 5 files changed, 183 insertions(+), 59 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts index 38fb3fbffec78..4ae9532f404e9 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc-model.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc-model.ts @@ -18,7 +18,7 @@ import * as theia from '@theia/plugin'; import type * as monaco from '@theia/monaco-editor-core'; import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering'; import { UriComponents } from './uri-components'; -import { CompletionItemTag, SnippetString } from '../plugin/types-impl'; +import { CompletionItemTag, DocumentPasteEditKind, SnippetString } from '../plugin/types-impl'; import { Event as TheiaEvent } from '@theia/core/lib/common/event'; import { URI } from '@theia/core/shared/vscode-uri'; import { SerializedRegExp } from './plugin-api-rpc'; @@ -330,7 +330,7 @@ export interface DocumentDropEdit { } export interface DocumentDropEditProviderMetadata { - readonly id: string; + readonly providedDropEditKinds?: readonly DocumentPasteEditKind[]; readonly dropMimeTypes: readonly string[]; } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index f405a67633964..a72d2af839842 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -198,6 +198,8 @@ import { TextMergeTabInput, WebviewEditorTabInput, DocumentPasteEdit, + DocumentPasteEditKind, + DocumentPasteTriggerKind, ExternalUriOpenerPriority, EditSessionIdentityMatch, TerminalOutputAnchor, @@ -1403,6 +1405,8 @@ export function createAPIFactory( TerminalOutputAnchor, TerminalExitReason, DocumentPasteEdit, + DocumentPasteEditKind, + DocumentPasteTriggerKind, ExternalUriOpenerPriority, TerminalQuickFixTerminalCommand, TerminalQuickFixOpener, diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index d46a05d63c4f4..4b647e8be9894 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1643,15 +1643,11 @@ export class DocumentLink { @es5ClassCompat export class DocumentDropEdit { - - id?: string; - - priority?: number; - - label?: string; - + title?: string; + kind: DocumentPasteEditKind; + handledMimeType?: string; + yieldTo?: ReadonlyArray; insertText: string | SnippetString; - additionalEdit?: WorkspaceEdit; constructor(insertText: string | SnippetString) { @@ -3740,19 +3736,57 @@ export class InteractiveWindowInput { // #endregion // #region DocumentPaste +export class DocumentPasteEditKind { + static Empty: DocumentPasteEditKind; + + constructor(public readonly value: string) { } + + /** @stubbed */ + append(...parts: string[]): CodeActionKind { + return CodeActionKind.Empty; + }; + + /** @stubbed */ + intersects(other: CodeActionKind): boolean { + return false; + } + + /** @stubbed */ + contains(other: CodeActionKind): boolean { + return false; + } +} +DocumentPasteEditKind.Empty = new DocumentPasteEditKind(''); + @es5ClassCompat export class DocumentPasteEdit { - constructor(insertText: string | SnippetString, id: string, label: string) { + constructor(insertText: string | SnippetString, title: string, kind: DocumentPasteEditKind) { this.insertText = insertText; - this.id = id; - this.label = label; + this.title = title; + this.kind = kind; } + title: string; + kind: DocumentPasteEditKind; insertText: string | SnippetString; additionalEdit?: WorkspaceEdit; - id: string; - label: string; - priority?: number; + yieldTo?: readonly DocumentPasteEditKind[]; } + +/** + * The reason why paste edits were requested. + */ +export enum DocumentPasteTriggerKind { + /** + * Pasting was requested as part of a normal paste operation. + */ + Automatic = 0, + + /** + * Pasting was requested by the user with the `paste as` command. + */ + PasteAs = 1, +} + // #endregion // #region DocumentPaste diff --git a/packages/plugin/src/theia.proposed.documentPaste.d.ts b/packages/plugin/src/theia.proposed.documentPaste.d.ts index 2289ab1686f6a..42cd879080079 100644 --- a/packages/plugin/src/theia.proposed.documentPaste.d.ts +++ b/packages/plugin/src/theia.proposed.documentPaste.d.ts @@ -23,54 +23,107 @@ export module '@theia/plugin' { /** - * Provider invoked when the user copies and pastes code. + * The reason why paste edits were requested. */ - export interface DocumentPasteEditProvider { + export enum DocumentPasteTriggerKind { + /** + * Pasting was requested as part of a normal paste operation. + */ + Automatic = 0, + + /** + * Pasting was requested by the user with the `paste as` command. + */ + PasteAs = 1, + } + + /** + * Additional information about the paste operation. + */ + + export interface DocumentPasteEditContext { + /** + * Requested kind of paste edits to return. + */ + readonly only: DocumentPasteEditKind | undefined; + + /** + * The reason why paste edits were requested. + */ + readonly triggerKind: DocumentPasteTriggerKind; + } + + /** + * Provider invoked when the user copies or pastes in a {@linkcode TextDocument}. + */ + interface DocumentPasteEditProvider { /** * Optional method invoked after the user copies text in a file. * - * During {@link prepareDocumentPaste}, an extension can compute metadata that is attached to - * a {@link DataTransfer} and is passed back to the provider in {@link provideDocumentPasteEdits}. + * This allows the provider to attach copy metadata to the {@link DataTransfer} + * which is then passed back to providers in {@linkcode provideDocumentPasteEdits}. + * + * Note that currently any changes to the {@linkcode DataTransfer} are isolated to the current editor session. + * This means that added metadata cannot be seen by other applications. * * @param document Document where the copy took place. - * @param ranges Ranges being copied in the `document`. - * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@link provideDocumentPasteEdits}. + * @param ranges Ranges being copied in {@linkcode document}. + * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@linkcode provideDocumentPasteEdits}. + * This object is only valid for the duration of this method. * @param token A cancellation token. + * + * @return Optional thenable that resolves when all changes to the `dataTransfer` are complete. */ prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable; /** * Invoked before the user pastes into a document. * - * In this method, extensions can return a workspace edit that replaces the standard pasting behavior. + * Returned edits can replace the standard pasting behavior. * * @param document Document being pasted into - * @param ranges Currently selected ranges in the document. - * @param dataTransfer The data transfer associated with the paste. + * @param ranges Range in the {@linkcode document} to paste into. + * @param dataTransfer The {@link DataTransfer data transfer} associated with the paste. This object is only valid for the duration of the paste operation. + * @param context Additional context for the paste. * @param token A cancellation token. * - * @return Optional workspace edit that applies the paste. Return undefined to use standard pasting. + * @return Set of potential {@link DocumentPasteEdit edits} that apply the paste. Return `undefined` to use standard pasting. */ - provideDocumentPasteEdits?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + provideDocumentPasteEdits?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, context: DocumentPasteEditContext, + token: CancellationToken): ProviderResult; + + /** + * Optional method which fills in the {@linkcode DocumentPasteEdit.additionalEdit} before the edit is applied. + * + * This is called once per edit and should be used if generating the complete edit may take a long time. + * Resolve can only be used to change {@link DocumentPasteEdit.additionalEdit}. + * + * @param pasteEdit The {@linkcode DocumentPasteEdit} to resolve. + * @param token A cancellation token. + * + * @returns The resolved paste edit or a thenable that resolves to such. It is OK to return the given + * `pasteEdit`. If no result is returned, the given `pasteEdit` is used. + */ + resolveDocumentPasteEdit?(pasteEdit: T, token: CancellationToken): ProviderResult; } /** - * An operation applied on paste + * An edit applied on paste. */ class DocumentPasteEdit { + /** * Human readable label that describes the edit. */ - label: string; + title: string; /** - * Controls the ordering or multiple paste edits. If this provider yield to edits, it will be shown lower in the list. + * {@link DocumentPasteEditKind Kind} of the edit. + * + * Used to identify specific types of edits. */ - yieldTo?: ReadonlyArray< - | { readonly extensionId: string; readonly providerId: string } - | { readonly mimeType: string } - >; + kind: DocumentPasteEditKind; /** * The text or snippet to insert at the pasted locations. @@ -83,43 +136,77 @@ export module '@theia/plugin' { additionalEdit?: WorkspaceEdit; /** - * @param insertText The text or snippet to insert at the pasted locations. + * Controls the ordering of paste edits provided by multiple providers. * - * TODO: Reverse args, but this will break existing consumers :( + * If this edit yields to another, it will be shown lower in the list of paste edit. */ - constructor(insertText: string | SnippetString, id: string, label: string); + yieldTo?: readonly DocumentPasteEditKind[]; + + /** + * Create a new paste edit. + * + * @param insertText The text or snippet to insert at the pasted locations. + * @param title Human readable label that describes the edit. + * @param kind {@link DocumentPasteEditKind Kind} of the edit. + */ + constructor(insertText: string | SnippetString, title: string, kind: DocumentPasteEditKind); + } + + /** + * TODO: Share with code action kind? + */ + class DocumentPasteEditKind { + static readonly Empty: DocumentPasteEditKind; + + // TODO: Add `Text` any others? + + private constructor(value: string); + + readonly value: string; + + append(...parts: string[]): CodeActionKind; + intersects(other: CodeActionKind): boolean; + contains(other: CodeActionKind): boolean; } interface DocumentPasteProviderMetadata { /** - * Identifies the provider. - * - * This id is used when users configure the default provider for paste. + * List of {@link DocumentPasteEditKind kinds} that the provider may return in {@linkcode DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits}. * - * This id should be unique within the extension but does not need to be unique across extensions. + * The provider will only be invoked when one of these kinds is being requested. For normal pasting, all providers will be invoked. */ - readonly id: string; + readonly providedPasteEditKinds: readonly DocumentPasteEditKind[]; /** - * Mime types that {@link DocumentPasteEditProvider.prepareDocumentPaste provideDocumentPasteEdits} may add on copy. + * Mime types that {@linkcode DocumentPasteEditProvider.prepareDocumentPaste prepareDocumentPaste} may add on copy. */ readonly copyMimeTypes?: readonly string[]; /** - * Mime types that {@link DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits} should be invoked for. + * Mime types that {@linkcode DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits} should be invoked for. * * This can either be an exact mime type such as `image/png`, or a wildcard pattern such as `image/*`. * * Use `text/uri-list` for resources dropped from the explorer or other tree views in the workbench. * - * Use `files` to indicate that the provider should be invoked if any {@link DataTransferFile files} are present in the {@link DataTransfer}. - * Note that {@link DataTransferFile} entries are only created when dropping content from outside the editor, such as + * Use `files` to indicate that the provider should be invoked if any {@link DataTransferFile files} are present in the {@linkcode DataTransfer}. + * Note that {@linkcode DataTransferFile} entries are only created when dropping content from outside the editor, such as * from the operating system. */ readonly pasteMimeTypes?: readonly string[]; } namespace languages { + /** + * Registers a new {@linkcode DocumentPasteEditProvider}. + * + * @param selector A selector that defines the documents this provider applies to. + * @param provider A paste editor provider. + * @param metadata Additional metadata about the provider. + * + * @returns A {@link Disposable} that unregisters this provider when disposed of. + * @stubbed + */ export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable; } } diff --git a/packages/plugin/src/theia.proposed.dropMetadata.d.ts b/packages/plugin/src/theia.proposed.dropMetadata.d.ts index a8f58d436f2f3..073ddc63db270 100644 --- a/packages/plugin/src/theia.proposed.dropMetadata.d.ts +++ b/packages/plugin/src/theia.proposed.dropMetadata.d.ts @@ -29,7 +29,16 @@ export module '@theia/plugin' { /** * Human readable label that describes the edit. */ - label?: string; + title?: string; + + /** + * {@link DocumentPasteEditKind Kind} of the edit. + * + * Used to identify specific types of edits. + * + * TODO: use own type? + */ + kind: DocumentPasteEditKind; /** * The mime type from the {@link DataTransfer} that this edit applies. @@ -39,21 +48,11 @@ export module '@theia/plugin' { /** * Controls the ordering or multiple paste edits. If this provider yield to edits, it will be shown lower in the list. */ - yieldTo?: ReadonlyArray< - | { readonly extensionId: string; readonly providerId: string } - | { readonly mimeType: string } - >; + yieldTo?: ReadonlyArray; } export interface DocumentDropEditProviderMetadata { - /** - * Identifies the provider. - * - * This id is used when users configure the default provider for drop. - * - * This id should be unique within the extension but does not need to be unique across extensions. - */ - readonly id: string; + readonly providedDropEditKinds?: readonly DocumentPasteEditKind[]; /** * List of data transfer types that the provider supports. From e718be03e139e22d08876170e66e61f8919cce5b Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Fri, 26 Apr 2024 16:07:40 +0200 Subject: [PATCH 197/441] Bump API version to 1.88.1 (#13646) Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + dev-packages/application-package/src/api.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a7b9d9a10cdd..2b928620a1162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [application-manager] Generate Extension Info in server application to avoid empty About Dialog [#13590](https://github.com/eclipse-theia/theia/pull/13590) - contributed on behalf of STMicroelectronics +- [application-package] bumped the default supported API from `1.87.2` to `1.88.1` [#13646](https://github.com/eclipse-theia/theia/pull/13646) - contributed on behalf of STMicroelectronics - [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) - [test] stub VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index d92a85ce42d43..52408c20aead5 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.87.2'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.88.1'; From f9942147fbe504c0461cde10268dbf18eccca2d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:55:33 +0200 Subject: [PATCH 198/441] Translation update for version 1.49.0 (#13654) Co-authored-by: jfaltermeier --- packages/core/i18n/nls.cs.json | 7 +++++-- packages/core/i18n/nls.de.json | 7 +++++-- packages/core/i18n/nls.es.json | 7 +++++-- packages/core/i18n/nls.fr.json | 7 +++++-- packages/core/i18n/nls.hu.json | 7 +++++-- packages/core/i18n/nls.it.json | 7 +++++-- packages/core/i18n/nls.ja.json | 7 +++++-- packages/core/i18n/nls.json | 7 +++++-- packages/core/i18n/nls.pl.json | 7 +++++-- packages/core/i18n/nls.pt-br.json | 7 +++++-- packages/core/i18n/nls.pt-pt.json | 7 +++++-- packages/core/i18n/nls.ru.json | 7 +++++-- packages/core/i18n/nls.zh-cn.json | 7 +++++-- 13 files changed, 65 insertions(+), 26 deletions(-) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 992275d56b70a..b469dbdb06c8f 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Upravit stav...", + "notebook.cell.changeToCode": "Změna buňky na kód", + "notebook.cell.changeToMarkdown": "Změna buňky na Mardown", "notebook.cell.insertMarkdownCellAbove": "Vložení buňky Markdown nad", "notebook.cell.insertMarkdownCellBelow": "Vložení buňky Markdown níže", - "notebook.cell.to-code-cell": "Změna buňky na kód", - "notebook.cell.to-markdown-cell": "Změna buňky na Mardown", "terminal:new:profile": "Vytvoření nového integrovaného terminálu z profilu", "terminal:profile:default": "Zvolte výchozí profil terminálu", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "skryté", "config.untrackedChanges.mixed": "smíšené", "config.untrackedChanges.separate": "samostatné stránky", + "dirtyDiff": { + "close": "Zavřít Změnit pohled" + }, "history": "Historie", "noRepositoryFound": "Nebylo nalezeno žádné úložiště", "unamend": "Odstranit změny", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index da230756de3f8..2a98cdd7f6d04 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Bedingung...", + "notebook.cell.changeToCode": "Zelle in Code ändern", + "notebook.cell.changeToMarkdown": "Zelle in Mardown ändern", "notebook.cell.insertMarkdownCellAbove": "Markdown-Zelle oben einfügen", "notebook.cell.insertMarkdownCellBelow": "Markdown-Zelle unten einfügen", - "notebook.cell.to-code-cell": "Zelle in Code ändern", - "notebook.cell.to-markdown-cell": "Zelle in Mardown ändern", "terminal:new:profile": "Neues integriertes Terminal aus einem Profil erstellen", "terminal:profile:default": "Wählen Sie das Standard-Terminalprofil", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "versteckt", "config.untrackedChanges.mixed": "gemischt", "config.untrackedChanges.separate": "getrennt", + "dirtyDiff": { + "close": "Schließen Ändern Peek-Ansicht" + }, "history": "Geschichte", "noRepositoryFound": "Kein Repository gefunden", "unamend": "Ändern", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 26ef471361279..eecd92e087dd9 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condición...", + "notebook.cell.changeToCode": "Cambiar celda por código", + "notebook.cell.changeToMarkdown": "Cambiar Celda a Mardown", "notebook.cell.insertMarkdownCellAbove": "Insertar celda Markdown arriba", "notebook.cell.insertMarkdownCellBelow": "Insertar celda Markdown abajo", - "notebook.cell.to-code-cell": "Cambiar celda por código", - "notebook.cell.to-markdown-cell": "Cambiar Celda a Mardown", "terminal:new:profile": "Crear un nuevo terminal integrado a partir de un perfil", "terminal:profile:default": "Elija el perfil de terminal por defecto", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "oculto", "config.untrackedChanges.mixed": "mixto", "config.untrackedChanges.separate": "separar", + "dirtyDiff": { + "close": "Cerrar Cambiar Vista Peek" + }, "history": "Historia", "noRepositoryFound": "No se ha encontrado ningún repositorio", "unamend": "Sin modificar", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index 29e091d0c35e7..ae2afdac91b2d 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Condition...", + "notebook.cell.changeToCode": "Changer la cellule en code", + "notebook.cell.changeToMarkdown": "Changer la cellule en Mardown", "notebook.cell.insertMarkdownCellAbove": "Insérer une cellule Markdown au-dessus", "notebook.cell.insertMarkdownCellBelow": "Insérer une cellule Markdown en dessous", - "notebook.cell.to-code-cell": "Changer la cellule en code", - "notebook.cell.to-markdown-cell": "Changer la cellule en Mardown", "terminal:new:profile": "Créer un nouveau terminal intégré à partir d'un profil", "terminal:profile:default": "Choisissez le profil du terminal par défaut", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "caché", "config.untrackedChanges.mixed": "mixte", "config.untrackedChanges.separate": "séparé", + "dirtyDiff": { + "close": "Fermer Modifier Regarder" + }, "history": "Histoire", "noRepositoryFound": "Aucun référentiel trouvé", "unamend": "Non modifié", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index 41c447d17dcc4..dea9c9f30191c 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Szerkesztési feltétel...", + "notebook.cell.changeToCode": "Cellát kódra váltani", + "notebook.cell.changeToMarkdown": "Cellát átváltoztatni Mardown-ra", "notebook.cell.insertMarkdownCellAbove": "Markdown-cella beszúrása fent", "notebook.cell.insertMarkdownCellBelow": "Markdown-cella beszúrása az alábbiakban", - "notebook.cell.to-code-cell": "Cellát kódra váltani", - "notebook.cell.to-markdown-cell": "Cellát átváltoztatni Mardown-ra", "terminal:new:profile": "Új integrált terminál létrehozása profilból", "terminal:profile:default": "Válassza ki az alapértelmezett terminálprofilt", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "rejtett", "config.untrackedChanges.mixed": "vegyes", "config.untrackedChanges.separate": "külön", + "dirtyDiff": { + "close": "Bezárás Változás Peek View" + }, "history": "Történelem", "noRepositoryFound": "Nem talált tárolóhely", "unamend": "Visszavonja a", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 0bc23922e3044..c2acda6f6aec9 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Modifica della condizione...", + "notebook.cell.changeToCode": "Cambia cella in codice", + "notebook.cell.changeToMarkdown": "Cambiare la cella in Mardown", "notebook.cell.insertMarkdownCellAbove": "Inserire la cella Markdown sopra", "notebook.cell.insertMarkdownCellBelow": "Inserire la cella Markdown in basso", - "notebook.cell.to-code-cell": "Cambia cella in codice", - "notebook.cell.to-markdown-cell": "Cambiare la cella in Mardown", "terminal:new:profile": "Creare un nuovo terminale integrato da un profilo", "terminal:profile:default": "Scegliere il profilo del terminale predefinito", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "nascosto", "config.untrackedChanges.mixed": "misto", "config.untrackedChanges.separate": "separato", + "dirtyDiff": { + "close": "Chiudi Cambia vista Peek" + }, "history": "Storia", "noRepositoryFound": "Nessun repository trovato", "unamend": "Unamend", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index e194152794a6b..41318df05a9ce 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "編集条件...", + "notebook.cell.changeToCode": "セルをコードに変更", + "notebook.cell.changeToMarkdown": "セルをマーダウンに変更", "notebook.cell.insertMarkdownCellAbove": "マークダウン・セルを上に挿入する", "notebook.cell.insertMarkdownCellBelow": "下にマークダウン・セルを挿入する", - "notebook.cell.to-code-cell": "セルをコードに変更", - "notebook.cell.to-markdown-cell": "セルをマーダウンに変更", "terminal:new:profile": "プロファイルから新しい統合端末を作成する", "terminal:profile:default": "デフォルトの端末プロファイルを選択", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "隠れ", "config.untrackedChanges.mixed": "合成", "config.untrackedChanges.separate": "別", + "dirtyDiff": { + "close": "クローズチェンジ・ピーキング・ビュー" + }, "history": "歴史", "noRepositoryFound": "リポジトリが見つからない", "unamend": "くり返す", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index 0552f9f7b2b01..ebac886adc918 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Edit Condition...", + "notebook.cell.changeToCode": "Change Cell to Code", + "notebook.cell.changeToMarkdown": "Change Cell to Mardown", "notebook.cell.insertMarkdownCellAbove": "Insert Markdown Cell Above", "notebook.cell.insertMarkdownCellBelow": "Insert Markdown Cell Below", - "notebook.cell.to-code-cell": "Change Cell to Code", - "notebook.cell.to-markdown-cell": "Change Cell to Mardown", "terminal:new:profile": "Create New Integrated Terminal from a Profile", "terminal:profile:default": "Choose the default Terminal Profile", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "hidden", "config.untrackedChanges.mixed": "mixed", "config.untrackedChanges.separate": "separate", + "dirtyDiff": { + "close": "Close Change Peek View" + }, "history": "History", "noRepositoryFound": "No repository found", "unamend": "Unamend", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index e8e79a9c7c28c..35c19def759d2 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Warunek edycji...", + "notebook.cell.changeToCode": "Zmień komórkę na kod", + "notebook.cell.changeToMarkdown": "Zmień komórkę na Mardown", "notebook.cell.insertMarkdownCellAbove": "Wstaw komórkę Markdown powyżej", "notebook.cell.insertMarkdownCellBelow": "Wstaw komórkę Markdown poniżej", - "notebook.cell.to-code-cell": "Zmień komórkę na kod", - "notebook.cell.to-markdown-cell": "Zmień komórkę na Mardown", "terminal:new:profile": "Tworzenie nowego zintegrowanego terminalu z profilu", "terminal:profile:default": "Wybierz domyślny profil terminala", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "ukryte", "config.untrackedChanges.mixed": "mieszane", "config.untrackedChanges.separate": "oddzielna strona", + "dirtyDiff": { + "close": "Zamknij widok podglądu zmian" + }, "history": "Historia", "noRepositoryFound": "Nie znaleziono repozytorium", "unamend": "Zmienić", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index d5f3a783a4b27..585af09cc8955 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condição...", + "notebook.cell.changeToCode": "Alterar célula para código", + "notebook.cell.changeToMarkdown": "Mudança de célula para Mardown", "notebook.cell.insertMarkdownCellAbove": "Inserir célula Markdown acima", "notebook.cell.insertMarkdownCellBelow": "Inserir célula Markdown abaixo", - "notebook.cell.to-code-cell": "Alterar célula para código", - "notebook.cell.to-markdown-cell": "Mudança de célula para Mardown", "terminal:new:profile": "Criar um novo terminal integrado a partir de um perfil", "terminal:profile:default": "Escolha o Perfil do Terminal padrão", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "oculto", "config.untrackedChanges.mixed": "misto", "config.untrackedChanges.separate": "separado", + "dirtyDiff": { + "close": "Fechar Alterar Vista panorâmica" + }, "history": "História", "noRepositoryFound": "Não foi encontrado nenhum repositório", "unamend": "Unamend", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 1ef5bb8b4e7c5..9b0ac58bfb796 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Editar condição...", + "notebook.cell.changeToCode": "Alterar célula para código", + "notebook.cell.changeToMarkdown": "Alterar Célula para Mardown", "notebook.cell.insertMarkdownCellAbove": "Inserir célula Markdown acima", "notebook.cell.insertMarkdownCellBelow": "Inserir célula Markdown abaixo", - "notebook.cell.to-code-cell": "Alterar célula para código", - "notebook.cell.to-markdown-cell": "Alterar Célula para Mardown", "terminal:new:profile": "Criar Novo Terminal Integrado a partir de um Perfil", "terminal:profile:default": "Escolha o Perfil Terminal padrão", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "escondido", "config.untrackedChanges.mixed": "misto", "config.untrackedChanges.separate": "em separado", + "dirtyDiff": { + "close": "Fechar Alterar Vista panorâmica" + }, "history": "História", "noRepositoryFound": "Não foi encontrado nenhum repositório", "unamend": "Unamend", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index d12889b106ac0..788a3251d5164 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "Изменить состояние...", + "notebook.cell.changeToCode": "Измените ячейку на код", + "notebook.cell.changeToMarkdown": "Измените ячейку на Мардаун", "notebook.cell.insertMarkdownCellAbove": "Вставить ячейку для уценки выше", "notebook.cell.insertMarkdownCellBelow": "Вставить ячейку для уценки ниже", - "notebook.cell.to-code-cell": "Измените ячейку на код", - "notebook.cell.to-markdown-cell": "Измените ячейку на Мардаун", "terminal:new:profile": "Создание нового интегрированного терминала из профиля", "terminal:profile:default": "Выберите профиль терминала по умолчанию", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "скрытый", "config.untrackedChanges.mixed": "смешанный", "config.untrackedChanges.separate": "отдельный", + "dirtyDiff": { + "close": "Закрыть Изменить Посмотреть" + }, "history": "История", "noRepositoryFound": "Репозиторий не найден", "unamend": "не изменять", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 15124a791a7ff..fd5dc52c42350 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -1,9 +1,9 @@ { "debug.breakpoint.editCondition": "编辑条件...", + "notebook.cell.changeToCode": "将单元格更改为代码", + "notebook.cell.changeToMarkdown": "将 Cell 改为 Mardown", "notebook.cell.insertMarkdownCellAbove": "在上方插入 Markdown 单元格", "notebook.cell.insertMarkdownCellBelow": "在下面插入 Markdown 单元格", - "notebook.cell.to-code-cell": "将单元格更改为代码", - "notebook.cell.to-markdown-cell": "将 Cell 改为 Mardown", "terminal:new:profile": "从配置文件中创建新的集成终端", "terminal:profile:default": "选择默认的终端配置文件", "theia": { @@ -410,6 +410,9 @@ "config.untrackedChanges.hidden": "隐藏的", "config.untrackedChanges.mixed": "混合的", "config.untrackedChanges.separate": "分开", + "dirtyDiff": { + "close": "关闭 更改 窥视" + }, "history": "历史", "noRepositoryFound": "没有找到存储库", "unamend": "撤销", From 90c6334f86688a4b53141dc911e41c2dae79584b Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Mon, 29 Apr 2024 11:23:11 +0200 Subject: [PATCH 199/441] feat: splash screen support for Electron (#13505) Enhances the ElectronMainApplication to optionally render a splash screen until the frontend is ready. The splash screen can be configured via the application config object "theia.frontend.config.electron.splashScreenOptions". Mandatory is the option "content" which specifies a relative path from the application root to the content of the splash screen. Optionally "width", "height", "minDuration" and "maxDuration" can be handed over too. Configures the Electron example application to show a Theia logo splash screen. Implements #13410 Contributed on behalf of Pragmatiqu IT GmbH --- CHANGELOG.md | 1 + .../src/application-props.ts | 37 +++++- examples/electron/package.json | 8 +- examples/electron/resources/theia-logo.svg | 32 ++++++ .../electron-main-application.ts | 108 ++++++++++++++++-- .../electron-main/theia-electron-window.ts | 10 +- 6 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 examples/electron/resources/theia-logo.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b928620a1162..6493c519bac06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [application-manager] Generate Extension Info in server application to avoid empty About Dialog [#13590](https://github.com/eclipse-theia/theia/pull/13590) - contributed on behalf of STMicroelectronics - [application-package] bumped the default supported API from `1.87.2` to `1.88.1` [#13646](https://github.com/eclipse-theia/theia/pull/13646) - contributed on behalf of STMicroelectronics +- [core] Splash Screen Support for Electron [#13505](https://github.com/eclipse-theia/theia/pull/13505) - contributed on behalf of Pragmatiqu IT GmbH - [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) - [test] stub VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index a443a60df679d..91de09fc319b7 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -33,8 +33,36 @@ export type ElectronFrontendApplicationConfig = RequiredRecursive + + + + + + + + + + + + + + + diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 9c0f410fba9ba..e97114df94c8c 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -22,16 +22,16 @@ import { AddressInfo } from 'net'; import { promises as fs } from 'fs'; import { existsSync, mkdirSync } from 'fs-extra'; import { fork, ForkOptions } from 'child_process'; -import { DefaultTheme, FrontendApplicationConfig } from '@theia/application-package/lib/application-props'; +import { DefaultTheme, ElectronFrontendApplicationConfig, FrontendApplicationConfig } from '@theia/application-package/lib/application-props'; import URI from '../common/uri'; import { FileUri } from '../common/file-uri'; -import { Deferred } from '../common/promise-util'; +import { Deferred, timeout } from '../common/promise-util'; import { MaybePromise } from '../common/types'; import { ContributionProvider } from '../common/contribution-provider'; import { ElectronSecurityTokenService } from './electron-security-token-service'; import { ElectronSecurityToken } from '../electron-common/electron-token'; import Storage = require('electron-store'); -import { Disposable, DisposableCollection, isOSX, isWindows } from '../common'; +import { CancellationTokenSource, Disposable, DisposableCollection, isOSX, isWindows } from '../common'; import { DEFAULT_WINDOW_HASH, WindowSearchParams } from '../common/window'; import { TheiaBrowserWindowOptions, TheiaElectronWindow, TheiaElectronWindowFactory } from './theia-electron-window'; import { ElectronMainApplicationGlobals } from './electron-main-constants'; @@ -182,6 +182,7 @@ export class ElectronMainApplication { protected windows = new Map(); protected restarting = false; + /** Used to temporarily store the reference to an early created main window */ protected initialWindow?: BrowserWindow; get config(): FrontendApplicationConfig { @@ -287,18 +288,110 @@ export class ElectronMainApplication { return this.didUseNativeWindowFrameOnStart.get(webContents.id) ? 'native' : 'custom'; } + protected async determineSplashScreenBounds(initialWindowBounds: { x: number, y: number, width: number, height: number }): + Promise<{ x: number, y: number, width: number, height: number }> { + const splashScreenOptions = this.getSplashScreenOptions(); + const width = splashScreenOptions?.width ?? 640; + const height = splashScreenOptions?.height ?? 480; + + // determine the screen on which to show the splash screen via the center of the window to show + const windowCenterPoint = { x: initialWindowBounds.x + (initialWindowBounds.width / 2), y: initialWindowBounds.y + (initialWindowBounds.height / 2) }; + const { bounds } = screen.getDisplayNearestPoint(windowCenterPoint); + + // place splash screen center of screen + const screenCenterPoint = { x: bounds.x + (bounds.width / 2), y: bounds.y + (bounds.height / 2) }; + const x = screenCenterPoint.x - (width / 2); + const y = screenCenterPoint.y - (height / 2); + + return { + x, y, width, height + }; + } + + protected isShowWindowEarly(): boolean { + return !!this.config.electron.showWindowEarly && + !('THEIA_ELECTRON_NO_EARLY_WINDOW' in process.env && process.env.THEIA_ELECTRON_NO_EARLY_WINDOW === '1'); + } + protected showInitialWindow(): void { - if (this.config.electron.showWindowEarly && - !('THEIA_ELECTRON_NO_EARLY_WINDOW' in process.env && process.env.THEIA_ELECTRON_NO_EARLY_WINDOW === '1')) { - console.log('Showing main window early'); + if (this.isShowWindowEarly() || this.isShowSplashScreen()) { app.whenReady().then(async () => { const options = await this.getLastWindowOptions(); + // If we want to show a splash screen, don't auto open the main window + if (this.isShowSplashScreen()) { + options.preventAutomaticShow = true; + } this.initialWindow = await this.createWindow({ ...options }); - this.initialWindow.show(); + + if (this.isShowSplashScreen()) { + console.log('Showing splash screen'); + this.configureAndShowSplashScreen(this.initialWindow); + } + + // Show main window early if windows shall be shown early and splash screen is not configured + if (this.isShowWindowEarly() && !this.isShowSplashScreen()) { + console.log('Showing main window early'); + this.initialWindow.show(); + } }); } } + protected async configureAndShowSplashScreen(mainWindow: BrowserWindow): Promise { + const splashScreenOptions = this.getSplashScreenOptions()!; + console.debug('SplashScreen options', splashScreenOptions); + + const splashScreenBounds = await this.determineSplashScreenBounds(mainWindow.getBounds()); + const splashScreenWindow = new BrowserWindow({ + ...splashScreenBounds, + frame: false, + alwaysOnTop: true, + show: false + }); + + if (this.isShowWindowEarly()) { + console.log('Showing splash screen early'); + splashScreenWindow.show(); + } else { + splashScreenWindow.on('ready-to-show', () => { + splashScreenWindow.show(); + }); + } + + splashScreenWindow.loadFile(path.resolve(this.globals.THEIA_APP_PROJECT_PATH, splashScreenOptions.content!).toString()); + + // close splash screen and show main window once frontend is ready or a timeout is hit + const cancelTokenSource = new CancellationTokenSource(); + const minTime = timeout(splashScreenOptions.minDuration ?? 0, cancelTokenSource.token); + const maxTime = timeout(splashScreenOptions.maxDuration ?? 30000, cancelTokenSource.token); + + const showWindowAndCloseSplashScreen = () => { + cancelTokenSource.cancel(); + if (!mainWindow.isVisible()) { + mainWindow.show(); + } + splashScreenWindow.close(); + }; + TheiaRendererAPI.onApplicationStateChanged(mainWindow.webContents, state => { + if (state === 'ready') { + minTime.then(() => showWindowAndCloseSplashScreen()); + } + }); + maxTime.then(() => showWindowAndCloseSplashScreen()); + return splashScreenWindow; + } + + protected isShowSplashScreen(): boolean { + return typeof this.config.electron.splashScreenOptions === 'object' && !!this.config.electron.splashScreenOptions.content; + } + + protected getSplashScreenOptions(): ElectronFrontendApplicationConfig.SplashScreenOptions | undefined { + if (this.isShowSplashScreen()) { + return this.config.electron.splashScreenOptions; + } + return undefined; + } + /** * Use this rather than creating `BrowserWindow` instances from scratch, since some security parameters need to be set, this method will do it. * @@ -316,6 +409,7 @@ export class ElectronMainApplication { electronWindow.window.on('focus', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'focus')); this.attachSaveWindowState(electronWindow.window); this.configureNativeSecondaryWindowCreation(electronWindow.window); + return electronWindow.window; } diff --git a/packages/core/src/electron-main/theia-electron-window.ts b/packages/core/src/electron-main/theia-electron-window.ts index 17c82fa9a4511..985fc01220fbc 100644 --- a/packages/core/src/electron-main/theia-electron-window.ts +++ b/packages/core/src/electron-main/theia-electron-window.ts @@ -37,6 +37,12 @@ export interface TheiaBrowserWindowOptions extends BrowserWindowConstructorOptio * in which case we want to invalidate the stored options and use the default options instead. */ screenLayout?: string; + /** + * By default, the window will be shown as soon as the content is ready to render. + * This can be prevented by handing over preventAutomaticShow: `true`. + * Use this for fine-grained control over when to show the window, e.g. to coordinate with a splash screen. + */ + preventAutomaticShow?: boolean; } export const TheiaBrowserWindowOptions = Symbol('TheiaBrowserWindowOptions'); @@ -76,7 +82,9 @@ export class TheiaElectronWindow { protected init(): void { this._window = new BrowserWindow(this.options); this._window.setMenuBarVisibility(false); - this.attachReadyToShow(); + if (!this.options.preventAutomaticShow) { + this.attachReadyToShow(); + } this.restoreMaximizedState(); this.attachCloseListeners(); this.trackApplicationState(); From d413dadd87a70fce1c80df7eb22d9d2529cc129f Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 29 Apr 2024 14:08:23 +0200 Subject: [PATCH 200/441] Fix unix terminals for bundled apps (#13658) --- .../native-webpack-plugin/src/native-webpack-plugin.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts index 7ff3c13e20fcc..eafa1a01ac386 100644 --- a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts +++ b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts @@ -124,6 +124,10 @@ export class NativeWebpackPlugin { const dllFile = require.resolve('node-pty/build/Release/winpty.dll'); const targetDllFile = path.join(targetDirectory, 'winpty.dll'); await this.copyExecutable(dllFile, targetDllFile); + } else { + const sourceFile = require.resolve('node-pty/build/Release/spawn-helper'); + const targetFile = path.join(targetDirectory, 'spawn-helper'); + await this.copyExecutable(sourceFile, targetFile); } } From 2560a8fa44efc822e89654a400260d98dceb5d00 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 29 Apr 2024 14:29:32 +0200 Subject: [PATCH 201/441] core: update re-exports for 1.49.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 6ebe084fce222..44750b88a67eb 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.48.0`](https://www.npmjs.com/package/@theia/application-package/v/1.48.0)) - - `@theia/request` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.48.0`](https://www.npmjs.com/package/@theia/request/v/1.48.0)) + - `@theia/application-package` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) + - `@theia/request` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From 92990fd46fe88d5fb36286527e6a9853924855e6 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 29 Apr 2024 14:29:40 +0200 Subject: [PATCH 202/441] v1.49.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 18 ++-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 90 ++++++++-------- examples/browser/package.json | 102 +++++++++--------- examples/electron/package.json | 100 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 28 ++--- packages/dev-container/package.json | 16 +-- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 18 ++-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 +++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 12 +-- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 12 +-- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 18 ++-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 72 files changed, 524 insertions(+), 524 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index d5783d1051d8a..5f153fa5ef03f 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.48.0", - "@theia/ffmpeg": "1.48.0", - "@theia/native-webpack-plugin": "1.48.0", + "@theia/application-package": "1.49.0", + "@theia/ffmpeg": "1.49.0", + "@theia/native-webpack-plugin": "1.49.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 25bee70100b5f..10cc21e0a8240 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.48.0", + "@theia/request": "1.49.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 4bd2ad9878d22..e8283d617d5f4 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,13 +32,12 @@ "clean": "theiaext clean" }, "dependencies": { - "patch-package": "^8.0.0", - "@theia/application-manager": "1.48.0", - "@theia/application-package": "1.48.0", - "@theia/ffmpeg": "1.48.0", - "@theia/localization-manager": "1.48.0", - "@theia/ovsx-client": "1.48.0", - "@theia/request": "1.48.0", + "@theia/application-manager": "1.49.0", + "@theia/application-package": "1.49.0", + "@theia/ffmpeg": "1.49.0", + "@theia/localization-manager": "1.49.0", + "@theia/ovsx-client": "1.49.0", + "@theia/request": "1.49.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", @@ -51,6 +50,7 @@ "limiter": "^2.1.0", "log-update": "^4.0.0", "mocha": "^10.1.0", + "patch-package": "^8.0.0", "puppeteer": "19.7.2", "puppeteer-core": "19.7.2", "puppeteer-to-istanbul": "1.4.0", @@ -64,4 +64,4 @@ "@types/node-fetch": "^2.5.7", "@types/proxy-from-env": "^1.0.1" } -} \ No newline at end of file +} diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index adebabdf71a11..ebbfba69ed3ab 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index 42819cde08eb2..7238863aff6a2 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~4.5.5" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index dd6d3b3bc8e0b..6502832c2165a 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.48.0", + "version": "1.49.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index bfce1cb9154e3..fb6d6a0cbf67d 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.48.0", + "@theia/request": "1.49.0", "semver": "^7.5.4", "tslib": "^2.6.2" } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index bc80e10a6a5c5..c96faee5ffd49 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.48.0", + "version": "1.49.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.48.0", - "@theia/ext-scripts": "1.48.0", - "@theia/re-exports": "1.48.0", + "@theia/core": "1.49.0", + "@theia/ext-scripts": "1.49.0", + "@theia/re-exports": "1.49.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index e2f4ca82fdcf1..896541e54e442 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.48.0", + "version": "1.49.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index 7b8dffbe1e572..e532099c08618 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index 0b25cf1bc7068..adddf0c9ac13e 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index aef5d1a762cfb..ce21ee47c43ee 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/plugin-ext-headless": "1.48.0" + "@theia/core": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/plugin-ext-headless": "1.49.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 2aac38f2a1459..cb9a09b45b939 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.48.0", - "@theia/ovsx-client": "1.48.0", - "@theia/search-in-workspace": "1.48.0", - "@theia/test": "1.48.0", - "@theia/toolbar": "1.48.0", - "@theia/vsx-registry": "1.48.0", - "@theia/workspace": "1.48.0" + "@theia/output": "1.49.0", + "@theia/ovsx-client": "1.49.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/test": "1.49.0", + "@theia/toolbar": "1.49.0", + "@theia/vsx-registry": "1.49.0", + "@theia/workspace": "1.49.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 03ec3d04f74c8..95dc5c56a778c 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.48.0" + "@theia/core": "1.49.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index fdf75ae2fe9fd..d9617d165a0ba 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.48.0", + "version": "1.49.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,49 +15,49 @@ } }, "dependencies": { - "@theia/api-samples": "1.48.0", - "@theia/bulk-edit": "1.48.0", - "@theia/callhierarchy": "1.48.0", - "@theia/console": "1.48.0", - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/editor-preview": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/getting-started": "1.48.0", - "@theia/git": "1.48.0", - "@theia/keymaps": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/memory-inspector": "1.48.0", - "@theia/messages": "1.48.0", - "@theia/metrics": "1.48.0", - "@theia/mini-browser": "1.48.0", - "@theia/monaco": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/outline-view": "1.48.0", - "@theia/output": "1.48.0", - "@theia/plugin-dev": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/plugin-ext-vscode": "1.48.0", - "@theia/plugin-metrics": "1.48.0", - "@theia/preferences": "1.48.0", - "@theia/preview": "1.48.0", - "@theia/process": "1.48.0", - "@theia/property-view": "1.48.0", - "@theia/scm": "1.48.0", - "@theia/scm-extra": "1.48.0", - "@theia/search-in-workspace": "1.48.0", - "@theia/secondary-window": "1.48.0", - "@theia/task": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/timeline": "1.48.0", - "@theia/toolbar": "1.48.0", - "@theia/typehierarchy": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/vsx-registry": "1.48.0", - "@theia/workspace": "1.48.0" + "@theia/api-samples": "1.49.0", + "@theia/bulk-edit": "1.49.0", + "@theia/callhierarchy": "1.49.0", + "@theia/console": "1.49.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/editor-preview": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/getting-started": "1.49.0", + "@theia/git": "1.49.0", + "@theia/keymaps": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/memory-inspector": "1.49.0", + "@theia/messages": "1.49.0", + "@theia/metrics": "1.49.0", + "@theia/mini-browser": "1.49.0", + "@theia/monaco": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/outline-view": "1.49.0", + "@theia/output": "1.49.0", + "@theia/plugin-dev": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/plugin-ext-vscode": "1.49.0", + "@theia/plugin-metrics": "1.49.0", + "@theia/preferences": "1.49.0", + "@theia/preview": "1.49.0", + "@theia/process": "1.49.0", + "@theia/property-view": "1.49.0", + "@theia/scm": "1.49.0", + "@theia/scm-extra": "1.49.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/secondary-window": "1.49.0", + "@theia/task": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/timeline": "1.49.0", + "@theia/toolbar": "1.49.0", + "@theia/typehierarchy": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/vsx-registry": "1.49.0", + "@theia/workspace": "1.49.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -73,6 +73,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.48.0" + "@theia/cli": "1.49.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index 4c026ac437477..d7e6cfec6a352 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.48.0", + "version": "1.49.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,55 +20,55 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.48.0", - "@theia/api-samples": "1.48.0", - "@theia/bulk-edit": "1.48.0", - "@theia/callhierarchy": "1.48.0", - "@theia/console": "1.48.0", - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", - "@theia/dev-container": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/editor-preview": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/getting-started": "1.48.0", - "@theia/git": "1.48.0", - "@theia/keymaps": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/memory-inspector": "1.48.0", - "@theia/messages": "1.48.0", - "@theia/metrics": "1.48.0", - "@theia/mini-browser": "1.48.0", - "@theia/monaco": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/notebook": "1.48.0", - "@theia/outline-view": "1.48.0", - "@theia/output": "1.48.0", - "@theia/plugin-dev": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/plugin-ext-headless": "1.48.0", - "@theia/plugin-ext-vscode": "1.48.0", - "@theia/plugin-metrics": "1.48.0", - "@theia/preferences": "1.48.0", - "@theia/preview": "1.48.0", - "@theia/process": "1.48.0", - "@theia/property-view": "1.48.0", - "@theia/remote": "1.48.0", - "@theia/scm": "1.48.0", - "@theia/scm-extra": "1.48.0", - "@theia/search-in-workspace": "1.48.0", - "@theia/secondary-window": "1.48.0", - "@theia/task": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/test": "1.48.0", - "@theia/timeline": "1.48.0", - "@theia/toolbar": "1.48.0", - "@theia/typehierarchy": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/vsx-registry": "1.48.0", - "@theia/workspace": "1.48.0" + "@theia/api-provider-sample": "1.49.0", + "@theia/api-samples": "1.49.0", + "@theia/bulk-edit": "1.49.0", + "@theia/callhierarchy": "1.49.0", + "@theia/console": "1.49.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", + "@theia/dev-container": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/editor-preview": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/getting-started": "1.49.0", + "@theia/git": "1.49.0", + "@theia/keymaps": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/memory-inspector": "1.49.0", + "@theia/messages": "1.49.0", + "@theia/metrics": "1.49.0", + "@theia/mini-browser": "1.49.0", + "@theia/monaco": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/notebook": "1.49.0", + "@theia/outline-view": "1.49.0", + "@theia/output": "1.49.0", + "@theia/plugin-dev": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/plugin-ext-headless": "1.49.0", + "@theia/plugin-ext-vscode": "1.49.0", + "@theia/plugin-metrics": "1.49.0", + "@theia/preferences": "1.49.0", + "@theia/preview": "1.49.0", + "@theia/process": "1.49.0", + "@theia/property-view": "1.49.0", + "@theia/remote": "1.49.0", + "@theia/scm": "1.49.0", + "@theia/scm-extra": "1.49.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/secondary-window": "1.49.0", + "@theia/task": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/test": "1.49.0", + "@theia/timeline": "1.49.0", + "@theia/toolbar": "1.49.0", + "@theia/typehierarchy": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/vsx-registry": "1.49.0", + "@theia/workspace": "1.49.0" }, "scripts": { "clean": "theia clean", @@ -91,6 +91,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.48.0" + "@theia/cli": "1.49.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 923fdb81518f8..a310f569423f0 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.48.0", + "version": "1.49.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -26,54 +26,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.48.0", - "@theia/api-samples": "1.48.0", - "@theia/bulk-edit": "1.48.0", - "@theia/callhierarchy": "1.48.0", - "@theia/console": "1.48.0", - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", - "@theia/dev-container": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/editor-preview": "1.48.0", - "@theia/electron": "1.48.0", - "@theia/external-terminal": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/getting-started": "1.48.0", - "@theia/git": "1.48.0", - "@theia/keymaps": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/memory-inspector": "1.48.0", - "@theia/messages": "1.48.0", - "@theia/metrics": "1.48.0", - "@theia/mini-browser": "1.48.0", - "@theia/monaco": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/outline-view": "1.48.0", - "@theia/output": "1.48.0", - "@theia/plugin-dev": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/plugin-ext-headless": "1.48.0", - "@theia/plugin-ext-vscode": "1.48.0", - "@theia/preferences": "1.48.0", - "@theia/preview": "1.48.0", - "@theia/process": "1.48.0", - "@theia/property-view": "1.48.0", - "@theia/remote": "1.48.0", - "@theia/scm": "1.48.0", - "@theia/scm-extra": "1.48.0", - "@theia/search-in-workspace": "1.48.0", - "@theia/secondary-window": "1.48.0", - "@theia/task": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/timeline": "1.48.0", - "@theia/toolbar": "1.48.0", - "@theia/typehierarchy": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/vsx-registry": "1.48.0", - "@theia/workspace": "1.48.0" + "@theia/api-provider-sample": "1.49.0", + "@theia/api-samples": "1.49.0", + "@theia/bulk-edit": "1.49.0", + "@theia/callhierarchy": "1.49.0", + "@theia/console": "1.49.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", + "@theia/dev-container": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/editor-preview": "1.49.0", + "@theia/electron": "1.49.0", + "@theia/external-terminal": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/getting-started": "1.49.0", + "@theia/git": "1.49.0", + "@theia/keymaps": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/memory-inspector": "1.49.0", + "@theia/messages": "1.49.0", + "@theia/metrics": "1.49.0", + "@theia/mini-browser": "1.49.0", + "@theia/monaco": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/outline-view": "1.49.0", + "@theia/output": "1.49.0", + "@theia/plugin-dev": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/plugin-ext-headless": "1.49.0", + "@theia/plugin-ext-vscode": "1.49.0", + "@theia/preferences": "1.49.0", + "@theia/preview": "1.49.0", + "@theia/process": "1.49.0", + "@theia/property-view": "1.49.0", + "@theia/remote": "1.49.0", + "@theia/scm": "1.49.0", + "@theia/scm-extra": "1.49.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/secondary-window": "1.49.0", + "@theia/task": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/timeline": "1.49.0", + "@theia/toolbar": "1.49.0", + "@theia/typehierarchy": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/vsx-registry": "1.49.0", + "@theia/workspace": "1.49.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -91,7 +91,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.48.0", + "@theia/cli": "1.49.0", "electron": "^23.2.4" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index c1c53206a572a..cd0e04f845015 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.48.0", + "version": "1.49.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index f2f9f7afd0002..715a667ab608b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.48.0", + "version": "1.49.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 35ccebc2fae66..a63c50027fa26 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.48.0", + "@theia/workspace": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index 6bc112ef41042..ff55e82e52ca8 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 16f3b873acc76..c3ba587ce32f2 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index d692c66fc789b..f127f61e37f06 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.48.0", - "@theia/request": "1.48.0", + "@theia/application-package": "1.49.0", + "@theia/request": "1.49.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -206,8 +206,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", - "@theia/re-exports": "1.48.0", + "@theia/ext-scripts": "1.49.0", + "@theia/re-exports": "1.49.0", "minimist": "^1.2.0" }, "nyc": { diff --git a/packages/debug/package.json b/packages/debug/package.json index f3ab2c9bd9ac6..d25c0532758b3 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,21 +1,21 @@ { "name": "@theia/debug", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.48.0", - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/console": "1.49.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.48.0", - "@theia/process": "1.48.0", - "@theia/task": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/output": "1.49.0", + "@theia/process": "1.49.0", + "@theia/task": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/workspace": "1.49.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -58,7 +58,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index 72c816c517e54..d51c2170b0a11 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,15 +1,15 @@ { "name": "@theia/dev-container", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/output": "1.48.0", - "@theia/remote": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/output": "1.49.0", + "@theia/remote": "1.49.0", + "@theia/workspace": "1.49.0", "dockerode": "^4.0.2", - "uuid": "^8.0.0", - "jsonc-parser": "^2.2.0" + "jsonc-parser": "^2.2.0", + "uuid": "^8.0.0" }, "publishConfig": { "access": "public" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index 8f4314adb39cd..ff800ee986882 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/navigator": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/navigator": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index b65a872605af6..4e76681cb87f7 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/variable-resolver": "1.48.0", + "@theia/core": "1.49.0", + "@theia/variable-resolver": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index f259a338c9f2b..0ace72c8a205b 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", - "@theia/re-exports": "1.48.0" + "@theia/ext-scripts": "1.49.0", + "@theia/re-exports": "1.49.0" }, "peerDependencies": { "electron": "^23.2.4" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index ca2b90a94d2b3..a4054ef33ba19 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/workspace": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 0f6d4f1dcb2b8..052c376758d58 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/process": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/process": "1.49.0", + "@theia/workspace": "1.49.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index d07a2453577cf..3d8a65d90ba1f 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -73,7 +73,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index fd99b9f9d23ce..0992a56fd6767 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/keymaps": "1.48.0", - "@theia/preview": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/keymaps": "1.49.0", + "@theia/preview": "1.49.0", + "@theia/workspace": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index d7e9ddf1c8038..2c9a0ceded313 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.48.0", - "@theia/scm": "1.48.0", - "@theia/scm-extra": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/navigator": "1.49.0", + "@theia/scm": "1.49.0", + "@theia/scm-extra": "1.49.0", + "@theia/workspace": "1.49.0", "@types/diff": "^3.2.2", "@types/p-queue": "^2.3.1", "diff": "^3.4.0", @@ -67,7 +67,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index 855cd1da96bd2..5879738cc886e 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.48.0", - "@theia/userstorage": "1.48.0", + "@theia/preferences": "1.49.0", + "@theia/userstorage": "1.49.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index d885ffb474502..0815d6cc2fc74 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/workspace": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 97f4a002bc94c..304b95c255c43 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index 1d74195290d3e..b9e20be90a58f 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 296a00aa7783d..d6d836f49753e 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 1c78b7ff7695b..5f18723ddd0c7 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index fd67ee2d6cef1..885304f399a11 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/markers": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/markers": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/outline-view": "1.49.0", + "@theia/workspace": "1.49.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index 5b846f477b597..da73f3da4a724 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/workspace": "1.49.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index f96708192efc9..4230c133036ce 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,14 +1,14 @@ { "name": "@theia/notebook", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", - "@theia/outline-view": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", + "@theia/outline-view": "1.49.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, @@ -45,9 +45,9 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", - "@types/vscode-notebook-renderer": "^1.72.0", - "@types/markdown-it": "^12.2.3" + "@theia/ext-scripts": "1.49.0", + "@types/markdown-it": "^12.2.3", + "@types/vscode-notebook-renderer": "^1.72.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index 54934ab695de8..a2be8bf2631e9 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index aae39f23c0454..84100459dd984 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index c51e7180b6e9c..7939a13bab50c 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/output": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/output": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/workspace": "1.49.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index 68792a0be9560..d881283142dd8 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/terminal": "1.48.0", + "@theia/core": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/terminal": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index a753581864f20..e679a78cf59f6 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,22 +1,22 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.48.0", - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/callhierarchy": "1.49.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.48.0", - "@theia/plugin": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/typehierarchy": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/workspace": "1.48.0", - "@theia/outline-view": "1.48.0", + "@theia/navigator": "1.49.0", + "@theia/outline-view": "1.49.0", + "@theia/plugin": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/typehierarchy": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/workspace": "1.49.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 69bfae2bf80a1..f88dff0ba7958 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.48.0", - "@theia/callhierarchy": "1.48.0", - "@theia/console": "1.48.0", - "@theia/core": "1.48.0", - "@theia/debug": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/editor-preview": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/messages": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/bulk-edit": "1.49.0", + "@theia/callhierarchy": "1.49.0", + "@theia/console": "1.49.0", + "@theia/core": "1.49.0", + "@theia/debug": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/editor-preview": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/messages": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.48.0", - "@theia/notebook": "1.48.0", - "@theia/output": "1.48.0", - "@theia/plugin": "1.48.0", - "@theia/preferences": "1.48.0", - "@theia/scm": "1.48.0", - "@theia/search-in-workspace": "1.48.0", - "@theia/task": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/timeline": "1.48.0", - "@theia/typehierarchy": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/workspace": "1.48.0", - "@theia/test": "1.48.0", + "@theia/navigator": "1.49.0", + "@theia/notebook": "1.49.0", + "@theia/output": "1.49.0", + "@theia/plugin": "1.49.0", + "@theia/preferences": "1.49.0", + "@theia/scm": "1.49.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/task": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/test": "1.49.0", + "@theia/timeline": "1.49.0", + "@theia/typehierarchy": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/workspace": "1.49.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 54d9c74c58a6a..c843d7bd50184 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.48.0", - "@theia/metrics": "1.48.0", + "@theia/core": "1.49.0", + "@theia/metrics": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.48.0", - "@theia/plugin-ext": "1.48.0", + "@theia/plugin": "1.49.0", + "@theia/plugin-ext": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 41e305a2c5ce0..36e44f7bcd3c4 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 4752a78a050e9..85fbaa30a8357 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/userstorage": "1.49.0", + "@theia/workspace": "1.49.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index 760bbdd78d620..e21dd7cb3d48b 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/mini-browser": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/mini-browser": "1.49.0", + "@theia/monaco": "1.49.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index ca4e0cd22b8c3..0a83f10498a15 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index e2c6576078bb3..a82e63a3e76a7 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index f371be6ffa48c..624e74771e02e 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -12,10 +12,10 @@ "decompress-unzip": "^4.0.1", "express-http-proxy": "^1.6.3", "glob": "^8.1.0", - "ssh2": "^1.15.0", - "ssh2-sftp-client": "^9.1.0", "socket.io": "^4.5.3", "socket.io-client": "^4.5.3", + "ssh2": "^1.15.0", + "ssh2-sftp-client": "^9.1.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index 66ae1944bdf76..e346b7b069682 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/scm": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/scm": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index cad4914e6fe58..1ec63d0bc4927 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,12 +1,12 @@ { "name": "@theia/scm", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^3.2.2", "diff": "^3.4.0", @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 4f89297ab7453..8875bdc22a436 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/process": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/process": "1.49.0", + "@theia/workspace": "1.49.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index 4729fffd71d4e..23543736851a9 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index 0409517f2ffb7..f4ada852b7b60 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/markers": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/markers": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.48.0", - "@theia/terminal": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/process": "1.49.0", + "@theia/terminal": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/workspace": "1.49.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index a149bfb1bf49e..d01054f84426a 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/process": "1.48.0", - "@theia/variable-resolver": "1.48.0", - "@theia/workspace": "1.48.0", - "@theia/file-search": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/process": "1.49.0", + "@theia/variable-resolver": "1.49.0", + "@theia/workspace": "1.49.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index 1936586573a7e..c9d180784203d 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/terminal": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/terminal": "1.49.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index eb77860b006a8..d5e19db776992 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/navigator": "1.48.0", + "@theia/core": "1.49.0", + "@theia/navigator": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index e3ba668a727bf..ca0fdb37f31a3 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", - "@theia/file-search": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/monaco": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", + "@theia/file-search": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/monaco": "1.49.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.48.0", - "@theia/userstorage": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/search-in-workspace": "1.49.0", + "@theia/userstorage": "1.49.0", + "@theia/workspace": "1.49.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 8d42c6b7f134c..564e8d2d37839 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/editor": "1.48.0", + "@theia/core": "1.49.0", + "@theia/editor": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 00a85d008faf8..114d89c74f34c 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index 0a4b2bf56c714..072dc08158c1d 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.48.0", + "@theia/core": "1.49.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 06a027303262c..bc4602e1e2e67 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/navigator": "1.48.0", - "@theia/ovsx-client": "1.48.0", - "@theia/plugin-ext": "1.48.0", - "@theia/plugin-ext-vscode": "1.48.0", - "@theia/preferences": "1.48.0", - "@theia/workspace": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/navigator": "1.49.0", + "@theia/ovsx-client": "1.49.0", + "@theia/plugin-ext": "1.49.0", + "@theia/plugin-ext-vscode": "1.49.0", + "@theia/preferences": "1.49.0", + "@theia/workspace": "1.49.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -54,7 +54,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0", + "@theia/ext-scripts": "1.49.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index 7970e5d19145e..b0ba638c5efbb 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.48.0", + "version": "1.49.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.48.0", - "@theia/filesystem": "1.48.0", - "@theia/variable-resolver": "1.48.0", + "@theia/core": "1.49.0", + "@theia/filesystem": "1.49.0", + "@theia/variable-resolver": "1.49.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.48.0" + "@theia/ext-scripts": "1.49.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 834fdd36b495f..e9ebb8dd7bdd5 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.48.0", + "version": "1.49.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 8aee6a6efd846..3adeb5e79e204 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.48.0", + "version": "1.49.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index b025d0f0def04..caada66babf7e 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.48.0", + "version": "1.49.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.48.0" + "@theia/api-provider-sample": "1.49.0" }, "scripts": { "prepare": "yarn -s package", From 9c9456b8f7d32f9788d60f83377f109f1743aaf5 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 29 Apr 2024 11:46:37 +0200 Subject: [PATCH 203/441] docs: updated changelog for 1.49.0 Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6493c519bac06..342df3ad32707 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,56 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## not yet released + + +## v1.49.0 - 04/29/2024 + +- [application-manager] added logic to generate Extension Info in server application to avoid empty About Dialog [#13590](https://github.com/eclipse-theia/theia/pull/13590) - contributed on behalf of STMicroelectronics +- [application-manager] fixed spawn calls for node LTS versions [#13614](https://github.com/eclipse-theia/theia/pull/13614) - [application-package] bumped the default supported API from `1.87.2` to `1.88.1` [#13646](https://github.com/eclipse-theia/theia/pull/13646) - contributed on behalf of STMicroelectronics -- [core] Splash Screen Support for Electron [#13505](https://github.com/eclipse-theia/theia/pull/13505) - contributed on behalf of Pragmatiqu IT GmbH +- [cli] added "patches" folder to package.json "files" field [#13554](https://github.com/eclipse-theia/theia/pull/13554) - contributed on behalf of STMicroelectronics +- [core] added a new built-in handler to open files with system application [#13601](https://github.com/eclipse-theia/theia/pull/13601) +- [core] added logic to always consider the "passthrough" commmand enabled for keybindings [#13564](https://github.com/eclipse-theia/theia/pull/13564) - contributed on behalf of STMicroelectronics +- [core] added Splash Screen Support for Electron [#13505](https://github.com/eclipse-theia/theia/pull/13505) - contributed on behalf of Pragmatiqu IT GmbH +- [core] fixed window revealing when navigating with multiple windows [#13561](https://github.com/eclipse-theia/theia/pull/13561) - contributed on behalf of STMicroelectronics +- [core] improved "Open With..." command UX [#13573](https://github.com/eclipse-theia/theia/pull/13573) +- [filesystem] added logic to open editor on file upload [#13578](https://github.com/eclipse-theia/theia/pull/13578) +- [monaco] added logic to prevent duplicate Clipboard actions in editor context menu [#13626](https://github.com/eclipse-theia/theia/pull/13626) +- [monaco] fixed monaco localization [#13557](https://github.com/eclipse-theia/theia/pull/13557) +- [notebook] added additional keybings to the notebook editor [#13594](https://github.com/eclipse-theia/theia/pull/13594) +- [notebook] added logic to force notebook scrollbar update after content change [#13575](https://github.com/eclipse-theia/theia/pull/13575) +- [notebook] added logic to read execution summary [#13567](https://github.com/eclipse-theia/theia/pull/13567) +- [notebook] added logic to select notebook cell language [#13615](https://github.com/eclipse-theia/theia/pull/13615) +- [notebook] added logic to show short title for notebook toolbar commands [#13586](https://github.com/eclipse-theia/theia/pull/13586) +- [notebook] added logic to use notebook URI as context for toolbar commands [#13585](https://github.com/eclipse-theia/theia/pull/13585) +- [notebook] added shift+enter keybinding for markdown cells [#13563](https://github.com/eclipse-theia/theia/pull/13563) +- [notebook] added support for Outline-View and Breadcrumbs [#13562](https://github.com/eclipse-theia/theia/pull/13562) +- [notebook] added support for truncated notebook output commands [#13555](https://github.com/eclipse-theia/theia/pull/13555) +- [notebook] disabled clear all outputs in notebook main toolbar [#13569](https://github.com/eclipse-theia/theia/pull/13569) +- [notebook] fixed clear cell outputs command [#13640](https://github.com/eclipse-theia/theia/pull/13640) +- [notebook] fixed kernel autobind for on startup opened notebooks [#13598](https://github.com/eclipse-theia/theia/pull/13598) +- [notebook] fixed logic to set context for multiple notebooks [#13566](https://github.com/eclipse-theia/theia/pull/13566) +- [notebook] fixed notebook cell EOL splitting [#13574](https://github.com/eclipse-theia/theia/pull/13574) +- [notebook] fixed notebook model/cell disposal [#13606](https://github.com/eclipse-theia/theia/pull/13606) +- [notebook] fixed notebook widget icon on reload [#13612](https://github.com/eclipse-theia/theia/pull/13612) +- [notebook] improved notebook cell context key handling [#13572](https://github.com/eclipse-theia/theia/pull/13572) +- [notebook] improved notebook markdown cell rendering [#13577](https://github.com/eclipse-theia/theia/pull/13577) +- [plugin] added logic to hide empty plugin view containers from user [#13581](https://github.com/eclipse-theia/theia/pull/13581) +- [plugin] added logic to ignore vsix files in local-plugins dir [#13435](https://github.com/eclipse-theia/theia/pull/13435) - contributed on behalf of STMicroelectronics +- [plugin] fixed `onLanguage` activation event [#13630](https://github.com/eclipse-theia/theia/pull/13630) +- [plugin] fixed issue with webview communication for Safari [#13587](https://github.com/eclipse-theia/theia/pull/13587) +- [plugin] updated `DropMetada` and `documentPaste` proposed API for 1.88 compatibility [#13632](https://github.com/eclipse-theia/theia/pull/13632) +- [plugin] updated back-end plugin deployment logic [#13643](https://github.com/eclipse-theia/theia/pull/13643) - contributed on behalf of STMicroelectronics +- [process] fixed spawn calls for node LTS versions [#13614](https://github.com/eclipse-theia/theia/pull/13614) +- [remote] fixed remote support in packaged apps [#13584](https://github.com/eclipse-theia/theia/pull/13584) - [scm] added support for dirty diff peek view [#13104](https://github.com/eclipse-theia/theia/pull/13104) -- [test] stub VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) +- [terminal] fixed spawn calls for node LTS versions [#13614](https://github.com/eclipse-theia/theia/pull/13614) +- [test] stubbed VS Code `Test Coverage` API [#13631](https://github.com/eclipse-theia/theia/pull/13631) - contributed on behalf of STMicroelectronics +- [vsx-registry] fixed logic to bind Extension search bar within view container [#13623](https://github.com/eclipse-theia/theia/pull/13623) -[Breaking Changes:](#breaking_changes_not_yet_released) +[Breaking Changes:](#breaking_changes_1.49.0) - [scm] revised some of the dirty diff related types [#13104](https://github.com/eclipse-theia/theia/pull/13104) - replaced `DirtyDiff.added/removed/modified` with `changes`, which provides more detailed information about the changes From 68cb05528957f98072b791dfe23d55bcc95b8ab7 Mon Sep 17 00:00:00 2001 From: Vladimir Piskarev Date: Mon, 29 Apr 2024 17:28:17 +0300 Subject: [PATCH 204/441] Fix incorrect URI conversions in `custom-editors-main.ts` (#13653) --- .../browser/custom-editors/custom-editors-main.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts index 51dca8780740a..a7951da9167ca 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts @@ -139,14 +139,14 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { widget.onMove(async (newResource: TheiaURI) => { const oldModel = modelRef; modelRef = await this.getOrCreateCustomEditorModel(modelType, newResource, viewType, onMoveCancelTokenSource.token); - this.proxy.$onMoveCustomEditor(identifier.id, URI.file(newResource.path.toString()), viewType); + this.proxy.$onMoveCustomEditor(identifier.id, newResource.toComponents(), viewType); oldModel.dispose(); }); } const _cancellationSource = new CancellationTokenSource(); await this.proxy.$resolveWebviewEditor( - URI.file(resource.path.toString()), + resource.toComponents(), identifier.id, viewType, this.labelProvider.getName(resource)!, @@ -309,7 +309,7 @@ export class MainCustomEditorModel implements CustomEditorModel { editorPreferences: EditorPreferences, cancellation: CancellationToken, ): Promise { - const { editable } = await proxy.$createCustomDocument(URI.file(resource.path.toString()), viewType, {}, cancellation); + const { editable } = await proxy.$createCustomDocument(resource.toComponents(), viewType, {}, cancellation); return new MainCustomEditorModel(proxy, viewType, resource, editable, undoRedoService, fileService, editorPreferences); } @@ -339,7 +339,7 @@ export class MainCustomEditorModel implements CustomEditorModel { } get resource(): URI { - return URI.file(this.editorResource.path.toString()); + return URI.from(this.editorResource.toComponents()); } get dirty(): boolean { @@ -441,7 +441,7 @@ export class MainCustomEditorModel implements CustomEditorModel { async saveCustomEditorAs(resource: TheiaURI, targetResource: TheiaURI, options?: SaveOptions): Promise { if (this.editable) { const source = new CancellationTokenSource(); - await this.proxy.$onSaveAs(this.resource, this.viewType, URI.file(targetResource.path.toString()), source.token); + await this.proxy.$onSaveAs(this.resource, this.viewType, targetResource.toComponents(), source.token); this.change(() => { this.savePoint = this.currentEditIndex; }); @@ -561,7 +561,7 @@ export class CustomTextEditorModel implements CustomEditorModel { } get resource(): URI { - return URI.file(this.editorResource.path.toString()); + return URI.from(this.editorResource.toComponents()); } get dirty(): boolean { From 020a4cbf94a7bc38f58a2c1d6346e19c7d069f95 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 29 Apr 2024 16:44:02 +0200 Subject: [PATCH 205/441] select next node when on first or last line of editor (#13656) Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 26 ++++++++++++++++--- .../contributions/notebook-context-keys.ts | 5 ++++ .../browser/view-model/notebook-cell-model.ts | 8 +++--- .../src/browser/view/notebook-cell-editor.tsx | 25 +++++++++++++++++- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 00ae102716ac7..d3d3fb25e8c88 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -24,8 +24,9 @@ import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; -import { NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; +import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; import { NotebookClipboardService } from '../service/notebook-clipboard-service'; +import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -110,6 +111,9 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon @inject(NotebookClipboardService) protected notebookClipboardService: NotebookClipboardService; + @inject(ContextKeyService) + protected contextKeyService: ContextKeyService; + registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, { execute: (notebookModel: NotebookModel, cellKind: CellKind = CellKind.Markup, index?: number | 'above' | 'below') => { @@ -169,15 +173,29 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon commands.registerCommand(NotebookCommands.CHANGE_SELECTED_CELL, { execute: (change: number | CellChangeDirection) => { - const model = this.notebookEditorWidgetService.focusedEditor?.model; + const focusedEditor = this.notebookEditorWidgetService.focusedEditor; + const model = focusedEditor?.model; if (model && typeof change === 'number') { model.setSelectedCell(model.cells[change]); } else if (model && model.selectedCell) { const currentIndex = model.cells.indexOf(model.selectedCell); + const shouldFocusEditor = this.contextKeyService.match('editorTextFocus'); + if (change === CellChangeDirection.Up && currentIndex > 0) { model.setSelectedCell(model.cells[currentIndex - 1]); + if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + model.selectedCell.requestFocusEditor('lastLine'); + } } else if (change === CellChangeDirection.Down && currentIndex < model.cells.length - 1) { model.setSelectedCell(model.cells[currentIndex + 1]); + if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + model.selectedCell.requestFocusEditor(); + } + } + + if (model.selectedCell.cellKind === CellKind.Markup) { + // since were losing focus from the cell editor, we need to focus the notebook editor again + focusedEditor?.node.focus(); } } } @@ -294,13 +312,13 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon command: NotebookCommands.CHANGE_SELECTED_CELL.id, keybinding: 'up', args: CellChangeDirection.Up, - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_FIRST_LINE}) && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, { command: NotebookCommands.CHANGE_SELECTED_CELL.id, keybinding: 'down', args: CellChangeDirection.Down, - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_LAST_LINE}) && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, { command: NotebookCommands.CUT_SELECTED_CELL.id, diff --git a/packages/notebook/src/browser/contributions/notebook-context-keys.ts b/packages/notebook/src/browser/contributions/notebook-context-keys.ts index 5bc987d154897..787d63748b10c 100644 --- a/packages/notebook/src/browser/contributions/notebook-context-keys.ts +++ b/packages/notebook/src/browser/contributions/notebook-context-keys.ts @@ -58,6 +58,9 @@ export const NOTEBOOK_INTERRUPTIBLE_KERNEL = 'notebookInterruptibleKernel'; export const NOTEBOOK_MISSING_KERNEL_EXTENSION = 'notebookMissingKernelExtension'; export const NOTEBOOK_HAS_OUTPUTS = 'notebookHasOutputs'; +export const NOTEBOOK_CELL_CURSOR_FIRST_LINE = 'cellEditorCursorPositionFirstLine'; +export const NOTEBOOK_CELL_CURSOR_LAST_LINE = 'cellEditorCursorPositionLastLine'; + export namespace NotebookContextKeys { export function initNotebookContextKeys(service: ContextKeyService): void { service.createKey(HAS_OPENED_NOTEBOOK, false); @@ -93,6 +96,8 @@ export namespace NotebookContextKeys { service.createKey(NOTEBOOK_CELL_INPUT_COLLAPSED, false); service.createKey(NOTEBOOK_CELL_OUTPUT_COLLAPSED, false); service.createKey(NOTEBOOK_CELL_RESOURCE, ''); + service.createKey(NOTEBOOK_CELL_CURSOR_FIRST_LINE, false); + service.createKey(NOTEBOOK_CELL_CURSOR_LAST_LINE, false); // Kernels service.createKey(NOTEBOOK_KERNEL, undefined); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index ea637ae256fbb..303d075e0cd2a 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -36,6 +36,8 @@ import { LanguageService } from '@theia/core/lib/browser/language-service'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel; +export type CellEditorFocusRequest = number | 'lastLine' | undefined; + export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container { const child = parent.createChild(); @@ -104,7 +106,7 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onDidRequestCellEditChangeEmitter = new Emitter(); readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event; - protected readonly onWillFocusCellEditorEmitter = new Emitter(); + protected readonly onWillFocusCellEditorEmitter = new Emitter(); readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event; protected readonly onWillBlurCellEditorEmitter = new Emitter(); @@ -278,9 +280,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidRequestCellEditChangeEmitter.fire(false); } - requestFocusEditor(): void { + requestFocusEditor(focusRequest?: CellEditorFocusRequest): void { this.requestEdit(); - this.onWillFocusCellEditorEmitter.fire(); + this.onWillFocusCellEditorEmitter.fire(focusRequest); } requestBlurEditor(): void { diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index f0e8de320eed2..0ffeef23f88ab 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -25,6 +25,7 @@ import { NotebookContextManager } from '../service/notebook-context-manager'; import { DisposableCollection, OS } from '@theia/core'; import { NotebookViewportService } from './notebook-viewport-service'; import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo'; +import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys'; interface CellEditorProps { notebookModel: NotebookModel, @@ -54,8 +55,19 @@ export class CellEditor extends React.Component { override componentDidMount(): void { this.disposeEditor(); - this.toDispose.push(this.props.cell.onWillFocusCellEditor(() => { + this.toDispose.push(this.props.cell.onWillFocusCellEditor(focusRequest => { this.editor?.getControl().focus(); + const lineCount = this.editor?.getControl().getModel()?.getLineCount(); + if (focusRequest && lineCount) { + this.editor?.getControl().setPosition(focusRequest === 'lastLine' ? + { lineNumber: lineCount, column: 1 } : + { lineNumber: focusRequest, column: 1 }, + 'keyboard'); + } + const currentLine = this.editor?.getControl().getPosition()?.lineNumber; + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, currentLine === 1); + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount); + })); this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { @@ -119,10 +131,21 @@ export class CellEditor extends React.Component { })); this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => { this.props.notebookContextManager.onDidEditorTextFocus(true); + this.props.notebookModel.setSelectedCell(cell); })); this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { this.props.notebookContextManager.onDidEditorTextFocus(false); })); + this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => { + if (e.secondaryPositions.length === 0) { + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, e.position.lineNumber === 1); + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, + e.position.lineNumber === this.editor!.getControl().getModel()!.getLineCount()); + } else { + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, false); + this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, false); + } + })); if (cell.editing && notebookModel.selectedCell === cell) { this.editor.getControl().focus(); } From fd988bcb9dff811b9a334d048b73f70172fc0538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 30 Apr 2024 10:12:54 +0200 Subject: [PATCH 206/441] Add dummy command to fix github authentication built-int flows (#13611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also includes code to consider sessions which are not created, but restored from storage at registration time Fixes #13599 Partial fix for #12821 Contributed on behalof of STMicroelectronics Signed-off-by: Thomas Mäder --- .../browser/plugin-vscode-commands-contribution.ts | 10 ++++++++++ packages/plugin-ext/src/plugin/authentication-ext.ts | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index 092f71e93d16a..ba9790c4f051a 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -82,6 +82,12 @@ import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/v import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution'; export namespace VscodeCommands { + + export const GET_CODE_EXCHANGE_ENDPOINTS: Command = { + id: 'workbench.getCodeExchangeProxyEndpoints' // this command is used in the github auth built-in + // see: https://github.com/microsoft/vscode/blob/191be39e5ac872e03f9d79cc859d9917f40ad935/extensions/github-authentication/src/githubServer.ts#L60 + }; + export const OPEN: Command = { id: 'vscode.open' }; @@ -230,6 +236,10 @@ export class PluginVscodeCommandsContribution implements CommandContribution { } registerCommands(commands: CommandRegistry): void { + commands.registerCommand(VscodeCommands.GET_CODE_EXCHANGE_ENDPOINTS, { + execute: () => undefined // this is a dummy implementation: only used in the case of web apps, which is not supported yet. + }); + commands.registerCommand(VscodeCommands.OPEN, { isVisible: () => false, execute: async (resource: URI | string, columnOrOptions?: ViewColumn | TextDocumentShowOptions) => { diff --git a/packages/plugin-ext/src/plugin/authentication-ext.ts b/packages/plugin-ext/src/plugin/authentication-ext.ts index 25dba1325d0ab..7cf0e3e1c349a 100644 --- a/packages/plugin-ext/src/plugin/authentication-ext.ts +++ b/packages/plugin-ext/src/plugin/authentication-ext.ts @@ -63,6 +63,17 @@ export class AuthenticationExtImpl implements AuthenticationExt { } this.authenticationProviders.set(id, provider); + + provider.getSessions().then(sessions => { // sessions might have been restored from secret storage + if (sessions.length > 0) { + this.proxy.$onDidChangeSessions(id, { + added: sessions, + removed: [], + changed: [] + }); + } + }); + const listener = provider.onDidChangeSessions(e => { this.proxy.$onDidChangeSessions(id, e); }); From cfbfe2833171dc6c811839657d6f194188617d63 Mon Sep 17 00:00:00 2001 From: Maddobun <76917395+Maddobun@users.noreply.github.com> Date: Tue, 30 Apr 2024 05:28:31 -0400 Subject: [PATCH 207/441] Use transitive binding for TerminalFrontendContribution (#13667) Allows downstream to customize the frontend lifecycle behavior of the TerminalFrontendContribution class. Signed-off-by: Leo Zhu --- packages/terminal/src/browser/terminal-frontend-module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/terminal/src/browser/terminal-frontend-module.ts b/packages/terminal/src/browser/terminal-frontend-module.ts index 5b105f527dbd9..8f37ce4eef80c 100644 --- a/packages/terminal/src/browser/terminal-frontend-module.ts +++ b/packages/terminal/src/browser/terminal-frontend-module.ts @@ -134,5 +134,5 @@ export default new ContainerModule(bind => { return new DefaultTerminalProfileService(userStore, contributedStore); }).inSingletonScope(); - bind(FrontendApplicationContribution).to(TerminalFrontendContribution); + bind(FrontendApplicationContribution).toService(TerminalFrontendContribution); }); From 11d6cd0be86c15ba3271f4222af59993aade4996 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 30 Apr 2024 14:38:03 +0200 Subject: [PATCH 208/441] Added additional css to notebook output webviews (#13666) * Added additional css to notebook ooutput webviews Signed-off-by: Jonah Iden * renamed symbol to be more descriptive Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../renderers/cell-output-webview.tsx | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 2e8b91a5a0c24..159fb7e7f6561 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -38,15 +38,82 @@ import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-m const CellModel = Symbol('CellModel'); const Notebook = Symbol('NotebookModel'); +export const AdditionalNotebookCellOutputCss = Symbol('AdditionalNotebookCellOutputCss'); export function createCellOutputWebviewContainer(ctx: interfaces.Container, cell: NotebookCellModel, notebook: NotebookModel): interfaces.Container { const child = ctx.createChild(); child.bind(CellModel).toConstantValue(cell); child.bind(Notebook).toConstantValue(notebook); + child.bind(AdditionalNotebookCellOutputCss).toConstantValue(DEFAULT_NOTEBOOK_OUTPUT_CSS); child.bind(CellOutputWebviewImpl).toSelf().inSingletonScope(); return child; } +export const DEFAULT_NOTEBOOK_OUTPUT_CSS = ` +table { + border-collapse: collapse; + border-spacing: 0; +} + +table th, +table td { + border: 1px solid; +} + +table > thead > tr > th { + text-align: left; + border-bottom: 1px solid; +} + +table > thead > tr > th, +table > thead > tr > td, +table > tbody > tr > th, +table > tbody > tr > td { + padding: 5px 10px; +} + +table > tbody > tr + tr > td { + border-top: 1px solid; +} + +table, +thead, +tr, +th, +td, +tbody { + border: none !important; + border-color: transparent; + border-spacing: 0; + border-collapse: collapse; +} + +table, +th, +tr { + vertical-align: middle; + text-align: right; +} + +thead { + font-weight: bold; + background-color: rgba(130, 130, 130, 0.16); +} + +th, +td { + padding: 4px 8px; +} + +tr:nth-child(even) { + background-color: rgba(130, 130, 130, 0.08); +} + +tbody th { + font-weight: normal; +} +`; + @injectable() export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @@ -77,6 +144,9 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @inject(QuickPickService) protected readonly quickPickService: QuickPickService; + @inject(AdditionalNotebookCellOutputCss) + protected readonly additionalOutputCss: string; + readonly id = generateUuid(); protected editor: NotebookEditorWidget | undefined; @@ -129,7 +199,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { 'jupyter.viewOutput', 'workbench.action.openLargeOutput', 'cellOutput.enableScrolling', - ] + ], }); this.webviewWidget.setHTML(await this.createWebviewContent()); @@ -236,6 +306,9 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { + From 6a82f78767c486014c4e677847ad8ee3ce0e96b1 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 30 Apr 2024 14:57:07 +0200 Subject: [PATCH 209/441] Fixed storing of the notebook-outlineview state data (#13648) * fixed storing of the notebook-outlineview state data Signed-off-by: Jonah Iden * fixed is function and URI handling Signed-off-by: Jonah Iden * improved is method for NotebookCellOutlineNode Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-label-provider-contribution.ts | 38 +++++++++++++++---- .../notebook-outline-contribution.ts | 20 +++++----- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts b/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts index ec75fcf2cc5c5..5e9be6fd13252 100644 --- a/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-label-provider-contribution.ts @@ -16,11 +16,13 @@ import { codicon, LabelProvider, LabelProviderContribution } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { CellKind } from '../../common'; +import { CellKind, CellUri } from '../../common'; import { NotebookService } from '../service/notebook-service'; import { NotebookCellOutlineNode } from './notebook-outline-contribution'; import type Token = require('markdown-it/lib/token'); import markdownit = require('@theia/core/shared/markdown-it'); +import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { URI } from '@theia/core'; @injectable() export class NotebookLabelProviderContribution implements LabelProviderContribution { @@ -41,23 +43,43 @@ export class NotebookLabelProviderContribution implements LabelProviderContribut } getIcon(element: NotebookCellOutlineNode): string { - return element.notebookCell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code'); + const cell = this.findCellByUri(element.uri); + if (cell) { + return cell.cellKind === CellKind.Markup ? codicon('markdown') : codicon('code'); + } + return ''; } getName(element: NotebookCellOutlineNode): string { - return element.notebookCell.cellKind === CellKind.Code ? - element.notebookCell.text.split('\n')[0] : - this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {})); + const cell = this.findCellByUri(element.uri); + if (cell) { + return cell.cellKind === CellKind.Code ? + cell.text.split('\n')[0] : + this.extractPlaintext(this.markdownIt.parse(cell.text.split('\n')[0], {})); + } + return ''; } getLongName(element: NotebookCellOutlineNode): string { - return element.notebookCell.cellKind === CellKind.Code ? - element.notebookCell.text.split('\n')[0] : - this.extractPlaintext(this.markdownIt.parse(element.notebookCell.text.split('\n')[0], {})); + const cell = this.findCellByUri(element.uri); + if (cell) { + return cell.cellKind === CellKind.Code ? + cell.text.split('\n')[0] : + this.extractPlaintext(this.markdownIt.parse(cell.text.split('\n')[0], {})); + } + return ''; } extractPlaintext(parsedMarkdown: Token[]): string { return parsedMarkdown.map(token => token.children ? this.extractPlaintext(token.children) : token.content).join(''); } + findCellByUri(uri: URI): NotebookCellModel | undefined { + const parsed = CellUri.parse(uri); + if (parsed) { + return this.notebookService.getNotebookEditorModel(parsed.notebook)?.cells.find(cell => cell.handle === parsed?.handle); + } + return undefined; + } + } diff --git a/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts b/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts index e1423dbc457a7..deea82d6a05b0 100644 --- a/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-outline-contribution.ts @@ -21,18 +21,20 @@ import { OutlineViewService } from '@theia/outline-view/lib/browser/outline-view import { NotebookModel } from '../view-model/notebook-model'; import { OutlineSymbolInformationNode } from '@theia/outline-view/lib/browser/outline-view-widget'; import { NotebookEditorWidget } from '../notebook-editor-widget'; -import { NotebookCellModel } from '../view-model/notebook-cell-model'; -import { DisposableCollection, URI } from '@theia/core'; +import { DisposableCollection, isObject, URI } from '@theia/core'; import { CellKind, CellUri } from '../../common'; import { NotebookService } from '../service/notebook-service'; export interface NotebookCellOutlineNode extends OutlineSymbolInformationNode { - notebookCell: NotebookCellModel; uri: URI; } export namespace NotebookCellOutlineNode { export function is(element: object): element is NotebookCellOutlineNode { - return TreeNode.is(element) && OutlineSymbolInformationNode.is(element) && 'notebookCell' in element; + return TreeNode.is(element) + && OutlineSymbolInformationNode.is(element) + && isObject(element) + && element.uri instanceof URI + && element.uri.scheme === CellUri.cellUriScheme; } } @@ -94,17 +96,17 @@ export class NotebookOutlineContribution implements FrontendApplicationContribut children: [], selected: model.selectedCell === cell, expanded: false, - notebookCell: cell, - uri: model.uri, + uri: cell.uri, } as NotebookCellOutlineNode)); } selectCell(node: object): void { if (NotebookCellOutlineNode.is(node)) { - const parsed = CellUri.parse(node.notebookCell.uri); + const parsed = CellUri.parse(node.uri); const model = parsed && this.notebookService.getNotebookEditorModel(parsed.notebook); - if (model) { - model.setSelectedCell(node.notebookCell); + const cell = model?.cells.find(c => c.handle === parsed?.handle); + if (model && cell) { + model.setSelectedCell(cell); } } } From c1f222dc054f8f4ea31d8d36cdf154f69176efbd Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 30 Apr 2024 15:51:10 +0200 Subject: [PATCH 210/441] Align to vscode notebook commands (#13645) * aligned commands to vscodes commands this makes more keybindings available. Also implements to notbookOutputInputFocused context key Signed-off-by: Jonah Iden * fixed type and forgotten dispose of emitter Signed-off-by: Jonah Iden * fixed lint Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 6 +-- .../notebook-cell-actions-contribution.ts | 39 ++++++++++--------- .../contributions/notebook-context-keys.ts | 2 + .../src/browser/notebook-editor-widget.tsx | 8 ++++ .../service/notebook-context-manager.ts | 6 +++ .../renderers/cell-output-webview.tsx | 2 + .../renderers/output-webview-internal.ts | 10 +++++ .../renderers/webview-communication.ts | 8 +++- 8 files changed, 58 insertions(+), 23 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index d3d3fb25e8c88..bfa3dfb56f1fd 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -70,17 +70,17 @@ export namespace NotebookCommands { }); export const CUT_SELECTED_CELL = Command.toDefaultLocalizedCommand({ - id: 'notebook.cut-selected-cell', + id: 'notebook.cell.cut', category: 'Notebook', }); export const COPY_SELECTED_CELL = Command.toDefaultLocalizedCommand({ - id: 'notebook.copy-selected-cell', + id: 'notebook.cell.copy', category: 'Notebook', }); export const PASTE_CELL = Command.toDefaultLocalizedCommand({ - id: 'notebook.paste-cell', + id: 'notebook.cell.paste', category: 'Notebook', }); } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 322a54a5c16d1..e3522f4112f45 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -120,24 +120,24 @@ export namespace NotebookCellCommands { label: 'Change Cell to Mardown' }); - export const COLLAPSE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ - id: 'notebook.cell.collapseCellOutput', + export const TOGGLE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.toggleOutputs', category: 'Notebook', label: 'Collapse Cell Output', }); - export const EXPAND_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ - id: 'notebook.cell.expandCellOutput', - category: 'Notebook', - label: 'Expand Cell Output', - }); - export const CHANGE_CELL_LANGUAGE = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.changeLanguage', category: 'Notebook', label: 'Change Cell Language', }); + export const TOGGLE_LINE_NUMBERS = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.toggleLineNumbers', + category: 'Notebook', + label: 'Show Cell Line Numbers', + }); + } @injectable() @@ -351,20 +351,11 @@ export class NotebookCellActionContribution implements MenuContribution, Command changeCellType(notebookModel, cell, CellKind.Markup); })); - commands.registerCommand(NotebookCellCommands.COLLAPSE_CELL_OUTPUT, { - execute: () => { - const selectedCell = this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; - if (selectedCell) { - selectedCell.outputVisible = false; - } - } - }); - - commands.registerCommand(NotebookCellCommands.EXPAND_CELL_OUTPUT, { + commands.registerCommand(NotebookCellCommands.TOGGLE_CELL_OUTPUT, { execute: () => { const selectedCell = this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; if (selectedCell) { - selectedCell.outputVisible = true; + selectedCell.outputVisible = !selectedCell.outputVisible; } } }); @@ -387,6 +378,16 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }); + commands.registerCommand(NotebookCellCommands.TOGGLE_LINE_NUMBERS, { + execute: () => { + const selectedCell = this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; + if (selectedCell) { + const currentLineNumber = selectedCell.editorOptions?.lineNumbers; + selectedCell.editorOptions = { ...selectedCell.editorOptions, lineNumbers: !currentLineNumber || currentLineNumber === 'off' ? 'on' : 'off' }; + } + } + }); + } protected editableCellCommandHandler(execute: (notebookModel: NotebookModel, cell: NotebookCellModel, output?: NotebookCellOutputModel) => void): CommandHandler { diff --git a/packages/notebook/src/browser/contributions/notebook-context-keys.ts b/packages/notebook/src/browser/contributions/notebook-context-keys.ts index 787d63748b10c..97c18175f327a 100644 --- a/packages/notebook/src/browser/contributions/notebook-context-keys.ts +++ b/packages/notebook/src/browser/contributions/notebook-context-keys.ts @@ -30,6 +30,7 @@ export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = 'notebookFindWidg export const NOTEBOOK_EDITOR_FOCUSED = 'notebookEditorFocused'; export const NOTEBOOK_CELL_LIST_FOCUSED = 'notebookCellListFocused'; export const NOTEBOOK_OUTPUT_FOCUSED = 'notebookOutputFocused'; +export const NOTEBOOK_OUTPUT_INPUT_FOCUSED = 'notebookOutputInputFocused'; export const NOTEBOOK_EDITOR_EDITABLE = 'notebookEditable'; export const NOTEBOOK_HAS_RUNNING_CELL = 'notebookHasRunningCell'; export const NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON = 'notebookUseConsolidatedOutputButton'; @@ -74,6 +75,7 @@ export namespace NotebookContextKeys { service.createKey(NOTEBOOK_EDITOR_FOCUSED, false); service.createKey(NOTEBOOK_CELL_LIST_FOCUSED, false); service.createKey(NOTEBOOK_OUTPUT_FOCUSED, false); + service.createKey(NOTEBOOK_OUTPUT_INPUT_FOCUSED, false); service.createKey(NOTEBOOK_EDITOR_EDITABLE, true); service.createKey(NOTEBOOK_HAS_RUNNING_CELL, false); service.createKey(NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, false); diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 5c4cac3870f60..c068ab924add6 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -120,6 +120,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected readonly onDidReceiveKernelMessageEmitter = new Emitter(); readonly onDidReceiveKernelMessage = this.onDidReceiveKernelMessageEmitter.event; + protected readonly onDidChangeOutputInputFocusEmitter = new Emitter(); + readonly onDidChangeOutputInputFocus = this.onDidChangeOutputInputFocusEmitter.event; + protected readonly renderers = new Map(); protected _model?: NotebookModel; protected _ready: Deferred = new Deferred(); @@ -251,12 +254,17 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.onDidReceiveKernelMessageEmitter.fire(message); } + outputInputFocusChanged(focused: boolean): void { + this.onDidChangeOutputInputFocusEmitter.fire(focused); + } + override dispose(): void { this.notebookContextManager.dispose(); this.onDidChangeModelEmitter.dispose(); this.onDidPostKernelMessageEmitter.dispose(); this.onDidReceiveKernelMessageEmitter.dispose(); this.onPostRendererMessageEmitter.dispose(); + this.onDidChangeOutputInputFocusEmitter.dispose(); this.viewportService.dispose(); this._model?.dispose(); super.dispose(); diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 7d5f47fca571a..7638bdc979556 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -23,6 +23,7 @@ import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, + NOTEBOOK_OUTPUT_INPUT_FOCUSED, NOTEBOOK_VIEW_TYPE } from '../contributions/notebook-context-keys'; import { NotebookEditorWidget } from '../notebook-editor-widget'; @@ -101,6 +102,11 @@ export class NotebookContextManager { widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e)); + widget.onDidChangeOutputInputFocus(focus => { + this.scopedStore.setContext(NOTEBOOK_OUTPUT_INPUT_FOCUSED, focus); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_OUTPUT_INPUT_FOCUSED])); + }); + this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_VIEW_TYPE, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_KERNEL])); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 159fb7e7f6561..efd1845c92e6f 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -286,6 +286,8 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { // console.log('from webview customKernelMessage ', JSON.stringify(message.message)); this.editor.recieveKernelMessage(message.message); break; + case 'inputFocusChanged': + this.editor?.outputInputFocusChanged(message.focused); } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 887e523f02cb4..8818378ed4446 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -581,5 +581,15 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { return this.originalAppendChild(node); }; + const focusChange = (event: FocusEvent, focus: boolean) => { + if (event.target instanceof HTMLInputElement) { + theia.postMessage({ type: 'inputFocusChanged', focused: focus } as webviewCommunication.InputFocusChange); + } + }; + + window.addEventListener('focusin', (event: FocusEvent) => focusChange(event, true)); + + window.addEventListener('focusout', (event: FocusEvent) => focusChange(event, false)); + theia.postMessage({ type: 'initialized' }); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts index 97586cc17e774..4989fbef9df2e 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts @@ -76,7 +76,12 @@ export interface WheelMessage { readonly deltaX: number; } -export type FromWebviewMessage = WebviewInitialized | OnDidRenderOutput | WheelMessage | CustomRendererMessage | KernelMessage; +export interface InputFocusChange { + readonly type: 'inputFocusChanged'; + readonly focused: boolean; +} + +export type FromWebviewMessage = WebviewInitialized | OnDidRenderOutput | WheelMessage | CustomRendererMessage | KernelMessage | InputFocusChange; export interface Output { id: string @@ -88,3 +93,4 @@ export interface OutputItem { readonly mime: string; readonly data: Uint8Array; } + From b20a7513502fada4045b375f068152a7235e6c05 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 30 Apr 2024 17:53:20 +0200 Subject: [PATCH 211/441] Use responsive design for the main notebook toolbar (#13663) * main toolbar adjusting to maximum size by putting items in to a context menu Signed-off-by: Jonah Iden * fixed memory leak Signed-off-by: Jonah Iden * fixed issues with max and min hidden items Signed-off-by: Jonah Iden * fixed lint Signed-off-by: Jonah Iden * fixed when incresing width of notebook Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 3 + packages/notebook/src/browser/style/index.css | 1 + .../browser/view/notebook-main-toolbar.tsx | 89 +++++++++++++++++-- 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index bfa3dfb56f1fd..ee37243bdd706 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -304,6 +304,8 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon order: '30', when: NOTEBOOK_HAS_OUTPUTS }); + + menus.registerIndependentSubmenu(NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU, ''); } registerKeybindings(keybindings: KeybindingRegistry): void { @@ -344,4 +346,5 @@ export namespace NotebookMenus { export const NOTEBOOK_MAIN_TOOLBAR = 'notebook/toolbar'; export const NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP = [NOTEBOOK_MAIN_TOOLBAR, 'cell-add-group']; export const NOTEBOOK_MAIN_TOOLBAR_EXECUTION_GROUP = [NOTEBOOK_MAIN_TOOLBAR, 'cell-execution-group']; + export const NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU = 'notebook-main-toolbar-hidden-items-context-menu'; } diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index ce8afa1a658b4..5fd8ce1e2f904 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -225,6 +225,7 @@ .theia-notebook-main-toolbar-item-text { padding: 0 4px; + white-space: nowrap; } .theia-notebook-toolbar-separator { diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index 696bb496232a5..f3d20a1eb63c7 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -15,7 +15,7 @@ // ***************************************************************************** import { ArrayUtils, CommandRegistry, CompoundMenuNodeRole, DisposableCollection, MenuModelRegistry, MenuNode, nls } from '@theia/core'; import * as React from '@theia/core/shared/react'; -import { codicon } from '@theia/core/lib/browser'; +import { codicon, ContextMenuRenderer } from '@theia/core/lib/browser'; import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookKernelService } from '../service/notebook-kernel-service'; @@ -32,6 +32,7 @@ export interface NotebookMainToolbarProps { contextKeyService: ContextKeyService; editorNode: HTMLElement; notebookContextManager: NotebookContextManager; + contextMenuRenderer: ContextMenuRenderer; } @injectable() @@ -41,6 +42,7 @@ export class NotebookMainToolbarRenderer { @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry; @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager; + @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; render(notebookModel: NotebookModel, editorNode: HTMLElement): React.ReactNode { return ; + notebookContextManager={this.notebookContextManager} + contextMenuRenderer={this.contextMenuRenderer} />; } } -export class NotebookMainToolbar extends React.Component { +interface NotebookMainToolbarState { + selectedKernelLabel?: string; + numberOfHiddenItems: number; +} + +export class NotebookMainToolbar extends React.Component { + + // The minimum area between items and kernel select before hiding items in a context menu + static readonly MIN_FREE_AREA = 10; protected toDispose = new DisposableCollection(); @@ -61,10 +72,18 @@ export class NotebookMainToolbar extends React.Component this.calculateItemsToHide()); + constructor(props: NotebookMainToolbarProps) { super(props); - this.state = { selectedKernelLabel: props.notebookKernelService.getSelectedOrSuggestedKernel(props.notebookModel)?.label }; + this.state = { + selectedKernelLabel: props.notebookKernelService.getSelectedOrSuggestedKernel(props.notebookModel)?.label, + numberOfHiddenItems: 0, + }; this.toDispose.push(props.notebookKernelService.onDidChangeSelectedKernel(event => { if (props.notebookModel.uri.isEqual(event.notebook)) { this.setState({ selectedKernelLabel: props.notebookKernelService.getKernel(event.newKernel ?? '')?.label }); @@ -97,10 +116,49 @@ export class NotebookMainToolbar extends React.Component this.lastGapElementWidth && this.state.numberOfHiddenItems > 0) { + this.setState({ ...this.state, numberOfHiddenItems: 0 }); + this.lastGapElementWidth = this.gapElement.getBoundingClientRect().width; + } + } + + protected renderContextMenu(event: MouseEvent, menuItems: readonly MenuNode[]): void { + const hiddenItems = menuItems.slice(menuItems.length - this.calculateNumberOfHiddenItems(menuItems)); + const contextMenu = this.props.menuRegistry.getMenu([NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU]); + + contextMenu.children.map(item => item.id).forEach(id => contextMenu.removeNode(id)); + hiddenItems.forEach(item => contextMenu.addNode(item)); + + this.props.contextMenuRenderer.render({ + anchor: event, + menuPath: [NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_HIDDEN_ITEMS_CONTEXT_MENU], + context: this.props.editorNode, + args: [this.props.notebookModel.uri] + }); + } + override render(): React.ReactNode { + const menuItems = this.getMenuItems(); return
    - {this.getMenuItems().map(item => this.renderMenuItem(item))} -
    + {menuItems.slice(0, menuItems.length - this.calculateNumberOfHiddenItems(menuItems)).map(item => this.renderMenuItem(item))} + { + this.state.numberOfHiddenItems > 0 && + this.renderContextMenu(e.nativeEvent, menuItems)} /> + } +
    this.gapElementChanged(element)} style={{ flexGrow: 1 }}>
    this.props.commandRegistry.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, this.props.notebookModel)}> @@ -108,7 +166,18 @@ export class NotebookMainToolbar extends React.Component
    -
    ; +
    ; + } + + protected gapElementChanged(element: HTMLDivElement | null): void { + if (this.gapElement) { + this.resizeObserver.unobserve(this.gapElement); + } + this.gapElement = element ?? undefined; + if (this.gapElement) { + this.lastGapElementWidth = this.gapElement.getBoundingClientRect().width; + this.resizeObserver.observe(this.gapElement); + } } protected renderMenuItem(item: MenuNode, submenu?: string): React.ReactNode { @@ -157,4 +226,10 @@ export class NotebookMainToolbar extends React.Component item.children && item.children.length > 0) .forEach(item => this.getAllContextKeys(item.children!, keySet)); } + + protected calculateNumberOfHiddenItems(allMenuItems: readonly MenuNode[]): number { + return this.state.numberOfHiddenItems >= allMenuItems.length ? + allMenuItems.length : + this.state.numberOfHiddenItems % allMenuItems.length; + } } From ef04dc709e298885993955c422e9adc6ecf517d5 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 6 May 2024 13:05:16 +0200 Subject: [PATCH 212/441] Improve notebook cell model lifecycle (#13675) --- .../src/browser/monaco-text-model-service.ts | 2 +- .../notebook-monaco-text-model-service.ts | 4 +-- .../src/browser/service/notebook-service.ts | 2 +- .../browser/view-model/notebook-cell-model.ts | 6 ++--- .../src/browser/view-model/notebook-model.ts | 6 +---- .../notebook/src/common/notebook-common.ts | 2 +- .../src/plugin/editors-and-documents.ts | 4 +++ .../src/plugin/notebook/notebook-document.ts | 16 +++++++++--- .../src/plugin/notebook/notebooks.ts | 26 ++++++++++--------- 9 files changed, 40 insertions(+), 28 deletions(-) diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index 318de5575ba9e..0dc538054bbe0 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -113,7 +113,7 @@ export class MonacoTextModelService implements ITextModelService { * creates a model which is not saved by the model service. * this will therefore also not be created on backend side. */ - createUnmangedModel(raw: monaco.Uri | URI): Promise { + createUnmanagedModel(raw: monaco.Uri | URI): Promise { return this.loadModel(new URI(raw.toString())); } diff --git a/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts b/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts index 6b0c5e8576206..3d43a4bb5988b 100644 --- a/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts +++ b/packages/notebook/src/browser/service/notebook-monaco-text-model-service.ts @@ -31,7 +31,7 @@ export class NotebookMonacoTextModelService { protected readonly monacoTextModelService: MonacoTextModelService; protected readonly cellmodels = new ReferenceCollection( - uri => this.monacoTextModelService.createUnmangedModel(new URI(uri)) + uri => this.monacoTextModelService.createUnmanagedModel(new URI(uri)) ); getOrCreateNotebookCellModelReference(uri: URI): Promise> { @@ -39,7 +39,7 @@ export class NotebookMonacoTextModelService { } async createTextModelsForNotebook(notebook: NotebookModel): Promise { - await Promise.all(notebook.cells.map(cell => this.getOrCreateNotebookCellModelReference(cell.uri))); + await Promise.all(notebook.cells.map(cell => cell.resolveTextModel())); } get onDidCreateNotebookCellModel(): Event { diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index 4b9b1ba3b5a2d..78795e34d0f9f 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -119,7 +119,7 @@ export class NotebookService implements Disposable { this.notebookModels.set(resource.uri.toString(), model); // Resolve cell text models right after creating the notebook model // This ensures that all text models are available in the plugin host - this.textModelService.createTextModelsForNotebook(model); + await this.textModelService.createTextModelsForNotebook(model); this.didAddNotebookDocumentEmitter.fire(model); model.onDidDispose(() => { this.notebookModels.delete(resource.uri.toString()); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 303d075e0cd2a..038666cd24d21 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -264,7 +264,6 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidChangeMetadataEmitter.dispose(); this.onDidChangeInternalMetadataEmitter.dispose(); this.onDidChangeLanguageEmitter.dispose(); - this.textModel?.dispose(); this.toDispose.dispose(); } @@ -356,9 +355,10 @@ export class NotebookCellModel implements NotebookCell, Disposable { const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri); this.textModel = ref.object; - this.textModel.onDidChangeContent(e => { + this.toDispose.push(ref); + this.toDispose.push(this.textModel.onDidChangeContent(e => { this.props.source = e.model.getText(); - }); + })); return ref.object; } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 4230666db5e63..ed792866028b3 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -331,7 +331,7 @@ export class NotebookModel implements Saveable, Disposable { } - protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise { + protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void { const cells = newCells.map(cell => { const handle = this.nextHandle++; return this.cellModelFactory({ @@ -362,10 +362,6 @@ export class NotebookModel implements Saveable, Disposable { async () => this.replaceCells(start, deleteCount, newCells, false)); } - // Ensure that all text model have been created - // Otherwise we run into a race condition once we fire `onDidChangeContent` - await Promise.all(cells.map(cell => cell.resolveTextModel())); - this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes }); if (cells.length > 0) { diff --git a/packages/notebook/src/common/notebook-common.ts b/packages/notebook/src/common/notebook-common.ts index bf7355035c796..58e8fdc8ada9b 100644 --- a/packages/notebook/src/common/notebook-common.ts +++ b/packages/notebook/src/common/notebook-common.ts @@ -273,7 +273,7 @@ export namespace CellUri { const s = handle.toString(_radix); const p = s.length < _lengths.length ? _lengths[s.length - 1] : 'z'; - const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')} `; + const fragment = `${p}${s}s${Buffer.from(BinaryBuffer.fromString(notebook.scheme).buffer).toString('base64')}`; return notebook.withScheme(cellUriScheme).withFragment(fragment); } diff --git a/packages/plugin-ext/src/plugin/editors-and-documents.ts b/packages/plugin-ext/src/plugin/editors-and-documents.ts index d5a98a9863fa3..23a39b050875e 100644 --- a/packages/plugin-ext/src/plugin/editors-and-documents.ts +++ b/packages/plugin-ext/src/plugin/editors-and-documents.ts @@ -46,6 +46,10 @@ export class EditorsAndDocumentsExtImpl implements EditorsAndDocumentsExt { private readonly editors = new Map(); async $acceptEditorsAndDocumentsDelta(delta: EditorsAndDocumentsDelta): Promise { + this.acceptEditorsAndDocumentsDelta(delta); + } + + acceptEditorsAndDocumentsDelta(delta: EditorsAndDocumentsDelta): void { const removedDocuments = new Array(); const addedDocuments = new Array(); const removedEditors = new Array(); diff --git a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts index 456e218b3d250..edc9a008853d0 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebook-document.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebook-document.ts @@ -27,6 +27,7 @@ import * as typeConverters from '../type-converters'; import { ModelAddedData, NotebookCellDto, NotebookCellsChangedEventDto, NotebookModelAddedData, NotebookOutputDto } from '../../common'; import { NotebookRange } from '../types-impl'; import { DocumentsExtImpl } from '../documents'; +import { UriComponents } from '../../common/uri-components'; class RawContentChangeEvent { @@ -345,6 +346,9 @@ export class NotebookDocument implements Disposable { return; } + const addedDocuments: ModelAddedData[] = []; + const removedDocuments: UriComponents[] = []; + const contentChangeEvents: RawContentChangeEvent[] = []; splices.reverse().forEach(splice => { @@ -353,9 +357,7 @@ export class NotebookDocument implements Disposable { const extCell = new Cell(this, this.editorsAndDocuments, cell); if (!initialization) { - this.editorsAndDocuments.$acceptEditorsAndDocumentsDelta({ - addedDocuments: [Cell.asModelAddData(cell)] - }); + addedDocuments.push(Cell.asModelAddData(cell)); } return extCell; }); @@ -364,10 +366,18 @@ export class NotebookDocument implements Disposable { const deletedItems = this.cells.splice(splice.start, splice.deleteCount, ...newCells); for (const cell of deletedItems) { changeEvent.deletedItems.push(cell.apiCell); + removedDocuments.push(cell.uri.toComponents()); } contentChangeEvents.push(changeEvent); }); + if (addedDocuments.length > 0 || removedDocuments.length > 0) { + this.editorsAndDocuments.acceptEditorsAndDocumentsDelta({ + addedDocuments, + removedDocuments + }); + } + if (bucket) { for (const changeEvent of contentChangeEvents) { bucket.push(changeEvent.asApiEvent()); diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 58a920e76d992..a618f8ad0222d 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -22,7 +22,7 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR import { URI as TheiaURI } from '../types-impl'; import * as theia from '@theia/plugin'; import { - CommandRegistryExt, ModelAddedData, NotebookCellStatusBarListDto, NotebookDataDto, + CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto, NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin, PLUGIN_RPC_CONTEXT } from '../../common'; @@ -205,7 +205,6 @@ export class NotebooksExtImpl implements NotebooksExt { async $acceptDocumentsAndEditorsDelta(delta: NotebookDocumentsAndEditorsDelta): Promise { const removedCellDocuments: UriComponents[] = []; - const addedCellDocuments: ModelAddedData[] = []; if (delta.removedDocuments) { for (const uri of delta.removedDocuments) { const revivedUri = URI.fromComponents(uri); @@ -226,10 +225,12 @@ export class NotebooksExtImpl implements NotebooksExt { } } - // publish all removed cell documents first - await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ - removedDocuments: removedCellDocuments - }); + if (removedCellDocuments.length > 0) { + // publish all removed cell documents first + this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ + removedDocuments: removedCellDocuments + }); + } if (delta.addedDocuments) { for (const modelData of delta.addedDocuments) { @@ -250,17 +251,18 @@ export class NotebooksExtImpl implements NotebooksExt { this.documents.get(uri.toString())?.dispose(); this.documents.set(uri.toString(), document); - addedCellDocuments.push(...modelData.cells.map(cell => Cell.asModelAddData(cell))); + if (modelData.cells.length > 0) { + // Publish new cell documents before calling the notebook document open event + // During this event, extensions might request the cell document and we want to make sure it is available + this.textDocumentsAndEditors.acceptEditorsAndDocumentsDelta({ + addedDocuments: modelData.cells.map(cell => Cell.asModelAddData(cell)) + }); + } this.onDidOpenNotebookDocumentEmitter.fire(document.apiNotebook); } } - // publish all added cell documents in a separate call - await this.textDocumentsAndEditors.$acceptEditorsAndDocumentsDelta({ - addedDocuments: addedCellDocuments - }); - if (delta.addedEditors) { for (const editorModelData of delta.addedEditors) { if (this.editors.has(editorModelData.id)) { From f27d3b28cf8203d69b4035257e6ab9d14bf62979 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Mon, 6 May 2024 13:14:22 +0200 Subject: [PATCH 213/441] Handle `isFileSystemResource` context key (#13664) --- .../src/browser/navigator-context-key-service.ts | 10 ++++++++++ packages/navigator/src/browser/navigator-widget.tsx | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/navigator/src/browser/navigator-context-key-service.ts b/packages/navigator/src/browser/navigator-context-key-service.ts index e8c7fd8b0d666..df741f240cdc4 100644 --- a/packages/navigator/src/browser/navigator-context-key-service.ts +++ b/packages/navigator/src/browser/navigator-context-key-service.ts @@ -45,12 +45,22 @@ export class NavigatorContextKeyService { return this._explorerResourceIsFolder; } + protected _isFileSystemResource: ContextKey; + + /** + * True when the Explorer or editor file is a file system resource that can be handled from a file system provider. + */ + get isFileSystemResource(): ContextKey { + return this._isFileSystemResource; + } + @postConstruct() protected init(): void { this._explorerViewletVisible = this.contextKeyService.createKey('explorerViewletVisible', false); this._explorerViewletFocus = this.contextKeyService.createKey('explorerViewletFocus', false); this._filesExplorerFocus = this.contextKeyService.createKey('filesExplorerFocus', false); this._explorerResourceIsFolder = this.contextKeyService.createKey('explorerResourceIsFolder', false); + this._isFileSystemResource = this.contextKeyService.createKey('isFileSystemResource', false); } } diff --git a/packages/navigator/src/browser/navigator-widget.tsx b/packages/navigator/src/browser/navigator-widget.tsx index c4a2ae3857cb3..030102a9f85e1 100644 --- a/packages/navigator/src/browser/navigator-widget.tsx +++ b/packages/navigator/src/browser/navigator-widget.tsx @@ -19,7 +19,7 @@ import { Message } from '@theia/core/shared/@phosphor/messaging'; import URI from '@theia/core/lib/common/uri'; import { CommandService } from '@theia/core/lib/common'; import { Key, TreeModel, ContextMenuRenderer, ExpandableTreeNode, TreeProps, TreeNode } from '@theia/core/lib/browser'; -import { DirNode } from '@theia/filesystem/lib/browser'; +import { DirNode, FileStatNodeData } from '@theia/filesystem/lib/browser'; import { WorkspaceService, WorkspaceCommands } from '@theia/workspace/lib/browser'; import { WorkspaceNode, WorkspaceRootNode } from './navigator-tree'; import { FileNavigatorModel } from './navigator-model'; @@ -210,6 +210,10 @@ export class FileNavigatorWidget extends AbstractNavigatorTreeWidget { protected updateSelectionContextKeys(): void { this.contextKeyService.explorerResourceIsFolder.set(DirNode.is(this.model.selectedNodes[0])); + // As `FileStatNode` only created if `FileService.resolve` was successful, we can safely assume that + // a valid `FileSystemProvider` is available for the selected node. So we skip an additional check + // for provider availability here and check the node type. + this.contextKeyService.isFileSystemResource.set(FileStatNodeData.is(this.model.selectedNodes[0])); } } From fda4e5630290bef85badfc4dd11f9fb8f8293d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 6 May 2024 13:33:15 +0200 Subject: [PATCH 214/441] Update built-ins to 1.88.1 level (#13673) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of ST Microelectronics Signed-off-by: Thomas Mäder --- examples/api-tests/src/typescript.spec.js | 4 ++-- package.json | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index ff2da90e99141..e6f08d28344b9 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -102,10 +102,10 @@ describe('TypeScript', function () { const editorWidget = widget instanceof EditorWidget ? widget : undefined; const editor = MonacoEditor.get(editorWidget); assert.isDefined(editor); + await timeout(1000); // workaround for https://github.com/eclipse-theia/theia/issues/13679 // wait till tsserver is running, see: // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103 - await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile')); - // wait till projects are loaded, see: + // await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile')); // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911 await waitForAnimation(() => !progressStatusBarItem.currentProgress); return /** @type {MonacoEditor} */ (editor); diff --git a/package.json b/package.json index d82581281ddd4..d795de6073493 100644 --- a/package.json +++ b/package.json @@ -102,11 +102,9 @@ ], "theiaPluginsDir": "plugins", "theiaPlugins": { - "eclipse-theia.builtin-extension-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.83.1/file/eclipse-theia.builtin-extension-pack-1.83.1.vsix", + "eclipse-theia.builtin-extension-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.88.1/file/eclipse-theia.builtin-extension-pack-1.88.1.vsix", "EditorConfig.EditorConfig": "https://open-vsx.org/api/EditorConfig/EditorConfig/0.16.6/file/EditorConfig.EditorConfig-0.16.6.vsix", - "dbaeumer.vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.4.2/file/dbaeumer.vscode-eslint-2.4.2.vsix", - "ms-vscode.js-debug": "https://open-vsx.org/api/ms-vscode/js-debug/1.83.1/file/ms-vscode.js-debug-1.83.1.vsix", - "ms-vscode.js-debug-companion": "https://open-vsx.org/api/ms-vscode/js-debug-companion/1.1.2/file/ms-vscode.js-debug-companion-1.1.2.vsix" + "dbaeumer.vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.4.2/file/dbaeumer.vscode-eslint-2.4.2.vsix" }, "theiaPluginsExcludeIds": [ "ms-vscode.js-debug-companion", From 324457a160faa08b8ecf00cf3e27063964fc3c3f Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Mon, 6 May 2024 14:42:24 +0200 Subject: [PATCH 215/441] Fix "Open With..." command visibility (#13678) --- packages/navigator/src/browser/navigator-contribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts index 7a914a2d9ea1a..3a41805dc16b6 100644 --- a/packages/navigator/src/browser/navigator-contribution.ts +++ b/packages/navigator/src/browser/navigator-contribution.ts @@ -384,6 +384,7 @@ export class FileNavigatorContribution extends AbstractViewContribution Date: Mon, 6 May 2024 15:11:15 +0200 Subject: [PATCH 216/441] chore: refer to commit sha of actions instead of tags (#13625) Instead of using tags to refer to a version of an action (e.g. `actions/checkout@v3`), we now use its commit sha. The problem with tags is that they are mutable and can be changed to point to a different commit. This opens a vector for supply chain attacks. For one action, the user account had been renamed from 'nick-invision' to 'nick-fields' which caused a redirect when resolving the action with the old username. This is potentially dangerous when the account name gets claimed again, so we now use the new user name to refer to the action. Increases the version of the create-pull-request action from v4 to v6 to have matching versions of the action across workflows. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- .github/workflows/ci-cd.yml | 12 ++++++------ .github/workflows/license-check.yml | 6 +++--- .github/workflows/native-dependencies.yml | 8 ++++---- .github/workflows/performance-tests.yml | 8 ++++---- .github/workflows/playwright.yml | 6 +++--- .github/workflows/production-smoke-test.yml | 6 +++--- .github/workflows/publish-gh-pages.yml | 10 +++++----- .github/workflows/publish-next.yml | 6 +++--- .github/workflows/publish-release.yml | 10 +++++----- .github/workflows/set-milestone-on-pr.yml | 4 ++-- .github/workflows/translation.yml | 10 +++++----- 11 files changed, 43 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 298a30258921d..2bdf6bf21774a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -18,16 +18,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18.x registry-url: 'https://registry.npmjs.org' - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: '3.11' @@ -59,16 +59,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: ${{ matrix.node }} registry-url: 'https://registry.npmjs.org' - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: '3.11' diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 82a562fa2adeb..c235e3e0a21d6 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -28,18 +28,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: fetch-depth: 2 - name: Use Node.js ${{ matrix.node }} - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: ${{ matrix.node }} registry-url: 'https://registry.npmjs.org' - name: Use Java ${{ matrix.java }} - uses: actions/setup-java@v3 + uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0 with: distribution: 'adopt' java-version: ${{ matrix.java }} diff --git a/.github/workflows/native-dependencies.yml b/.github/workflows/native-dependencies.yml index 095ff9912e9fc..009aa57436497 100644 --- a/.github/workflows/native-dependencies.yml +++ b/.github/workflows/native-dependencies.yml @@ -10,17 +10,17 @@ jobs: os: ['ubuntu-20.04', 'windows-latest', 'macos-latest'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 # Update the node version here after every Electron upgrade - name: Use Node.js 18.17.0 - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: '18.17.0' registry-url: 'https://registry.npmjs.org' - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: '3.11' @@ -44,7 +44,7 @@ jobs: run: yarn zip:native:dependencies - name: Upload Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: native-dependencies path: ./scripts/native-dependencies-*.zip diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 82f338bfad597..8ee67b675cd6d 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -12,16 +12,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: "18.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: '3.11' @@ -44,7 +44,7 @@ jobs: run: xvfb-run yarn performance:startup:electron - name: Analyze performance results - uses: benchmark-action/github-action-benchmark@v1 + uses: benchmark-action/github-action-benchmark@fd31771ce86cc65eab85653da103f71ab1b4479c # v1.9.0 with: name: Performance Benchmarks tool: "customSmallerIsBetter" diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 03c73cdeb4922..bd800baad34e0 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,16 +20,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js "18.x" - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: "18.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: "3.11" diff --git a/.github/workflows/production-smoke-test.yml b/.github/workflows/production-smoke-test.yml index 51a5267eb67c0..0f65de998de67 100644 --- a/.github/workflows/production-smoke-test.yml +++ b/.github/workflows/production-smoke-test.yml @@ -18,16 +18,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js "18.x" - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: "18.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: "3.11" diff --git a/.github/workflows/publish-gh-pages.yml b/.github/workflows/publish-gh-pages.yml index 68e5311ca3004..8840fdda45173 100644 --- a/.github/workflows/publish-gh-pages.yml +++ b/.github/workflows/publish-gh-pages.yml @@ -15,18 +15,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: fetch-depth: 0 # To fetch all history for all branches and tags. (Will be required for caching with lerna: https://github.com/markuplint/markuplint/pull/111) - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: '18.x' registry-url: 'https://registry.npmjs.org' - name: Use Python 3.x - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: '3.x' @@ -45,14 +45,14 @@ jobs: NODE_OPTIONS: --max_old_space_size=9216 - name: Publish GH Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./gh-pages force_orphan: true # will only keep latest commit on branch gh-pages - name: Publish NPM - uses: nick-invision/retry@v2 + uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 with: timeout_minutes: 5 retry_wait_seconds: 30 diff --git a/.github/workflows/publish-next.yml b/.github/workflows/publish-next.yml index 07a015fe022e4..05cec96a6325c 100644 --- a/.github/workflows/publish-next.yml +++ b/.github/workflows/publish-next.yml @@ -12,20 +12,20 @@ jobs: timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: # To fetch all history for all branches and tags. # Required for lerna to determine the version of the next package. fetch-depth: 0 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: "3.11" diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f4b0b67a332a5..7ab6475f09b96 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -22,16 +22,16 @@ jobs: timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: "3.11" @@ -54,7 +54,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} - name: Get Actor User Data - uses: octokit/request-action@v2.x + uses: octokit/request-action@21d174fc38ff59af9cf4d7e07347d29df6dbaa99 # v2.3.0 id: actor_user_data with: route: GET /users/{user} @@ -63,7 +63,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: commiter: ${{ github.actor }} <${{ fromJson(steps.actor_user_data.outputs.data).email }}> author: ${{ github.actor }} <${{ fromJson(steps.actor_user_data.outputs.data).email }}> diff --git a/.github/workflows/set-milestone-on-pr.yml b/.github/workflows/set-milestone-on-pr.yml index 226b68474c4f3..b44da2fce9250 100644 --- a/.github/workflows/set-milestone-on-pr.yml +++ b/.github/workflows/set-milestone-on-pr.yml @@ -25,13 +25,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - id: compute-milestone run: | export THEIA_CORE_VERSION=$(node -p "require(\"./packages/core/package.json\").version") echo "MILESTONE_NUMBER=$(npx -q semver@7 --increment minor $THEIA_CORE_VERSION)" >> $GITHUB_ENV - id: set - uses: actions/github-script@v3 + uses: actions/github-script@ffc2c79a5b2490bd33e0a41c1de74b877714d736 # v3.2.0 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/.github/workflows/translation.yml b/.github/workflows/translation.yml index 815165c7f202e..2a5f58508fe1a 100644 --- a/.github/workflows/translation.yml +++ b/.github/workflows/translation.yml @@ -10,16 +10,16 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - name: Use Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.x - uses: actions/setup-python@v4 + uses: actions/setup-python@b64ffcaf5b410884ad320a9cfac8866006a109aa # v4.8.0 with: python-version: "3.11" @@ -44,7 +44,7 @@ jobs: DEEPL_API_TOKEN: ${{ secrets.DEEPL_API_TOKEN }} - name: Get Actor User Data - uses: octokit/request-action@v2.x + uses: octokit/request-action@21d174fc38ff59af9cf4d7e07347d29df6dbaa99 # v2.3.0 id: actor_user_data with: route: GET /users/{user} @@ -53,7 +53,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Pull Request - uses: peter-evans/create-pull-request@v4 + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 with: commiter: ${{ github.actor }} <${{ fromJson(steps.actor_user_data.outputs.data).email }}> author: ${{ github.actor }} <${{ fromJson(steps.actor_user_data.outputs.data).email }}> From 0501d747d03ee8eeaea0880c92e640cc1d410cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 6 May 2024 15:28:28 +0200 Subject: [PATCH 217/441] Stub `registerMappedEditProvider` (#13681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../plugin-ext/src/plugin/plugin-context.ts | 8 +++ packages/plugin/src/theia.d.ts | 1 + .../theia.proposed.mappedEditsProvider.d.ts | 59 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index a72d2af839842..fa19ce1e12d17 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -1218,9 +1218,17 @@ export function createAPIFactory( } }; + const chat: typeof theia.chat = { + /** @stubbed MappedEditsProvider */ + registerMappedEditsProvider(documentSelector: theia.DocumentSelector, provider: theia.MappedEditsProvider): Disposable { + return Disposable.NULL; + } + }; + return { version: require('../../package.json').version, authentication, + chat, commands, comments, window, diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 3ffce743ddb84..17468c2b7b1bd 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -30,6 +30,7 @@ import './theia.proposed.dropMetadata'; import './theia.proposed.editSessionIdentityProvider'; import './theia.proposed.extensionsAny'; import './theia.proposed.externalUriOpener'; +import './theia.proposed.mappedEditsProvider'; import './theia.proposed.notebookCellExecutionState'; import './theia.proposed.notebookKernelSource'; import './theia.proposed.notebookMessaging'; diff --git a/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts b/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts new file mode 100644 index 0000000000000..cedc13e167e6b --- /dev/null +++ b/packages/plugin/src/theia.proposed.mappedEditsProvider.d.ts @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export module '@theia/plugin' { + + export interface DocumentContextItem { + readonly uri: Uri; + readonly version: number; + readonly ranges: Range[]; + } + + export interface MappedEditsContext { + documents: DocumentContextItem[][]; + } + + /** + * Interface for providing mapped edits for a given document. + */ + export interface MappedEditsProvider { + /** + * Provide mapped edits for a given document. + * @param document The document to provide mapped edits for. + * @param codeBlocks Code blocks that come from an LLM's reply. + * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks + * and let the lang server decide what to do with them. + * @param context The context for providing mapped edits. + * @param token A cancellation token. + * @returns A provider result of text edits. + */ + provideMappedEdits( + document: TextDocument, + codeBlocks: string[], + context: MappedEditsContext, + token: CancellationToken + ): ProviderResult; + } + + export namespace chat { + export function registerMappedEditsProvider(documentSelector: DocumentSelector, provider: MappedEditsProvider): Disposable; + } +} From ba95d1825cd010ebcba939266c2b0352c8c2ec48 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Tue, 7 May 2024 08:24:12 +0200 Subject: [PATCH 218/441] chore: update electron to ^28.2.8 (#13580) Updates the Electron dependency to ^28.2.8 which is also used in VS Code. Additional changes: - update electron-mocha to ^12.3.0 to fix the Electron smoke test - adds a workaround for a type clash in rebuild.ts --- .../application-manager/src/rebuild.ts | 7 +- examples/electron/package.json | 2 +- package.json | 2 +- packages/core/README.md | 2 +- packages/electron/README.md | 2 +- packages/electron/package.json | 2 +- yarn.lock | 131 +++++++----------- 7 files changed, 63 insertions(+), 85 deletions(-) diff --git a/dev-packages/application-manager/src/rebuild.ts b/dev-packages/application-manager/src/rebuild.ts index 82e454be7c802..cbb0e59e72403 100644 --- a/dev-packages/application-manager/src/rebuild.ts +++ b/dev-packages/application-manager/src/rebuild.ts @@ -304,7 +304,12 @@ async function guardExit(run: (token: ExitToken) => Promise): Promise { return await run(token); } finally { for (const signal of EXIT_SIGNALS) { - process.off(signal, signalListener); + // FIXME we have a type clash here between Node, Electron and Mocha. + // Typescript is resolving here to Electron's Process interface which extends the NodeJS.EventEmitter interface + // However instead of the actual NodeJS.EventEmitter interface it resolves to an empty stub of Mocha + // Therefore it can't find the correct "off" signature and throws an error + // By casting to the NodeJS.EventEmitter ourselves, we short circuit the resolving and it succeeds + (process as NodeJS.EventEmitter).off(signal, signalListener); } } } diff --git a/examples/electron/package.json b/examples/electron/package.json index a310f569423f0..fd403b59e02ac 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -92,6 +92,6 @@ }, "devDependencies": { "@theia/cli": "1.49.0", - "electron": "^23.2.4" + "electron": "^28.2.8" } } diff --git a/package.json b/package.json index d795de6073493..39b930fdc69aa 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "chalk": "4.0.0", "concurrently": "^3.5.0", "debug": "^4.3.2", - "electron-mocha": "^11.0.2", + "electron-mocha": "^12.3.0", "eslint": "7", "eslint-plugin-deprecation": "~1.2.1", "eslint-plugin-import": "^2.27.5", diff --git a/packages/core/README.md b/packages/core/README.md index 44750b88a67eb..c6887f0664ac1 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -70,7 +70,7 @@ export class SomeClass { - `@theia/core/electron-shared/...` - `native-keymap` (from [`native-keymap@^2.2.1`](https://www.npmjs.com/package/native-keymap)) - - `electron` (from [`electron@^23.2.4`](https://www.npmjs.com/package/electron)) + - `electron` (from [`electron@^28.2.8`](https://www.npmjs.com/package/electron)) - `electron-store` (from [`electron-store@^8.0.0`](https://www.npmjs.com/package/electron-store)) - `fix-path` (from [`fix-path@^3.0.0`](https://www.npmjs.com/package/fix-path)) - `@theia/core/shared/...` diff --git a/packages/electron/README.md b/packages/electron/README.md index 945e4f15c23e0..de0e9ec00b7d2 100644 --- a/packages/electron/README.md +++ b/packages/electron/README.md @@ -18,7 +18,7 @@ The `@theia/electron` extension bundles all Electron-specific dependencies and c - `@theia/electron/shared/...` - `native-keymap` (from [`native-keymap@^2.2.1`](https://www.npmjs.com/package/native-keymap)) - - `electron` (from [`electron@^23.2.4`](https://www.npmjs.com/package/electron)) + - `electron` (from [`electron@^28.2.8`](https://www.npmjs.com/package/electron)) - `electron-store` (from [`electron-store@^8.0.0`](https://www.npmjs.com/package/electron-store)) - `fix-path` (from [`fix-path@^3.0.0`](https://www.npmjs.com/package/fix-path)) diff --git a/packages/electron/package.json b/packages/electron/package.json index 0ace72c8a205b..a31c805bc1252 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -12,7 +12,7 @@ "@theia/re-exports": "1.49.0" }, "peerDependencies": { - "electron": "^23.2.4" + "electron": "^28.2.8" }, "theiaReExports": { "shared": { diff --git a/yarn.lock b/yarn.lock index 8b45a3820e7f0..7417e078ff7a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2140,7 +2140,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^16.11.26", "@types/node@^18.11.18": +"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18": version "18.19.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.9.tgz#6c2624c3a05bfa3a2735c533f95597ffacbb5608" integrity sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw== @@ -2492,11 +2492,6 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - "@virtuoso.dev/react-urx@^0.2.12": version "0.2.13" resolved "https://registry.yarnpkg.com/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz#e2cfc42d259d2a002695e7517d34cb97b64ee9c4" @@ -4478,13 +4473,6 @@ debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, de dependencies: ms "2.1.2" -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - debug@^3.0.1, debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -4901,17 +4889,16 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" -electron-mocha@^11.0.2: - version "11.0.2" - resolved "https://registry.yarnpkg.com/electron-mocha/-/electron-mocha-11.0.2.tgz#f8fd6c3af539f3c7a9aed4aba29cf12c3f408810" - integrity sha512-fOk+zUgSIsmL2cuIrd7IlK4eRhGVi1PYIB3QvqiBO+6f6AP8XLkYkT9eORlL2xwaS3yAAk02Y+4OTuhtqHPkEQ== +electron-mocha@^12.3.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/electron-mocha/-/electron-mocha-12.3.0.tgz#10b08a227667c44a3cdcb377069bcc7a13b6868e" + integrity sha512-PwAlZxe7+4aZ2ml2toC3dkAfrw5WsRo1P0P2uRYN7jLyaLQXD9VYMY22T9eI/JOhNUGaKy1dlYML429yk6/lFw== dependencies: ansi-colors "^4.1.1" electron-window "^0.8.0" - fs-extra "^10.0.0" - mocha "^9.1.1" - which "^2.0.2" - yargs "^16.2.0" + mocha "^10.4.0" + which "^4.0.0" + yargs "^17.7.2" electron-rebuild@^3.2.7: version "3.2.9" @@ -4953,13 +4940,13 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@^23.2.4: - version "23.3.13" - resolved "https://registry.yarnpkg.com/electron/-/electron-23.3.13.tgz#bd2ae8eef83d1ed9504410fbe03598176c5f8817" - integrity sha512-BaXtHEb+KYKLouUXlUVDa/lj9pj4F5kiE0kwFdJV84Y2EU7euIDgPthfKtchhr5MVHmjtavRMIV/zAwEiSQ9rQ== +electron@^28.2.8: + version "28.2.10" + resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.10.tgz#4e168568406a8b1e9b9a5859e988c905b9a57570" + integrity sha512-0rGBJNogcl2FIRxGRUv9zuMaBP78nSBJW+Bd1U7OGeg8IEkSIbHOhfn71XoGxgbOUSCEXjjyftq4mtAAVbUsZQ== dependencies: "@electron/get" "^2.0.0" - "@types/node" "^16.11.26" + "@types/node" "^18.11.18" extract-zip "^2.0.1" emoji-regex@^8.0.0: @@ -6193,6 +6180,17 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@8.1.0, glob@^8.0.1, glob@^8.0.3, glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@^10.2.2: version "10.3.10" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" @@ -6216,17 +6214,6 @@ glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1, glob@^8.0.3, glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - glob@^9.2.0: version "9.3.5" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21" @@ -6321,11 +6308,6 @@ graceful-fs@4.2.11, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.1 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - handlebars@^4.7.7: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" @@ -7098,6 +7080,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -8104,13 +8091,6 @@ minimatch@3.0.5: dependencies: brace-expansion "^1.1.7" -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== - dependencies: - brace-expansion "^1.1.7" - minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -8297,32 +8277,28 @@ mocha@^10.1.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -mocha@^9.1.1: - version "9.2.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" - integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== +mocha@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.4.0.tgz#ed03db96ee9cfc6d20c56f8e2af07b961dbae261" + integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" - debug "4.3.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.2.0" - growl "1.10.5" + glob "8.1.0" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "4.2.1" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.1" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - workerpool "6.2.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -8437,11 +8413,6 @@ nano@^10.1.3: node-abort-controller "^3.0.1" qs "^6.11.0" -nanoid@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== - nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -12176,13 +12147,6 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@2.0.2, which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - which@^1.2.0, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -12190,6 +12154,13 @@ which@^1.2.0, which@^1.2.9: dependencies: isexe "^2.0.0" +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + which@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" @@ -12197,6 +12168,13 @@ which@^3.0.0: dependencies: isexe "^2.0.0" +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + wide-align@^1.1.0, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -12222,11 +12200,6 @@ worker-loader@^3.0.8: loader-utils "^2.0.0" schema-utils "^3.0.0" -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== - workerpool@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" @@ -12500,7 +12473,7 @@ yargs@^15.0.2, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.0.1, yargs@^17.6.2: +yargs@^17.0.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== From 767311b0b84f967cacad00763564936c57c9398e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 7 May 2024 16:45:30 +0200 Subject: [PATCH 219/441] Improve support for creating new notebooks (#13696) --- .../notebook-actions-contribution.ts | 11 +++--- .../browser/notebook-editor-widget-factory.ts | 5 ++- .../notebook-model-resolver-service.ts | 28 ++++++--------- .../src/browser/service/notebook-service.ts | 35 ++++++++++--------- .../src/browser/view-model/notebook-model.ts | 31 ++++++---------- .../notebooks/notebook-documents-main.ts | 16 +++++---- .../notebooks/notebook-editors-main.ts | 17 +++++---- .../main/browser/notebooks/notebooks-main.ts | 2 +- .../src/plugin/notebook/notebooks.ts | 25 +++++++++++-- 9 files changed, 93 insertions(+), 77 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index ee37243bdd706..a261b6dfa934f 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -120,16 +120,17 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; let insertIndex: number = 0; - if (index && index >= 0) { - insertIndex = index as number; + if (typeof index === 'number' && index >= 0) { + insertIndex = index; } else if (notebookModel.selectedCell && typeof index === 'string') { // if index is -1 insert below otherwise at the index of the selected cell which is above the selected. insertIndex = notebookModel.cells.indexOf(notebookModel.selectedCell) + (index === 'below' ? 1 : 0); } - let firstCodeCell; + let cellLanguage: string = 'markdown'; if (cellKind === CellKind.Code) { - firstCodeCell = notebookModel.cells.find(cell => cell.cellKind === CellKind.Code); + const firstCodeCell = notebookModel.cells.find(cell => cell.cellKind === CellKind.Code); + cellLanguage = firstCodeCell?.language ?? 'plaintext'; } notebookModel.applyEdits([{ @@ -138,7 +139,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon count: 0, cells: [{ cellKind, - language: firstCodeCell?.language ?? 'markdown', + language: cellLanguage, source: '', outputs: [], metadata: {}, diff --git a/packages/notebook/src/browser/notebook-editor-widget-factory.ts b/packages/notebook/src/browser/notebook-editor-widget-factory.ts index 3582f867e4beb..4cebc7c38663c 100644 --- a/packages/notebook/src/browser/notebook-editor-widget-factory.ts +++ b/packages/notebook/src/browser/notebook-editor-widget-factory.ts @@ -61,8 +61,7 @@ export class NotebookEditorWidgetFactory implements WidgetFactory { return editor; } - private async createEditor(uri: URI, notebookType: string): Promise { - + protected async createEditor(uri: URI, notebookType: string): Promise { return this.createNotebookEditorWidget({ uri, notebookType, @@ -70,7 +69,7 @@ export class NotebookEditorWidgetFactory implements WidgetFactory { }); } - private setLabels(editor: NotebookEditorWidget, uri: URI): void { + protected setLabels(editor: NotebookEditorWidget, uri: URI): void { editor.title.caption = uri.path.fsPath(); if (editor.model?.readOnly) { editor.title.caption += ` • ${nls.localizeByDefault('Read-only')}`; diff --git a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts index ff7e8d368ecd9..ccfee12e5a40c 100644 --- a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts +++ b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts @@ -14,11 +14,11 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Emitter, Resource, ResourceProvider, URI } from '@theia/core'; +import { Emitter, Resource, ResourceProvider, UNTITLED_SCHEME, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { UriComponents } from '@theia/core/lib/common/uri'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { CellKind, NotebookData } from '../../common'; +import { NotebookData } from '../../common'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from './notebook-service'; import { NotebookTypeRegistry } from '../notebook-type-registry'; @@ -28,6 +28,7 @@ import { match } from '@theia/core/lib/common/glob'; export interface UntitledResource { untitledResource: URI | undefined } + @injectable() export class NotebookModelResolverService { @@ -49,14 +50,15 @@ export class NotebookModelResolverService { readonly onDidSaveNotebook = this.onDidSaveNotebookEmitter.event; async resolve(resource: URI, viewType?: string): Promise { - + const existingModel = this.notebookService.getNotebookEditorModel(resource); if (!viewType) { - const existingViewType = this.notebookService.getNotebookEditorModel(resource)?.viewType; - if (existingViewType) { - viewType = existingViewType; + if (existingModel) { + return existingModel; } else { viewType = this.findViewTypeForResource(resource); } + } else if (existingModel?.viewType === viewType) { + return existingModel; } if (!viewType) { @@ -86,7 +88,7 @@ export class NotebookModelResolverService { const suffix = this.getPossibleFileEnding(notebookTypeInfo.selector ?? []) ?? ''; for (let counter = 1; ; counter++) { const candidate = new URI() - .withScheme('untitled') + .withScheme(UNTITLED_SCHEME) .withPath(`Untitled-notebook-${counter}${suffix}`) .withQuery(viewType); if (!this.notebookService.getNotebookEditorModel(candidate)) { @@ -94,7 +96,7 @@ export class NotebookModelResolverService { break; } } - } else if (arg.untitledResource.scheme === 'untitled') { + } else if (arg.untitledResource.scheme === UNTITLED_SCHEME) { resource = arg.untitledResource; } else { throw new Error('Invalid untitled resource: ' + arg.untitledResource.toString() + ' untitled resources with associated file path are not supported yet'); @@ -108,16 +110,8 @@ export class NotebookModelResolverService { protected async resolveExistingNotebookData(resource: Resource, viewType: string): Promise { if (resource.uri.scheme === 'untitled') { - return { - cells: [ - { - cellKind: CellKind.Markup, - language: 'markdown', - outputs: [], - source: '' - } - ], + cells: [], metadata: {} }; } else { diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index 78795e34d0f9f..3707d9f656da0 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -110,11 +110,8 @@ export class NotebookService implements Disposable { } async createNotebookModel(data: NotebookData, viewType: string, resource: Resource): Promise { - const serializer = this.notebookProviders.get(viewType)?.serializer; - if (!serializer) { - throw new Error('no notebook serializer for ' + viewType); - } - + const dataProvider = await this.getNotebookDataProvider(viewType); + const serializer = dataProvider.serializer; const model = this.notebookModelFactory({ data, resource, viewType, serializer }); this.notebookModels.set(resource.uri.toString(), model); // Resolve cell text models right after creating the notebook model @@ -129,13 +126,11 @@ export class NotebookService implements Disposable { } async getNotebookDataProvider(viewType: string): Promise { - await this.ready.promise; - - const result = await this.waitForNotebookProvider(viewType); - if (!result) { + try { + return await this.waitForNotebookProvider(viewType); + } catch { throw new Error(`No provider registered for view type: '${viewType}'`); } - return result; } /** @@ -143,11 +138,12 @@ export class NotebookService implements Disposable { * It takes a few seconds for the plugin host to start so that notebook data providers can be registered. * This methods waits until the notebook provider is registered. */ - protected async waitForNotebookProvider(type: string): Promise { - if (this.notebookProviders.has(type)) { - return this.notebookProviders.get(type); + protected waitForNotebookProvider(type: string): Promise { + const existing = this.notebookProviders.get(type); + if (existing) { + return Promise.resolve(existing); } - const deferred = new Deferred(); + const deferred = new Deferred(); // 20 seconds of timeout const timeoutDuration = 20_000; @@ -161,7 +157,12 @@ export class NotebookService implements Disposable { if (viewType === type) { clearTimeout(timeout); disposable.dispose(); - deferred.resolve(this.notebookProviders.get(type)); + const newProvider = this.notebookProviders.get(type); + if (!newProvider) { + deferred.reject(new Error(`Notebook provider for type ${type} is invalid`)); + } else { + deferred.resolve(newProvider); + } } }); timeout = setTimeout(() => { @@ -170,7 +171,9 @@ export class NotebookService implements Disposable { deferred.reject(new Error(`Timed out while waiting for notebook serializer for type ${type} to be registered`)); }, timeoutDuration); - await Promise.all(this.willUseNotebookSerializerEmitter.fire(type)); + this.ready.promise.then(() => { + this.willUseNotebookSerializerEmitter.fire(type); + }); return deferred.promise; } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index ed792866028b3..fe44f00fd1a08 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -156,7 +156,7 @@ export class NotebookModel implements Saveable, Disposable { this.onDidDisposeEmitter.fire(); } - async save(options: SaveOptions): Promise { + async save(options?: SaveOptions): Promise { this.dirtyCells = []; this.dirty = false; @@ -186,25 +186,8 @@ export class NotebookModel implements Saveable, Disposable { if (!rawData) { throw new Error('could not read notebook snapshot'); } - const data = JSON.parse(rawData); - const cells = data.cells.map((cell: CellData, index: number) => { - const handle = this.nextHandle++; - return this.cellModelFactory({ - uri: CellUri.generate(this.uri, handle), - handle: handle, - source: cell.source, - language: cell.language, - cellKind: cell.cellKind, - outputs: cell.outputs, - metadata: cell.metadata, - internalMetadata: cell.internalMetadata, - collapseState: cell.collapseState - }); - }); - this.addCellOutputListeners(cells); - - this.metadata = data.metadata; - + const data = JSON.parse(rawData) as NotebookData; + this.setData(data); } async revert(options?: Saveable.RevertOptions): Promise { @@ -229,6 +212,14 @@ export class NotebookModel implements Saveable, Disposable { } } + setData(data: NotebookData): void { + // Replace all cells in the model + this.replaceCells(0, this.cells.length, data.cells, false); + this.metadata = data.metadata; + this.dirty = false; + this.onDidChangeContentEmitter.fire(); + } + undo(): void { // TODO we probably need to check if a monaco editor is focused and if so, not undo this.undoRedoService.undo(this.uri); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index 761c63bf1c738..e17840b778e5d 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -25,6 +25,7 @@ import { MAIN_RPC_CONTEXT, NotebookCellsChangedEventDto, NotebookDataDto, Notebo import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; +import { NotebookOpenHandler } from '@theia/notebook/lib/browser/notebook-open-handler'; export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { @@ -35,7 +36,8 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { protected readonly notebookModelResolverService: NotebookModelResolverService; - protected notebookMonacoTextModelService: NotebookMonacoTextModelService; + protected readonly notebookMonacoTextModelService: NotebookMonacoTextModelService; + protected readonly notebookOpenHandler: NotebookOpenHandler; constructor( rpc: RPCProtocol, @@ -43,6 +45,7 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_EXT); this.notebookModelResolverService = container.get(NotebookModelResolverService); + this.notebookOpenHandler = container.get(NotebookOpenHandler); // forward dirty and save events this.disposables.push(this.notebookModelResolverService.onDidChangeDirty(model => this.proxy.$acceptDirtyStateChanged(model.uri.toComponents(), model.isDirty()))); @@ -157,10 +160,10 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { this.proxy.$acceptDirtyStateChanged(ref.uri.toComponents(), true); // apply content changes... slightly HACKY -> this triggers a change event - // if (options.content) { - // const data = NotebookDto.fromNotebookDataDto(options.content); - // ref.notebook.reset(data.cells, data.metadata, ref.object.notebook.transientOptions); - // } + if (options.content) { + const data = NotebookDto.fromNotebookDataDto(options.content); + ref.setData(data); + } return ref.uri.toComponents(); } @@ -171,9 +174,8 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { async $trySaveNotebook(uriComponents: UriComponents): Promise { const uri = URI.fromComponents(uriComponents); - const ref = await this.notebookModelResolverService.resolve(uri); - await ref.save({}); + await ref.save(); ref.dispose(); return true; } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts index 3a9a50208a6b9..1a479206da286 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts @@ -20,16 +20,17 @@ import { UriComponents, URI } from '@theia/core/lib/common/uri'; import { CellRange } from '@theia/notebook/lib/common'; -import { NotebookEditorWidget } from '@theia/notebook/lib/browser'; +import { NotebookEditorWidget, NotebookService } from '@theia/notebook/lib/browser'; import { MAIN_RPC_CONTEXT, NotebookDocumentShowOptions, NotebookEditorRevealType, NotebookEditorsExt, NotebookEditorsMain } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { interfaces } from '@theia/core/shared/inversify'; -import { open, OpenerService } from '@theia/core/lib/browser'; +import { NotebookOpenHandler } from '@theia/notebook/lib/browser/notebook-open-handler'; export class NotebookEditorsMainImpl implements NotebookEditorsMain { protected readonly proxy: NotebookEditorsExt; - protected readonly openerService: OpenerService; + protected readonly notebookService: NotebookService; + protected readonly notebookOpenHandler: NotebookOpenHandler; protected readonly mainThreadEditors = new Map(); @@ -38,12 +39,16 @@ export class NotebookEditorsMainImpl implements NotebookEditorsMain { container: interfaces.Container ) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTEBOOK_EDITORS_EXT); - this.openerService = container.get(OpenerService); + this.notebookService = container.get(NotebookService); + this.notebookOpenHandler = container.get(NotebookOpenHandler); } async $tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: NotebookDocumentShowOptions): Promise { - const editor = await open(this.openerService, URI.fromComponents(uriComponents), {}); - return (editor as NotebookEditorWidget).id; + const editor = await this.notebookOpenHandler.open(URI.fromComponents(uriComponents), { + notebookType: viewType + }); + await editor.ready; + return editor.id; } $tryRevealRange(id: string, range: CellRange, revealType: NotebookEditorRevealType): Promise { throw new Error('Method not implemented.'); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts index 87d1027ebf6d5..1a742fed9b433 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebooks-main.ts @@ -58,7 +58,7 @@ export class NotebooksMainImpl implements NotebooksMain { const plugins = container.get(HostedPluginSupport); this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT); - this.notebookService.onWillUseNotebookSerializer(async event => plugins.activateByNotebookSerializer(event)); + this.notebookService.onWillUseNotebookSerializer(event => plugins.activateByNotebookSerializer(event)); this.notebookService.markReady(); commands.registerArgumentProcessor({ processArgument: arg => { diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index a618f8ad0222d..6dd1b6da06cf8 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -372,6 +372,27 @@ export class NotebooksExtImpl implements NotebooksExt { this.editors.set(editorId, editor); } + private waitForNotebookEditor(editorId: string, duration = 2000): Promise { + const existing = this.editors.get(editorId); + if (existing) { + return Promise.resolve(existing.apiEditor); + } + return new Promise((resolve, reject) => { + const listener = this.onDidChangeVisibleNotebookEditors(() => { + const editor = this.editors.get(editorId); + if (editor) { + clearTimeout(timeout); + listener.dispose(); + resolve(editor.apiEditor); + } + }); + const timeout = setTimeout(() => { + listener.dispose(); + reject(new Error(`Notebook editor did NOT open in ${duration}ms: ${editorId}`)); + }, duration); + }); + } + async createNotebookDocument(options: { viewType: string; content?: theia.NotebookData }): Promise { const canonicalUri = await this.notebookDocumentsProxy.$tryCreateNotebook({ viewType: options.viewType, @@ -395,7 +416,7 @@ export class NotebooksExtImpl implements NotebooksExt { notebookOrUri = await this.openNotebookDocument(notebookOrUri as TheiaURI); } - const notebook = notebookOrUri as theia.NotebookDocument; + const notebook = notebookOrUri; let resolvedOptions: NotebookDocumentShowOptions; if (typeof options === 'object') { @@ -412,7 +433,7 @@ export class NotebooksExtImpl implements NotebooksExt { } const editorId = await this.notebookEditors.$tryShowNotebookDocument(notebook.uri, notebook.notebookType, resolvedOptions); - const editor = editorId && this.editors.get(editorId)?.apiEditor; + const editor = editorId && await this.waitForNotebookEditor(editorId); if (editor) { return editor; From 1028ca7795e7981d0864b92810e59b35cbb189a2 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 8 May 2024 09:10:04 +0200 Subject: [PATCH 220/441] Added basics for notebook cell drag image renderers (#13698) * added basics for drag image renderers and improved basic drag image slightly Signed-off-by: Jonah Iden * fix lint Signed-off-by: Jonah Iden * reiew chages Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- packages/notebook/src/browser/style/index.css | 8 ++++++++ .../browser/view/notebook-cell-list-view.tsx | 17 +++++++++++++++-- .../browser/view/notebook-code-cell-view.tsx | 7 +++++++ .../view/notebook-markdown-cell-view.tsx | 6 ++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 5fd8ce1e2f904..cdfdb60f934de 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -281,3 +281,11 @@ line-height: 22px; opacity: 0.7; } + +.theia-notebook-drag-ghost-image { + position: absolute; + top: -99999px; + left: -99999px; + height: 500px; + width: 500px; +} diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 88a6416b2333d..065a2a19a2edb 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -25,6 +25,7 @@ import { NotebookCellActionContribution } from '../contributions/notebook-cell-a export interface CellRenderer { render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode + renderDragImage(cell: NotebookCellModel): HTMLElement } interface CellListProps { @@ -43,6 +44,8 @@ export class NotebookCellListView extends React.Component this.onDragStart(e, index)} + onDragStart={e => this.onDragStart(e, index, cell)} onDragOver={e => this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} draggable={true} @@ -114,12 +117,22 @@ export class NotebookCellListView extends React.Component, index: number): void { + protected onDragStart(event: React.DragEvent, index: number, cell: NotebookCellModel): void { event.stopPropagation(); if (!this.isEnabled()) { event.preventDefault(); return; } + + if (this.dragGhost) { + this.dragGhost.remove(); + } + this.dragGhost = document.createElement('div'); + this.dragGhost.classList.add('theia-notebook-drag-ghost-image'); + this.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div')); + document.body.appendChild(this.dragGhost); + event.dataTransfer.setDragImage(this.dragGhost, -10, 0); + event.dataTransfer.setData('text/theia-notebook-cell-index', index.toString()); event.dataTransfer.setData('text/plain', this.props.notebookModel.cells[index].source); } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 11f5ac626baf3..596cc22af4c26 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -99,6 +99,13 @@ export class NotebookCodeCellRenderer implements CellRenderer {
    ; } + renderDragImage(cell: NotebookCellModel): HTMLElement { + const dragImage = document.createElement('div'); + dragImage.className = 'theia-notebook-drag-image'; + dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/codeText', 'Code cell selected'); + return dragImage; + } + protected getOrCreateMonacoFontInfo(): BareFontInfo { if (!this.fontInfo) { this.fontInfo = this.createFontInfo(); diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index e02c82c11db4d..1b5cd348dde63 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -42,6 +42,12 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { cell={cell} notebookModel={notebookModel} notebookContextManager={this.notebookContextManager} />; } + renderDragImage(cell: NotebookCellModel): HTMLElement { + const dragImage = document.createElement('div'); + dragImage.className = 'theia-notebook-drag-image'; + dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/markdownText', 'Mardown cell selected'); + return dragImage; + } } interface MarkdownCellProps { From ea87347b44fd5784dcc12fe79c9c7aa3c642599e Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 8 May 2024 12:18:03 +0200 Subject: [PATCH 221/441] Remove git Theia extension from examples (#13274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fail tests if selected repo is undefined * Fix handling selected repository * Wait more robustly for dismissal of widgets via escape. Signed-off-by: Jonas Helming Co-authored-by: Thomas Mäder --- .gitignore | 2 - .theia/settings.json | 2 +- examples/api-tests/src/scm.spec.js | 54 ++++++++++++++++++++++- examples/api-tests/src/typescript.spec.js | 43 +++++++++++------- examples/browser/.theia/settings.json | 7 +++ examples/browser/.theia/tasks.json | 3 ++ examples/browser/package.json | 3 +- examples/browser/tsconfig.json | 3 -- examples/electron/package.json | 3 +- examples/electron/tsconfig.json | 3 -- package.json | 2 - 11 files changed, 92 insertions(+), 33 deletions(-) create mode 100644 examples/browser/.theia/settings.json create mode 100644 examples/browser/.theia/tasks.json diff --git a/.gitignore b/.gitignore index d8cde8f9d7d9a..aab5678aabd83 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,6 @@ errorShots examples/*/src-gen examples/*/gen-webpack.config.js examples/*/gen-webpack.node.config.js -examples/*/.theia -examples/*/.vscode examples/*/.test .browser_modules **/docs/api diff --git a/.theia/settings.json b/.theia/settings.json index 23199246981cf..822df91fdbcc4 100644 --- a/.theia/settings.json +++ b/.theia/settings.json @@ -1,5 +1,5 @@ { - "editor.formatOnSave": true, + "editor.formatOnSave": false, "editor.insertSpaces": true, "[typescript]": { "editor.tabSize": 4 diff --git a/examples/api-tests/src/scm.spec.js b/examples/api-tests/src/scm.spec.js index ef759e7187ab8..7ff2979f98113 100644 --- a/examples/api-tests/src/scm.spec.js +++ b/examples/api-tests/src/scm.spec.js @@ -14,17 +14,23 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** + + + // @ts-check describe('SCM', function () { const { assert } = chai; + const { animationFrame } = require('@theia/core/lib/browser/browser'); + const { HostedPluginSupport } = require('@theia/plugin-ext/lib/hosted/browser/hosted-plugin'); const Uri = require('@theia/core/lib/common/uri'); const { ApplicationShell } = require('@theia/core/lib/browser/shell/application-shell'); const { ContextKeyService } = require('@theia/core/lib/browser/context-key-service'); const { ScmContribution } = require('@theia/scm/lib/browser/scm-contribution'); const { ScmService } = require('@theia/scm/lib/browser/scm-service'); const { ScmWidget } = require('@theia/scm/lib/browser/scm-widget'); + const { CommandRegistry } = require('@theia/core/lib/common'); /** @type {import('inversify').Container} */ const container = window['theia'].container; @@ -32,6 +38,8 @@ describe('SCM', function () { const scmContribution = container.get(ScmContribution); const shell = container.get(ApplicationShell); const service = container.get(ScmService); + const commandRegistry = container.get(CommandRegistry); + const pluginService = container.get(HostedPluginSupport); /** @type {ScmWidget} */ let scmWidget; @@ -39,10 +47,49 @@ describe('SCM', function () { /** @type {ScmService} */ let scmService; + const gitPluginId = 'vscode.git'; + + /** + * @param {() => unknown} condition + * @param {number | undefined} [timeout] + * @param {string | undefined} [message] + * @returns {Promise} + */ + function waitForAnimation(condition, timeout, message) { + const success = new Promise(async (resolve, reject) => { + if (timeout === undefined) { + timeout = 100000; + } + + let timedOut = false; + const handle = setTimeout(() => { + console.log(message); + timedOut = true; + }, timeout); + + do { + await animationFrame(); + } while (!timedOut && !condition()); + if (timedOut) { + reject(new Error(message ?? 'Wait for animation timed out.')); + } else { + clearTimeout(handle); + resolve(undefined); + } + + }); + return success; + } + beforeEach(async () => { + if (!pluginService.getPlugin(gitPluginId)) { + throw new Error(gitPluginId + ' should be started'); + } + await pluginService.activatePlugin(gitPluginId); await shell.leftPanelHandler.collapse(); scmWidget = await scmContribution.openView({ activate: true, reveal: true }); scmService = service; + await waitForAnimation(() => scmService.selectedRepository, 10000, 'selected repository is not defined'); }); afterEach(() => { @@ -53,7 +100,6 @@ describe('SCM', function () { }); describe('scm-view', () => { - it('the view should open and activate successfully', () => { assert.notEqual(scmWidget, undefined); assert.strictEqual(scmWidget, shell.activeWidget); @@ -125,6 +171,9 @@ describe('SCM', function () { const foundRepository = scmService.findRepository(new Uri.default(rootUri)); assert.notEqual(foundRepository, undefined); } + else { + assert.fail('Selected repository is undefined'); + } }); it('should not find a repository for an unknown uri', () => { @@ -150,6 +199,9 @@ describe('SCM', function () { assert.notEqual(commit, undefined); } } + else { + assert.fail('Selected repository is undefined'); + } }); }); diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index e6f08d28344b9..89619d9138bf7 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -210,16 +210,8 @@ describe('TypeScript', function () { await assertPeekOpened(editor); console.log('closePeek() - Attempt to close by sending "Escape"'); - keybindings.dispatchKeyDown('Escape'); - await waitForAnimation(() => { - const isClosed = !contextKeyService.match('listFocus'); - if (!isClosed) { - console.log('...'); - keybindings.dispatchKeyDown('Escape'); - return false; - } - return true; - }); + await dismissWithEscape('listFocus'); + assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isFalse(contextKeyService.match('referenceSearchVisible')); assert.isFalse(contextKeyService.match('listFocus')); @@ -510,7 +502,23 @@ describe('TypeScript', function () { assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber: 28, column: 1 }).word, 'foo'); }); + async function dismissWithEscape(contextKey) { + keybindings.dispatchKeyDown('Escape'); + // once in a while, a second "Escape" is needed to dismiss widget + return waitForAnimation(() => { + const suggestWidgetDismissed = !contextKeyService.match(contextKey); + if (!suggestWidgetDismissed) { + console.log(`Re-try to dismiss ${contextKey} using "Escape" key`); + keybindings.dispatchKeyDown('Escape'); + return false; + } + return true; + }, 5000, `${contextKey} widget not dismissed`); + } + it('editor.action.triggerParameterHints', async function () { + this.timeout(30000); + console.log('start trigger parameter hint'); const editor = await openEditor(demoFileUri); // const demoInstance = new DemoClass('|demo'); editor.getControl().setPosition({ lineNumber: 24, column: 37 }); @@ -520,13 +528,14 @@ describe('TypeScript', function () { assert.isFalse(contextKeyService.match('parameterHintsVisible')); await commands.executeCommand('editor.action.triggerParameterHints'); + console.log('trigger command'); await waitForAnimation(() => contextKeyService.match('parameterHintsVisible')); + console.log('context key matched'); assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isTrue(contextKeyService.match('parameterHintsVisible')); - keybindings.dispatchKeyDown('Escape'); - await waitForAnimation(() => !contextKeyService.match('parameterHintsVisible')); + await dismissWithEscape('parameterHintsVisible'); assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isFalse(contextKeyService.match('parameterHintsVisible')); @@ -542,12 +551,13 @@ describe('TypeScript', function () { const hover = editor.getControl().getContribution('editor.contrib.hover'); assert.isTrue(contextKeyService.match('editorTextFocus')); - assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData'])); + assert.isFalse(contextKeyService.match('editorHoverVisible')); await commands.executeCommand('editor.action.showHover'); let doLog = true; - await waitForAnimation(() => hover['_contentWidget']?.['_widget']?.['_visibleData']); + await waitForAnimation(() => contextKeyService.match('editorHoverVisible')); + assert.isTrue(contextKeyService.match('editorHoverVisible')); assert.isTrue(contextKeyService.match('editorTextFocus')); - assert.isTrue(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData'])); + assert.deepEqual(nodeAsString(hover['_contentWidget']?.['_widget']?.['_hover']?.['contentsDomNode']).trim(), ` DIV { DIV { @@ -572,8 +582,7 @@ DIV { } } }`.trim()); - keybindings.dispatchKeyDown('Escape'); - await waitForAnimation(() => !hover['_contentWidget']?.['_widget']?.['_visibleData']); + await dismissWithEscape('editorHoverVisible'); assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData'])); }); diff --git a/examples/browser/.theia/settings.json b/examples/browser/.theia/settings.json new file mode 100644 index 0000000000000..005b2ddfae9ac --- /dev/null +++ b/examples/browser/.theia/settings.json @@ -0,0 +1,7 @@ +{ + "files.autoSave": "afterDelay", + "workbench.editor.closeOnFileDelete": true, + "git.autoRepositoryDetection": true, + "git.openRepositoryInParentFolders": "always" +} + diff --git a/examples/browser/.theia/tasks.json b/examples/browser/.theia/tasks.json new file mode 100644 index 0000000000000..b37b3b4607ddf --- /dev/null +++ b/examples/browser/.theia/tasks.json @@ -0,0 +1,3 @@ +{ + "tasks": [] +} diff --git a/examples/browser/package.json b/examples/browser/package.json index d7e6cfec6a352..1c2a64f21647f 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -33,7 +33,6 @@ "@theia/file-search": "1.49.0", "@theia/filesystem": "1.49.0", "@theia/getting-started": "1.49.0", - "@theia/git": "1.49.0", "@theia/keymaps": "1.49.0", "@theia/markers": "1.49.0", "@theia/memory-inspector": "1.49.0", @@ -93,4 +92,4 @@ "devDependencies": { "@theia/cli": "1.49.0" } -} +} \ No newline at end of file diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index 8318b446c62cb..c04673f8d70a7 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -41,9 +41,6 @@ { "path": "../../packages/getting-started" }, - { - "path": "../../packages/git" - }, { "path": "../../packages/keymaps" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index fd403b59e02ac..4f38f0295383e 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -41,7 +41,6 @@ "@theia/file-search": "1.49.0", "@theia/filesystem": "1.49.0", "@theia/getting-started": "1.49.0", - "@theia/git": "1.49.0", "@theia/keymaps": "1.49.0", "@theia/markers": "1.49.0", "@theia/memory-inspector": "1.49.0", @@ -94,4 +93,4 @@ "@theia/cli": "1.49.0", "electron": "^28.2.8" } -} +} \ No newline at end of file diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index a5073361ded87..91edb2ac8dc55 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -47,9 +47,6 @@ { "path": "../../packages/getting-started" }, - { - "path": "../../packages/git" - }, { "path": "../../packages/keymaps" }, diff --git a/package.json b/package.json index 39b930fdc69aa..80c34865f1309 100644 --- a/package.json +++ b/package.json @@ -109,8 +109,6 @@ "theiaPluginsExcludeIds": [ "ms-vscode.js-debug-companion", "vscode.extension-editing", - "vscode.git", - "vscode.git-base", "vscode.github", "vscode.github-authentication", "vscode.microsoft-authentication", From f0fa2c2609aec4a57797a14ac9dec1903d898df6 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 8 May 2024 14:22:48 +0200 Subject: [PATCH 222/441] Stop execution when deleting cell (#13701) * Stop execution when deleting cell Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../browser/service/notebook-execution-service.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/notebook/src/browser/service/notebook-execution-service.ts b/packages/notebook/src/browser/service/notebook-execution-service.ts index 022607a5720ff..58c64e2bfafad 100644 --- a/packages/notebook/src/browser/service/notebook-execution-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-service.ts @@ -88,6 +88,15 @@ export class NotebookExecutionService { // request execution if (validCellExecutions.length > 0) { + const cellRemoveListener = notebook.onDidAddOrRemoveCell(e => { + if (e.rawEvent.changes.some(c => c.deleteCount > 0)) { + const executionsToCancel = validCellExecutions.filter(exec => !notebook.cells.find(cell => cell.handle === exec.cellHandle)); + if (executionsToCancel.length > 0) { + kernel.cancelNotebookCellExecution(notebook.uri, executionsToCancel.map(c => c.cellHandle)); + executionsToCancel.forEach(exec => exec.complete({})); + } + } + }); await this.runExecutionParticipants(validCellExecutions); this.notebookKernelService.selectKernelForNotebook(kernel, notebook); @@ -97,6 +106,9 @@ export class NotebookExecutionService { if (unconfirmed.length) { unconfirmed.forEach(exe => exe.complete({})); } + + cellRemoveListener.dispose(); + } } From b9dcbe798f938c637cc90e359abc10258602bb50 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Fri, 10 May 2024 06:28:16 +0200 Subject: [PATCH 223/441] Perform yarn upgrade (#13423) The commit performs a `yarn upgrade` of the framework to better represent what downstream applications pull with our version ranges, and to resolve known security vulnerabilities which were pulled by our lockfile. The changes also make sure that our declared ranges for dependencies are correct and fixes any compilation errors. Contributed on behalf of STMicroelectronics --- yarn.lock | 1024 +++++++++++++++++++++++++++-------------------------- 1 file changed, 531 insertions(+), 493 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7417e078ff7a2..0c0c5d9f0203f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -36,20 +36,20 @@ integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.10.0", "@babel/core@^7.7.5": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" - integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" + integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" "@babel/helper-compilation-targets" "^7.23.6" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.7" - "@babel/parser" "^7.23.6" - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" + "@babel/helpers" "^7.23.9" + "@babel/parser" "^7.23.9" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -92,9 +92,9 @@ semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.22.15": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" - integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== + version "7.23.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea" + integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.20" @@ -115,17 +115,6 @@ regexpu-core "^5.3.1" semver "^6.3.1" -"@babel/helper-define-polyfill-provider@^0.4.4": - version "0.4.4" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz#64df615451cb30e94b59a9696022cffac9a10088" - integrity sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - "@babel/helper-define-polyfill-provider@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" @@ -257,14 +246,14 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.22.19" -"@babel/helpers@^7.23.7": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" - integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== +"@babel/helpers@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" + integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== dependencies: - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" "@babel/highlight@^7.10.4", "@babel/highlight@^7.23.4": version "7.23.4" @@ -275,10 +264,10 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.22.15", "@babel/parser@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" - integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== +"@babel/parser@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" + integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": version "7.23.3" @@ -443,10 +432,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.23.7": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz#3aa0b4f2fa3788b5226ef9346cf6d16ec61f99cd" - integrity sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA== +"@babel/plugin-transform-async-generator-functions@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz#9adaeb66fc9634a586c5df139c6240d41ed801ce" + integrity sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ== dependencies: "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" @@ -625,10 +614,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" - integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== +"@babel/plugin-transform-modules-systemjs@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz#105d3ed46e4a21d257f83a2f9e2ee4203ceda6be" + integrity sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw== dependencies: "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-module-transforms" "^7.23.3" @@ -758,15 +747,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-runtime@^7.10.0": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz#52bbd20054855beb9deae3bee9ceb05289c343e6" - integrity sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz#2c64d0680fc8e09e1dfe8fd5c646fe72abd82004" + integrity sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ== dependencies: "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.7" - babel-plugin-polyfill-corejs3 "^0.8.7" - babel-plugin-polyfill-regenerator "^0.5.4" + babel-plugin-polyfill-corejs2 "^0.4.8" + babel-plugin-polyfill-corejs3 "^0.9.0" + babel-plugin-polyfill-regenerator "^0.5.5" semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.23.3": @@ -837,9 +826,9 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.10.0": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.8.tgz#7d6f8171ea7c221ecd28059e65ad37c20e441e3e" - integrity sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.9.tgz#beace3b7994560ed6bf78e4ae2073dff45387669" + integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A== dependencies: "@babel/compat-data" "^7.23.5" "@babel/helper-compilation-targets" "^7.23.6" @@ -868,7 +857,7 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.23.3" - "@babel/plugin-transform-async-generator-functions" "^7.23.7" + "@babel/plugin-transform-async-generator-functions" "^7.23.9" "@babel/plugin-transform-async-to-generator" "^7.23.3" "@babel/plugin-transform-block-scoped-functions" "^7.23.3" "@babel/plugin-transform-block-scoping" "^7.23.4" @@ -890,7 +879,7 @@ "@babel/plugin-transform-member-expression-literals" "^7.23.3" "@babel/plugin-transform-modules-amd" "^7.23.3" "@babel/plugin-transform-modules-commonjs" "^7.23.3" - "@babel/plugin-transform-modules-systemjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.9" "@babel/plugin-transform-modules-umd" "^7.23.3" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.23.3" @@ -916,9 +905,9 @@ "@babel/plugin-transform-unicode-regex" "^7.23.3" "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.7" - babel-plugin-polyfill-corejs3 "^0.8.7" - babel-plugin-polyfill-regenerator "^0.5.4" + babel-plugin-polyfill-corejs2 "^0.4.8" + babel-plugin-polyfill-corejs3 "^0.9.0" + babel-plugin-polyfill-regenerator "^0.5.5" core-js-compat "^3.31.0" semver "^6.3.1" @@ -937,25 +926,25 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.10.0", "@babel/runtime@^7.8.4": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" - integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== +"@babel/template@^7.22.15", "@babel/template@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" + integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" -"@babel/traverse@^7.23.7": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" - integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== +"@babel/traverse@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== dependencies: "@babel/code-frame" "^7.23.5" "@babel/generator" "^7.23.6" @@ -963,15 +952,15 @@ "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.6" - "@babel/types" "^7.23.6" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.4.4": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" - integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.4.4": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== dependencies: "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" @@ -1077,18 +1066,18 @@ "@sinclair/typebox" "^0.27.8" "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz#9b18145d26cf33d08576cf4c7665b28554480ed7" + integrity sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" "@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/set-array@^1.0.1": version "1.1.2" @@ -1109,9 +1098,9 @@ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + version "0.3.23" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz#afc96847f3f07841477f303eed687707a5aacd80" + integrity sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -1629,11 +1618,11 @@ integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@playwright/test@^1.37.1": - version "1.41.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.41.1.tgz#6954139ed4a67999f1b17460aa3d184f4b334f18" - integrity sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw== + version "1.41.2" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.41.2.tgz#bd9db40177f8fd442e16e14e0389d23751cdfc54" + integrity sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg== dependencies: - playwright "1.41.1" + playwright "1.41.2" "@sigstore/bundle@^1.1.0": version "1.1.0" @@ -1820,9 +1809,9 @@ "@types/chai" "*" "@types/chai@*", "@types/chai@^4.2.7": - version "4.3.11" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c" - integrity sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ== + version "4.3.12" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.12.tgz#b192fe1c553b54f45d20543adc2ab88455a07d5e" + integrity sha512-zNKDHG/1yxm8Il6uCCVsm+dRdEsJlFoDu73X17y09bId6UwoYww+vFBsAcRzl8knM1sab3Dp1VRikFQwDOtDDw== "@types/chai@4.3.0": version "4.3.0" @@ -1902,9 +1891,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.56.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" - integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== + version "8.56.3" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.3.tgz#d1f6b2303ac5ed53cb2cf59e0ab680cde1698f5f" + integrity sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -1922,9 +1911,9 @@ "@types/express" "*" "@types/express-serve-static-core@^4.17.33": - version "4.17.41" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6" - integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA== + version "4.17.43" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz#10d8444be560cb789c4735aea5eac6e5af45df54" + integrity sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2141,9 +2130,9 @@ form-data "^4.0.0" "@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18": - version "18.19.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.9.tgz#6c2624c3a05bfa3a2735c533f95597ffacbb5608" - integrity sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw== + version "18.19.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.18.tgz#7526471b28828d1fef1f7e4960fb9477e6e4369c" + integrity sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg== dependencies: undici-types "~5.26.4" @@ -2185,16 +2174,16 @@ integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== "@types/react-dom@^18.0.6": - version "18.2.18" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" - integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== + version "18.2.19" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.19.tgz#b84b7c30c635a6c26c6a6dfbb599b2da9788be58" + integrity sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0.15": - version "18.2.48" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1" - integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== + version "18.2.59" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.59.tgz#14c7bcab22e2ce71d9eaa02f78d3d55067724d7f" + integrity sha512-DE+F6BYEC8VtajY85Qr7mmhTd/79rJKIHCg99MU9SWPB4xvLb6D1za2vYflgZfmPqQVEr6UqJTnLXEwzpVPuOg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2240,9 +2229,9 @@ integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== "@types/semver@^7.5.0": - version "7.5.6" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" - integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/send@*": version "0.17.4" @@ -2337,9 +2326,9 @@ integrity sha512-MfmEI3A2McbUV2WaijoTgLOAs9chwHN4WmqOedl3jdtlbzJBWIQ9ZFmQdzPa3lYr5j8DJhRg3KB5AIM/BBfg9Q== "@types/vscode@^1.50.0": - version "1.85.0" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.85.0.tgz#46beb07f0f626665b52d1e2294382b2bc63b602e" - integrity sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g== + version "1.86.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.86.0.tgz#5d5f233137b27e51d7ad1462600005741296357a" + integrity sha512-DnIXf2ftWv+9LWOB5OJeIeaLigLHF7fdXF6atfc7X5g2w/wVZBgk0amP7b+ub5xAuW1q7qP5YcFvOcit/DtyCQ== "@types/which@^1.3.2": version "1.3.2" @@ -2538,9 +2527,9 @@ yauzl "^2.9.2" "@vscode/vsce@^2.15.0": - version "2.22.0" - resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.22.0.tgz#1eb3ebc6b89581a150bb44dd7d731a064019b617" - integrity sha512-8df4uJiM3C6GZ2Sx/KilSKVxsetrTBBIUb3c0W4B1EWHcddioVs5mkyDKtMNP0khP/xBILVSzlXxhV+nm2rC9A== + version "2.24.0" + resolved "https://registry.yarnpkg.com/@vscode/vsce/-/vsce-2.24.0.tgz#7f835b9fdd5bfedcecd62a6c4d684841a74974d4" + integrity sha512-p6CIXpH5HXDqmUkgFXvIKTjZpZxy/uDx4d/UsfhS9vQUun43KDNUbYeZocyAHgqcJlPEurgArHz9te1PPiqPyA== dependencies: azure-devops-node-api "^11.0.1" chalk "^2.4.2" @@ -2873,23 +2862,24 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.3, ajv@^8.9.0: uri-js "^4.2.2" allure-commandline@^2.23.1: - version "2.26.0" - resolved "https://registry.yarnpkg.com/allure-commandline/-/allure-commandline-2.26.0.tgz#9c08fad9f8a9f73130273bb69d4a1e0cf75da62c" - integrity sha512-udKAxlKUfhD6iH3YuR8nPF72nxHYGJItqgqSvI1pJCLlO1hJHhctfwkQbtKgXugRB6d0Uf8AYUSIXobojRVPOg== + version "2.27.0" + resolved "https://registry.yarnpkg.com/allure-commandline/-/allure-commandline-2.27.0.tgz#abde1a14d4b95e7f63dd727a4bb2e5df44e03fe0" + integrity sha512-KuxKEZ2Joa0LCcM9w8AWgWJgmB5d3VqSgaJhPC6pEsdRwAObT/JE8NY0u4mJ61+c2mhAPGIetGV9jgP3gzxgIg== -allure-js-commons@2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-2.11.1.tgz#cd4d123af49d1b2064d821bc355e221676eb1964" - integrity sha512-A7Eiofwj46JBbK2XsM9FKmbhTYrdok+5M2EzI5ueJ/S+T12xvINBrrKdtjkqFvz/oH9qA/iKHawlJc4MSQbxLQ== +allure-js-commons@2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-2.13.0.tgz#1b2ea72ba1f9fdc26acee49a0d556be64f458909" + integrity sha512-IUT5Lyw+pAUku89Bxp6zltY6DFLYbt6vfpjRxM4Du4wKdPDrFNSPh4iPlKo3+11uctQqO+9xWUq9jH2c6SxKKA== dependencies: properties "^1.2.1" + strip-ansi "^5.2.0" allure-playwright@^2.5.0: - version "2.11.1" - resolved "https://registry.yarnpkg.com/allure-playwright/-/allure-playwright-2.11.1.tgz#6f6194791fb9b9178bb45becd1ee769faa4a8158" - integrity sha512-xzSFJ5Xrc8AxMM9fkpvvEOwjcuGWUiksx3mQiWFHALpUS1cgsJxm5M30omgqe6/rfbMBsIM2w4ufMts4N37+2w== + version "2.13.0" + resolved "https://registry.yarnpkg.com/allure-playwright/-/allure-playwright-2.13.0.tgz#c13ccf0a49aec9fee7afdef362ddc8ae95349134" + integrity sha512-AlPZWR7Yrc71UkWtKGkmsEFv6NI02KrzjgfVVxjN/fgpUcmYpQKUFnQbzm8/hUeYrALAl6PJfS6kWFsTaBy4Qw== dependencies: - allure-js-commons "2.11.1" + allure-js-commons "2.13.0" anser@^2.0.1: version "2.1.1" @@ -2918,6 +2908,11 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -3060,13 +3055,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" + call-bind "^1.0.5" + is-array-buffer "^3.0.4" array-differ@^3.0.0: version "3.0.0" @@ -3111,16 +3106,27 @@ array-uniq@^1.0.1: resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== -array.prototype.findlastindex@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" - integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== +array.prototype.filter@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz#423771edeb417ff5914111fff4277ea0624c0d0e" + integrity sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.findlastindex@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz#d1c50f0b3a9da191981ff8942a0aedd82794404f" + integrity sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" @@ -3143,27 +3149,28 @@ array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: es-shim-unscopables "^1.0.0" array.prototype.tosorted@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" - integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== + version "1.1.3" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz#c8c89348337e51b8a3c48a9227f9ce93ceedcba8" + integrity sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.1.0" + es-shim-unscopables "^1.0.2" -arraybuffer.prototype.slice@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" - integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" arrify@^1.0.1: @@ -3256,21 +3263,14 @@ autosize@^4.0.2: resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.4.tgz#924f13853a466b633b9309330833936d8bccce03" integrity sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -axios@^1.0.0: - version "1.6.6" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.6.tgz#878db45401d91fe9e53aed8ac962ed93bde8dd1c" - integrity sha512-XZLZDFfXKM9U/Y/B4nNynfCRUqNyVZ4sBC/n9GDRCkq9vd2mIvKjKKsbIh1WPmHmNbg6ND7cTBY3Y2+u1G3/2Q== +available-typed-arrays@^1.0.6, available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" + possible-typed-array-names "^1.0.0" -axios@^1.6.2: +axios@^1.0.0, axios@^1.6.2: version "1.6.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== @@ -3297,7 +3297,7 @@ babel-loader@^8.2.2: make-dir "^3.1.0" schema-utils "^2.6.5" -babel-plugin-polyfill-corejs2@^0.4.7: +babel-plugin-polyfill-corejs2@^0.4.8: version "0.4.8" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== @@ -3306,15 +3306,15 @@ babel-plugin-polyfill-corejs2@^0.4.7: "@babel/helper-define-polyfill-provider" "^0.5.0" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.7: - version "0.8.7" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz#941855aa7fdaac06ed24c730a93450d2b2b76d04" - integrity sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA== +babel-plugin-polyfill-corejs3@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81" + integrity sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.4" - core-js-compat "^3.33.1" + "@babel/helper-define-polyfill-provider" "^0.5.0" + core-js-compat "^3.34.0" -babel-plugin-polyfill-regenerator@^0.5.4: +babel-plugin-polyfill-regenerator@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== @@ -3519,13 +3519,13 @@ browserfs@^1.4.3: async "^2.1.4" pako "^1.0.4" -browserslist@^4.21.10, browserslist@^4.22.2: - version "4.22.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" - integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== +browserslist@^4.21.10, browserslist@^4.22.2, browserslist@^4.22.3: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== dependencies: - caniuse-lite "^1.0.30001565" - electron-to-chromium "^1.4.601" + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" node-releases "^2.0.14" update-browserslist-db "^1.0.13" @@ -3702,14 +3702,16 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" function-bind "^1.1.2" - get-intrinsic "^1.2.1" - set-function-length "^1.1.1" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" callsites@^3.0.0: version "3.1.0" @@ -3735,10 +3737,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001565: - version "1.0.30001580" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001580.tgz#e3c76bc6fe020d9007647044278954ff8cd17d1e" - integrity sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA== +caniuse-lite@^1.0.30001587: + version "1.0.30001591" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz#16745e50263edc9f395895a7cd468b9f3767cf33" + integrity sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ== caseless@~0.12.0: version "0.12.0" @@ -4286,12 +4288,12 @@ copy-webpack-plugin@^8.1.1: schema-utils "^3.0.0" serialize-javascript "^5.0.1" -core-js-compat@^3.31.0, core-js-compat@^3.33.1: - version "3.35.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" - integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== +core-js-compat@^3.31.0, core-js-compat@^3.34.0: + version "3.36.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.36.0.tgz#087679119bc2fdbdefad0d45d8e5d307d45ba190" + integrity sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw== dependencies: - browserslist "^4.22.2" + browserslist "^4.22.3" core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" @@ -4382,9 +4384,9 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" css-loader@^6.2.0: - version "6.9.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.9.1.tgz#9ec9a434368f2bdfeffbf8f6901a1ce773586c6b" - integrity sha512-OzABOh0+26JKFdMzlK6PY1u5Zx8+Ck7CVRlcGNZoY9qwJjdfu2VWFuprTIpPW+Av5TZTVViYWcFQaEEQURLknQ== + version "6.10.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.10.0.tgz#7c172b270ec7b833951b52c348861206b184a4b7" + integrity sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw== dependencies: icss-utils "^5.1.0" postcss "^8.4.33" @@ -4466,7 +4468,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4621,14 +4623,14 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-data-property@^1.0.1, define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== +define-data-property@^1.0.1, define-data-property@^1.1.2, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: - get-intrinsic "^1.2.1" + es-define-property "^1.0.0" + es-errors "^1.3.0" gopd "^1.0.1" - has-property-descriptors "^1.0.0" define-lazy-prop@^2.0.0: version "2.0.0" @@ -4715,9 +4717,9 @@ diff@^4.0.1: integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diff@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" - integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== dir-glob@^2.0.0: version "2.2.2" @@ -4928,10 +4930,10 @@ electron-store@^8.0.0: conf "^10.2.0" type-fest "^2.17.0" -electron-to-chromium@^1.4.601: - version "1.4.645" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.645.tgz#117f964252eb2f0ff00fc7360cb3080e2cf66e3c" - integrity sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw== +electron-to-chromium@^1.4.668: + version "1.4.682" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.682.tgz#27577b88ccccc810e09b05093345cf1830f1bd65" + integrity sha512-oCglfs8yYKs9RQjJFOHonSnhikPK3y+0SvSYc/YpYJV//6rqc0/hbwd0c7vgK4vrl6y2gJAwjkhkSGWK+z4KRA== electron-window@^0.8.0: version "0.8.1" @@ -4995,9 +4997,9 @@ engine.io-client@~6.5.2: xmlhttprequest-ssl "~2.0.0" engine.io-parser@~5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb" - integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== + version "5.2.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" + integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== engine.io@~6.5.2: version "6.5.4" @@ -5059,9 +5061,9 @@ envinfo@7.8.1: integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== envinfo@^7.7.3: - version "7.11.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" - integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + version "7.11.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1" + integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg== err-code@^2.0.2: version "2.0.3" @@ -5082,86 +5084,106 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1: - version "1.22.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" - integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.2" - available-typed-arrays "^1.0.5" - call-bind "^1.0.5" - es-set-tostringtag "^2.0.1" +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.22.4: + version "1.22.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.4.tgz#26eb2e7538c3271141f5754d31aabfdb215f27bf" + integrity sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.6" + call-bind "^1.0.7" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.2" es-to-primitive "^1.2.1" function.prototype.name "^1.1.6" - get-intrinsic "^1.2.2" - get-symbol-description "^1.0.0" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" globalthis "^1.0.3" gopd "^1.0.1" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.2" has-proto "^1.0.1" has-symbols "^1.0.3" - hasown "^2.0.0" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" + hasown "^2.0.1" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.12" + is-typed-array "^1.1.13" is-weakref "^1.0.2" object-inspect "^1.13.1" object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - safe-array-concat "^1.0.1" - safe-regex-test "^1.0.0" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.0" + safe-regex-test "^1.0.3" string.prototype.trim "^1.2.8" string.prototype.trimend "^1.0.7" string.prototype.trimstart "^1.0.7" - typed-array-buffer "^1.0.0" + typed-array-buffer "^1.0.1" typed-array-byte-length "^1.0.0" typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.13" + which-typed-array "^1.1.14" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.0.0, es-errors@^1.1.0, es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.0.12: - version "1.0.15" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" - integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== + version "1.0.17" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz#123d1315780df15b34eb181022da43e734388bb8" + integrity sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ== dependencies: asynciterator.prototype "^1.0.0" - call-bind "^1.0.2" + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.22.1" - es-set-tostringtag "^2.0.1" - function-bind "^1.1.1" - get-intrinsic "^1.2.1" + es-abstract "^1.22.4" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" globalthis "^1.0.3" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.2" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.5" + internal-slot "^1.0.7" iterator.prototype "^1.1.2" - safe-array-concat "^1.0.1" + safe-array-concat "^1.1.0" es-module-lexer@^1.2.1: version "1.4.1" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== -es-set-tostringtag@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" - integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== +es-set-tostringtag@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== dependencies: - get-intrinsic "^1.2.2" - has-tostringtag "^1.0.0" - hasown "^2.0.0" + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" -es-shim-unscopables@^1.0.0: +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== @@ -5188,9 +5210,9 @@ es6-promise@^4.1.1, es6-promise@^4.2.4: integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" @@ -5228,9 +5250,9 @@ eslint-import-resolver-node@^0.3.9: resolve "^1.22.4" eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + version "2.8.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== dependencies: debug "^3.2.7" @@ -5623,9 +5645,9 @@ fastest-levenshtein@^1.0.12: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" - integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" @@ -5800,9 +5822,9 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== flatted@^3.2.9: - version "3.2.9" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" - integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== follow-redirects@^1.0.0, follow-redirects@^1.15.4: version "1.15.5" @@ -5956,7 +5978,7 @@ fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1, function-bind@^1.1.2: +function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== @@ -6029,11 +6051,12 @@ get-func-name@^2.0.1, get-func-name@^2.0.2: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== +get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: + es-errors "^1.3.0" function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" @@ -6084,13 +6107,14 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" git-raw-commits@^3.0.0: version "3.0.0" @@ -6168,18 +6192,6 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@8.1.0, glob@^8.0.1, glob@^8.0.3, glob@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" @@ -6345,29 +6357,29 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" - integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - get-intrinsic "^1.2.2" + es-define-property "^1.0.0" -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.1, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbols "^1.0.2" + has-symbols "^1.0.3" has-unicode@2.0.1, has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" @@ -6382,10 +6394,10 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" -hasown@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" - integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== +hasown@^2.0.0, hasown@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" + integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== dependencies: function-bind "^1.1.2" @@ -6536,9 +6548,9 @@ https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: debug "4" https-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" - integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== dependencies: agent-base "^7.0.2" debug "4" @@ -6626,9 +6638,9 @@ ignore@^4.0.6: integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== ignore@^5.0.4, ignore@^5.1.8, ignore@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" - integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== image-size@~0.5.0: version "0.5.5" @@ -6723,12 +6735,12 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^6.0.1" -internal-slot@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" - integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== +internal-slot@^1.0.5, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== dependencies: - get-intrinsic "^1.2.2" + es-errors "^1.3.0" hasown "^2.0.0" side-channel "^1.0.4" @@ -6742,24 +6754,26 @@ inversify@^6.0.1: resolved "https://registry.yarnpkg.com/inversify/-/inversify-6.0.2.tgz#dc7fa0348213d789d35ffb719dea9685570989c7" integrity sha512-i9m8j/7YIv4mDuYXUAcrpKPSaju/CIly9AHK5jvCBeoiM/2KEsuCQTTP+rzSWWpLYWRukdXFSl6ZTk2/uumbiA== -ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" + get-intrinsic "^1.2.1" is-arrayish@^0.2.1: version "0.2.1" @@ -6895,9 +6909,9 @@ is-natural-number@^4.0.1: integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-object@^1.0.4: version "1.0.7" @@ -6962,11 +6976,11 @@ is-set@^2.0.1: integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" is-ssh@^1.4.0: version "1.4.0" @@ -7011,12 +7025,12 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - which-typed-array "^1.1.11" + which-typed-array "^1.1.14" is-typedarray@^1.0.0: version "1.0.0" @@ -7143,9 +7157,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.1.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" - integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -7229,6 +7243,11 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jschardet@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.3.0.tgz#06e2636e16c8ada36feebbdc08aa34e6a9b3ff75" @@ -7827,9 +7846,9 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== "lru-cache@^9.1.1 || ^10.0.0": - version "10.1.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" - integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== lunr@^2.3.9: version "2.3.9" @@ -8078,11 +8097,12 @@ min-indent@^1.0.0: integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== mini-css-extract-plugin@^2.6.1: - version "2.7.7" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.7.tgz#4acf02f362c641c38fb913bfcb7ca2fc4a7cf339" - integrity sha512-+0n11YGyRavUR3IlaOzJ0/4Il1avMvJ1VJfhWfCn24ITQXhRr1gghbhhrda6tgtNcpZaWKdSuwKq20Jb7fnlyw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz#1aeae2a90a954b6426c9e8311eab36b450f553a0" + integrity sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg== dependencies: schema-utils "^4.0.0" + tapable "^2.2.1" minimatch@3.0.5: version "3.0.5" @@ -8251,9 +8271,9 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mocha@^10.1.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + version "10.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9" + integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg== dependencies: ansi-colors "4.1.1" browser-stdout "1.3.1" @@ -8262,13 +8282,12 @@ mocha@^10.1.0: diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.2.0" + glob "8.1.0" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" @@ -8413,11 +8432,6 @@ nano@^10.1.3: node-abort-controller "^3.0.1" qs "^6.11.0" -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== - nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -8454,9 +8468,9 @@ neo-async@^2.6.2: integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== nise@^5.1.0: - version "5.1.7" - resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.7.tgz#03ca96539efb306612eb60a8c5d6beeb208e27e5" - integrity sha512-wWtNUhkT7k58uvWTB/Gy26eA/EJKtPZFVAhEilN5UYVmmGRYOURbejRUyKm0Uu9XVEW7K5nBOZfR8VMB4QR2RQ== + version "5.1.9" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.9.tgz#0cb73b5e4499d738231a473cd89bd8afbb618139" + integrity sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww== dependencies: "@sinonjs/commons" "^3.0.0" "@sinonjs/fake-timers" "^11.2.2" @@ -8465,9 +8479,9 @@ nise@^5.1.0: path-to-regexp "^6.2.1" node-abi@*, node-abi@^3.0.0, node-abi@^3.3.0: - version "3.54.0" - resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.54.0.tgz#f6386f7548817acac6434c6cba02999c9aebcc69" - integrity sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA== + version "3.56.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.56.0.tgz#ca807d5ff735ac6bbbd684ae3ff2debc1c2a40a7" + integrity sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q== dependencies: semver "^7.3.5" @@ -8898,7 +8912,7 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1 resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1, object-inspect@^1.9.0: +object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== @@ -8908,7 +8922,7 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.4: +object.assign@^4.1.4, object.assign@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -8937,14 +8951,15 @@ object.fromentries@^2.0.6, object.fromentries@^2.0.7: es-abstract "^1.22.1" object.groupby@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" - integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.2.tgz#494800ff5bab78fd0eff2835ec859066e00192ec" + integrity sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + array.prototype.filter "^1.0.3" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.0.0" object.hasown@^1.1.2: version "1.1.3" @@ -9401,9 +9416,9 @@ pause-stream@0.0.11: through "~2.3" pdfobject@^2.0.201604172: - version "2.2.12" - resolved "https://registry.yarnpkg.com/pdfobject/-/pdfobject-2.2.12.tgz#b789e4606b69763f2f3ae501ff003f3db8231943" - integrity sha512-D0oyD/sj8j82AMaJhoyMaY1aD5TkbpU3FbJC6w9/cpJlZRpYHqAkutXw1Ca/FKjYPZmTAu58uGIfgOEaDlbY8A== + version "2.3.0" + resolved "https://registry.yarnpkg.com/pdfobject/-/pdfobject-2.3.0.tgz#467b4ffcd621518aa0e66fc191fdd669f3118b06" + integrity sha512-w/9pXDXTDs3IDmOri/w8lM/w6LHR0/F4fcBLLzH+4csSoyshQ5su0TE7k0FLHZO7aOjVLDGecqd1M89+PVpVAA== pend@~1.2.0: version "1.2.0" @@ -9471,17 +9486,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.41.1: - version "1.41.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.1.tgz#9c152670010d9d6f970f34b68e3e935d3c487431" - integrity sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg== +playwright-core@1.41.2: + version "1.41.2" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.2.tgz#db22372c708926c697acc261f0ef8406606802d9" + integrity sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA== -playwright@1.41.1: - version "1.41.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.1.tgz#83325f34165840d019355c2a78a50f21ed9b9c85" - integrity sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ== +playwright@1.41.2: + version "1.41.2" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.2.tgz#4e760b1c79f33d9129a8c65cc27953be6dd35042" + integrity sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A== dependencies: - playwright-core "1.41.1" + playwright-core "1.41.2" optionalDependencies: fsevents "2.3.2" @@ -9494,6 +9509,11 @@ portfinder@^1.0.28: debug "^3.2.7" mkdirp "^0.5.6" +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + postcss-modules-extract-imports@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" @@ -9536,9 +9556,9 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.4.33: - version "8.4.33" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" - integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + version "8.4.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7" + integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== dependencies: nanoid "^3.3.7" picocolors "^1.0.0" @@ -10057,14 +10077,15 @@ reflect-metadata@^0.1.10: integrity sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A== reflect.getprototypeof@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" - integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz#e0bd28b597518f16edaf9c0e292c631eb13e0674" + integrity sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.0.0" + get-intrinsic "^1.2.3" globalthis "^1.0.3" which-builtin-type "^1.1.3" @@ -10102,14 +10123,15 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" -regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" - integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - set-function-name "^2.0.0" + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" regexpp@^3.1.0: version "3.2.0" @@ -10308,7 +10330,7 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -safe-array-concat@^1.0.1: +safe-array-concat@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== @@ -10328,13 +10350,13 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex-test@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" - integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" + call-bind "^1.0.6" + es-errors "^1.3.0" is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2, safer-buffer@~2.1.0: @@ -10436,9 +10458,9 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== dependencies: lru-cache "^6.0.0" @@ -10504,25 +10526,27 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" - integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== +set-function-length@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" + integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== dependencies: - define-data-property "^1.1.1" + define-data-property "^1.1.2" + es-errors "^1.3.0" function-bind "^1.1.2" - get-intrinsic "^1.2.2" + get-intrinsic "^1.2.3" gopd "^1.0.1" has-property-descriptors "^1.0.1" set-function-name@^2.0.0, set-function-name@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" - integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: - define-data-property "^1.0.1" + define-data-property "^1.1.4" + es-errors "^1.3.0" functions-have-names "^1.2.3" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.2" setimmediate@^1.0.5, setimmediate@~1.0.4: version "1.0.5" @@ -10589,13 +10613,14 @@ shiki@^0.10.1: vscode-textmate "5.2.0" side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + version "1.0.5" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" + integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.6" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" @@ -10683,10 +10708,11 @@ smart-buffer@^4.2.0: integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socket.io-adapter@~2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" - integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== + version "2.5.4" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz#4fdb1358667f6d68f25343353bd99bd11ee41006" + integrity sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg== dependencies: + debug "~4.3.4" ws "~8.11.0" socket.io-client@^4.5.3: @@ -10739,11 +10765,11 @@ socks-proxy-agent@^7.0.0: socks "^2.6.2" socks@^2.3.3, socks@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + version "2.8.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.1.tgz#22c7d9dd7882649043cba0eafb49ae144e3457af" + integrity sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" sort-keys@^2.0.0: @@ -10816,9 +10842,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" - integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -10829,9 +10855,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.16" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" - integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== + version "3.0.17" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" + integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== split-ca@^1.0.1: version "1.0.1" @@ -10859,7 +10885,7 @@ split@^1.0.1: dependencies: through "2" -sprintf-js@^1.1.2: +sprintf-js@^1.1.2, sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== @@ -11027,6 +11053,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11158,7 +11191,7 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tapable@^2.1.1, tapable@^2.2.0: +tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== @@ -11273,9 +11306,9 @@ terser-webpack-plugin@^5.3.10: terser "^5.26.0" terser@^5.26.0: - version "5.27.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" - integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== + version "5.28.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.28.1.tgz#bf00f7537fd3a798c352c2d67d67d65c915d1b28" + integrity sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -11561,44 +11594,49 @@ type-is@^1.6.4, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== +typed-array-buffer@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + version "1.0.5" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.5.tgz#57d44da160296d8663fd63180a1802ebf25905d5" + integrity sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" for-each "^0.3.3" - is-typed-array "^1.1.9" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" typed-rest-client@^1.8.4: version "1.8.11" @@ -12030,9 +12068,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.76.0: - version "5.90.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.0.tgz#313bfe16080d8b2fee6e29b6c986c0714ad4290e" - integrity sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w== + version "5.90.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.3.tgz#37b8f74d3ded061ba789bb22b31e82eed75bd9ac" + integrity sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" @@ -12136,16 +12174,16 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== -which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: - version "1.1.13" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" - integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== +which-typed-array@^1.1.14, which-typed-array@^1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" + integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.4" + available-typed-arrays "^1.0.6" + call-bind "^1.0.5" for-each "^0.3.3" gopd "^1.0.1" - has-tostringtag "^1.0.0" + has-tostringtag "^1.0.1" which@^1.2.0, which@^1.2.9: version "1.3.1" From a2277914b95f3dba36d6acf77ff9d10689d1d049 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Fri, 10 May 2024 08:28:52 +0200 Subject: [PATCH 224/441] Make browserWindow of splashScreen transparent (#13699) Currently an image with a transparent background is rendered with a white background. This looks weird and is not the expected behavior when a transparent image is used. With this change the browserWindow is set to transparent, so that the background is not rendered. If adoptors want a white background they can simply use an image with a white background (or any other color). --- packages/core/src/electron-main/electron-main-application.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index e97114df94c8c..e6fac94c46733 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -346,7 +346,8 @@ export class ElectronMainApplication { ...splashScreenBounds, frame: false, alwaysOnTop: true, - show: false + show: false, + transparent: true, }); if (this.isShowWindowEarly()) { From 38eb31945130bb68fc793d4291d8d9f416541cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Sat, 11 May 2024 08:03:05 +0200 Subject: [PATCH 225/441] Don't reveal the focused element when updating the tree rows (#13703) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13461 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/browser/tree/tree-widget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index 0c4f0c4cc7bdc..9c33cd5633b8d 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -339,7 +339,7 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { } } this.rows = new Map(rowsToUpdate); - this.updateScrollToRow(); + this.update(); } protected getDepthForNode(node: TreeNode, depths: Map): number { From cb2bd96b362408c90179005a5b415ddbd452ce41 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 15 May 2024 08:23:38 +0200 Subject: [PATCH 226/441] TreeWidget: Ensure globalSelection is correctly set when opening context menu (#13710) * TreeWidget: Ensure globalSelection is correctly set when opening context menu Signed-off-by: Jonah Iden * nicer solution than only checking when opening context menu Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden * removed forgotten artifact Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- packages/core/src/browser/tree/tree-widget.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/src/browser/tree/tree-widget.tsx b/packages/core/src/browser/tree/tree-widget.tsx index 9c33cd5633b8d..9888aae99874b 100644 --- a/packages/core/src/browser/tree/tree-widget.tsx +++ b/packages/core/src/browser/tree/tree-widget.tsx @@ -302,6 +302,12 @@ export class TreeWidget extends ReactWidget implements StatefulWidget { } }) ]); + + this.node.addEventListener('focusin', e => { + if (this.model.selectedNodes.length && (!this.selectionService.selection || !TreeWidgetSelection.isSource(this.selectionService.selection, this))) { + this.updateGlobalSelection(); + } + }); } this.toDispose.push(this.corePreferences.onPreferenceChanged(preference => { if (preference.preferenceName === 'workbench.tree.renderIndentGuides') { From a9f3c7783796a7d09ac5b802a12ce753d7a7ee91 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 15 May 2024 14:11:13 +0200 Subject: [PATCH 227/441] Support plugin copying for remote feature (#13369) --- .../hosting/electron-ws-origin-validator.ts | 2 +- .../src/node/backend-application-module.ts | 4 +- .../{ => remote}/backend-remote-service.ts | 0 .../node/remote/remote-cli-contribution.ts | 34 ++++++++++++++ .../node/remote/remote-copy-contribution.ts | 45 +++++++++++++++++++ .../plugin-ext/src/common/plugin-protocol.ts | 1 + .../node/hosted-plugin-deployer-handler.ts | 6 +++ .../main/node/plugin-ext-backend-module.ts | 9 ++++ .../node/plugin-remote-cli-contribution.ts | 36 +++++++++++++++ .../node/plugin-remote-copy-contribution.ts | 36 +++++++++++++++ .../src/main/node/plugin-service.ts | 2 +- .../backend-remote-service-impl.ts | 2 +- .../electron-node/remote-backend-module.ts | 7 +-- .../remote-connection-service.ts | 7 +-- .../remote/src/electron-node/remote-types.ts | 7 +-- .../app-native-dependency-contribution.ts | 2 +- .../setup/main-copy-contribution.ts | 2 +- .../setup/remote-copy-contribution.ts | 41 ++++++----------- .../setup/remote-copy-service.ts | 10 +++-- .../remote-native-dependency-contribution.ts | 2 +- .../setup/remote-native-dependency-service.ts | 2 +- .../setup/remote-node-setup-service.ts | 2 +- .../setup/remote-setup-script-service.ts | 2 +- .../setup/remote-setup-service.ts | 22 +++++++-- .../src/node/vsx-cli-deployer-participant.ts | 6 +-- .../vsx-registry/src/node/vsx-remote-cli.ts | 39 ++++++++++++++++ 26 files changed, 267 insertions(+), 61 deletions(-) rename packages/core/src/node/{ => remote}/backend-remote-service.ts (100%) create mode 100644 packages/core/src/node/remote/remote-cli-contribution.ts create mode 100644 packages/core/src/node/remote/remote-copy-contribution.ts create mode 100644 packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts create mode 100644 packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts create mode 100644 packages/vsx-registry/src/node/vsx-remote-cli.ts diff --git a/packages/core/src/electron-node/hosting/electron-ws-origin-validator.ts b/packages/core/src/electron-node/hosting/electron-ws-origin-validator.ts index 43494b9ffd4c5..d9bceab8db793 100644 --- a/packages/core/src/electron-node/hosting/electron-ws-origin-validator.ts +++ b/packages/core/src/electron-node/hosting/electron-ws-origin-validator.ts @@ -16,7 +16,7 @@ import * as http from 'http'; import { inject, injectable } from 'inversify'; -import { BackendRemoteService } from '../../node/backend-remote-service'; +import { BackendRemoteService } from '../../node/remote/backend-remote-service'; import { WsRequestValidatorContribution } from '../../node/ws-request-validators'; @injectable() diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index a89a7a8a52ea6..f5a17db9bd514 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -41,7 +41,8 @@ import { bindNodeStopwatch, bindBackendStopwatchServer } from './performance'; import { OSBackendProviderImpl } from './os-backend-provider'; import { BackendRequestFacade } from './request/backend-request-facade'; import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking'; -import { BackendRemoteService } from './backend-remote-service'; +import { BackendRemoteService } from './remote/backend-remote-service'; +import { RemoteCliContribution } from './remote/remote-cli-contribution'; decorate(injectable(), ApplicationPackage); @@ -124,6 +125,7 @@ export const backendApplicationModule = new ContainerModule(bind => { bind(ProxyCliContribution).toSelf().inSingletonScope(); bind(CliContribution).toService(ProxyCliContribution); + bindContributionProvider(bind, RemoteCliContribution); bind(BackendRemoteService).toSelf().inSingletonScope(); bind(BackendRequestFacade).toSelf().inSingletonScope(); bind(ConnectionHandler).toDynamicValue( diff --git a/packages/core/src/node/backend-remote-service.ts b/packages/core/src/node/remote/backend-remote-service.ts similarity index 100% rename from packages/core/src/node/backend-remote-service.ts rename to packages/core/src/node/remote/backend-remote-service.ts diff --git a/packages/core/src/node/remote/remote-cli-contribution.ts b/packages/core/src/node/remote/remote-cli-contribution.ts new file mode 100644 index 0000000000000..7f25cb3fb952c --- /dev/null +++ b/packages/core/src/node/remote/remote-cli-contribution.ts @@ -0,0 +1,34 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import type { OS } from '../../common/os'; +import type { MaybePromise } from '../../common/types'; + +export interface RemotePlatform { + os: OS.Type + arch: string +} + +export interface RemoteCliContext { + platform: RemotePlatform; + directory: string; +} + +export const RemoteCliContribution = Symbol('RemoteCliContribution'); + +export interface RemoteCliContribution { + enhanceArgs(context: RemoteCliContext): MaybePromise; +} diff --git a/packages/core/src/node/remote/remote-copy-contribution.ts b/packages/core/src/node/remote/remote-copy-contribution.ts new file mode 100644 index 0000000000000..c0a746c71679d --- /dev/null +++ b/packages/core/src/node/remote/remote-copy-contribution.ts @@ -0,0 +1,45 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { MaybePromise } from '../../common/types'; + +export const RemoteCopyContribution = Symbol('RemoteCopyContribution'); + +export interface RemoteCopyContribution { + copy(registry: RemoteCopyRegistry): MaybePromise +} + +export interface RemoteCopyOptions { + /** + * The mode that the file should be set to once copied to the remote. + * + * Only relevant for POSIX-like systems + */ + mode?: number; +} + +export interface RemoteFile { + path: string + target: string + options?: RemoteCopyOptions; +} + +export interface RemoteCopyRegistry { + getFiles(): RemoteFile[]; + glob(pattern: string, target?: string): Promise; + file(file: string, target?: string, options?: RemoteCopyOptions): void; + directory(dir: string, target?: string): Promise; +} diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts index 61f457a9a572c..222b699797831 100644 --- a/packages/plugin-ext/src/common/plugin-protocol.ts +++ b/packages/plugin-ext/src/common/plugin-protocol.ts @@ -984,6 +984,7 @@ export interface PluginDeployerHandler { deployFrontendPlugins(frontendPlugins: PluginDeployerEntry[]): Promise; deployBackendPlugins(backendPlugins: PluginDeployerEntry[]): Promise; + getDeployedPlugins(): Promise; getDeployedPluginsById(pluginId: string): DeployedPlugin[]; getDeployedPlugin(pluginId: PluginIdentifiers.VersionedId): DeployedPlugin | undefined; diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts index 4acce219513da..49cce6b693389 100644 --- a/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts +++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-deployer-handler.ts @@ -83,6 +83,12 @@ export class HostedPluginDeployerHandler implements PluginDeployerHandler { return Array.from(this.deployedBackendPlugins.values()); } + async getDeployedPlugins(): Promise { + await this.frontendPluginsMetadataDeferred.promise; + await this.backendPluginsMetadataDeferred.promise; + return [...this.deployedFrontendPlugins.values(), ...this.deployedBackendPlugins.values()]; + } + getDeployedPluginsById(pluginId: string): DeployedPlugin[] { const matches: DeployedPlugin[] = []; const handle = (plugins: Iterable): void => { diff --git a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts index fc3b6996f458d..fdb6825dc519a 100644 --- a/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts +++ b/packages/plugin-ext/src/main/node/plugin-ext-backend-module.ts @@ -42,6 +42,10 @@ import { PluginUninstallationManager } from './plugin-uninstallation-manager'; import { LocalizationServerImpl } from '@theia/core/lib/node/i18n/localization-server'; import { PluginLocalizationServer } from './plugin-localization-server'; import { PluginMgmtCliContribution } from './plugin-mgmt-cli-contribution'; +import { PluginRemoteCliContribution } from './plugin-remote-cli-contribution'; +import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { PluginRemoteCopyContribution } from './plugin-remote-copy-contribution'; +import { RemoteCopyContribution } from '@theia/core/lib/node/remote/remote-copy-contribution'; export function bindMainBackend(bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound, rebind: interfaces.Rebind): void { bind(PluginApiContribution).toSelf().inSingletonScope(); @@ -89,6 +93,11 @@ export function bindMainBackend(bind: interfaces.Bind, unbind: interfaces.Unbind bind(PluginMgmtCliContribution).toSelf().inSingletonScope(); bind(CliContribution).toService(PluginMgmtCliContribution); + bind(PluginRemoteCliContribution).toSelf().inSingletonScope(); + bind(RemoteCliContribution).toService(PluginRemoteCliContribution); + bind(PluginRemoteCopyContribution).toSelf().inSingletonScope(); + bind(RemoteCopyContribution).toService(PluginRemoteCopyContribution); + bind(WebviewBackendSecurityWarnings).toSelf().inSingletonScope(); bind(BackendApplicationContribution).toService(WebviewBackendSecurityWarnings); diff --git a/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts b/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts new file mode 100644 index 0000000000000..2df189ce8d57a --- /dev/null +++ b/packages/plugin-ext/src/main/node/plugin-remote-cli-contribution.ts @@ -0,0 +1,36 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { MaybePromise } from '@theia/core'; +import { RemoteCliContext, RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { PluginCliContribution } from './plugin-cli-contribution'; + +@injectable() +export class PluginRemoteCliContribution implements RemoteCliContribution { + + @inject(PluginCliContribution) + protected readonly pluginCliContribution: PluginCliContribution; + + enhanceArgs(context: RemoteCliContext): MaybePromise { + const pluginsFolder = this.pluginCliContribution.localDir(); + if (!pluginsFolder) { + return []; + } else { + return ['--plugins=local-dir:./plugins']; + } + } +} diff --git a/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts b/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts new file mode 100644 index 0000000000000..2b7314f167118 --- /dev/null +++ b/packages/plugin-ext/src/main/node/plugin-remote-copy-contribution.ts @@ -0,0 +1,36 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { RemoteCopyContribution, RemoteCopyRegistry } from '@theia/core/lib/node/remote/remote-copy-contribution'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { PluginCliContribution } from './plugin-cli-contribution'; +import { FileUri } from '@theia/core/lib/common/file-uri'; + +@injectable() +export class PluginRemoteCopyContribution implements RemoteCopyContribution { + + @inject(PluginCliContribution) + protected readonly pluginCliContribution: PluginCliContribution; + + async copy(registry: RemoteCopyRegistry): Promise { + const localDir = this.pluginCliContribution.localDir(); + if (localDir) { + const fsPath = FileUri.fsPath(localDir); + await registry.directory(fsPath, 'plugins'); + } + + } +} diff --git a/packages/plugin-ext/src/main/node/plugin-service.ts b/packages/plugin-ext/src/main/node/plugin-service.ts index 91763379a5e0b..756c97975b93f 100644 --- a/packages/plugin-ext/src/main/node/plugin-service.ts +++ b/packages/plugin-ext/src/main/node/plugin-service.ts @@ -26,7 +26,7 @@ import { environment } from '@theia/core/shared/@theia/application-package/lib/e import { WsRequestValidatorContribution } from '@theia/core/lib/node/ws-request-validators'; import { MaybePromise } from '@theia/core/lib/common'; import { ApplicationPackage } from '@theia/core/shared/@theia/application-package'; -import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service'; +import { BackendRemoteService } from '@theia/core/lib/node/remote/backend-remote-service'; @injectable() export class PluginApiContribution implements BackendApplicationContribution, WsRequestValidatorContribution { diff --git a/packages/remote/src/electron-node/backend-remote-service-impl.ts b/packages/remote/src/electron-node/backend-remote-service-impl.ts index 805481b9eb790..db88035eeb4ef 100644 --- a/packages/remote/src/electron-node/backend-remote-service-impl.ts +++ b/packages/remote/src/electron-node/backend-remote-service-impl.ts @@ -17,7 +17,7 @@ import { CliContribution } from '@theia/core/lib/node'; import { injectable } from '@theia/core/shared/inversify'; import { Arguments, Argv } from '@theia/core/shared/yargs'; -import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service'; +import { BackendRemoteService } from '@theia/core/lib/node/remote/backend-remote-service'; export const REMOTE_START = 'remote'; diff --git a/packages/remote/src/electron-node/remote-backend-module.ts b/packages/remote/src/electron-node/remote-backend-module.ts index 2197bb28a3d8b..ad0084eb8ae79 100644 --- a/packages/remote/src/electron-node/remote-backend-module.ts +++ b/packages/remote/src/electron-node/remote-backend-module.ts @@ -27,13 +27,14 @@ import { RemoteCopyService } from './setup/remote-copy-service'; import { RemoteSetupService } from './setup/remote-setup-service'; import { RemoteNativeDependencyService } from './setup/remote-native-dependency-service'; import { BackendRemoteServiceImpl } from './backend-remote-service-impl'; -import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service'; +import { BackendRemoteService } from '@theia/core/lib/node/remote/backend-remote-service'; import { RemoteNodeSetupService } from './setup/remote-node-setup-service'; import { RemotePosixScriptStrategy, RemoteSetupScriptService, RemoteWindowsScriptStrategy } from './setup/remote-setup-script-service'; import { RemoteStatusService, RemoteStatusServicePath } from '../electron-common/remote-status-service'; import { RemoteStatusServiceImpl } from './remote-status-service'; import { ConnectionHandler, RpcConnectionHandler, bindContributionProvider } from '@theia/core'; -import { RemoteCopyContribution, RemoteCopyRegistry } from './setup/remote-copy-contribution'; +import { RemoteCopyRegistryImpl } from './setup/remote-copy-contribution'; +import { RemoteCopyContribution } from '@theia/core/lib/node/remote/remote-copy-contribution'; import { MainCopyContribution } from './setup/main-copy-contribution'; import { RemoteNativeDependencyContribution } from './setup/remote-native-dependency-contribution'; import { AppNativeDependencyContribution } from './setup/app-native-dependency-contribution'; @@ -68,7 +69,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(RemotePosixScriptStrategy).toSelf().inSingletonScope(); bind(RemoteSetupScriptService).toSelf().inSingletonScope(); bind(RemoteNativeDependencyService).toSelf().inSingletonScope(); - bind(RemoteCopyRegistry).toSelf().inSingletonScope(); + bind(RemoteCopyRegistryImpl).toSelf().inSingletonScope(); bindContributionProvider(bind, RemoteCopyContribution); bindContributionProvider(bind, RemoteNativeDependencyContribution); bind(MainCopyContribution).toSelf().inSingletonScope(); diff --git a/packages/remote/src/electron-node/remote-connection-service.ts b/packages/remote/src/electron-node/remote-connection-service.ts index ce776b8590fc4..e86612ee60257 100644 --- a/packages/remote/src/electron-node/remote-connection-service.ts +++ b/packages/remote/src/electron-node/remote-connection-service.ts @@ -18,8 +18,8 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { RemoteConnection } from './remote-types'; import { Disposable } from '@theia/core'; import { RemoteCopyService } from './setup/remote-copy-service'; -import { RemoteNativeDependencyService } from './setup/remote-native-dependency-service'; import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { RemoteSetupService } from './setup/remote-setup-service'; @injectable() export class RemoteConnectionService implements BackendApplicationContribution { @@ -27,8 +27,9 @@ export class RemoteConnectionService implements BackendApplicationContribution { @inject(RemoteCopyService) protected readonly copyService: RemoteCopyService; - @inject(RemoteNativeDependencyService) - protected readonly nativeDependencyService: RemoteNativeDependencyService; + // Workaround for the fact that connection scoped services cannot directly inject these services. + @inject(RemoteSetupService) + protected readonly remoteSetupService: RemoteSetupService; protected readonly connections = new Map(); diff --git a/packages/remote/src/electron-node/remote-types.ts b/packages/remote/src/electron-node/remote-types.ts index 9c829ee201499..c8ab798b1a15a 100644 --- a/packages/remote/src/electron-node/remote-types.ts +++ b/packages/remote/src/electron-node/remote-types.ts @@ -14,14 +14,9 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable, Event, OS } from '@theia/core'; +import { Disposable, Event } from '@theia/core'; import * as net from 'net'; -export interface RemotePlatform { - os: OS.Type - arch: string -} - export type RemoteStatusReport = (message: string) => void; export interface ExpressLayer { diff --git a/packages/remote/src/electron-node/setup/app-native-dependency-contribution.ts b/packages/remote/src/electron-node/setup/app-native-dependency-contribution.ts index 1e0d8e52719cb..4e0ffbd3e365a 100644 --- a/packages/remote/src/electron-node/setup/app-native-dependency-contribution.ts +++ b/packages/remote/src/electron-node/setup/app-native-dependency-contribution.ts @@ -16,7 +16,7 @@ import { injectable } from '@theia/core/shared/inversify'; import { RemoteNativeDependencyContribution, DownloadOptions, DependencyDownload } from './remote-native-dependency-contribution'; -import { RemotePlatform } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; import { OS } from '@theia/core'; @injectable() diff --git a/packages/remote/src/electron-node/setup/main-copy-contribution.ts b/packages/remote/src/electron-node/setup/main-copy-contribution.ts index e615af69b33cf..452d0eb15409c 100644 --- a/packages/remote/src/electron-node/setup/main-copy-contribution.ts +++ b/packages/remote/src/electron-node/setup/main-copy-contribution.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable } from '@theia/core/shared/inversify'; -import { RemoteCopyContribution, RemoteCopyRegistry } from './remote-copy-contribution'; +import { RemoteCopyContribution, RemoteCopyRegistry } from '@theia/core/lib/node/remote/remote-copy-contribution'; @injectable() export class MainCopyContribution implements RemoteCopyContribution { diff --git a/packages/remote/src/electron-node/setup/remote-copy-contribution.ts b/packages/remote/src/electron-node/setup/remote-copy-contribution.ts index 4aecf9e8f0746..9c9130e8c48f9 100644 --- a/packages/remote/src/electron-node/setup/remote-copy-contribution.ts +++ b/packages/remote/src/electron-node/setup/remote-copy-contribution.ts @@ -16,36 +16,15 @@ import { ApplicationPackage } from '@theia/core/shared/@theia/application-package'; import { inject, injectable } from '@theia/core/shared/inversify'; +import { RemoteCopyRegistry, RemoteFile, RemoteCopyOptions } from '@theia/core/lib/node/remote/remote-copy-contribution'; import { glob as globCallback } from 'glob'; import { promisify } from 'util'; import * as path from 'path'; -import { MaybePromise } from '@theia/core'; const promiseGlob = promisify(globCallback); -export const RemoteCopyContribution = Symbol('RemoteCopyContribution'); - -export interface RemoteCopyContribution { - copy(registry: RemoteCopyRegistry): MaybePromise -} - -export interface RemoteCopyOptions { - /** - * The mode that the file should be set to once copied to the remote. - * - * Only relevant for POSIX-like systems - */ - mode?: number; -} - -export interface RemoteFile { - path: string - target: string - options?: RemoteCopyOptions; -} - @injectable() -export class RemoteCopyRegistry { +export class RemoteCopyRegistryImpl implements RemoteCopyRegistry { @inject(ApplicationPackage) protected readonly applicationPackage: ApplicationPackage; @@ -57,14 +36,16 @@ export class RemoteCopyRegistry { } async glob(pattern: string, target?: string): Promise { + return this.doGlob(pattern, this.applicationPackage.projectPath, target); + } + + async doGlob(pattern: string, cwd: string, target?: string): Promise { const projectPath = this.applicationPackage.projectPath; - const globResult = await promiseGlob(pattern, { - cwd: projectPath - }); + const globResult = await promiseGlob(pattern, { cwd, nodir: true }); for (const file of globResult) { const targetFile = this.withTarget(file, target); this.files.push({ - path: file, + path: path.relative(projectPath, path.resolve(cwd, file)), target: targetFile }); } @@ -80,7 +61,11 @@ export class RemoteCopyRegistry { } async directory(dir: string, target?: string): Promise { - return this.glob(dir + '/**', target); + let absoluteDir = dir; + if (!path.isAbsolute(absoluteDir)) { + absoluteDir = path.join(this.applicationPackage.projectPath, dir); + } + return this.doGlob('**/*', absoluteDir, target ?? dir); } protected withTarget(file: string, target?: string): string { diff --git a/packages/remote/src/electron-node/setup/remote-copy-service.ts b/packages/remote/src/electron-node/setup/remote-copy-service.ts index ffe46d40f99e4..4d6200c22f263 100644 --- a/packages/remote/src/electron-node/setup/remote-copy-service.ts +++ b/packages/remote/src/electron-node/setup/remote-copy-service.ts @@ -20,10 +20,12 @@ import * as fs from 'fs'; import * as os from 'os'; import { ApplicationPackage } from '@theia/core/shared/@theia/application-package'; import { inject, injectable, named } from '@theia/core/shared/inversify'; -import { RemoteConnection, RemotePlatform } from '../remote-types'; +import { RemoteConnection } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; import { RemoteNativeDependencyService } from './remote-native-dependency-service'; import { ContributionProvider } from '@theia/core'; -import { RemoteCopyContribution, RemoteCopyRegistry, RemoteFile } from './remote-copy-contribution'; +import { RemoteCopyRegistryImpl } from './remote-copy-contribution'; +import { RemoteCopyContribution, RemoteFile } from '@theia/core/lib/node/remote/remote-copy-contribution'; @injectable() export class RemoteCopyService { @@ -31,8 +33,8 @@ export class RemoteCopyService { @inject(ApplicationPackage) protected readonly applicationPackage: ApplicationPackage; - @inject(RemoteCopyRegistry) - protected readonly copyRegistry: RemoteCopyRegistry; + @inject(RemoteCopyRegistryImpl) + protected readonly copyRegistry: RemoteCopyRegistryImpl; @inject(RemoteNativeDependencyService) protected readonly nativeDependencyService: RemoteNativeDependencyService; diff --git a/packages/remote/src/electron-node/setup/remote-native-dependency-contribution.ts b/packages/remote/src/electron-node/setup/remote-native-dependency-contribution.ts index ece62ba8c45a5..3a5260645658f 100644 --- a/packages/remote/src/electron-node/setup/remote-native-dependency-contribution.ts +++ b/packages/remote/src/electron-node/setup/remote-native-dependency-contribution.ts @@ -16,7 +16,7 @@ import { isObject } from '@theia/core'; import { RequestOptions } from '@theia/core/shared/@theia/request'; -import { RemotePlatform } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; export interface FileDependencyResult { path: string; diff --git a/packages/remote/src/electron-node/setup/remote-native-dependency-service.ts b/packages/remote/src/electron-node/setup/remote-native-dependency-service.ts index 6caa905cbdaa5..579ccd5437327 100644 --- a/packages/remote/src/electron-node/setup/remote-native-dependency-service.ts +++ b/packages/remote/src/electron-node/setup/remote-native-dependency-service.ts @@ -21,7 +21,7 @@ import * as decompress from 'decompress'; import * as path from 'path'; import * as fs from 'fs/promises'; import { DependencyDownload, DirectoryDependencyDownload, RemoteNativeDependencyContribution } from './remote-native-dependency-contribution'; -import { RemotePlatform } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; const decompressTar = require('decompress-tar'); const decompressTargz = require('decompress-targz'); diff --git a/packages/remote/src/electron-node/setup/remote-node-setup-service.ts b/packages/remote/src/electron-node/setup/remote-node-setup-service.ts index f559c2c494cde..49567e537736a 100644 --- a/packages/remote/src/electron-node/setup/remote-node-setup-service.ts +++ b/packages/remote/src/electron-node/setup/remote-node-setup-service.ts @@ -21,7 +21,7 @@ import * as os from 'os'; import { inject, injectable } from '@theia/core/shared/inversify'; import { RequestService } from '@theia/core/shared/@theia/request'; import { RemoteSetupScriptService } from './remote-setup-script-service'; -import { RemotePlatform } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; import { OS } from '@theia/core'; /** diff --git a/packages/remote/src/electron-node/setup/remote-setup-script-service.ts b/packages/remote/src/electron-node/setup/remote-setup-script-service.ts index b6a7011d59ea3..afa606283c2ac 100644 --- a/packages/remote/src/electron-node/setup/remote-setup-script-service.ts +++ b/packages/remote/src/electron-node/setup/remote-setup-script-service.ts @@ -16,7 +16,7 @@ import { OS } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { RemotePlatform } from '../remote-types'; +import { RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; export interface RemoteScriptStrategy { exec(): string; diff --git a/packages/remote/src/electron-node/setup/remote-setup-service.ts b/packages/remote/src/electron-node/setup/remote-setup-service.ts index 951f62429da8f..9ef150807d985 100644 --- a/packages/remote/src/electron-node/setup/remote-setup-service.ts +++ b/packages/remote/src/electron-node/setup/remote-setup-service.ts @@ -14,12 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject, injectable } from '@theia/core/shared/inversify'; -import { RemoteConnection, RemoteExecResult, RemotePlatform, RemoteStatusReport } from '../remote-types'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { RemoteConnection, RemoteExecResult, RemoteStatusReport } from '../remote-types'; +import { RemoteCliContext, RemoteCliContribution, RemotePlatform } from '@theia/core/lib/node/remote/remote-cli-contribution'; import { ApplicationPackage } from '@theia/core/shared/@theia/application-package'; import { RemoteCopyService } from './remote-copy-service'; import { RemoteNativeDependencyService } from './remote-native-dependency-service'; -import { OS, THEIA_VERSION } from '@theia/core'; +import { ContributionProvider, OS, THEIA_VERSION } from '@theia/core'; import { RemoteNodeSetupService } from './remote-node-setup-service'; import { RemoteSetupScriptService } from './remote-setup-script-service'; @@ -52,6 +53,9 @@ export class RemoteSetupService { @inject(ApplicationPackage) protected readonly applicationPackage: ApplicationPackage; + @inject(ContributionProvider) @named(RemoteCliContribution) + protected readonly cliContributions: ContributionProvider; + async setup(options: RemoteSetupOptions): Promise { const { connection, @@ -106,11 +110,21 @@ export class RemoteSetupService { // We might to switch to PowerShell beforehand on Windows prefix = this.scriptService.exec(platform) + ' '; } + const remoteContext: RemoteCliContext = { + platform, + directory: remotePath + }; + const args: string[] = ['--hostname=0.0.0.0', `--port=${connection.remotePort ?? 0}`, '--remote']; + for (const cli of this.cliContributions.getContributions()) { + if (cli.enhanceArgs) { + args.push(...await cli.enhanceArgs(remoteContext)); + } + } // Change to the remote application path and start a node process with the copied main.js file // This way, our current working directory is set as expected const result = await connection.execPartial(`${prefix}cd "${remotePath}";${nodeExecutable}`, stdout => localAddressRegex.test(stdout), - [mainJsFile, '--hostname=0.0.0.0', `--port=${connection.remotePort ?? 0}`, '--remote']); + [mainJsFile, ...args]); const match = localAddressRegex.exec(result.stdout); if (!match) { diff --git a/packages/vsx-registry/src/node/vsx-cli-deployer-participant.ts b/packages/vsx-registry/src/node/vsx-cli-deployer-participant.ts index e3390cc0bb7dc..b7b34c7c1ce08 100644 --- a/packages/vsx-registry/src/node/vsx-cli-deployer-participant.ts +++ b/packages/vsx-registry/src/node/vsx-cli-deployer-participant.ts @@ -29,7 +29,7 @@ export class VsxCliDeployerParticipant implements PluginDeployerParticipant { protected readonly vsxCli: VsxCli; async onWillStart(context: PluginDeployerStartContext): Promise { - const pluginUris = this.vsxCli.pluginsToInstall.map(async id => { + const pluginUris = await Promise.all(this.vsxCli.pluginsToInstall.map(async id => { try { const resolvedPath = path.resolve(id); const stat = await fs.promises.stat(resolvedPath); @@ -40,7 +40,7 @@ export class VsxCliDeployerParticipant implements PluginDeployerParticipant { // expected if file does not exist } return VSXExtensionUri.fromVersionedId(id).toString(); - }); - context.userEntries.push(...await Promise.all(pluginUris)); + })); + context.userEntries.push(...pluginUris); } } diff --git a/packages/vsx-registry/src/node/vsx-remote-cli.ts b/packages/vsx-registry/src/node/vsx-remote-cli.ts new file mode 100644 index 0000000000000..dc75d9c2b16d7 --- /dev/null +++ b/packages/vsx-registry/src/node/vsx-remote-cli.ts @@ -0,0 +1,39 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { RemoteCliContext, RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { PluginDeployerHandler, PluginType } from '@theia/plugin-ext'; + +@injectable() +export class VsxRemoteCli implements RemoteCliContribution { + + @inject(PluginDeployerHandler) + protected readonly pluginDeployerHandler: PluginDeployerHandler; + + async enhanceArgs(context: RemoteCliContext): Promise { + const deployedPlugins = await this.pluginDeployerHandler.getDeployedPlugins(); + // Plugin IDs can be duplicated between frontend and backend plugins, so we create a set first + const installPluginArgs = Array.from( + new Set( + deployedPlugins + .filter(plugin => plugin.type === PluginType.User) + .map(p => `--install-plugin=${p.metadata.model.id}`) + ) + ); + return installPluginArgs; + } +} From da32fed695fcfe224b6841fb077bd10f1051d76b Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Wed, 15 May 2024 14:25:25 +0200 Subject: [PATCH 228/441] fix: do not override user-defined THEIA_CONFIG_DIR (#13708) Fixes #13700 Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- packages/core/src/node/env-variables/env-variables-server.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/core/src/node/env-variables/env-variables-server.ts b/packages/core/src/node/env-variables/env-variables-server.ts index 57cdf059756fd..eae08768da14f 100644 --- a/packages/core/src/node/env-variables/env-variables-server.ts +++ b/packages/core/src/node/env-variables/env-variables-server.ts @@ -46,6 +46,11 @@ export class EnvVariablesServerImpl implements EnvVariablesServer { } protected async createConfigDirUri(): Promise { + if (process.env.THEIA_CONFIG_DIR) { + // this has been explicitly set by the user, so we do not override its value + return FileUri.create(process.env.THEIA_CONFIG_DIR).toString(); + } + const dataFolderPath = join(BackendApplicationPath, 'data'); const userDataPath = join(dataFolderPath, 'user-data'); const dataFolderExists = this.pathExistenceCache[dataFolderPath] ??= await pathExists(dataFolderPath); From dacc1aeda03290ce49a3cd02af180ee20c2a8092 Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Thu, 16 May 2024 09:51:23 +0200 Subject: [PATCH 229/441] doc: improve WebSocketConnectionProvider deprecation message (#13713) The current deprecation message leads to confusion as the referenced class is not exported and not usable as an injection symbol. The message is improved and the new ServiceConnectionProvider is exported. Contributed on behalf of STMicroelectronics --- packages/core/src/browser/messaging/index.ts | 1 + packages/core/src/browser/messaging/ws-connection-provider.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/browser/messaging/index.ts b/packages/core/src/browser/messaging/index.ts index c2b82e9ab402d..5112fc973508d 100644 --- a/packages/core/src/browser/messaging/index.ts +++ b/packages/core/src/browser/messaging/index.ts @@ -15,3 +15,4 @@ // ***************************************************************************** export * from './ws-connection-provider'; +export * from './service-connection-provider'; diff --git a/packages/core/src/browser/messaging/ws-connection-provider.ts b/packages/core/src/browser/messaging/ws-connection-provider.ts index 3f9a3b45c7e73..642b3c3d26972 100644 --- a/packages/core/src/browser/messaging/ws-connection-provider.ts +++ b/packages/core/src/browser/messaging/ws-connection-provider.ts @@ -22,7 +22,8 @@ decorate(injectable(), RpcProxyFactory); decorate(unmanaged(), RpcProxyFactory, 0); /** - * @deprecated This class serves to keep API compatibility for a while. Use {@linkcode ServiceConnectionProvider} instead. + * @deprecated This class serves to keep API compatibility for a while. + * Use the {@linkcode RemoteConnectionProvider} as the injection symbol and {@linkcode ServiceConnectionProvider} as the type instead. */ @injectable() export class WebSocketConnectionProvider { From 44b4f27c63028c1bdc7dcd9b565d666428af6f97 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 16 May 2024 17:04:06 +0200 Subject: [PATCH 230/441] notebook: Select the Last Cell when deleting selected last cell (#13715) * notebook: Select the Last Cell when deleting selected cell with index larger than avaialable cells Signed-off-by: Jonah Iden * better length check Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- packages/notebook/src/browser/view-model/notebook-model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index fe44f00fd1a08..5c12186b4c403 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -314,7 +314,7 @@ export class NotebookModel implements Saveable, Disposable { // if selected cell is affected update it because it can potentially have been replaced if (cell === this.selectedCell) { - this.setSelectedCell(this.cells[cellIndex]); + this.setSelectedCell(this.cells[Math.min(cellIndex, this.cells.length - 1)]); } } From d9e58c3995cbb1131027596c4fec35a7001807dd Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 21 May 2024 00:52:06 +0200 Subject: [PATCH 231/441] Refactor auto save mechanism (#13683) --- CHANGELOG.md | 6 + examples/api-tests/src/saveable.spec.js | 2 +- examples/api-tests/src/typescript.spec.js | 9 +- .../browser/common-frontend-contribution.ts | 4 +- .../browser/frontend-application-module.ts | 6 +- packages/core/src/browser/index.ts | 1 + .../core/src/browser/save-resource-service.ts | 60 ---- packages/core/src/browser/saveable-service.ts | 328 ++++++++++++++++++ packages/core/src/browser/saveable.ts | 140 +++----- .../src/browser/shell/application-shell.ts | 23 +- .../default-secondary-window-service.ts | 8 +- packages/editor/src/browser/editor-command.ts | 29 +- .../src/browser/filesystem-frontend-module.ts | 8 +- ...vice.ts => filesystem-saveable-service.ts} | 17 +- .../monaco/src/browser/monaco-editor-model.ts | 22 +- .../src/browser/monaco-text-model-service.ts | 8 - .../monaco/src/browser/monaco-workspace.ts | 6 +- .../src/browser/notebook-frontend-module.ts | 3 +- .../notebook-model-resolver-service.ts | 2 +- .../src/browser/view-model/notebook-model.ts | 60 ++-- .../custom-editors/custom-editor-widget.ts | 13 +- .../custom-editors/custom-editors-main.ts | 51 +-- .../browser/editors-and-documents-main.ts | 6 +- .../workspace-frontend-contribution.ts | 4 +- 24 files changed, 510 insertions(+), 306 deletions(-) delete mode 100644 packages/core/src/browser/save-resource-service.ts create mode 100644 packages/core/src/browser/saveable-service.ts rename packages/filesystem/src/browser/{filesystem-save-resource-service.ts => filesystem-saveable-service.ts} (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 342df3ad32707..6f750f0c6c97b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ [Breaking Changes:](#breaking_changes_not_yet_released) --> +## 1.50.0 + +[Breaking Changes:](#breaking_changes_1.50.0) + +- [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. + ## v1.49.0 - 04/29/2024 - [application-manager] added logic to generate Extension Info in server application to avoid empty About Dialog [#13590](https://github.com/eclipse-theia/theia/pull/13590) - contributed on behalf of STMicroelectronics diff --git a/examples/api-tests/src/saveable.spec.js b/examples/api-tests/src/saveable.spec.js index 5a50637b786ca..091ae06f14ee0 100644 --- a/examples/api-tests/src/saveable.spec.js +++ b/examples/api-tests/src/saveable.spec.js @@ -81,13 +81,13 @@ describe('Saveable', function () { afterEach(async () => { toTearDown.dispose(); - await preferences.set('files.autoSave', autoSave, undefined, rootUri.toString()); // @ts-ignore editor = undefined; // @ts-ignore widget = undefined; await editorManager.closeAll({ save: false }); await fileService.delete(fileUri.parent, { fromUserGesture: false, useTrash: false, recursive: true }); + await preferences.set('files.autoSave', autoSave, undefined, rootUri.toString()); }); it('normal save', async function () { diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index 89619d9138bf7..a2432070f1f77 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -64,7 +64,7 @@ describe('TypeScript', function () { const rootUri = workspaceService.tryGetRoots()[0].resource; const demoFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-file.ts'); const definitionFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-definitions-file.ts'); - let originalAutoSaveValue = preferences.inspect('files.autoSave').globalValue; + let originalAutoSaveValue = preferences.get('files.autoSave'); before(async function () { await pluginService.didStart; @@ -73,8 +73,9 @@ describe('TypeScript', function () { throw new Error(pluginId + ' should be started'); } await pluginService.activatePlugin(pluginId); - }).concat(preferences.set('files.autoSave', 'off', PreferenceScope.User))); - await preferences.set('files.refactoring.autoSave', 'off', PreferenceScope.User); + })); + await preferences.set('files.autoSave', 'off'); + await preferences.set('files.refactoring.autoSave', 'off'); }); beforeEach(async function () { @@ -90,7 +91,7 @@ describe('TypeScript', function () { }); after(async () => { - await preferences.set('files.autoSave', originalAutoSaveValue, PreferenceScope.User); + await preferences.set('files.autoSave', originalAutoSaveValue); }) /** diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index dc7d940e005c2..89a0532c05d45 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -62,7 +62,7 @@ import { WindowService } from './window/window-service'; import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; import { DecorationStyle } from './decoration-style'; import { isPinned, Title, togglePinned, Widget } from './widgets'; -import { SaveResourceService } from './save-resource-service'; +import { SaveableService } from './saveable-service'; import { UserWorkingDirectoryProvider } from './user-working-directory-provider'; import { UNTITLED_SCHEME, UntitledResourceResolver } from '../common'; import { LanguageQuickPickService } from './i18n/language-quick-pick-service'; @@ -385,7 +385,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(OpenerService) protected readonly openerService: OpenerService, @inject(AboutDialog) protected readonly aboutDialog: AboutDialog, @inject(AsyncLocalizationProvider) protected readonly localizationProvider: AsyncLocalizationProvider, - @inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService, + @inject(SaveableService) protected readonly saveResourceService: SaveableService, ) { } @inject(ContextKeyService) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index b343a8b4125c8..bc21b7277c135 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -126,7 +126,7 @@ import { DockPanel, RendererHost } from './widgets'; import { TooltipService, TooltipServiceImpl } from './tooltip-service'; import { BackendRequestService, RequestService, REQUEST_SERVICE_PATH } from '@theia/request'; import { bindFrontendStopwatch, bindBackendStopwatch } from './performance'; -import { SaveResourceService } from './save-resource-service'; +import { SaveableService } from './saveable-service'; import { SecondaryWindowHandler } from './secondary-window-handler'; import { UserWorkingDirectoryProvider } from './user-working-directory-provider'; import { WindowTitleService } from './window/window-title-service'; @@ -449,7 +449,9 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bindFrontendStopwatch(bind); bindBackendStopwatch(bind); - bind(SaveResourceService).toSelf().inSingletonScope(); + bind(SaveableService).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(SaveableService); + bind(UserWorkingDirectoryProvider).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(UserWorkingDirectoryProvider); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index fe5906c3be729..42277fc2f7833 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -46,3 +46,4 @@ export * from './tooltip-service'; export * from './decoration-style'; export * from './styling-service'; export * from './hover-service'; +export * from './saveable-service'; diff --git a/packages/core/src/browser/save-resource-service.ts b/packages/core/src/browser/save-resource-service.ts deleted file mode 100644 index 82de2244315ab..0000000000000 --- a/packages/core/src/browser/save-resource-service.ts +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2022 Arm and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 - ********************************************************************************/ - -import { inject, injectable } from 'inversify'; -import { MessageService, UNTITLED_SCHEME, URI } from '../common'; -import { Navigatable, NavigatableWidget } from './navigatable-types'; -import { Saveable, SaveableSource, SaveOptions } from './saveable'; -import { Widget } from './widgets'; - -@injectable() -export class SaveResourceService { - @inject(MessageService) protected readonly messageService: MessageService; - - /** - * Indicate if the document can be saved ('Save' command should be disable if not). - */ - canSave(widget?: Widget): widget is Widget & (Saveable | SaveableSource) { - return Saveable.isDirty(widget) && (this.canSaveNotSaveAs(widget) || this.canSaveAs(widget)); - } - - canSaveNotSaveAs(widget?: Widget): widget is Widget & (Saveable | SaveableSource) { - // By default, we never allow a document to be saved if it is untitled. - return Boolean(widget && NavigatableWidget.getUri(widget)?.scheme !== UNTITLED_SCHEME); - } - - /** - * Saves the document - * - * No op if the widget is not saveable. - */ - async save(widget: Widget | undefined, options?: SaveOptions): Promise { - if (this.canSaveNotSaveAs(widget)) { - await Saveable.save(widget, options); - return NavigatableWidget.getUri(widget); - } else if (this.canSaveAs(widget)) { - return this.saveAs(widget, options); - } - } - - canSaveAs(saveable?: Widget): saveable is Widget & SaveableSource & Navigatable { - return false; - } - - saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { - return Promise.reject('Unsupported: The base SaveResourceService does not support saveAs action.'); - } -} diff --git a/packages/core/src/browser/saveable-service.ts b/packages/core/src/browser/saveable-service.ts new file mode 100644 index 0000000000000..12b273019885a --- /dev/null +++ b/packages/core/src/browser/saveable-service.ts @@ -0,0 +1,328 @@ +/******************************************************************************** + * Copyright (C) 2022 Arm and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + ********************************************************************************/ + +import type { ApplicationShell } from './shell'; +import { injectable } from 'inversify'; +import { UNTITLED_SCHEME, URI, Disposable, DisposableCollection, Emitter, Event } from '../common'; +import { Navigatable, NavigatableWidget } from './navigatable-types'; +import { AutoSaveMode, Saveable, SaveableSource, SaveableWidget, SaveOptions, SaveReason, setDirty, close, PostCreationSaveableWidget, ShouldSaveDialog } from './saveable'; +import { waitForClosed, Widget } from './widgets'; +import { FrontendApplicationContribution } from './frontend-application-contribution'; +import { FrontendApplication } from './frontend-application'; +import throttle = require('lodash.throttle'); + +@injectable() +export class SaveableService implements FrontendApplicationContribution { + + protected saveThrottles = new Map(); + protected saveMode: AutoSaveMode = 'off'; + protected saveDelay = 1000; + protected shell: ApplicationShell; + + protected readonly onDidAutoSaveChangeEmitter = new Emitter(); + protected readonly onDidAutoSaveDelayChangeEmitter = new Emitter(); + + get onDidAutoSaveChange(): Event { + return this.onDidAutoSaveChangeEmitter.event; + } + + get onDidAutoSaveDelayChange(): Event { + return this.onDidAutoSaveDelayChangeEmitter.event; + } + + get autoSave(): AutoSaveMode { + return this.saveMode; + } + + set autoSave(value: AutoSaveMode) { + this.updateAutoSaveMode(value); + } + + get autoSaveDelay(): number { + return this.saveDelay; + } + + set autoSaveDelay(value: number) { + this.updateAutoSaveDelay(value); + } + + onDidInitializeLayout(app: FrontendApplication): void { + this.shell = app.shell; + // Register restored editors first + for (const widget of this.shell.widgets) { + const saveable = Saveable.get(widget); + if (saveable) { + this.registerSaveable(widget, saveable); + } + } + this.shell.onDidAddWidget(e => { + const saveable = Saveable.get(e); + if (saveable) { + this.registerSaveable(e, saveable); + } + }); + this.shell.onDidChangeCurrentWidget(e => { + if (this.saveMode === 'onFocusChange') { + const widget = e.oldValue; + const saveable = Saveable.get(widget); + if (saveable && widget && this.shouldAutoSave(widget, saveable)) { + saveable.save({ + saveReason: SaveReason.FocusChange + }); + } + } + }); + this.shell.onDidRemoveWidget(e => { + this.saveThrottles.get(e)?.dispose(); + this.saveThrottles.delete(e); + }); + } + + protected updateAutoSaveMode(mode: AutoSaveMode): void { + this.saveMode = mode; + this.onDidAutoSaveChangeEmitter.fire(mode); + if (mode === 'onFocusChange') { + // If the new mode is onFocusChange, we need to save all dirty documents that are not focused + const widgets = this.shell.widgets; + for (const widget of widgets) { + const saveable = Saveable.get(widget); + if (saveable && widget !== this.shell.currentWidget && this.shouldAutoSave(widget, saveable)) { + saveable.save({ + saveReason: SaveReason.FocusChange + }); + } + } + } + } + + protected updateAutoSaveDelay(delay: number): void { + this.saveDelay = delay; + this.onDidAutoSaveDelayChangeEmitter.fire(delay); + } + + registerSaveable(widget: Widget, saveable: Saveable): Disposable { + const saveThrottle = new AutoSaveThrottle( + saveable, + this, + () => { + if (this.saveMode === 'afterDelay' && this.shouldAutoSave(widget, saveable)) { + saveable.save({ + saveReason: SaveReason.AfterDelay + }); + } + }, + this.addBlurListener(widget, saveable) + ); + this.saveThrottles.set(widget, saveThrottle); + this.applySaveableWidget(widget, saveable); + return saveThrottle; + } + + protected addBlurListener(widget: Widget, saveable: Saveable): Disposable { + const document = widget.node.ownerDocument; + const listener = (() => { + if (this.saveMode === 'onWindowChange' && !this.windowHasFocus(document) && this.shouldAutoSave(widget, saveable)) { + saveable.save({ + saveReason: SaveReason.FocusChange + }); + } + }).bind(this); + document.addEventListener('blur', listener); + return Disposable.create(() => { + document.removeEventListener('blur', listener); + }); + } + + protected windowHasFocus(document: Document): boolean { + if (document.visibilityState === 'hidden') { + return false; + } else if (document.hasFocus()) { + return true; + } + // TODO: Add support for iframes + return false; + } + + protected shouldAutoSave(widget: Widget, saveable: Saveable): boolean { + const uri = NavigatableWidget.getUri(widget); + if (uri?.scheme === UNTITLED_SCHEME) { + // Never auto-save untitled documents + return false; + } else { + return saveable.dirty; + } + } + + protected applySaveableWidget(widget: Widget, saveable: Saveable): void { + if (SaveableWidget.is(widget)) { + return; + } + const saveableWidget = widget as PostCreationSaveableWidget; + setDirty(saveableWidget, saveable.dirty); + saveable.onDirtyChanged(() => setDirty(saveableWidget, saveable.dirty)); + const closeWithSaving = this.createCloseWithSaving(); + const closeWithoutSaving = () => this.closeWithoutSaving(saveableWidget, false); + Object.assign(saveableWidget, { + closeWithoutSaving, + closeWithSaving, + close: closeWithSaving, + [close]: saveableWidget.close, + }); + } + + protected createCloseWithSaving(): (this: SaveableWidget, options?: SaveableWidget.CloseOptions) => Promise { + let closing = false; + const doSave = this.closeWithSaving.bind(this); + return async function (this: SaveableWidget, options?: SaveableWidget.CloseOptions): Promise { + if (closing) { + return; + } + closing = true; + try { + await doSave(this, options); + } finally { + closing = false; + } + }; + } + + protected async closeWithSaving(widget: PostCreationSaveableWidget, options?: SaveableWidget.CloseOptions): Promise { + const result = await this.shouldSaveWidget(widget, options); + if (typeof result === 'boolean') { + if (result) { + await this.save(widget, { + saveReason: SaveReason.AfterDelay + }); + if (!Saveable.isDirty(widget)) { + await widget.closeWithoutSaving(); + } + } else { + await widget.closeWithoutSaving(); + } + } + } + + protected async shouldSaveWidget(widget: PostCreationSaveableWidget, options?: SaveableWidget.CloseOptions): Promise { + if (!Saveable.isDirty(widget)) { + return false; + } + if (this.autoSave !== 'off') { + return true; + } + const notLastWithDocument = !Saveable.closingWidgetWouldLoseSaveable(widget, Array.from(this.saveThrottles.keys())); + if (notLastWithDocument) { + return widget.closeWithoutSaving(false).then(() => undefined); + } + if (options && options.shouldSave) { + return options.shouldSave(); + } + return new ShouldSaveDialog(widget).open(); + } + + protected async closeWithoutSaving(widget: PostCreationSaveableWidget, doRevert: boolean = true): Promise { + const saveable = Saveable.get(widget); + if (saveable && doRevert && saveable.dirty && saveable.revert) { + await saveable.revert(); + } + widget[close](); + return waitForClosed(widget); + } + + /** + * Indicate if the document can be saved ('Save' command should be disable if not). + */ + canSave(widget?: Widget): widget is Widget & (Saveable | SaveableSource) { + return Saveable.isDirty(widget) && (this.canSaveNotSaveAs(widget) || this.canSaveAs(widget)); + } + + canSaveNotSaveAs(widget?: Widget): widget is Widget & (Saveable | SaveableSource) { + // By default, we never allow a document to be saved if it is untitled. + return Boolean(widget && NavigatableWidget.getUri(widget)?.scheme !== UNTITLED_SCHEME); + } + + /** + * Saves the document + * + * No op if the widget is not saveable. + */ + async save(widget: Widget | undefined, options?: SaveOptions): Promise { + if (this.canSaveNotSaveAs(widget)) { + await Saveable.save(widget, options); + return NavigatableWidget.getUri(widget); + } else if (this.canSaveAs(widget)) { + return this.saveAs(widget, options); + } + } + + canSaveAs(saveable?: Widget): saveable is Widget & SaveableSource & Navigatable { + return false; + } + + saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise { + return Promise.reject('Unsupported: The base SaveResourceService does not support saveAs action.'); + } +} + +export class AutoSaveThrottle implements Disposable { + + private _saveable: Saveable; + private _callback: () => void; + private _saveService: SaveableService; + private _disposable: DisposableCollection; + private _throttle?: ReturnType; + + constructor(saveable: Saveable, saveService: SaveableService, callback: () => void, ...disposables: Disposable[]) { + this._callback = callback; + this._saveable = saveable; + this._saveService = saveService; + this._disposable = new DisposableCollection( + ...disposables, + saveable.onContentChanged(() => { + this.throttledSave(); + }), + saveable.onDirtyChanged(() => { + this.throttledSave(); + }), + saveService.onDidAutoSaveChange(() => { + this.throttledSave(); + }), + saveService.onDidAutoSaveDelayChange(() => { + this.throttledSave(true); + }) + ); + } + + protected throttledSave(reset = false): void { + this._throttle?.cancel(); + if (reset) { + this._throttle = undefined; + } + if (this._saveService.autoSave === 'afterDelay' && this._saveable.dirty) { + if (!this._throttle) { + this._throttle = throttle(() => this._callback(), this._saveService.autoSaveDelay, { + leading: false, + trailing: true + }); + } + this._throttle(); + } + } + + dispose(): void { + this._disposable.dispose(); + } + +} diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index bfedeff9a19e6..2f9390ca2b8fd 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -20,14 +20,23 @@ import { Emitter, Event } from '../common/event'; import { MaybePromise } from '../common/types'; import { Key } from './keyboard/keys'; import { AbstractDialog } from './dialogs'; -import { waitForClosed } from './widgets'; import { nls } from '../common/nls'; -import { Disposable, isObject } from '../common'; +import { DisposableCollection, isObject } from '../common'; + +export type AutoSaveMode = 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; export interface Saveable { readonly dirty: boolean; + /** + * This event is fired when the content of the `dirty` variable changes. + */ readonly onDirtyChanged: Event; - readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; + /** + * This event is fired when the content of the saveable changes. + * While `onDirtyChanged` is fired to notify the UI that the widget is dirty, + * `onContentChanged` is used for the auto save throttling. + */ + readonly onContentChanged: Event; /** * Saves dirty changes. */ @@ -53,11 +62,15 @@ export interface SaveableSource { export class DelegatingSaveable implements Saveable { dirty = false; protected readonly onDirtyChangedEmitter = new Emitter(); + protected readonly onContentChangedEmitter = new Emitter(); get onDirtyChanged(): Event { return this.onDirtyChangedEmitter.event; } - autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange' = 'off'; + + get onContentChanged(): Event { + return this.onContentChangedEmitter.event; + } async save(options?: SaveOptions): Promise { await this._delegate?.save(options); @@ -68,16 +81,19 @@ export class DelegatingSaveable implements Saveable { applySnapshot?(snapshot: object): void; protected _delegate?: Saveable; - protected toDispose?: Disposable; + protected toDispose = new DisposableCollection(); set delegate(delegate: Saveable) { - this.toDispose?.dispose(); + this.toDispose.dispose(); + this.toDispose = new DisposableCollection(); this._delegate = delegate; - this.toDispose = delegate.onDirtyChanged(() => { + this.toDispose.push(delegate.onDirtyChanged(() => { this.dirty = delegate.dirty; this.onDirtyChangedEmitter.fire(); - }); - this.autoSave = delegate.autoSave; + })); + this.toDispose.push(delegate.onContentChanged(() => { + this.onContentChangedEmitter.fire(); + })); if (this.dirty !== delegate.dirty) { this.dirty = delegate.dirty; this.onDirtyChangedEmitter.fire(); @@ -131,52 +147,6 @@ export namespace Saveable { } } - async function closeWithoutSaving(this: PostCreationSaveableWidget, doRevert: boolean = true): Promise { - const saveable = get(this); - if (saveable && doRevert && saveable.dirty && saveable.revert) { - await saveable.revert(); - } - this[close](); - return waitForClosed(this); - } - - function createCloseWithSaving( - getOtherSaveables?: () => Array, - doSave?: (widget: Widget, options?: SaveOptions) => Promise - ): (this: SaveableWidget, options?: SaveableWidget.CloseOptions) => Promise { - let closing = false; - return async function (this: SaveableWidget, options: SaveableWidget.CloseOptions): Promise { - if (closing) { return; } - const saveable = get(this); - if (!saveable) { return; } - closing = true; - try { - const result = await shouldSave(saveable, () => { - const notLastWithDocument = !closingWidgetWouldLoseSaveable(this, getOtherSaveables?.() ?? []); - if (notLastWithDocument) { - return this.closeWithoutSaving(false).then(() => undefined); - } - if (options && options.shouldSave) { - return options.shouldSave(); - } - return new ShouldSaveDialog(this).open(); - }); - if (typeof result === 'boolean') { - if (result) { - await (doSave?.(this) ?? Saveable.save(this)); - if (!isDirty(this)) { - await this.closeWithoutSaving(); - } - } else { - await this.closeWithoutSaving(); - } - } - } finally { - closing = false; - } - }; - } - export async function confirmSaveBeforeClose(toClose: Iterable, others: Widget[]): Promise { for (const widget of toClose) { const saveable = Saveable.get(widget); @@ -197,49 +167,9 @@ export namespace Saveable { return true; } - /** - * @param widget the widget that may be closed - * @param others widgets that will not be closed. - * @returns `true` if widget is saveable and no widget among the `others` refers to the same saveable. `false` otherwise. - */ - function closingWidgetWouldLoseSaveable(widget: Widget, others: Widget[]): boolean { - const saveable = get(widget); - return !!saveable && !others.some(otherWidget => otherWidget !== widget && get(otherWidget) === saveable); - } - - export function apply( - widget: Widget, - getOtherSaveables?: () => Array, - doSave?: (widget: Widget, options?: SaveOptions) => Promise, - ): SaveableWidget | undefined { - if (SaveableWidget.is(widget)) { - return widget; - } + export function closingWidgetWouldLoseSaveable(widget: Widget, others: Widget[]): boolean { const saveable = Saveable.get(widget); - if (!saveable) { - return undefined; - } - const saveableWidget = widget as SaveableWidget; - setDirty(saveableWidget, saveable.dirty); - saveable.onDirtyChanged(() => setDirty(saveableWidget, saveable.dirty)); - const closeWithSaving = createCloseWithSaving(getOtherSaveables, doSave); - return Object.assign(saveableWidget, { - closeWithoutSaving, - closeWithSaving, - close: closeWithSaving, - [close]: saveableWidget.close, - }); - } - export async function shouldSave(saveable: Saveable, cb: () => MaybePromise): Promise { - if (!saveable.dirty) { - return false; - } - - if (saveable.autoSave !== 'off') { - return true; - } - - return cb(); + return !!saveable && !others.some(otherWidget => otherWidget !== widget && Saveable.get(otherWidget) === saveable); } } @@ -302,11 +232,27 @@ export const enum FormatType { DIRTY }; +export enum SaveReason { + Manual = 1, + AfterDelay = 2, + FocusChange = 3 +} + +export namespace SaveReason { + export function isManual(reason?: number): reason is typeof SaveReason.Manual { + return reason === SaveReason.Manual; + } +} + export interface SaveOptions { /** * Formatting type to apply when saving. */ readonly formatType?: FormatType; + /** + * The reason for saving the resource. + */ + readonly saveReason?: SaveReason; } /** diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index 424c0bf43dcb5..c752bffdf1827 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -24,7 +24,7 @@ import { Message } from '@phosphor/messaging'; import { IDragEvent } from '@phosphor/dragdrop'; import { RecursivePartial, Event as CommonEvent, DisposableCollection, Disposable, environment, isObject } from '../../common'; import { animationFrame } from '../browser'; -import { Saveable, SaveableWidget, SaveOptions, SaveableSource } from '../saveable'; +import { Saveable, SaveableWidget, SaveOptions } from '../saveable'; import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar'; import { TheiaDockPanel, BOTTOM_AREA_ID, MAIN_AREA_ID } from './theia-dock-panel'; import { SidePanelHandler, SidePanel, SidePanelHandlerFactory } from './side-panel-handler'; @@ -38,7 +38,7 @@ import { waitForRevealed, waitForClosed, PINNED_CLASS } from '../widgets'; import { CorePreferences } from '../core-preferences'; import { BreadcrumbsRendererFactory } from '../breadcrumbs/breadcrumbs-renderer'; import { Deferred } from '../../common/promise-util'; -import { SaveResourceService } from '../save-resource-service'; +import { SaveableService } from '../saveable-service'; import { nls } from '../../common/nls'; import { SecondaryWindowHandler } from '../secondary-window-handler'; import URI from '../../common/uri'; @@ -272,7 +272,7 @@ export class ApplicationShell extends Widget { @inject(FrontendApplicationStateService) protected readonly applicationStateService: FrontendApplicationStateService, @inject(ApplicationShellOptions) @optional() options: RecursivePartial = {}, @inject(CorePreferences) protected readonly corePreferences: CorePreferences, - @inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService, + @inject(SaveableService) protected readonly saveableService: SaveableService, @inject(SecondaryWindowHandler) protected readonly secondaryWindowHandler: SecondaryWindowHandler, @inject(WindowService) protected readonly windowService: WindowService ) { @@ -1231,13 +1231,6 @@ export class ApplicationShell extends Widget { } this.tracker.add(widget); this.checkActivation(widget); - Saveable.apply( - widget, - () => this.widgets.filter((maybeSaveable): maybeSaveable is Widget & SaveableSource => !!Saveable.get(maybeSaveable)), - async (toSave, options) => { - await this.saveResourceService.save(toSave, options); - }, - ); if (ApplicationShell.TrackableWidgetProvider.is(widget)) { for (const toTrack of widget.getTrackableWidgets()) { this.track(toTrack); @@ -2043,21 +2036,21 @@ export class ApplicationShell extends Widget { * Test whether the current widget is dirty. */ canSave(): boolean { - return this.saveResourceService.canSave(this.currentWidget); + return this.saveableService.canSave(this.currentWidget); } /** * Save the current widget if it is dirty. */ async save(options?: SaveOptions): Promise { - await this.saveResourceService.save(this.currentWidget, options); + await this.saveableService.save(this.currentWidget, options); } /** * Test whether there is a dirty widget. */ canSaveAll(): boolean { - return this.tracker.widgets.some(widget => this.saveResourceService.canSave(widget)); + return this.tracker.widgets.some(widget => this.saveableService.canSave(widget)); } /** @@ -2065,8 +2058,8 @@ export class ApplicationShell extends Widget { */ async saveAll(options?: SaveOptions): Promise { for (const widget of this.widgets) { - if (this.saveResourceService.canSaveNotSaveAs(widget)) { - await this.saveResourceService.save(widget, options); + if (this.saveableService.canSaveNotSaveAs(widget)) { + await this.saveableService.save(widget, options); } } } diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index 6bcd00b116489..4e415476f5887 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -21,6 +21,7 @@ import { ApplicationShell } from '../shell'; import { Saveable } from '../saveable'; import { PreferenceService } from '../preferences'; import { environment } from '../../common'; +import { SaveableService } from '../saveable-service'; @injectable() export class DefaultSecondaryWindowService implements SecondaryWindowService { @@ -43,6 +44,9 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { @inject(PreferenceService) protected readonly preferenceService: PreferenceService; + @inject(SaveableService) + protected readonly saveResourceService: SaveableService; + @postConstruct() init(): void { // Set up messaging with secondary windows @@ -93,7 +97,7 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { newWindow.addEventListener('DOMContentLoaded', () => { newWindow.addEventListener('beforeunload', evt => { const saveable = Saveable.get(widget); - const wouldLoseState = !!saveable && saveable.dirty && saveable.autoSave === 'off'; + const wouldLoseState = !!saveable && saveable.dirty && this.saveResourceService.autoSave === 'off'; if (wouldLoseState) { evt.returnValue = ''; evt.preventDefault(); @@ -104,7 +108,7 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { newWindow.addEventListener('unload', () => { const saveable = Saveable.get(widget); shell.closeWidget(widget.id, { - save: !!saveable && saveable.dirty && saveable.autoSave !== 'off' + save: !!saveable && saveable.dirty && this.saveResourceService.autoSave !== 'off' }); const extIndex = this.secondaryWindows.indexOf(newWindow); diff --git a/packages/editor/src/browser/editor-command.ts b/packages/editor/src/browser/editor-command.ts index 0a721159659a1..80b13412b6ef3 100644 --- a/packages/editor/src/browser/editor-command.ts +++ b/packages/editor/src/browser/editor-command.ts @@ -15,15 +15,12 @@ // ***************************************************************************** import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; -import { CommandContribution, CommandRegistry, Command } from '@theia/core/lib/common'; -import { CommonCommands, PreferenceService, LabelProvider, ApplicationShell, QuickInputService, QuickPickValue } from '@theia/core/lib/browser'; +import { CommonCommands, PreferenceService, LabelProvider, ApplicationShell, QuickInputService, QuickPickValue, SaveableService } from '@theia/core/lib/browser'; import { EditorManager } from './editor-manager'; -import { EditorPreferences } from './editor-preferences'; -import { ResourceProvider, MessageService } from '@theia/core'; +import { CommandContribution, CommandRegistry, Command, ResourceProvider, MessageService, nls } from '@theia/core'; import { LanguageService } from '@theia/core/lib/browser/language-service'; import { SUPPORTED_ENCODINGS } from '@theia/core/lib/browser/supported-encodings'; import { EncodingMode } from './editor'; -import { nls } from '@theia/core/lib/common/nls'; import { EditorLanguageQuickPickService } from './editor-language-quick-pick-service'; export namespace EditorCommands { @@ -209,7 +206,8 @@ export namespace EditorCommands { @injectable() export class EditorCommandContribution implements CommandContribution { - public static readonly AUTOSAVE_PREFERENCE: string = 'files.autoSave'; + static readonly AUTOSAVE_PREFERENCE: string = 'files.autoSave'; + static readonly AUTOSAVE_DELAY_PREFERENCE: string = 'files.autoSaveDelay'; @inject(ApplicationShell) protected readonly shell: ApplicationShell; @@ -217,13 +215,14 @@ export class EditorCommandContribution implements CommandContribution { @inject(PreferenceService) protected readonly preferencesService: PreferenceService; - @inject(EditorPreferences) - protected readonly editorPreferences: EditorPreferences; + @inject(SaveableService) + protected readonly saveResourceService: SaveableService; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; - @inject(MessageService) protected readonly messageService: MessageService; + @inject(MessageService) + protected readonly messageService: MessageService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @@ -242,9 +241,15 @@ export class EditorCommandContribution implements CommandContribution { @postConstruct() protected init(): void { - this.editorPreferences.onPreferenceChanged(e => { - if (e.preferenceName === 'files.autoSave' && e.newValue !== 'off') { - this.shell.saveAll(); + this.preferencesService.ready.then(() => { + this.saveResourceService.autoSave = this.preferencesService.get(EditorCommandContribution.AUTOSAVE_PREFERENCE) ?? 'off'; + this.saveResourceService.autoSaveDelay = this.preferencesService.get(EditorCommandContribution.AUTOSAVE_DELAY_PREFERENCE) ?? 1000; + }); + this.preferencesService.onPreferenceChanged(e => { + if (e.preferenceName === EditorCommandContribution.AUTOSAVE_PREFERENCE) { + this.saveResourceService.autoSave = this.preferencesService.get(EditorCommandContribution.AUTOSAVE_PREFERENCE) ?? 'off'; + } else if (e.preferenceName === EditorCommandContribution.AUTOSAVE_DELAY_PREFERENCE) { + this.saveResourceService.autoSaveDelay = this.preferencesService.get(EditorCommandContribution.AUTOSAVE_DELAY_PREFERENCE) ?? 1000; } }); } diff --git a/packages/filesystem/src/browser/filesystem-frontend-module.ts b/packages/filesystem/src/browser/filesystem-frontend-module.ts index c15fad6790a11..3f960edff1e77 100644 --- a/packages/filesystem/src/browser/filesystem-frontend-module.ts +++ b/packages/filesystem/src/browser/filesystem-frontend-module.ts @@ -31,8 +31,8 @@ import { RemoteFileServiceContribution } from './remote-file-service-contributio import { FileSystemWatcherErrorHandler } from './filesystem-watcher-error-handler'; import { FilepathBreadcrumbsContribution } from './breadcrumbs/filepath-breadcrumbs-contribution'; import { BreadcrumbsFileTreeWidget, createFileTreeBreadcrumbsWidget } from './breadcrumbs/filepath-breadcrumbs-container'; -import { FilesystemSaveResourceService } from './filesystem-save-resource-service'; -import { SaveResourceService } from '@theia/core/lib/browser/save-resource-service'; +import { FilesystemSaveableService } from './filesystem-saveable-service'; +import { SaveableService } from '@theia/core/lib/browser/saveable-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bindFileSystemPreferences(bind); @@ -65,8 +65,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(FilepathBreadcrumbsContribution).toSelf().inSingletonScope(); bind(BreadcrumbsContribution).toService(FilepathBreadcrumbsContribution); - bind(FilesystemSaveResourceService).toSelf().inSingletonScope(); - rebind(SaveResourceService).toService(FilesystemSaveResourceService); + bind(FilesystemSaveableService).toSelf().inSingletonScope(); + rebind(SaveableService).toService(FilesystemSaveableService); bind(FileTreeDecoratorAdapter).toSelf().inSingletonScope(); }); diff --git a/packages/filesystem/src/browser/filesystem-save-resource-service.ts b/packages/filesystem/src/browser/filesystem-saveable-service.ts similarity index 90% rename from packages/filesystem/src/browser/filesystem-save-resource-service.ts rename to packages/filesystem/src/browser/filesystem-saveable-service.ts index f992350d5fa78..39fe1e3eb3f98 100644 --- a/packages/filesystem/src/browser/filesystem-save-resource-service.ts +++ b/packages/filesystem/src/browser/filesystem-saveable-service.ts @@ -14,20 +14,25 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { environment, nls } from '@theia/core'; +import { environment, MessageService, nls } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { Navigatable, Saveable, SaveableSource, SaveOptions, Widget, open, OpenerService, ConfirmDialog, FormatType, CommonCommands } from '@theia/core/lib/browser'; -import { SaveResourceService } from '@theia/core/lib/browser/save-resource-service'; +import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import URI from '@theia/core/lib/common/uri'; import { FileService } from './file-service'; import { FileDialogService } from './file-dialog'; @injectable() -export class FilesystemSaveResourceService extends SaveResourceService { +export class FilesystemSaveableService extends SaveableService { - @inject(FileService) protected readonly fileService: FileService; - @inject(FileDialogService) protected readonly fileDialogService: FileDialogService; - @inject(OpenerService) protected readonly openerService: OpenerService; + @inject(MessageService) + protected readonly messageService: MessageService; + @inject(FileService) + protected readonly fileService: FileService; + @inject(FileDialogService) + protected readonly fileDialogService: FileDialogService; + @inject(OpenerService) + protected readonly openerService: OpenerService; /** * This method ensures a few things about `widget`: diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index 32a4731254cab..eef64d64aef89 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -52,8 +52,6 @@ export interface MonacoModelContentChangedEvent { export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDocument { - autoSave: EditorPreferences['files.autoSave'] = 'afterDelay'; - autoSaveDelay = 500; suppressOpenEditorWhenDirty = false; lineNumbersMinChars = 3; @@ -70,6 +68,10 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo protected readonly onDidChangeContentEmitter = new Emitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; + get onContentChanged(): Event { + return (listener, thisArgs, disposables) => this.onDidChangeContent(() => listener(), thisArgs, disposables); + } + protected readonly onDidSaveModelEmitter = new Emitter(); readonly onDidSaveModel = this.onDidSaveModelEmitter.event; @@ -364,7 +366,7 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo } save(options?: SaveOptions): Promise { - return this.scheduleSave(TextDocumentSaveReason.Manual, undefined, undefined, options); + return this.scheduleSave(options?.saveReason ?? TextDocumentSaveReason.Manual, undefined, undefined, options); } protected pendingOperation = Promise.resolve(); @@ -452,23 +454,9 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo } this.cancelSync(); this.setDirty(true); - this.doAutoSave(); this.trace(log => log('MonacoEditorModel.markAsDirty - exit')); } - protected doAutoSave(): void { - if (this.autoSave !== 'off' && this.resource.uri.scheme !== UNTITLED_SCHEME) { - const token = this.cancelSave(); - this.toDisposeOnAutoSave.dispose(); - const handle = window.setTimeout(() => { - this.scheduleSave(TextDocumentSaveReason.AfterDelay, token); - }, this.autoSaveDelay); - this.toDisposeOnAutoSave.push(Disposable.create(() => - window.clearTimeout(handle)) - ); - } - } - protected saveCancellationTokenSource = new CancellationTokenSource(); protected cancelSave(): CancellationToken { this.trace(log => log('MonacoEditorModel.cancelSave')); diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index 0dc538054bbe0..250d2e28d4780 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -156,16 +156,8 @@ export class MonacoTextModelService implements ITextModelService { protected updateModel(model: MonacoEditorModel, change?: EditorPreferenceChange): void { if (!change) { - model.autoSave = this.editorPreferences.get('files.autoSave', undefined, model.uri); - model.autoSaveDelay = this.editorPreferences.get('files.autoSaveDelay', undefined, model.uri); model.textEditorModel.updateOptions(this.getModelOptions(model)); } else if (change.affects(model.uri, model.languageId)) { - if (change.preferenceName === 'files.autoSave') { - model.autoSave = this.editorPreferences.get('files.autoSave', undefined, model.uri); - } - if (change.preferenceName === 'files.autoSaveDelay') { - model.autoSaveDelay = this.editorPreferences.get('files.autoSaveDelay', undefined, model.uri); - } const modelOption = this.toModelOption(change.preferenceName); if (modelOption) { model.textEditorModel.updateOptions(this.getModelOptions(model)); diff --git a/packages/monaco/src/browser/monaco-workspace.ts b/packages/monaco/src/browser/monaco-workspace.ts index 394c1b52a368e..6debea8d9d55d 100644 --- a/packages/monaco/src/browser/monaco-workspace.ts +++ b/packages/monaco/src/browser/monaco-workspace.ts @@ -42,6 +42,7 @@ import { SnippetParser } from '@theia/monaco-editor-core/esm/vs/editor/contrib/s import { TextEdit } from '@theia/monaco-editor-core/esm/vs/editor/common/languages'; import { SnippetController2 } from '@theia/monaco-editor-core/esm/vs/editor/contrib/snippet/browser/snippetController2'; import { isObject, MaybePromise, nls } from '@theia/core/lib/common'; +import { SaveableService } from '@theia/core/lib/browser'; export namespace WorkspaceFileEdit { export function is(arg: Edit): arg is monaco.languages.IWorkspaceFileEdit { @@ -124,6 +125,9 @@ export class MonacoWorkspace { @inject(ProblemManager) protected readonly problems: ProblemManager; + @inject(SaveableService) + protected readonly saveService: SaveableService; + @postConstruct() protected init(): void { this.resolveReady(); @@ -192,7 +196,7 @@ export class MonacoWorkspace { // acquired by the editor, thus losing the changes that made it dirty. this.textModelService.createModelReference(model.textEditorModel.uri).then(ref => { ( - model.autoSave !== 'off' ? new Promise(resolve => model.onDidSaveModel(resolve)) : + this.saveService.autoSave !== 'off' ? new Promise(resolve => model.onDidSaveModel(resolve)) : this.editorManager.open(new URI(model.uri), { mode: 'open' }) ).then( () => ref.dispose() diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index ca0fd71b6ee4f..c79f729c4e143 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -27,7 +27,7 @@ import { NotebookEditorWidgetFactory } from './notebook-editor-widget-factory'; import { NotebookCellResourceResolver, NotebookOutputResourceResolver } from './notebook-cell-resource-resolver'; import { NotebookModelResolverService } from './service/notebook-model-resolver-service'; import { NotebookCellActionContribution } from './contributions/notebook-cell-actions-contribution'; -import { createNotebookModelContainer, NotebookModel, NotebookModelFactory, NotebookModelProps } from './view-model/notebook-model'; +import { createNotebookModelContainer, NotebookModel, NotebookModelFactory, NotebookModelProps, NotebookModelResolverServiceProxy } from './view-model/notebook-model'; import { createNotebookCellModelContainer, NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from './view-model/notebook-cell-model'; import { createNotebookEditorWidgetContainer, NotebookEditorWidgetContainerFactory, NotebookEditorProps, NotebookEditorWidget } from './notebook-editor-widget'; import { NotebookActionsContribution } from './contributions/notebook-actions-contribution'; @@ -71,6 +71,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); bind(NotebookModelResolverService).toSelf().inSingletonScope(); + bind(NotebookModelResolverServiceProxy).toService(NotebookModelResolverService); bind(NotebookOutputResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookOutputResourceResolver); diff --git a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts index ccfee12e5a40c..949fc0b779621 100644 --- a/packages/notebook/src/browser/service/notebook-model-resolver-service.ts +++ b/packages/notebook/src/browser/service/notebook-model-resolver-service.ts @@ -108,7 +108,7 @@ export class NotebookModelResolverService { return this.resolve(resource, viewType); } - protected async resolveExistingNotebookData(resource: Resource, viewType: string): Promise { + async resolveExistingNotebookData(resource: Resource, viewType: string): Promise { if (resource.uri.scheme === 'untitled') { return { cells: [], diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 5c12186b4c403..9fa632f3d33e4 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -33,6 +33,7 @@ import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-mod import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; +import type { NotebookModelResolverService } from '../service/notebook-model-resolver-service'; export const NotebookModelFactory = Symbol('NotebookModelFactory'); @@ -45,6 +46,8 @@ export function createNotebookModelContainer(parent: interfaces.Container, props return child; } +export const NotebookModelResolverServiceProxy = Symbol('NotebookModelResolverServiceProxy'); + const NotebookModelProps = Symbol('NotebookModelProps'); export interface NotebookModelProps { data: NotebookData; @@ -68,6 +71,9 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onDidChangeContentEmitter = new QueueableEmitter(); readonly onDidChangeContent = this.onDidChangeContentEmitter.event; + protected readonly onContentChangedEmitter = new Emitter(); + readonly onContentChanged = this.onContentChangedEmitter.event; + protected readonly onDidChangeSelectedCellEmitter = new Emitter(); readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event; @@ -89,15 +95,20 @@ export class NotebookModel implements Saveable, Disposable { @inject(NotebookCellModelFactory) protected cellModelFactory: NotebookCellModelFactory; - readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; + + @inject(NotebookModelResolverServiceProxy) + protected modelResolverService: NotebookModelResolverService; protected nextHandle: number = 0; protected _dirty = false; set dirty(dirty: boolean) { + const oldState = this._dirty; this._dirty = dirty; - this.onDirtyChangedEmitter.fire(); + if (oldState !== dirty) { + this.onDirtyChangedEmitter.fire(); + } } get dirty(): boolean { @@ -160,24 +171,16 @@ export class NotebookModel implements Saveable, Disposable { this.dirtyCells = []; this.dirty = false; - const serializedNotebook = await this.props.serializer.fromNotebook({ - cells: this.cells.map(cell => cell.getData()), - metadata: this.metadata - }); + const data = this.getData(); + const serializedNotebook = await this.props.serializer.fromNotebook(data); this.fileService.writeFile(this.uri, serializedNotebook); this.onDidSaveNotebookEmitter.fire(); } createSnapshot(): Saveable.Snapshot { - const model = this; return { - read(): string { - return JSON.stringify({ - cells: model.cells.map(cell => cell.getData()), - metadata: model.metadata - }); - } + read: () => JSON.stringify(this.getData()) }; } @@ -191,6 +194,15 @@ export class NotebookModel implements Saveable, Disposable { } async revert(options?: Saveable.RevertOptions): Promise { + if (!options?.soft) { + // Load the data from the file again + try { + const data = await this.modelResolverService.resolveExistingNotebookData(this.props.resource, this.props.viewType); + this.setData(data, false); + } catch (err) { + console.error('Failed to revert notebook', err); + } + } this.dirty = false; } @@ -205,21 +217,25 @@ export class NotebookModel implements Saveable, Disposable { this.dirtyCells.splice(this.dirtyCells.indexOf(cell), 1); } - const oldDirtyState = this._dirty; - this._dirty = this.dirtyCells.length > 0; - if (this.dirty !== oldDirtyState) { - this.onDirtyChangedEmitter.fire(); - } + this.dirty = this.dirtyCells.length > 0; } - setData(data: NotebookData): void { + setData(data: NotebookData, markDirty = true): void { // Replace all cells in the model + this.dirtyCells = []; this.replaceCells(0, this.cells.length, data.cells, false); this.metadata = data.metadata; - this.dirty = false; + this.dirty = markDirty; this.onDidChangeContentEmitter.fire(); } + getData(): NotebookData { + return { + cells: this.cells.map(cell => cell.getData()), + metadata: this.metadata + }; + } + undo(): void { // TODO we probably need to check if a monaco editor is focused and if so, not undo this.undoRedoService.undo(this.uri); @@ -262,7 +278,7 @@ export class NotebookModel implements Saveable, Disposable { end: edit.editType === CellEditType.Replace ? edit.index + edit.count : cellIndex, originalIndex: index }; - }).filter(edit => !!edit); + }); for (const { edit, cellIndex } of editsWithDetails) { const cell = this.cells[cellIndex]; @@ -319,7 +335,7 @@ export class NotebookModel implements Saveable, Disposable { } this.onDidChangeContentEmitter.fire(); - + this.onContentChangedEmitter.fire(); } protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts index 4f2e183a9063a..7c626f3b69357 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts @@ -18,7 +18,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify' import URI from '@theia/core/lib/common/uri'; import { FileOperation } from '@theia/filesystem/lib/common/files'; import { ApplicationShell, NavigatableWidget, Saveable, SaveableSource, SaveOptions } from '@theia/core/lib/browser'; -import { SaveResourceService } from '@theia/core/lib/browser/save-resource-service'; +import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import { Reference } from '@theia/core/lib/common/reference'; import { WebviewWidget } from '../webview/webview'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; @@ -38,13 +38,6 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, set modelRef(modelRef: Reference) { this._modelRef = modelRef; this.doUpdateContent(); - Saveable.apply( - this, - () => this.shell.widgets.filter(widget => !!Saveable.get(widget)), - async (widget, options) => { - await this.saveService.save(widget, options); - }, - ); } get saveable(): Saveable { return this._modelRef.object; @@ -56,8 +49,8 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, @inject(ApplicationShell) protected readonly shell: ApplicationShell; - @inject(SaveResourceService) - protected readonly saveService: SaveResourceService; + @inject(SaveableService) + protected readonly saveService: SaveableService; @postConstruct() protected override init(): void { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts index a7951da9167ca..4368a500f1e69 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts @@ -25,7 +25,7 @@ import { RPCProtocol } from '../../../common/rpc-protocol'; import { HostedPluginSupport } from '../../../hosted/browser/hosted-plugin'; import { PluginCustomEditorRegistry } from './plugin-custom-editor-registry'; import { CustomEditorWidget } from './custom-editor-widget'; -import { Emitter, UNTITLED_SCHEME } from '@theia/core'; +import { Emitter } from '@theia/core'; import { UriComponents } from '../../../common/uri-components'; import { URI } from '@theia/core/shared/vscode-uri'; import TheiaURI from '@theia/core/lib/common/uri'; @@ -189,7 +189,7 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { return this.customEditorService.models.add(resource, viewType, model); } case CustomEditorModelType.Custom: { - const model = MainCustomEditorModel.create(this.proxy, viewType, resource, this.undoRedoService, this.fileService, this.editorPreferences, cancellationToken); + const model = MainCustomEditorModel.create(this.proxy, viewType, resource, this.undoRedoService, this.fileService, cancellationToken); return this.customEditorService.models.add(resource, viewType, model); } } @@ -297,8 +297,8 @@ export class MainCustomEditorModel implements CustomEditorModel { private readonly onDirtyChangedEmitter = new Emitter(); readonly onDirtyChanged = this.onDirtyChangedEmitter.event; - autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; - autoSaveDelay: number; + private readonly onContentChangedEmitter = new Emitter(); + readonly onContentChanged = this.onContentChangedEmitter.event; static async create( proxy: CustomEditorsExt, @@ -306,11 +306,10 @@ export class MainCustomEditorModel implements CustomEditorModel { resource: TheiaURI, undoRedoService: UndoRedoService, fileService: FileService, - editorPreferences: EditorPreferences, cancellation: CancellationToken, ): Promise { const { editable } = await proxy.$createCustomDocument(resource.toComponents(), viewType, {}, cancellation); - return new MainCustomEditorModel(proxy, viewType, resource, editable, undoRedoService, fileService, editorPreferences); + return new MainCustomEditorModel(proxy, viewType, resource, editable, undoRedoService, fileService); } constructor( @@ -319,22 +318,8 @@ export class MainCustomEditorModel implements CustomEditorModel { private readonly editorResource: TheiaURI, private readonly editable: boolean, private readonly undoRedoService: UndoRedoService, - private readonly fileService: FileService, - private readonly editorPreferences: EditorPreferences + private readonly fileService: FileService ) { - this.autoSave = this.editorPreferences.get('files.autoSave', undefined, editorResource.toString()); - this.autoSaveDelay = this.editorPreferences.get('files.autoSaveDelay', undefined, editorResource.toString()); - - this.toDispose.push( - this.editorPreferences.onPreferenceChanged(event => { - if (event.preferenceName === 'files.autoSave') { - this.autoSave = this.editorPreferences.get('files.autoSave', undefined, editorResource.toString()); - } - if (event.preferenceName === 'files.autoSaveDelay') { - this.autoSaveDelay = this.editorPreferences.get('files.autoSaveDelay', undefined, editorResource.toString()); - } - }) - ); this.toDispose.push(this.onDirtyChangedEmitter); } @@ -505,13 +490,7 @@ export class MainCustomEditorModel implements CustomEditorModel { if (this.dirty !== wasDirty) { this.onDirtyChangedEmitter.fire(); } - - if (this.autoSave !== 'off' && this.dirty && this.resource.scheme !== UNTITLED_SCHEME) { - const handle = window.setTimeout(() => { - this.save(); - window.clearTimeout(handle); - }, this.autoSaveDelay); - } + this.onContentChangedEmitter.fire(); } } @@ -521,6 +500,8 @@ export class CustomTextEditorModel implements CustomEditorModel { private readonly toDispose = new DisposableCollection(); private readonly onDirtyChangedEmitter = new Emitter(); readonly onDirtyChanged = this.onDirtyChangedEmitter.event; + private readonly onContentChangedEmitter = new Emitter(); + readonly onContentChanged = this.onContentChangedEmitter.event; static async create( viewType: string, @@ -544,15 +525,13 @@ export class CustomTextEditorModel implements CustomEditorModel { this.onDirtyChangedEmitter.fire(); }) ); + this.toDispose.push( + this.editorTextModel.onContentChanged(e => { + this.onContentChangedEmitter.fire(); + }) + ); this.toDispose.push(this.onDirtyChangedEmitter); - } - - get autoSave(): 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange' { - return this.editorTextModel.autoSave; - } - - get autoSaveDelay(): number { - return this.editorTextModel.autoSaveDelay; + this.toDispose.push(this.onContentChangedEmitter); } dispose(): void { diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index fc4fb43d05afc..0078b76ba4c39 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -32,7 +32,7 @@ import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; import { TextEditorMain } from './text-editor-main'; import { DisposableCollection, Emitter, URI } from '@theia/core'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; -import { SaveResourceService } from '@theia/core/lib/browser/save-resource-service'; +import { SaveableService } from '@theia/core/lib/browser/saveable-service'; export class EditorsAndDocumentsMain implements Disposable { @@ -43,7 +43,7 @@ export class EditorsAndDocumentsMain implements Disposable { private readonly modelService: EditorModelService; private readonly editorManager: EditorManager; - private readonly saveResourceService: SaveResourceService; + private readonly saveResourceService: SaveableService; private readonly onTextEditorAddEmitter = new Emitter(); private readonly onTextEditorRemoveEmitter = new Emitter(); @@ -64,7 +64,7 @@ export class EditorsAndDocumentsMain implements Disposable { this.editorManager = container.get(EditorManager); this.modelService = container.get(EditorModelService); - this.saveResourceService = container.get(SaveResourceService); + this.saveResourceService = container.get(SaveableService); this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService); this.toDispose.push(this.stateComputer); diff --git a/packages/workspace/src/browser/workspace-frontend-contribution.ts b/packages/workspace/src/browser/workspace-frontend-contribution.ts index d8c28f14dd7be..21ae2698d297d 100644 --- a/packages/workspace/src/browser/workspace-frontend-contribution.ts +++ b/packages/workspace/src/browser/workspace-frontend-contribution.ts @@ -37,7 +37,7 @@ import { nls } from '@theia/core/lib/common/nls'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { FileStat } from '@theia/filesystem/lib/common/files'; import { UntitledWorkspaceExitDialog } from './untitled-workspace-exit-dialog'; -import { FilesystemSaveResourceService } from '@theia/filesystem/lib/browser/filesystem-save-resource-service'; +import { FilesystemSaveableService } from '@theia/filesystem/lib/browser/filesystem-saveable-service'; import { StopReason } from '@theia/core/lib/common/frontend-application-state'; export enum WorkspaceStates { @@ -72,7 +72,7 @@ export class WorkspaceFrontendContribution implements CommandContribution, Keybi @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService; @inject(EncodingRegistry) protected readonly encodingRegistry: EncodingRegistry; @inject(PreferenceConfigurations) protected readonly preferenceConfigurations: PreferenceConfigurations; - @inject(FilesystemSaveResourceService) protected readonly saveService: FilesystemSaveResourceService; + @inject(FilesystemSaveableService) protected readonly saveService: FilesystemSaveableService; @inject(WorkspaceFileService) protected readonly workspaceFileService: WorkspaceFileService; configure(): void { From 728604914568f50ecf2d9d9acc70ca54d62aea35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 22 May 2024 14:58:03 +0200 Subject: [PATCH 232/441] Upgrade the Theia build to use Typescript 5.4.5 (#13628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most changes are just typing to accomodate stricter checks in 5.4.5. Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 2 + .../localization-manager/package.json | 2 +- .../src/localization-extractor.ts | 11 +- examples/playwright/package.json | 2 +- examples/playwright/src/theia-app.ts | 5 +- package.json | 10 +- packages/core/src/browser/keybinding.spec.ts | 3 +- .../src/browser/test/mock-storage-service.ts | 2 +- packages/core/src/common/encoding-service.ts | 2 +- .../src/common/selection-command-handler.ts | 2 +- packages/filesystem/src/common/files.ts | 3 +- .../src/common/remote-file-system-provider.ts | 2 +- .../plugin-ext/src/common/plugin-api-rpc.ts | 10 +- .../src/main/browser/quick-open-main.ts | 2 +- .../plugin-ext/src/plugin/custom-editors.ts | 2 +- packages/plugin-ext/src/plugin/quick-open.ts | 2 +- packages/plugin-ext/src/plugin/text-editor.ts | 5 +- packages/plugin-ext/src/plugin/types-impl.ts | 10 +- packages/plugin/src/theia.d.ts | 2 +- .../src/browser/workspace-commands.ts | 13 -- yarn.lock | 209 ++++++++++-------- 21 files changed, 168 insertions(+), 133 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f750f0c6c97b..5528a7e6630b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ## 1.50.0 diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index 7238863aff6a2..e4fec1c2065a4 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -37,7 +37,7 @@ "fs-extra": "^4.0.2", "glob": "^7.2.0", "tslib": "^2.6.2", - "typescript": "~4.5.5" + "typescript": "~5.4.5" }, "devDependencies": { "@theia/ext-scripts": "1.49.0" diff --git a/dev-packages/localization-manager/src/localization-extractor.ts b/dev-packages/localization-manager/src/localization-extractor.ts index 387a54d90c641..1f2af7c7cd0ce 100644 --- a/dev-packages/localization-manager/src/localization-extractor.ts +++ b/dev-packages/localization-manager/src/localization-extractor.ts @@ -52,6 +52,14 @@ class SingleFileServiceHost implements ts.LanguageServiceHost { getScriptSnapshot = (name: string) => name === this.filename ? this.file : this.lib; getCurrentDirectory = () => ''; getDefaultLibFileName = () => 'lib.d.ts'; + readFile(file: string, encoding?: string | undefined): string | undefined { + if (file === this.filename) { + return this.file.getText(0, this.file.getLength()); + } + } + fileExists(file: string): boolean { + return this.filename === file; + } } class TypeScriptError extends Error { @@ -81,8 +89,9 @@ export async function extract(options: ExtractionOptions): Promise { const errors: string[] = []; for (const file of files) { const filePath = path.resolve(cwd, file); + const fileName = path.relative(cwd, file).split(path.sep).join('/'); const content = await fs.readFile(filePath, 'utf8'); - const fileLocalization = await extractFromFile(file, content, errors, options); + const fileLocalization = await extractFromFile(fileName, content, errors, options); localization = deepmerge(localization, fileLocalization); } if (errors.length > 0 && options.logs) { diff --git a/examples/playwright/package.json b/examples/playwright/package.json index cd0e04f845015..5258ed4df1233 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -39,7 +39,7 @@ "allure-commandline": "^2.23.1", "allure-playwright": "^2.5.0", "rimraf": "^2.6.1", - "typescript": "~4.5.5" + "typescript": "~5.4.5" }, "publishConfig": { "access": "public" diff --git a/examples/playwright/src/theia-app.ts b/examples/playwright/src/theia-app.ts index ab7aeba5e71e9..43b102df4f156 100644 --- a/examples/playwright/src/theia-app.ts +++ b/examples/playwright/src/theia-app.ts @@ -100,7 +100,8 @@ export class TheiaApp { return view; } - async openEditor(filePath: string, editorFactory: { new(filePath: string, app: TheiaApp): T }, + async openEditor(filePath: string, + editorFactory: { new(fp: string, app: TheiaApp): T }, editorName?: string, expectFileNodes = true): Promise { const explorer = await this.openView(TheiaExplorerView); if (!explorer) { @@ -135,7 +136,7 @@ export class TheiaApp { return editor; } - async activateExistingEditor(filePath: string, editorFactory: { new(filePath: string, app: TheiaApp): T }): Promise { + async activateExistingEditor(filePath: string, editorFactory: { new(fp: string, app: TheiaApp): T }): Promise { const editor = new editorFactory(filePath, this); if (!await editor.isTabVisible()) { throw new Error(`Could not find opened editor for file ${filePath}`); diff --git a/package.json b/package.json index 80c34865f1309..16d4757b0c462 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "@types/node": "18", "@types/sinon": "^10.0.6", "@types/temp": "^0.9.1", - "@typescript-eslint/eslint-plugin": "^4.8.1", - "@typescript-eslint/eslint-plugin-tslint": "^4.8.1", - "@typescript-eslint/parser": "^4.8.1", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/eslint-plugin-tslint": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vscode/vsce": "^2.15.0", "archiver": "^5.3.1", "chai": "4.3.10", @@ -54,7 +54,7 @@ "tslint": "^5.12.0", "typedoc": "^0.22.11", "typedoc-plugin-external-module-map": "1.3.2", - "typescript": "~4.5.5", + "typescript": "~5.4.5", "yargs": "^15.3.1" }, "scripts": { @@ -114,4 +114,4 @@ "vscode.microsoft-authentication", "ms-vscode.references-view" ] -} +} \ No newline at end of file diff --git a/packages/core/src/browser/keybinding.spec.ts b/packages/core/src/browser/keybinding.spec.ts index b556a505c849b..026b43b3f6f82 100644 --- a/packages/core/src/browser/keybinding.spec.ts +++ b/packages/core/src/browser/keybinding.spec.ts @@ -90,7 +90,8 @@ before(async () => { bind(StatusBar).toConstantValue({} as StatusBar); bind(MarkdownRendererImpl).toSelf().inSingletonScope(); bind(MarkdownRenderer).toService(MarkdownRendererImpl); - bind(MarkdownRendererFactory).toFactory(({ container }) => container.get(MarkdownRenderer)); + bind(MarkdownRendererFactory).toFactory(({ container }) => () => container.get(MarkdownRenderer)); + bind(CommandService).toService(CommandRegistry); bind(LabelParser).toSelf().inSingletonScope(); bind(ContextKeyService).to(ContextKeyServiceDummyImpl).inSingletonScope(); diff --git a/packages/core/src/browser/test/mock-storage-service.ts b/packages/core/src/browser/test/mock-storage-service.ts index ecba9447fbf81..4fe74c224d79a 100644 --- a/packages/core/src/browser/test/mock-storage-service.ts +++ b/packages/core/src/browser/test/mock-storage-service.ts @@ -22,7 +22,7 @@ import { injectable } from 'inversify'; */ @injectable() export class MockStorageService implements StorageService { - readonly data = new Map(); + readonly data = new Map(); // eslint-disable-next-line @typescript-eslint/no-explicit-any onSetDataCallback?: (key: string, data?: any) => void; diff --git a/packages/core/src/common/encoding-service.ts b/packages/core/src/common/encoding-service.ts index 529298873d856..1e4aaafe8d41a 100644 --- a/packages/core/src/common/encoding-service.ts +++ b/packages/core/src/common/encoding-service.ts @@ -318,7 +318,7 @@ export class EncodingService { }); } - encodeStream(value: string | Readable, options?: ResourceEncoding): Promise + encodeStream(value: string | Readable, options?: ResourceEncoding): Promise; encodeStream(value?: string | Readable, options?: ResourceEncoding): Promise; async encodeStream(value: string | Readable | undefined, options?: ResourceEncoding): Promise { let encoding = options?.encoding; diff --git a/packages/core/src/common/selection-command-handler.ts b/packages/core/src/common/selection-command-handler.ts index 3e32a33928cca..9764200c0aba8 100644 --- a/packages/core/src/common/selection-command-handler.ts +++ b/packages/core/src/common/selection-command-handler.ts @@ -18,7 +18,7 @@ import { CommandHandler } from './command'; import { SelectionService } from '../common/selection-service'; -export class SelectionCommandHandler implements CommandHandler { +export class SelectionCommandHandler implements CommandHandler { constructor( protected readonly selectionService: SelectionService, diff --git a/packages/filesystem/src/common/files.ts b/packages/filesystem/src/common/files.ts index 1f706a76f39ae..15c90b8c4baae 100644 --- a/packages/filesystem/src/common/files.ts +++ b/packages/filesystem/src/common/files.ts @@ -525,6 +525,7 @@ export interface WatchOptions { } export const enum FileSystemProviderCapabilities { + None = 0, FileReadWrite = 1 << 1, FileOpenReadWriteClose = 1 << 2, FileReadStream = 1 << 4, @@ -774,7 +775,7 @@ export interface ReadOnlyMessageFileSystemProvider { export namespace ReadOnlyMessageFileSystemProvider { export function is(arg: unknown): arg is ReadOnlyMessageFileSystemProvider { return isObject(arg) - && 'readOnlyMessage' in arg; + && 'readOnlyMessage' in arg; } } diff --git a/packages/filesystem/src/common/remote-file-system-provider.ts b/packages/filesystem/src/common/remote-file-system-provider.ts index 68f24eb45d55e..7035bc5460056 100644 --- a/packages/filesystem/src/common/remote-file-system-provider.ts +++ b/packages/filesystem/src/common/remote-file-system-provider.ts @@ -151,7 +151,7 @@ export class RemoteFileSystemProvider implements Required, D options: WatchOptions }>(); - private _capabilities: FileSystemProviderCapabilities = 0; + private _capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.None; get capabilities(): FileSystemProviderCapabilities { return this._capabilities; } private _readOnlyMessage: MarkdownString | undefined = undefined; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 33d84951c1cdc..ccdfb6ad1e6c8 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -499,7 +499,7 @@ export interface StatusBarMessageRegistryMain { export interface QuickOpenExt { $onItemSelected(handle: number): void; - $validateInput(input: string): Promise | undefined; + $validateInput(input: string): Promise; $acceptOnDidAccept(sessionId: number): Promise; $acceptDidChangeValue(sessionId: number, changedValue: string): Promise; @@ -1208,7 +1208,7 @@ export interface UndoStopOptions { } export interface ApplyEditsOptions extends UndoStopOptions { - setEndOfLine: EndOfLine; + setEndOfLine: EndOfLine | undefined; } export interface ThemeColor { @@ -1859,12 +1859,12 @@ export interface WebviewViewsMain extends Disposable { } export interface CustomEditorsExt { - $resolveWebviewEditor( + $resolveWebviewEditor( resource: UriComponents, newWebviewHandle: string, viewType: string, title: string, - widgetOpenerOptions: T | undefined, + widgetOpenerOptions: object | undefined, options: theia.WebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, openContext: theia.CustomDocumentOpenContext, cancellation: CancellationToken): Promise<{ editable: boolean }>; @@ -1887,7 +1887,7 @@ export interface CustomEditorsMain { $registerTextEditorProvider(viewType: string, options: theia.WebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void; $registerCustomEditorProvider(viewType: string, options: theia.WebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void; $unregisterEditorProvider(viewType: string): void; - $createCustomEditorPanel(handle: string, title: string, widgetOpenerOptions: T | undefined, options: theia.WebviewPanelOptions & theia.WebviewOptions): Promise; + $createCustomEditorPanel(handle: string, title: string, widgetOpenerOptions: object | undefined, options: theia.WebviewPanelOptions & theia.WebviewOptions): Promise; $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; } diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index 7d380c919a193..2a7f262bb66a1 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -127,7 +127,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } private toQuickPickItem(item: undefined): undefined; - private toQuickPickItem(item: TransferQuickPickItem): QuickPickItem + private toQuickPickItem(item: TransferQuickPickItem): QuickPickItem; private toQuickPickItem(item: TransferQuickPickItem | undefined): QuickPickItem | undefined { if (!item) { return undefined; diff --git a/packages/plugin-ext/src/plugin/custom-editors.ts b/packages/plugin-ext/src/plugin/custom-editors.ts index 63f0c21888593..4ec88ce886fe9 100644 --- a/packages/plugin-ext/src/plugin/custom-editors.ts +++ b/packages/plugin-ext/src/plugin/custom-editors.ts @@ -121,7 +121,7 @@ export class CustomEditorsExtImpl implements CustomEditorsExt { handler: string, viewType: string, title: string, - widgetOpenerOptions: T | undefined, + widgetOpenerOptions: object | undefined, options: theia.WebviewPanelOptions & theia.WebviewOptions, cancellation: CancellationToken ): Promise { diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index c74eed1fa65f1..7ed2773f56849 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -30,8 +30,8 @@ import { convertToTransferQuickPickItems } from './type-converters'; import { PluginPackage } from '../common/plugin-protocol'; import { QuickInputButtonHandle } from '@theia/core/lib/browser'; import { MaybePromise } from '@theia/core/lib/common/types'; -import Severity from '@theia/monaco-editor-core/esm/vs/base/common/severity'; import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; +import { Severity } from '@theia/core/lib/common/severity'; const canceledName = 'Canceled'; /** diff --git a/packages/plugin-ext/src/plugin/text-editor.ts b/packages/plugin-ext/src/plugin/text-editor.ts index 85b87747107ba..99c35fe8572ca 100644 --- a/packages/plugin-ext/src/plugin/text-editor.ts +++ b/packages/plugin-ext/src/plugin/text-editor.ts @@ -499,7 +499,7 @@ export interface TextEditOperation { export interface EditData { documentVersionId: number; edits: TextEditOperation[]; - setEndOfLine: EndOfLine; + setEndOfLine: EndOfLine | undefined; undoStopBefore: boolean; undoStopAfter: boolean; } @@ -507,13 +507,12 @@ export interface EditData { export class TextEditorEdit { private readonly documentVersionId: number; private collectedEdits: TextEditOperation[]; - private eol: EndOfLine; + private eol: EndOfLine | undefined; private readonly undoStopBefore: boolean; private readonly undoStopAfter: boolean; constructor(private document: theia.TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean }) { this.documentVersionId = document.version; this.collectedEdits = []; - this.eol = 0; this.undoStopBefore = options.undoStopBefore; this.undoStopAfter = options.undoStopAfter; } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 4b647e8be9894..fc4268fb0a8ce 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -79,7 +79,7 @@ export class URI extends CodeURI implements theia.Uri { */ static override revive(data: UriComponents | CodeURI): URI; static override revive(data: UriComponents | CodeURI | null): URI | null; - static override revive(data: UriComponents | CodeURI | undefined): URI | undefined + static override revive(data: UriComponents | CodeURI | undefined): URI | undefined; static override revive(data: UriComponents | CodeURI | undefined | null): URI | undefined | null { const uri = CodeURI.revive(data); return uri ? new URI(uri) : undefined; @@ -882,7 +882,7 @@ export class TextEdit { protected _range: Range; protected _newText: string; - protected _newEol: EndOfLine; + protected _newEol: EndOfLine | undefined; get range(): Range { return this._range; @@ -906,7 +906,7 @@ export class TextEdit { this._newText = value; } - get newEol(): EndOfLine { + get newEol(): EndOfLine | undefined { return this._newEol; } @@ -2060,8 +2060,8 @@ export class TreeItem { readonly accessibilityInformation?: AccessibilityInformation }; - constructor(label: string | theia.TreeItemLabel, collapsibleState?: theia.TreeItemCollapsibleState) - constructor(resourceUri: URI, collapsibleState?: theia.TreeItemCollapsibleState) + constructor(label: string | theia.TreeItemLabel, collapsibleState?: theia.TreeItemCollapsibleState); + constructor(resourceUri: URI, collapsibleState?: theia.TreeItemCollapsibleState); constructor(arg1: string | theia.TreeItemLabel | URI, public collapsibleState: theia.TreeItemCollapsibleState = TreeItemCollapsibleState.None) { if (arg1 instanceof URI) { this.resourceUri = arg1; diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 17468c2b7b1bd..d5cc70bd07896 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -7910,7 +7910,7 @@ export module '@theia/plugin' { * @param pattern A file glob pattern like `*.{ts,js}` that will be matched on file paths * relative to the base path. */ - constructor(base: WorkspaceFolder | Uri | string, pattern: string) + constructor(base: WorkspaceFolder | Uri | string, pattern: string); } /** diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index 1f9350d1d6bcb..a60ea862efc84 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -519,19 +519,6 @@ export class WorkspaceCommandContribution implements CommandContribution { return registry.executeCommand(saveCommand.id); } } - - protected areMultipleOpenHandlersPresent(openers: OpenHandler[], uri: URI): boolean { - let count = 0; - for (const opener of openers) { - if (opener.canHandle(uri) > 0) { - count++; - } - if (count > 1) { - return true; - } - } - return false; - } } export class WorkspaceRootUriAwareCommandHandler extends UriAwareCommandHandler { diff --git a/yarn.lock b/yarn.lock index 0c0c5d9f0203f..06f58761cdea8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -991,6 +991,18 @@ optionalDependencies: global-agent "^3.0.0" +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -1978,7 +1990,7 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2366,39 +2378,29 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin-tslint@^4.8.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-4.33.0.tgz#c0f2a5a8a53a915d6c24983888013b7e78e75b44" - integrity sha512-o3ujMErtZJPgiNRETRJefo1bFNrloocOa5dMU49OW/G+Rq92IbXTY6FSF5MOwrdQK1X+VBEcA8y6PhUPWGlYqA== +"@typescript-eslint/eslint-plugin-tslint@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-6.21.0.tgz#6ae90759b88718c059907c2db4834cbf876bba02" + integrity sha512-DktcL2dSnR90VCVHXYKUz40QQ5DY2lSvnbkQJ+b1BtWhj/sNXdtlmQR6vB6b4RyEm/GMhvLFj6Pq1MvVVXLMAg== dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - lodash "^4.17.21" + "@typescript-eslint/utils" "6.21.0" -"@typescript-eslint/eslint-plugin@^4.8.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" - integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== +"@typescript-eslint/eslint-plugin@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" + integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA== dependencies: - "@typescript-eslint/experimental-utils" "4.33.0" - "@typescript-eslint/scope-manager" "4.33.0" - debug "^4.3.1" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.1.0" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/experimental-utils@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" - integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== - dependencies: - "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/type-utils" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" "@typescript-eslint/experimental-utils@^2.19.2 || ^3.0.0": version "3.10.1" @@ -2411,33 +2413,44 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.8.1": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== +"@typescript-eslint/parser@^6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" - debug "^4.3.1" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== + dependencies: + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== +"@typescript-eslint/type-utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" + integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/utils" "6.21.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" "@typescript-eslint/types@3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== "@typescript-eslint/typescript-estree@3.10.1": version "3.10.1" @@ -2453,18 +2466,32 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" + integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + semver "^7.5.4" "@typescript-eslint/visitor-keys@3.10.1": version "3.10.1" @@ -2473,13 +2500,13 @@ dependencies: eslint-visitor-keys "^1.1.0" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "6.21.0" + eslint-visitor-keys "^3.4.1" "@virtuoso.dev/react-urx@^0.2.12": version "0.2.13" @@ -5335,13 +5362,6 @@ eslint-utils@^2.0.0, eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" @@ -5352,6 +5372,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + eslint@7: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -6267,7 +6292,7 @@ globalthis@^1.0.1, globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globby@11.1.0, globby@^11.0.3: +globby@11.1.0, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -6320,6 +6345,11 @@ graceful-fs@4.2.11, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.1 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + handlebars@^4.7.7: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" @@ -6637,7 +6667,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.1.8, ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -8118,6 +8148,13 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@9.0.3, minimatch@^9.0.0, minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.0, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -8139,13 +8176,6 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0, minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -11441,6 +11471,11 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +ts-api-utils@^1.0.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-md5@^1.2.2: version "1.3.1" resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511" @@ -11501,7 +11536,7 @@ tsutils@^2.29.0: dependencies: tslib "^1.8.1" -tsutils@^3.0.0, tsutils@^3.17.1, tsutils@^3.21.0: +tsutils@^3.0.0, tsutils@^3.17.1: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -11680,10 +11715,10 @@ typedoc@^0.22.11: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== -typescript@~4.5.5: - version "4.5.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" - integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== +typescript@~5.4.5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" From 3dc5537908ef7af792da3f9fed7962786afcfb80 Mon Sep 17 00:00:00 2001 From: Eric <76749688+eric-sclafani@users.noreply.github.com> Date: Thu, 23 May 2024 06:28:50 -0400 Subject: [PATCH 233/441] Fix README documentation link (#13726) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33d246b3b561a..cd8f42e42bfc3 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Eclipse Theia is an extensible framework to develop full-fledged multi-language ## Website -[Visit the Eclipse Theia website](http://www.theia-ide.org) for more information and [the Theia documentation](http://www.theia-ide.org/doc). +[Visit the Eclipse Theia website](http://www.theia-ide.org) for more information and [the Theia documentation](http://www.theia-ide.org/docs). ## Repositories This is the main repository for the Eclipse Theia project, containing the sources of the Theia Platform. Please open generic discussions, bug reports and feature requests about Theia on this repository. The Theia project also includes additional repositories, e.g. for the [artifacts building the Theia IDE](https://github.com/eclipse-theia/theia-blueprint) and the [Theia website](https://github.com/eclipse-theia/theia-website). Please also see the [overview of all Theia project repositories](https://github.com/eclipse-theia). From c66bf7a8531c804be72fc59146bbb9c33e515692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 27 May 2024 10:46:05 +0200 Subject: [PATCH 234/441] Update msgpckr to 1.10.2 (#13722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13532 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index f127f61e37f06..d03e45b862305 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -56,7 +56,7 @@ "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1", "markdown-it": "^12.3.2", - "msgpackr": "^1.10.1", + "msgpackr": "^1.10.2", "nsfw": "^2.2.4", "p-debounce": "^2.1.0", "perfect-scrollbar": "^1.3.0", diff --git a/yarn.lock b/yarn.lock index 06f58761cdea8..357df3bc60eb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8402,10 +8402,10 @@ msgpackr-extract@^3.0.2: "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.2" "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.2" -msgpackr@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.1.tgz#51953bb4ce4f3494f0c4af3f484f01cfbb306555" - integrity sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ== +msgpackr@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.10.2.tgz#a73de4767f76659e8c69cf9c80fdfce83937a44a" + integrity sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA== optionalDependencies: msgpackr-extract "^3.0.2" From b3c70a5855de3b3476ca4bd048fe936c753d086d Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Mon, 27 May 2024 11:10:37 +0200 Subject: [PATCH 235/441] Fix ESLint on Windows (#13731) --- dev-packages/private-re-exports/src/package-re-exports.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/private-re-exports/src/package-re-exports.ts b/dev-packages/private-re-exports/src/package-re-exports.ts index 65f0fddfc4750..ca76c6b816532 100644 --- a/dev-packages/private-re-exports/src/package-re-exports.ts +++ b/dev-packages/private-re-exports/src/package-re-exports.ts @@ -172,7 +172,7 @@ export class PackageReExports { // To get around this, we can spawn a sub NodeJS process that will run the asynchronous // logic and then synchronously wait for the serialized result on the standard output. const scriptPath = require.resolve('./bin-package-re-exports-from-package.js'); - const { stdout } = cp.spawnSync(process.argv0, [...process.execArgv, scriptPath, packageName], { + const { stdout } = cp.spawnSync(process.platform === 'win32' ? `"${process.argv0}"` : process.argv0, [...process.execArgv, scriptPath, packageName], { env: { ELECTRON_RUN_AS_NODE: '1' }, From b358fd79def48aaee9cb526e4886384659735ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 28 May 2024 11:24:48 +0200 Subject: [PATCH 236/441] Delegate showing help to the back end process. (#13729) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13727 The approach is to add non-functional cli contributions for any arguments that concern only the electron-main process. Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/package.json | 3 ++ .../electron-main-application.ts | 1 + .../cli/electron-backend-cli-module.ts | 24 +++++++++++++ .../cli/electron-cli-contribution.ts | 35 +++++++++++++++++++ packages/core/src/node/cli.ts | 4 +-- 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/electron-node/cli/electron-backend-cli-module.ts create mode 100644 packages/core/src/electron-node/cli/electron-cli-contribution.ts diff --git a/packages/core/package.json b/packages/core/package.json index d03e45b862305..837d9c05d7c45 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -152,6 +152,9 @@ "frontend": "lib/browser/window/browser-window-module", "frontendElectron": "lib/electron-browser/window/electron-window-module" }, + { + "backendElectron": "lib/electron-node/cli/electron-backend-cli-module" + }, { "frontend": "lib/browser/keyboard/browser-keyboard-module", "frontendElectron": "lib/electron-browser/keyboard/electron-keyboard-module", diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index e6fac94c46733..8d59807252593 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -209,6 +209,7 @@ export class ElectronMainApplication { async start(config: FrontendApplicationConfig): Promise { const argv = this.processArgv.getProcessArgvWithoutBin(process.argv); createYargs(argv, process.cwd()) + .help(false) .command('$0 [file]', false, cmd => cmd .option('electronUserData', { diff --git a/packages/core/src/electron-node/cli/electron-backend-cli-module.ts b/packages/core/src/electron-node/cli/electron-backend-cli-module.ts new file mode 100644 index 0000000000000..2b7a98176cb9f --- /dev/null +++ b/packages/core/src/electron-node/cli/electron-backend-cli-module.ts @@ -0,0 +1,24 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from 'inversify'; +import { ElectronCliContribution } from './electron-cli-contribution'; +import { CliContribution } from '../../node'; + +export default new ContainerModule(bind => { + bind(ElectronCliContribution).toSelf().inSingletonScope(); + bind(CliContribution).toService(ElectronCliContribution); +}); diff --git a/packages/core/src/electron-node/cli/electron-cli-contribution.ts b/packages/core/src/electron-node/cli/electron-cli-contribution.ts new file mode 100644 index 0000000000000..c8b5be382679a --- /dev/null +++ b/packages/core/src/electron-node/cli/electron-cli-contribution.ts @@ -0,0 +1,35 @@ +/******************************************************************************** + * Copyright (C) 2024 STMicroelectronics and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + ********************************************************************************/ + +import { injectable } from 'inversify'; +import { Argv, Arguments } from 'yargs'; +import { CliContribution } from '../../node'; +import { MaybePromise } from '../../common'; + +@injectable() +export class ElectronCliContribution implements CliContribution { + + configure(conf: Argv): void { + conf.option('electronUserData', { + type: 'string', + describe: 'The area where the electron main process puts its data' + }); + } + + setArguments(args: Arguments): MaybePromise { + } + +} diff --git a/packages/core/src/node/cli.ts b/packages/core/src/node/cli.ts index 949d233dfaf2f..28b9e7f0c8aa7 100644 --- a/packages/core/src/node/cli.ts +++ b/packages/core/src/node/cli.ts @@ -38,7 +38,7 @@ export class CliManager { async initializeCli(argv: string[], postSetArguments: () => Promise, defaultCommand: () => Promise): Promise { const pack = require('../../package.json'); const version = pack.version; - const command = yargs.version(version); + const command = yargs(argv, process.cwd()).version(version); command.exitProcess(this.isExit()); for (const contrib of this.contributionsProvider.getContributions()) { contrib.configure(command); @@ -54,7 +54,7 @@ export class CliManager { await postSetArguments(); }) .command('$0', false, () => { }, defaultCommand) - .parse(argv); + .parse(); } protected isExit(): boolean { From 5844c0d5fc683129f96eeef7ef5f0b3b06a376b8 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 28 May 2024 14:00:12 +0200 Subject: [PATCH 237/441] Add WindowState active in plugin API (#13718) fixes #13692 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 2 + .../plugin-ext/src/common/plugin-api-rpc.ts | 3 +- .../main/browser/window-activity-tracker.ts | 96 +++++++++++++++++++ .../src/main/browser/window-state-main.ts | 11 ++- .../plugin-ext/src/plugin/window-state.ts | 19 ++-- packages/plugin/src/theia.d.ts | 6 ++ 6 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 packages/plugin-ext/src/main/browser/window-activity-tracker.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5528a7e6630b6..b970ebb0320c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ ## 1.50.0 +- [plugin] Support WindowState active API [#13718](https://github.com/eclipse-theia/theia/pull/13718) - contributed on behalf of STMicroelectronics + [Breaking Changes:](#breaking_changes_1.50.0) - [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index ccdfb6ad1e6c8..c63e8ca5d0309 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -893,7 +893,8 @@ export interface WindowMain { } export interface WindowStateExt { - $onWindowStateChanged(focus: boolean): void; + $onDidChangeWindowFocus(focused: boolean): void; + $onDidChangeWindowActive(active: boolean): void; } export interface NotificationExt { diff --git a/packages/plugin-ext/src/main/browser/window-activity-tracker.ts b/packages/plugin-ext/src/main/browser/window-activity-tracker.ts new file mode 100644 index 0000000000000..239f0f7fa1ebc --- /dev/null +++ b/packages/plugin-ext/src/main/browser/window-activity-tracker.ts @@ -0,0 +1,96 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Disposable, Emitter, Event } from '@theia/core'; + +const CHECK_INACTIVITY_LIMIT = 30; +const CHECK_INACTIVITY_INTERVAL = 1000; + +const eventListenerOptions: AddEventListenerOptions = { + passive: true, + capture: true +}; +export class WindowActivityTracker implements Disposable { + + private inactivityCounter = 0; // number of times inactivity was checked since last reset + private readonly inactivityLimit = CHECK_INACTIVITY_LIMIT; // number of inactivity checks done before sending inactive signal + private readonly checkInactivityInterval = CHECK_INACTIVITY_INTERVAL; // check interval in milliseconds + private interval: NodeJS.Timeout | undefined; + + protected readonly onDidChangeActiveStateEmitter = new Emitter(); + private _activeState: boolean = true; + + constructor(readonly win: Window) { + this.initializeListeners(this.win); + } + + get onDidChangeActiveState(): Event { + return this.onDidChangeActiveStateEmitter.event; + } + + private set activeState(newState: boolean) { + if (this._activeState !== newState) { + this._activeState = newState; + this.onDidChangeActiveStateEmitter.fire(this._activeState); + } + } + + private initializeListeners(win: Window): void { + // currently assumes activity based on key/mouse/touch pressed, not on mouse move or scrolling. + win.addEventListener('mousedown', this.resetInactivity, eventListenerOptions); + win.addEventListener('keydown', this.resetInactivity, eventListenerOptions); + win.addEventListener('touchstart', this.resetInactivity, eventListenerOptions); + } + + dispose(): void { + this.stopTracking(); + this.win.removeEventListener('mousedown', this.resetInactivity); + this.win.removeEventListener('keydown', this.resetInactivity); + this.win.removeEventListener('touchstart', this.resetInactivity); + + } + + // Reset inactivity time + private resetInactivity = (): void => { + this.inactivityCounter = 0; + if (!this.interval) { + // it was not active. Set as active and restart tracking inactivity + this.activeState = true; + this.startTracking(); + } + }; + + // Check inactivity status + private checkInactivity = (): void => { + this.inactivityCounter++; + if (this.inactivityCounter >= this.inactivityLimit) { + this.activeState = false; + this.stopTracking(); + } + }; + + public startTracking(): void { + this.stopTracking(); + this.interval = setInterval(this.checkInactivity, this.checkInactivityInterval); + } + + public stopTracking(): void { + if (this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + } +} diff --git a/packages/plugin-ext/src/main/browser/window-state-main.ts b/packages/plugin-ext/src/main/browser/window-state-main.ts index c7e6800855ffc..1a48ea1f50e56 100644 --- a/packages/plugin-ext/src/main/browser/window-state-main.ts +++ b/packages/plugin-ext/src/main/browser/window-state-main.ts @@ -23,6 +23,7 @@ import { UriComponents } from '../../common/uri-components'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { open, OpenerService } from '@theia/core/lib/browser/opener-service'; import { ExternalUriService } from '@theia/core/lib/browser/external-uri-service'; +import { WindowActivityTracker } from './window-activity-tracker'; export class WindowStateMain implements WindowMain, Disposable { @@ -46,6 +47,10 @@ export class WindowStateMain implements WindowMain, Disposable { const fireDidBlur = () => this.onFocusChanged(false); window.addEventListener('blur', fireDidBlur); this.toDispose.push(Disposable.create(() => window.removeEventListener('blur', fireDidBlur))); + + const tracker = new WindowActivityTracker(window); + this.toDispose.push(tracker.onDidChangeActiveState(isActive => this.onActiveStateChanged(isActive))); + this.toDispose.push(tracker); } dispose(): void { @@ -53,7 +58,11 @@ export class WindowStateMain implements WindowMain, Disposable { } private onFocusChanged(focused: boolean): void { - this.proxy.$onWindowStateChanged(focused); + this.proxy.$onDidChangeWindowFocus(focused); + } + + private onActiveStateChanged(isActive: boolean): void { + this.proxy.$onDidChangeWindowActive(isActive); } async $openUri(uriComponent: UriComponents): Promise { diff --git a/packages/plugin-ext/src/plugin/window-state.ts b/packages/plugin-ext/src/plugin/window-state.ts index 4b4e58aac3bbd..ed6d4b9cbe208 100644 --- a/packages/plugin-ext/src/plugin/window-state.ts +++ b/packages/plugin-ext/src/plugin/window-state.ts @@ -31,21 +31,28 @@ export class WindowStateExtImpl implements WindowStateExt { constructor(rpc: RPCProtocol) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.WINDOW_MAIN); - this.windowStateCached = { focused: true }; // supposed tab is active on start + this.windowStateCached = { focused: true, active: true }; // supposed tab is active on start } getWindowState(): WindowState { return this.windowStateCached; } - $onWindowStateChanged(focused: boolean): void { - const state = { focused: focused }; - if (state === this.windowStateCached) { + $onDidChangeWindowFocus(focused: boolean): void { + this.onDidChangeWindowProperty('focused', focused); + } + + $onDidChangeWindowActive(active: boolean): void { + this.onDidChangeWindowProperty('active', active); + } + + onDidChangeWindowProperty(property: keyof WindowState, value: boolean): void { + if (value === this.windowStateCached[property]) { return; } - this.windowStateCached = state; - this.windowStateChangedEmitter.fire(state); + this.windowStateCached = { ...this.windowStateCached, [property]: value }; + this.windowStateChangedEmitter.fire(this.windowStateCached); } openUri(uri: URI): Promise { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index d5cc70bd07896..dd2d6cd7f2275 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -2741,6 +2741,12 @@ export module '@theia/plugin' { * Whether the current window is focused. */ readonly focused: boolean; + + /** + * Whether the window has been interacted with recently. This will change + * immediately on activity, or after a short time of user inactivity. + */ + readonly active: boolean; } /** From 525496b8f70a155a060811dfda90ca8bb01f1c9b Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 28 May 2024 16:52:32 +0200 Subject: [PATCH 238/441] [vscode] Update DropMetadata and DocumentPaste to 1.89 version (#13733) Update DropMetadata and DocumentPaste to 1.89 version fixes #13694 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + .../plugin-ext/src/plugin/plugin-context.ts | 2 + packages/plugin-ext/src/plugin/types-impl.ts | 33 ++- packages/plugin/src/theia.d.ts | 1 - .../src/theia.proposed.documentPaste.d.ts | 192 ++++++++++++++---- .../src/theia.proposed.dropMetadata.d.ts | 74 ------- 6 files changed, 179 insertions(+), 124 deletions(-) delete mode 100644 packages/plugin/src/theia.proposed.dropMetadata.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b970ebb0320c5..022f9a37570d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ [Breaking Changes:](#breaking_changes_1.50.0) - [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. +- [plugin] updated `DropMetada` and `documentPaste` proposed API for 1.89 compatibility [#13733](https://github.com/eclipse-theia/theia/pull/13733) - contributed on behalf of STMicroelectronics ## v1.49.0 - 04/29/2024 diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index fa19ce1e12d17..92b720f54c7e9 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -200,6 +200,7 @@ import { DocumentPasteEdit, DocumentPasteEditKind, DocumentPasteTriggerKind, + DocumentDropOrPasteEditKind, ExternalUriOpenerPriority, EditSessionIdentityMatch, TerminalOutputAnchor, @@ -1302,6 +1303,7 @@ export function createAPIFactory( MultiDocumentHighlight, DocumentLink, DocumentDropEdit, + DocumentDropOrPasteEditKind, CodeLens, CodeActionKind, CodeActionTrigger, diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index fc4268fb0a8ce..4dabfbe702e63 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -1641,12 +1641,35 @@ export class DocumentLink { } } +@es5ClassCompat +export class DocumentDropOrPasteEditKind { + static readonly Empty: DocumentDropOrPasteEditKind = new DocumentDropOrPasteEditKind(''); + + private static sep = '.'; + + constructor( + public readonly value: string + ) { } + + public append(...parts: string[]): DocumentDropOrPasteEditKind { + return new DocumentDropOrPasteEditKind((this.value ? [this.value, ...parts] : parts).join(DocumentDropOrPasteEditKind.sep)); + } + + public intersects(other: DocumentDropOrPasteEditKind): boolean { + return this.contains(other) || other.contains(this); + } + + public contains(other: DocumentDropOrPasteEditKind): boolean { + return this.value === other.value || other.value.startsWith(this.value + DocumentDropOrPasteEditKind.sep); + } +} + @es5ClassCompat export class DocumentDropEdit { title?: string; - kind: DocumentPasteEditKind; + kind: DocumentDropOrPasteEditKind; handledMimeType?: string; - yieldTo?: ReadonlyArray; + yieldTo?: ReadonlyArray; insertText: string | SnippetString; additionalEdit?: WorkspaceEdit; @@ -3760,16 +3783,16 @@ DocumentPasteEditKind.Empty = new DocumentPasteEditKind(''); @es5ClassCompat export class DocumentPasteEdit { - constructor(insertText: string | SnippetString, title: string, kind: DocumentPasteEditKind) { + constructor(insertText: string | SnippetString, title: string, kind: DocumentDropOrPasteEditKind) { this.insertText = insertText; this.title = title; this.kind = kind; } title: string; - kind: DocumentPasteEditKind; + kind: DocumentDropOrPasteEditKind; insertText: string | SnippetString; additionalEdit?: WorkspaceEdit; - yieldTo?: readonly DocumentPasteEditKind[]; + yieldTo?: ReadonlyArray; } /** diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index dd2d6cd7f2275..535109db71e61 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -26,7 +26,6 @@ import './theia.proposed.canonicalUriProvider'; import './theia.proposed.customEditorMove'; import './theia.proposed.diffCommand'; import './theia.proposed.documentPaste'; -import './theia.proposed.dropMetadata'; import './theia.proposed.editSessionIdentityProvider'; import './theia.proposed.extensionsAny'; import './theia.proposed.externalUriOpener'; diff --git a/packages/plugin/src/theia.proposed.documentPaste.d.ts b/packages/plugin/src/theia.proposed.documentPaste.d.ts index 42cd879080079..be606313530bb 100644 --- a/packages/plugin/src/theia.proposed.documentPaste.d.ts +++ b/packages/plugin/src/theia.proposed.documentPaste.d.ts @@ -22,6 +22,47 @@ export module '@theia/plugin' { + /** + * Identifies a {@linkcode DocumentDropEdit} or {@linkcode DocumentPasteEdit} + */ + class DocumentDropOrPasteEditKind { + static readonly Empty: DocumentDropOrPasteEditKind; + + private constructor(value: string); + + /** + * The raw string value of the kind. + */ + readonly value: string; + + /** + * Create a new kind by appending additional scopes to the current kind. + * + * Does not modify the current kind. + */ + append(...parts: string[]): DocumentDropOrPasteEditKind; + + /** + * Checks if this kind intersects `other`. + * + * The kind `"text.plain"` for example intersects `text`, `"text.plain"` and `"text.plain.list"`, + * but not `"unicorn"`, or `"textUnicorn.plain"`. + * + * @param other Kind to check. + */ + intersects(other: DocumentDropOrPasteEditKind): boolean; + + /** + * Checks if `other` is a sub-kind of this `DocumentDropOrPasteEditKind`. + * + * The kind `"text.plain"` for example contains `"text.plain"` and `"text.plain.list"`, + * but not `"text"` or `"unicorn.text.plain"`. + * + * @param other Kind to check. + */ + contains(other: DocumentDropOrPasteEditKind): boolean; + } + /** * The reason why paste edits were requested. */ @@ -45,7 +86,7 @@ export module '@theia/plugin' { /** * Requested kind of paste edits to return. */ - readonly only: DocumentPasteEditKind | undefined; + readonly only: DocumentDropOrPasteEditKind | undefined; /** * The reason why paste edits were requested. @@ -59,18 +100,18 @@ export module '@theia/plugin' { interface DocumentPasteEditProvider { /** - * Optional method invoked after the user copies text in a file. + * Optional method invoked after the user copies from a {@link TextEditor text editor}. * - * This allows the provider to attach copy metadata to the {@link DataTransfer} - * which is then passed back to providers in {@linkcode provideDocumentPasteEdits}. + * This allows the provider to attach metadata about the copied text to the {@link DataTransfer}. This data + * transfer is then passed back to providers in {@linkcode provideDocumentPasteEdits}. * - * Note that currently any changes to the {@linkcode DataTransfer} are isolated to the current editor session. - * This means that added metadata cannot be seen by other applications. + * Note that currently any changes to the {@linkcode DataTransfer} are isolated to the current editor window. + * This means that any added metadata cannot be seen by other editor windows or by other applications. * - * @param document Document where the copy took place. + * @param document Text document where the copy took place. * @param ranges Ranges being copied in {@linkcode document}. - * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for later use in {@linkcode provideDocumentPasteEdits}. - * This object is only valid for the duration of this method. + * @param dataTransfer The data transfer associated with the copy. You can store additional values on this for + * later use in {@linkcode provideDocumentPasteEdits}. This object is only valid for the duration of this method. * @param token A cancellation token. * * @return Optional thenable that resolves when all changes to the `dataTransfer` are complete. @@ -78,26 +119,29 @@ export module '@theia/plugin' { prepareDocumentPaste?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, token: CancellationToken): void | Thenable; /** - * Invoked before the user pastes into a document. + * Invoked before the user pastes into a {@link TextEditor text editor}. * * Returned edits can replace the standard pasting behavior. * * @param document Document being pasted into * @param ranges Range in the {@linkcode document} to paste into. - * @param dataTransfer The {@link DataTransfer data transfer} associated with the paste. This object is only valid for the duration of the paste operation. + * @param dataTransfer The {@link DataTransfer data transfer} associated with the paste. This object is only + * valid for the duration of the paste operation. * @param context Additional context for the paste. * @param token A cancellation token. * - * @return Set of potential {@link DocumentPasteEdit edits} that apply the paste. Return `undefined` to use standard pasting. + * @return Set of potential {@link DocumentPasteEdit edits} that can apply the paste. Only a single returned + * {@linkcode DocumentPasteEdit} is applied at a time. If multiple edits are returned from all providers, then + * the first is automatically applied and a widget is shown that lets the user switch to the other edits. */ - provideDocumentPasteEdits?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, context: DocumentPasteEditContext, - token: CancellationToken): ProviderResult; + provideDocumentPasteEdits?(document: TextDocument, ranges: readonly Range[], dataTransfer: DataTransfer, context: DocumentPasteEditContext, token: CancellationToken): + ProviderResult; /** * Optional method which fills in the {@linkcode DocumentPasteEdit.additionalEdit} before the edit is applied. * * This is called once per edit and should be used if generating the complete edit may take a long time. - * Resolve can only be used to change {@link DocumentPasteEdit.additionalEdit}. + * Resolve can only be used to change {@linkcode DocumentPasteEdit.additionalEdit}. * * @param pasteEdit The {@linkcode DocumentPasteEdit} to resolve. * @param token A cancellation token. @@ -109,7 +153,7 @@ export module '@theia/plugin' { } /** - * An edit applied on paste. + * An edit the applies a paste operation. */ class DocumentPasteEdit { @@ -119,14 +163,14 @@ export module '@theia/plugin' { title: string; /** - * {@link DocumentPasteEditKind Kind} of the edit. - * - * Used to identify specific types of edits. + * {@link DocumentDropOrPasteEditKind Kind} of the edit. */ - kind: DocumentPasteEditKind; + kind: DocumentDropOrPasteEditKind; /** * The text or snippet to insert at the pasted locations. + * + * If your edit requires more advanced insertion logic, set this to an empty string and provide an {@link DocumentPasteEdit.additionalEdit additional edit} instead. */ insertText: string | SnippetString; @@ -136,46 +180,33 @@ export module '@theia/plugin' { additionalEdit?: WorkspaceEdit; /** - * Controls the ordering of paste edits provided by multiple providers. + * Controls ordering when multiple paste edits can potentially be applied. * - * If this edit yields to another, it will be shown lower in the list of paste edit. + * If this edit yields to another, it will be shown lower in the list of possible paste edits shown to the user. */ - yieldTo?: readonly DocumentPasteEditKind[]; + yieldTo?: readonly DocumentDropOrPasteEditKind[]; /** * Create a new paste edit. * * @param insertText The text or snippet to insert at the pasted locations. * @param title Human readable label that describes the edit. - * @param kind {@link DocumentPasteEditKind Kind} of the edit. + * @param kind {@link DocumentDropOrPasteEditKind Kind} of the edit. */ - constructor(insertText: string | SnippetString, title: string, kind: DocumentPasteEditKind); + constructor(insertText: string | SnippetString, title: string, kind: DocumentDropOrPasteEditKind); } /** - * TODO: Share with code action kind? + * Provides additional metadata about how a {@linkcode DocumentPasteEditProvider} works. */ - class DocumentPasteEditKind { - static readonly Empty: DocumentPasteEditKind; - - // TODO: Add `Text` any others? - - private constructor(value: string); - - readonly value: string; - - append(...parts: string[]): CodeActionKind; - intersects(other: CodeActionKind): boolean; - contains(other: CodeActionKind): boolean; - } - interface DocumentPasteProviderMetadata { /** - * List of {@link DocumentPasteEditKind kinds} that the provider may return in {@linkcode DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits}. + * List of {@link DocumentDropOrPasteEditKind kinds} that the provider may return in + * {@linkcode DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits}. * - * The provider will only be invoked when one of these kinds is being requested. For normal pasting, all providers will be invoked. + * This is used to filter out providers when a specific {@link DocumentDropOrPasteEditKind kind} of edit is requested. */ - readonly providedPasteEditKinds: readonly DocumentPasteEditKind[]; + readonly providedPasteEditKinds: readonly DocumentDropOrPasteEditKind[]; /** * Mime types that {@linkcode DocumentPasteEditProvider.prepareDocumentPaste prepareDocumentPaste} may add on copy. @@ -190,12 +221,80 @@ export module '@theia/plugin' { * Use `text/uri-list` for resources dropped from the explorer or other tree views in the workbench. * * Use `files` to indicate that the provider should be invoked if any {@link DataTransferFile files} are present in the {@linkcode DataTransfer}. - * Note that {@linkcode DataTransferFile} entries are only created when dropping content from outside the editor, such as + * Note that {@linkcode DataTransferFile} entries are only created when pasting content from outside the editor, such as * from the operating system. */ readonly pasteMimeTypes?: readonly string[]; } + /** + * TODO on finalization: + * - Add ctor(insertText: string | SnippetString, title?: string, kind?: DocumentDropOrPasteEditKind); Can't be done as this is an extension to an existing class + */ + + export interface DocumentDropEdit { + /** + * Human readable label that describes the edit. + */ + title?: string; + + /** + * {@link DocumentDropOrPasteEditKind Kind} of the edit. + */ + kind: DocumentDropOrPasteEditKind; + + /** + * Controls the ordering or multiple edits. If this provider yield to edits, it will be shown lower in the list. + */ + yieldTo?: readonly DocumentDropOrPasteEditKind[]; + } + + export interface DocumentDropEditProvider { + // Overload that allows returning multiple edits + // Will be merged in on finalization + provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): + ProviderResult; + + /** + * Optional method which fills in the {@linkcode DocumentDropEdit.additionalEdit} before the edit is applied. + * + * This is called once per edit and should be used if generating the complete edit may take a long time. + * Resolve can only be used to change {@link DocumentDropEdit.additionalEdit}. + * + * @param pasteEdit The {@linkcode DocumentDropEdit} to resolve. + * @param token A cancellation token. + * + * @returns The resolved edit or a thenable that resolves to such. It is OK to return the given + * `edit`. If no result is returned, the given `edit` is used. + */ + resolveDocumentDropEdit?(edit: T, token: CancellationToken): ProviderResult; + } + + /** + * Provides additional metadata about how a {@linkcode DocumentDropEditProvider} works. + */ + export interface DocumentDropEditProviderMetadata { + /** + * List of {@link DocumentDropOrPasteEditKind kinds} that the provider may return in {@linkcode DocumentDropEditProvider.provideDocumentDropEdits provideDocumentDropEdits}. + * + * This is used to filter out providers when a specific {@link DocumentDropOrPasteEditKind kind} of edit is requested. + */ + readonly providedDropEditKinds?: readonly DocumentDropOrPasteEditKind[]; + + /** + * List of {@link DataTransfer} mime types that the provider can handle. + * + * This can either be an exact mime type such as `image/png`, or a wildcard pattern such as `image/*`. + * + * Use `text/uri-list` for resources dropped from the explorer or other tree views in the workbench. + * + * Use `files` to indicate that the provider should be invoked if any {@link DataTransferFile files} are present in the {@link DataTransfer}. + * Note that {@link DataTransferFile} entries are only created when dropping content from outside the editor, such as + * from the operating system. + */ + readonly dropMimeTypes: readonly string[]; + } + namespace languages { /** * Registers a new {@linkcode DocumentPasteEditProvider}. @@ -208,5 +307,10 @@ export module '@theia/plugin' { * @stubbed */ export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable; + + /** + * Overload which adds extra metadata. Will be removed on finalization. + */ + export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider, metadata?: DocumentDropEditProviderMetadata): Disposable; } } diff --git a/packages/plugin/src/theia.proposed.dropMetadata.d.ts b/packages/plugin/src/theia.proposed.dropMetadata.d.ts deleted file mode 100644 index 073ddc63db270..0000000000000 --- a/packages/plugin/src/theia.proposed.dropMetadata.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2023 TypeFox and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -// code copied and modified from https://github.com/microsoft/vscode/blob/1.79.0/src/vscode-dts/vscode.proposed.dropMetadata.d.ts - -export module '@theia/plugin' { - - // https://github.com/microsoft/vscode/issues/179430 - - export interface DocumentDropEdit { - - /** - * Human readable label that describes the edit. - */ - title?: string; - - /** - * {@link DocumentPasteEditKind Kind} of the edit. - * - * Used to identify specific types of edits. - * - * TODO: use own type? - */ - kind: DocumentPasteEditKind; - - /** - * The mime type from the {@link DataTransfer} that this edit applies. - */ - handledMimeType?: string; - - /** - * Controls the ordering or multiple paste edits. If this provider yield to edits, it will be shown lower in the list. - */ - yieldTo?: ReadonlyArray; - } - - export interface DocumentDropEditProviderMetadata { - readonly providedDropEditKinds?: readonly DocumentPasteEditKind[]; - - /** - * List of data transfer types that the provider supports. - * - * This can either be an exact mime type such as `image/png`, or a wildcard pattern such as `image/*`. - * - * Use `text/uri-list` for resources dropped from the explorer or other tree views in the workbench. - * - * Use `files` to indicate that the provider should be invoked if any {@link DataTransferFile files} are present in the {@link DataTransfer}. - * Note that {@link DataTransferFile} entries are only created when dropping content from outside the editor, such as - * from the operating system. - */ - readonly dropMimeTypes: readonly string[]; - } - - export namespace languages { - export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider, metadata?: DocumentDropEditProviderMetadata): Disposable; - } -} From 7788a58fae4a960750d0f46bd360bf88bca88e6c Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Wed, 29 May 2024 11:57:58 +0200 Subject: [PATCH 239/441] chore: add cross-env dependency to examples/playwright (#13634) This allows running the Playwright tests in Windows. Contributed on behalf of STMicroelectronics Signed-off-by: Olaf Lessenich --- examples/playwright/package.json | 5 +++-- yarn.lock | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/playwright/package.json b/examples/playwright/package.json index 5258ed4df1233..b685ab8597b8e 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -15,12 +15,12 @@ "clean": "rimraf lib *.tsbuildinfo .eslintcache", "build": "yarn && yarn clean && tsc --incremental && yarn lint && yarn playwright:install", "watch": "tsc -w --incremental", - "theia:start": "rimraf .tmp.cfg && THEIA_CONFIG_DIR=$PWD/.tmp.cfg yarn --cwd ../browser start", + "theia:start": "rimraf .tmp.cfg && cross-env THEIA_CONFIG_DIR=$PWD/.tmp.cfg yarn --cwd ../browser start", "lint": "eslint -c ./.eslintrc.js --ext .ts ./src", "lint:fix": "eslint -c ./.eslintrc.js --ext .ts ./src --fix", "playwright:install": "playwright install chromium", "ui-tests": "yarn build && playwright test --config=./configs/playwright.config.ts", - "ui-tests-electron": "yarn build && USE_ELECTRON=true playwright test --config=./configs/playwright.config.ts", + "ui-tests-electron": "yarn build && cross-env USE_ELECTRON=true playwright test --config=./configs/playwright.config.ts", "ui-tests-ci": "yarn build && playwright test --config=./configs/playwright.ci.config.ts", "ui-tests-headful": "yarn build && playwright test --config=./configs/playwright.headful.config.ts", "ui-tests-report-generate": "allure generate ./allure-results --clean -o allure-results/allure-report", @@ -38,6 +38,7 @@ "@types/fs-extra": "^9.0.8", "allure-commandline": "^2.23.1", "allure-playwright": "^2.5.0", + "cross-env": "^7.0.3", "rimraf": "^2.6.1", "typescript": "~5.4.5" }, diff --git a/yarn.lock b/yarn.lock index 357df3bc60eb0..7e743ace87f5e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4386,6 +4386,13 @@ crc32-stream@^4.0.2: crc-32 "^1.2.0" readable-stream "^3.4.0" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-fetch@3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" From db018469f68633a3b8f3ff763f80ab589cdacc3c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 29 May 2024 14:36:51 +0200 Subject: [PATCH 240/441] Fixed focus loss of the notebook editor widget when a cell editor was unfocused (#13741) Signed-off-by: Jonah Iden --- .../notebook/src/browser/view/notebook-cell-editor.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 0ffeef23f88ab..a356c9c0421a9 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -78,11 +78,9 @@ export class CellEditor extends React.Component { this.editor?.setLanguage(language); })); - this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(() => { - if (this.props.notebookModel.selectedCell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { - if (document.activeElement && 'blur' in document.activeElement) { - (document.activeElement as HTMLElement).blur(); - } + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(cell => { + if (cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { + this.props.notebookContextManager.context?.focus(); } })); if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) { From ffdb635ad02bf69d30e546e43e382d11691fa015 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 29 May 2024 15:56:38 +0200 Subject: [PATCH 241/441] Dev container improvements (#13714) * basics for preferences, remoteUser and Post Create Command Signed-off-by: Jonah Iden * setting preferences from command line when starting theia Signed-off-by: Jonah Iden * implemented more devcontainer properties: PostCreateCommand, RemoteUser, sttings and extensions Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden * more lint Signed-off-by: Jonah Iden * modify /etc/profile if possible to not have it overwrite the path when launching a terminal Signed-off-by: Jonah Iden * fixed stopping containers on conenction dispose Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden * added displaying of default forwarded ports by the container Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden * more lint because my local eslint is not working Signed-off-by: Jonah Iden * dynamic forwarded ports from devcontainer.json Signed-off-by: Jonah Iden * lint Signed-off-by: Jonah Iden * more lint Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../container-info-contribution.ts | 46 +++++++ .../dev-container-frontend-module.ts | 5 + .../remote-container-connection-provider.ts | 2 + .../dev-container-backend-module.ts | 11 ++ .../cli-enhancing-creation-contributions.ts | 68 +++++++++++ .../main-container-creation-contributions.ts | 113 +++++++++++++----- .../profile-file-modification-contribution.ts | 35 ++++++ .../src/electron-node/devcontainer-file.ts | 33 ++++- .../electron-node/docker-container-service.ts | 54 +++++---- .../remote-container-connection-provider.ts | 26 +++- packages/preferences/package.json | 3 +- .../preference-frontend-contribution.ts | 38 ++++++ .../src/browser/preference-frontend-module.ts | 9 +- .../preferences/src/common/cli-preferences.ts | 22 ++++ .../src/node/preference-backend-module.ts | 33 +++++ .../src/node/preference-cli-contribution.ts | 48 ++++++++ .../port-forwarding-service.ts | 10 +- .../remote-port-forwarding-provider.ts | 1 + .../remote-port-forwarding-provider.ts | 28 ++++- 19 files changed, 516 insertions(+), 69 deletions(-) create mode 100644 packages/dev-container/src/electron-browser/container-info-contribution.ts create mode 100644 packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts create mode 100644 packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts create mode 100644 packages/preferences/src/browser/preference-frontend-contribution.ts create mode 100644 packages/preferences/src/common/cli-preferences.ts create mode 100644 packages/preferences/src/node/preference-backend-module.ts create mode 100644 packages/preferences/src/node/preference-cli-contribution.ts diff --git a/packages/dev-container/src/electron-browser/container-info-contribution.ts b/packages/dev-container/src/electron-browser/container-info-contribution.ts new file mode 100644 index 0000000000000..06a39417f5a6b --- /dev/null +++ b/packages/dev-container/src/electron-browser/container-info-contribution.ts @@ -0,0 +1,46 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import type { ContainerInspectInfo } from 'dockerode'; +import { RemoteContainerConnectionProvider } from '../electron-common/remote-container-connection-provider'; +import { PortForwardingService } from '@theia/remote/lib/electron-browser/port-forwarding/port-forwarding-service'; + +@injectable() +export class ContainerInfoContribution implements FrontendApplicationContribution { + + @inject(RemoteContainerConnectionProvider) + protected readonly connectionProvider: RemoteContainerConnectionProvider; + + @inject(PortForwardingService) + protected readonly portForwardingService: PortForwardingService; + + containerInfo: ContainerInspectInfo | undefined; + + async onStart(): Promise { + this.containerInfo = await this.connectionProvider.getCurrentContainerInfo(parseInt(new URLSearchParams(location.search).get('port') ?? '0')); + + this.portForwardingService.forwardedPorts = Object.entries(this.containerInfo?.NetworkSettings.Ports ?? {}).flatMap(([_, ports]) => ( + ports.map(port => ({ + editing: false, + address: port.HostIp ?? '', + localPort: parseInt(port.HostPort ?? '0'), + origin: 'container' + })))); + } + +} diff --git a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts index 77cdd79844b72..807e5152cb2d9 100644 --- a/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts +++ b/packages/dev-container/src/electron-browser/dev-container-frontend-module.ts @@ -19,6 +19,8 @@ import { RemoteContainerConnectionProvider, RemoteContainerConnectionProviderPat import { ContainerConnectionContribution } from './container-connection-contribution'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import { ContainerOutputProvider } from './container-output-provider'; +import { ContainerInfoContribution } from './container-info-contribution'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; export default new ContainerModule(bind => { bind(ContainerConnectionContribution).toSelf().inSingletonScope(); @@ -30,4 +32,7 @@ export default new ContainerModule(bind => { const outputProvider = ctx.container.get(ContainerOutputProvider); return ServiceConnectionProvider.createLocalProxy(ctx.container, RemoteContainerConnectionProviderPath, outputProvider); }).inSingletonScope(); + + bind(ContainerInfoContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(ContainerInfoContribution); }); diff --git a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts index 236cb3113cd90..c6674f91371b9 100644 --- a/packages/dev-container/src/electron-common/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-common/remote-container-connection-provider.ts @@ -15,6 +15,7 @@ import { RpcServer } from '@theia/core'; import { ContainerOutputProvider } from './container-output-provider'; +import type { ContainerInspectInfo } from 'dockerode'; // ***************************************************************************** export const RemoteContainerConnectionProviderPath = '/remote/container'; @@ -46,4 +47,5 @@ export interface DevContainerFile { export interface RemoteContainerConnectionProvider extends RpcServer { connectToContainer(options: ContainerConnectionOptions): Promise; getDevContainerFiles(): Promise; + getCurrentContainerInfo(port: number): Promise; } diff --git a/packages/dev-container/src/electron-node/dev-container-backend-module.ts b/packages/dev-container/src/electron-node/dev-container-backend-module.ts index cac6e5e74ebf4..e03066275fba8 100644 --- a/packages/dev-container/src/electron-node/dev-container-backend-module.ts +++ b/packages/dev-container/src/electron-node/dev-container-backend-module.ts @@ -23,10 +23,16 @@ import { bindContributionProvider, ConnectionHandler, RpcConnectionHandler } fro import { registerContainerCreationContributions } from './devcontainer-contributions/main-container-creation-contributions'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; +import { ExtensionsContribution, registerTheiaStartOptionsContributions, SettingsContribution } from './devcontainer-contributions/cli-enhancing-creation-contributions'; +import { RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { ProfileFileModificationContribution } from './devcontainer-contributions/profile-file-modification-contribution'; export const remoteConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { bindContributionProvider(bind, ContainerCreationContribution); registerContainerCreationContributions(bind); + registerTheiaStartOptionsContributions(bind); + bind(ProfileFileModificationContribution).toSelf().inSingletonScope(); + bind(ContainerCreationContribution).toService(ProfileFileModificationContribution); bind(DevContainerConnectionProvider).toSelf().inSingletonScope(); bind(RemoteContainerConnectionProvider).toService(DevContainerConnectionProvider); @@ -44,4 +50,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ConnectionContainerModule).toConstantValue(remoteConnectionModule); bind(DevContainerFileService).toSelf().inSingletonScope(); + + bind(ExtensionsContribution).toSelf().inSingletonScope(); + bind(SettingsContribution).toSelf().inSingletonScope(); + bind(RemoteCliContribution).toService(ExtensionsContribution); + bind(RemoteCliContribution).toService(SettingsContribution); }); diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts new file mode 100644 index 0000000000000..7410d83ea1d06 --- /dev/null +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/cli-enhancing-creation-contributions.ts @@ -0,0 +1,68 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { RemoteCliContext, RemoteCliContribution } from '@theia/core/lib/node/remote/remote-cli-contribution'; +import { ContainerCreationContribution } from '../docker-container-service'; +import * as Docker from 'dockerode'; +import { DevContainerConfiguration, } from '../devcontainer-file'; +import { injectable, interfaces } from '@theia/core/shared/inversify'; + +export function registerTheiaStartOptionsContributions(bind: interfaces.Bind): void { + bind(ContainerCreationContribution).toService(ExtensionsContribution); + bind(ContainerCreationContribution).toService(SettingsContribution); +} + +@injectable() +export class ExtensionsContribution implements RemoteCliContribution, ContainerCreationContribution { + protected currentConfig: DevContainerConfiguration | undefined; + + enhanceArgs(context: RemoteCliContext): string[] { + if (!this.currentConfig) { + return []; + } + const extensions = [ + ...(this.currentConfig.extensions ?? []), + ...(this.currentConfig.customizations?.vscode?.extensions ?? []) + ]; + this.currentConfig = undefined; + return extensions?.map(extension => `--install-plugin=${extension}`); + } + + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { + this.currentConfig = containerConfig; + } +} + +@injectable() +export class SettingsContribution implements RemoteCliContribution, ContainerCreationContribution { + protected currentConfig: DevContainerConfiguration | undefined; + + enhanceArgs(context: RemoteCliContext): string[] { + if (!this.currentConfig) { + return []; + } + const settings = { + ...(this.currentConfig.settings ?? {}), + ...(this.currentConfig.customizations?.vscode?.settings ?? []) + }; + this.currentConfig = undefined; + return Object.entries(settings).map(([key, value]) => `--set-preference=${key}=${JSON.stringify(value)}`) ?? []; + } + + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration): Promise { + this.currentConfig = containerConfig; + } +} diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts index c0c91473201f0..ad0d0d4d265de 100644 --- a/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/main-container-creation-contributions.ts @@ -14,17 +14,22 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import * as Docker from 'dockerode'; -import { injectable, interfaces } from '@theia/core/shared/inversify'; +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; import { ContainerCreationContribution } from '../docker-container-service'; import { DevContainerConfiguration, DockerfileContainer, ImageContainer, NonComposeContainerBase } from '../devcontainer-file'; import { Path } from '@theia/core'; import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; +import * as fs from '@theia/core/shared/fs-extra'; +import { RemotePortForwardingProvider } from '@theia/remote/lib/electron-common/remote-port-forwarding-provider'; +import { RemoteDockerContainerConnection } from '../remote-container-connection-provider'; export function registerContainerCreationContributions(bind: interfaces.Bind): void { bind(ContainerCreationContribution).to(ImageFileContribution).inSingletonScope(); bind(ContainerCreationContribution).to(DockerFileContribution).inSingletonScope(); bind(ContainerCreationContribution).to(ForwardPortsContribution).inSingletonScope(); bind(ContainerCreationContribution).to(MountsContribution).inSingletonScope(); + bind(ContainerCreationContribution).to(RemoteUserContribution).inSingletonScope(); + bind(ContainerCreationContribution).to(PostCreateCommandContribution).inSingletonScope(); } @injectable() @@ -53,50 +58,62 @@ export class DockerFileContribution implements ContainerCreationContribution { // check if dockerfile container if (containerConfig.dockerFile || containerConfig.build?.dockerfile) { const dockerfile = (containerConfig.dockerFile ?? containerConfig.build?.dockerfile) as string; - const buildStream = await api.buildImage({ - context: containerConfig.context ?? new Path(containerConfig.location as string).dir.fsPath(), - src: [dockerfile], - } as Docker.ImageBuildContext, { - buildargs: containerConfig.build?.args - }); - // TODO probably have some console windows showing the output of the build - const imageId = await new Promise((res, rej) => api.modem.followProgress(buildStream, (err, outputs) => { - if (err) { - rej(err); - } else { - for (let i = outputs.length - 1; i >= 0; i--) { - if (outputs[i].aux?.ID) { - res(outputs[i].aux.ID); - return; + const context = containerConfig.context ?? new Path(containerConfig.location as string).dir.fsPath(); + try { + // ensure dockerfile exists + await fs.lstat(new Path(context as string).join(dockerfile).fsPath()); + + const buildStream = await api.buildImage({ + context, + src: [dockerfile], + } as Docker.ImageBuildContext, { + buildargs: containerConfig.build?.args + }); + // TODO probably have some console windows showing the output of the build + const imageId = await new Promise((res, rej) => api.modem.followProgress(buildStream!, (err, outputs) => { + if (err) { + rej(err); + } else { + for (let i = outputs.length - 1; i >= 0; i--) { + if (outputs[i].aux?.ID) { + res(outputs[i].aux.ID); + return; + } } } - } - }, progress => outputprovider.onRemoteOutput(OutputHelper.parseProgress(progress)))); - createOptions.Image = imageId; + }, progress => outputprovider.onRemoteOutput(OutputHelper.parseProgress(progress)))); + createOptions.Image = imageId; + } catch (error) { + outputprovider.onRemoteOutput(`could not build dockerfile "${dockerfile}" reason: ${error.message}`); + throw error; + } } } } @injectable() export class ForwardPortsContribution implements ContainerCreationContribution { - async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise { + + @inject(RemotePortForwardingProvider) + protected readonly portForwardingProvider: RemotePortForwardingProvider; + + async handlePostConnect(containerConfig: DevContainerConfiguration, connection: RemoteDockerContainerConnection): Promise { if (!containerConfig.forwardPorts) { return; } - for (const port of containerConfig.forwardPorts) { - let portKey: string; - let hostPort: string; - if (typeof port === 'string') { - const parts = port.split(':'); - portKey = isNaN(+parts[0]) ? parts[0] : `${parts[0]}/tcp`; - hostPort = parts[1] ?? parts[0]; + for (const forward of containerConfig.forwardPorts) { + let port: number; + let address: string | undefined; + if (typeof forward === 'string') { + const parts = forward.split(':'); + address = parts[0]; + port = parseInt(parts[1]); } else { - portKey = `${port}/tcp`; - hostPort = port.toString(); + port = forward; } - createOptions.ExposedPorts![portKey] = {}; - createOptions.HostConfig!.PortBindings[portKey] = [{ HostPort: hostPort }]; + + this.portForwardingProvider.forwardPort(connection.localPort, { port, address }); } } @@ -126,6 +143,40 @@ export class MountsContribution implements ContainerCreationContribution { } } +@injectable() +export class RemoteUserContribution implements ContainerCreationContribution { + async handleContainerCreation(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker): Promise { + if (containerConfig.remoteUser) { + createOptions.User = containerConfig.remoteUser; + } + } +} + +@injectable() +export class PostCreateCommandContribution implements ContainerCreationContribution { + async handlePostCreate?(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider): Promise { + if (containerConfig.postCreateCommand) { + const commands = typeof containerConfig.postCreateCommand === 'object' && !(containerConfig.postCreateCommand instanceof Array) ? + Object.values(containerConfig.postCreateCommand) : [containerConfig.postCreateCommand]; + for (const command of commands) { + try { + let exec; + if (command instanceof Array) { + exec = await container.exec({ Cmd: command, AttachStderr: true, AttachStdout: true }); + + } else { + exec = await container.exec({ Cmd: ['sh', '-c', command], AttachStderr: true, AttachStdout: true }); + } + const stream = await exec.start({ Tty: true }); + stream.on('data', chunk => outputprovider.onRemoteOutput(chunk.toString())); + } catch (error) { + outputprovider.onRemoteOutput('could not execute postCreateCommand ' + JSON.stringify(command) + ' reason:' + error.message); + } + } + } + } +} + export namespace OutputHelper { export interface Progress { id?: string; diff --git a/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts new file mode 100644 index 0000000000000..6cfa4033c0de5 --- /dev/null +++ b/packages/dev-container/src/electron-node/devcontainer-contributions/profile-file-modification-contribution.ts @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { DevContainerConfiguration } from '../devcontainer-file'; +import { ContainerCreationContribution } from '../docker-container-service'; +import * as Docker from 'dockerode'; +import { injectable } from '@theia/core/shared/inversify'; +import { ContainerOutputProvider } from '../../electron-common/container-output-provider'; + +/** + * this contribution changes the /etc/profile file so that it won't overwrite the PATH variable set by docker + */ +@injectable() +export class ProfileFileModificationContribution implements ContainerCreationContribution { + async handlePostCreate(containerConfig: DevContainerConfiguration, container: Docker.Container, api: Docker, outputprovider: ContainerOutputProvider): Promise { + const stream = await (await container.exec({ + Cmd: ['sh', '-c', 'sed -i \'s|PATH="\\([^"]*\\)"|PATH=${PATH:-"\\1"}|g\' /etc/profile'], User: 'root', + AttachStderr: true, AttachStdout: true + })).start({}); + stream.on('data', data => outputprovider.onRemoteOutput(data.toString())); + } +} diff --git a/packages/dev-container/src/electron-node/devcontainer-file.ts b/packages/dev-container/src/electron-node/devcontainer-file.ts index 5bd948dd17a90..ec77ac4b5e7f3 100644 --- a/packages/dev-container/src/electron-node/devcontainer-file.ts +++ b/packages/dev-container/src/electron-node/devcontainer-file.ts @@ -258,6 +258,21 @@ export interface DevContainerCommon { * The default is the same user as the container. */ remoteUser?: string + + /** + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * The default is no extensions being installed. + */ + extensions?: string[] + + /** + * settings to set in the container at launch in the settings.json. The expected format is key=value. + * The default is no preferences being set. + */ + settings?: { + [k: string]: unknown + } + /** * A command to run locally before anything else. This command is run before 'onCreateCommand'. * If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. @@ -365,7 +380,23 @@ export interface DevContainerCommon { * Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations. */ customizations?: { - [k: string]: unknown + [k: string]: unknown, + vscode?: { + /** + * extensions to install in the container at launch. The expeceted format is publisher.name[@version]. + * The default is no extensions being installed. + */ + extensions?: string[], + + /** + * settings to set in the container at launch in the settings.json. The expected format is key=value. + * The default is no preferences being set. + */ + settings?: { + [k: string]: unknown + } + [k: string]: unknown + } } additionalProperties?: { [k: string]: unknown diff --git a/packages/dev-container/src/electron-node/docker-container-service.ts b/packages/dev-container/src/electron-node/docker-container-service.ts index 3626e7c00b762..70dfbc2fa72a0 100644 --- a/packages/dev-container/src/electron-node/docker-container-service.ts +++ b/packages/dev-container/src/electron-node/docker-container-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { ContributionProvider, URI } from '@theia/core'; +import { ContributionProvider, MaybePromise, URI } from '@theia/core'; import { inject, injectable, named } from '@theia/core/shared/inversify'; import { WorkspaceServer } from '@theia/workspace/lib/common'; import * as fs from '@theia/core/shared/fs-extra'; @@ -23,14 +23,29 @@ import { LastContainerInfo } from '../electron-common/remote-container-connectio import { DevContainerConfiguration } from './devcontainer-file'; import { DevContainerFileService } from './dev-container-file-service'; import { ContainerOutputProvider } from '../electron-common/container-output-provider'; +import { RemoteDockerContainerConnection } from './remote-container-connection-provider'; export const ContainerCreationContribution = Symbol('ContainerCreationContributions'); export interface ContainerCreationContribution { - handleContainerCreation(createOptions: Docker.ContainerCreateOptions, + handleContainerCreation?(createOptions: Docker.ContainerCreateOptions, containerConfig: DevContainerConfiguration, api: Docker, - outputProvider?: ContainerOutputProvider): Promise; + outputProvider?: ContainerOutputProvider): MaybePromise; + + /** + * executed after creating and starting the container + */ + handlePostCreate?(containerConfig: DevContainerConfiguration, + container: Docker.Container, + api: Docker, + outputProvider?: ContainerOutputProvider): MaybePromise; + + /** + * executed after a connection has been established with the container and theia has been setup + */ + handlePostConnect?(containerConfig: DevContainerConfiguration, connection: RemoteDockerContainerConnection, + outputProvider?: ContainerOutputProvider): MaybePromise; } @injectable() @@ -70,6 +85,15 @@ export class DockerContainerService { return container; } + async postConnect(devcontainerFile: string, connection: RemoteDockerContainerConnection, outputProvider?: ContainerOutputProvider): Promise { + const devcontainerConfig = await this.devContainerFileService.getConfiguration(devcontainerFile); + + for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { + await containerCreateContrib.handlePostConnect?.(devcontainerConfig, connection, outputProvider); + } + + } + protected async buildContainer(docker: Docker, devcontainerFile: string, workspace: URI, outputProvider?: ContainerOutputProvider): Promise { const devcontainerConfig = await this.devContainerFileService.getConfiguration(devcontainerFile); @@ -92,33 +116,17 @@ export class DockerContainerService { }; for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { - await containerCreateContrib.handleContainerCreation(containerCreateOptions, devcontainerConfig, docker, outputProvider); + await containerCreateContrib.handleContainerCreation?.(containerCreateOptions, devcontainerConfig, docker, outputProvider); } // TODO add more config const container = await docker.createContainer(containerCreateOptions); await container.start(); - return container; - } - - protected getPortBindings(forwardPorts: (string | number)[]): { exposedPorts: {}, portBindings: {} } { - const res: { exposedPorts: { [key: string]: {} }, portBindings: { [key: string]: {} } } = { exposedPorts: {}, portBindings: {} }; - for (const port of forwardPorts) { - let portKey: string; - let hostPort: string; - if (typeof port === 'string') { - const parts = port.split(':'); - portKey = isNaN(+parts[0]) ? parts[0] : `${parts[0]}/tcp`; - hostPort = parts[1] ?? parts[0]; - } else { - portKey = `${port}/tcp`; - hostPort = port.toString(); - } - res.exposedPorts[portKey] = {}; - res.portBindings[portKey] = [{ HostPort: hostPort }]; + for (const containerCreateContrib of this.containerCreationContributions.getContributions()) { + await containerCreateContrib.handlePostCreate?.(devcontainerConfig, container, docker, outputProvider); } - return res; + return container; } } diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 214b98b3a19ad..08b2b9ac1f2a9 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -56,6 +56,9 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection @inject(DevContainerFileService) protected readonly devContainerFileService: DevContainerFileService; + @inject(RemoteConnectionService) + protected readonly remoteService: RemoteConnectionService; + protected outputProvider: ContainerOutputProvider | undefined; setClient(client: ContainerOutputProvider): void { @@ -100,6 +103,9 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection }); const localPort = (server.address() as net.AddressInfo).port; remote.localPort = localPort; + + await this.containerService.postConnect(options.devcontainerFile, remote, this.outputProvider); + return { containerId: container.id, workspacePath: (await container.inspect()).Mounts[0].Destination, @@ -128,6 +134,14 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection })); } + async getCurrentContainerInfo(port: number): Promise { + const connection = this.remoteConnectionService.getConnectionFromPort(port); + if (!connection || !(connection instanceof RemoteDockerContainerConnection)) { + return undefined; + } + return await connection.container.inspect(); + } + dispose(): void { } @@ -167,8 +181,6 @@ export class RemoteDockerContainerConnection implements RemoteConnection { docker: Docker; container: Docker.Container; - containerInfo: Docker.ContainerInspectInfo | undefined; - remoteSetupResult: RemoteSetupResult; protected activeTerminalSession: ContainerTerminalSession | undefined; @@ -180,10 +192,13 @@ export class RemoteDockerContainerConnection implements RemoteConnection { this.id = options.id; this.type = options.type; this.name = options.name; - this.onDidDisconnect(() => this.dispose()); this.docker = options.docker; this.container = options.container; + + this.docker.getEvents({ filters: { container: [this.container.id], event: ['stop'] } }).then(stream => { + stream.on('data', () => this.onDidDisconnectEmitter.fire()); + }); } async forwardOut(socket: Socket, port?: number): Promise { @@ -287,8 +302,9 @@ export class RemoteDockerContainerConnection implements RemoteConnection { return deferred.promise; } - dispose(): void { - this.container.stop(); + async dispose(): Promise { + // cant use dockerrode here since this needs to happen on one tick + exec(`docker stop ${this.container.id}`); } } diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 85fbaa30a8357..67c9f1529209d 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -21,7 +21,8 @@ }, "theiaExtensions": [ { - "frontend": "lib/browser/preference-frontend-module" + "frontend": "lib/browser/preference-frontend-module", + "backend": "lib/node/preference-backend-module" } ], "keywords": [ diff --git a/packages/preferences/src/browser/preference-frontend-contribution.ts b/packages/preferences/src/browser/preference-frontend-contribution.ts new file mode 100644 index 0000000000000..6f8a025236ff5 --- /dev/null +++ b/packages/preferences/src/browser/preference-frontend-contribution.ts @@ -0,0 +1,38 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { CliPreferences } from '../common/cli-preferences'; +import { PreferenceService, PreferenceScope } from '@theia/core/lib/browser/preferences/preference-service'; + +@injectable() +export class PreferenceFrontendContribution implements FrontendApplicationContribution { + @inject(CliPreferences) + protected readonly CliPreferences: CliPreferences; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + onStart(): void { + this.CliPreferences.getPreferences().then(async preferences => { + await this.preferenceService.ready; + for (const [key, value] of preferences) { + this.preferenceService.set(key, value, PreferenceScope.User); + } + }); + } +} diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index ec3c01aace7b4..a66fbace9102e 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -17,7 +17,7 @@ import '../../src/browser/style/index.css'; import './preferences-monaco-contribution'; import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; -import { bindViewContribution, OpenHandler } from '@theia/core/lib/browser'; +import { bindViewContribution, FrontendApplicationContribution, OpenHandler } from '@theia/core/lib/browser'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { PreferenceTreeGenerator } from './util/preference-tree-generator'; import { bindPreferenceProviders } from './preference-bindings'; @@ -29,6 +29,9 @@ import { PreferencesJsonSchemaContribution } from './preferences-json-schema-con import { MonacoJSONCEditor } from './monaco-jsonc-editor'; import { PreferenceTransaction, PreferenceTransactionFactory, preferenceTransactionFactoryCreator } from './preference-transaction-manager'; import { PreferenceOpenHandler } from './preference-open-handler'; +import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; +import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; +import { PreferenceFrontendContribution } from './preference-frontend-contribution'; export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind): void { bindPreferenceProviders(bind, unbind); @@ -50,6 +53,10 @@ export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind bind(MonacoJSONCEditor).toSelf().inSingletonScope(); bind(PreferenceTransaction).toSelf(); bind(PreferenceTransactionFactory).toFactory(preferenceTransactionFactoryCreator); + + bind(CliPreferences).toDynamicValue(ctx => ServiceConnectionProvider.createProxy(ctx.container, CliPreferencesPath)).inSingletonScope(); + bind(PreferenceFrontendContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution); } export default new ContainerModule((bind, unbind, isBound, rebind) => { diff --git a/packages/preferences/src/common/cli-preferences.ts b/packages/preferences/src/common/cli-preferences.ts new file mode 100644 index 0000000000000..f17d5d762dad0 --- /dev/null +++ b/packages/preferences/src/common/cli-preferences.ts @@ -0,0 +1,22 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export const CliPreferences = Symbol('CliPreferences'); +export const CliPreferencesPath = '/services/cli-preferences'; + +export interface CliPreferences { + getPreferences(): Promise<[string, unknown][]>; +} diff --git a/packages/preferences/src/node/preference-backend-module.ts b/packages/preferences/src/node/preference-backend-module.ts new file mode 100644 index 0000000000000..7b0db89f887d0 --- /dev/null +++ b/packages/preferences/src/node/preference-backend-module.ts @@ -0,0 +1,33 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { CliContribution } from '@theia/core/lib/node/cli'; +import { PreferenceCliContribution } from './preference-cli-contribution'; +import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module'; +import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; + +const preferencesConnectionModule = ConnectionContainerModule.create(({ bind, bindBackendService }) => { + bindBackendService(CliPreferencesPath, CliPreferences); +}); + +export default new ContainerModule(bind => { + bind(PreferenceCliContribution).toSelf().inSingletonScope(); + bind(CliPreferences).toService(PreferenceCliContribution); + bind(CliContribution).toService(PreferenceCliContribution); + + bind(ConnectionContainerModule).toConstantValue(preferencesConnectionModule); +}); diff --git a/packages/preferences/src/node/preference-cli-contribution.ts b/packages/preferences/src/node/preference-cli-contribution.ts new file mode 100644 index 0000000000000..0fd46963b486e --- /dev/null +++ b/packages/preferences/src/node/preference-cli-contribution.ts @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { Argv } from '@theia/core/shared/yargs'; +import { CliContribution } from '@theia/core/lib/node/cli'; +import { CliPreferences } from '../common/cli-preferences'; + +@injectable() +export class PreferenceCliContribution implements CliContribution, CliPreferences { + + protected preferences: [string, unknown][] = []; + + configure(conf: Argv<{}>): void { + conf.option('set-preference', { + nargs: 1, + desc: 'sets the specified preference' + }); + } + + setArguments(args: Record): void { + if (args.setPreference) { + const preferences: string[] = args.setPreference instanceof Array ? args.setPreference : [args.setPreference]; + for (const preference of preferences) { + const firstEqualIndex = preference.indexOf('='); + this.preferences.push([preference.substring(0, firstEqualIndex), JSON.parse(preference.substring(firstEqualIndex + 1))]); + } + } + } + + async getPreferences(): Promise<[string, unknown][]> { + return this.preferences; + } + +} diff --git a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts index 109cfb098a30d..7a5a0c58bfe33 100644 --- a/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts +++ b/packages/remote/src/electron-browser/port-forwarding/port-forwarding-service.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { Emitter } from '@theia/core'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { RemotePortForwardingProvider } from '../../electron-common/remote-port-forwarding-provider'; export interface ForwardedPort { @@ -36,6 +36,14 @@ export class PortForwardingService { forwardedPorts: ForwardedPort[] = []; + @postConstruct() + init(): void { + this.provider.getForwardedPorts().then(ports => { + this.forwardedPorts = ports.map(p => ({ address: p.address, localPort: p.port, editing: false })); + this.onDidChangePortsEmitter.fire(); + }); + } + forwardNewPort(origin?: string): ForwardedPort { const index = this.forwardedPorts.push({ editing: true, origin }); return this.forwardedPorts[index - 1]; diff --git a/packages/remote/src/electron-common/remote-port-forwarding-provider.ts b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts index 6f01e01da2ccd..a79bd5b9df285 100644 --- a/packages/remote/src/electron-common/remote-port-forwarding-provider.ts +++ b/packages/remote/src/electron-common/remote-port-forwarding-provider.ts @@ -26,4 +26,5 @@ export interface ForwardedPort { export interface RemotePortForwardingProvider { forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise; portRemoved(port: ForwardedPort): Promise; + getForwardedPorts(): Promise } diff --git a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts index 23c8e4ec9f579..6c5fbb9b68b48 100644 --- a/packages/remote/src/electron-node/remote-port-forwarding-provider.ts +++ b/packages/remote/src/electron-node/remote-port-forwarding-provider.ts @@ -18,6 +18,13 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { ForwardedPort, RemotePortForwardingProvider } from '../electron-common/remote-port-forwarding-provider'; import { createServer, Server } from 'net'; import { RemoteConnectionService } from './remote-connection-service'; +import { RemoteConnection } from './remote-types'; + +interface ForwardInfo { + connection: RemoteConnection + port: ForwardedPort + server: Server +} @injectable() export class RemotePortForwardingProviderImpl implements RemotePortForwardingProvider { @@ -25,7 +32,7 @@ export class RemotePortForwardingProviderImpl implements RemotePortForwardingPro @inject(RemoteConnectionService) protected readonly connectionService: RemoteConnectionService; - protected forwardedPorts: Map = new Map(); + protected static forwardedPorts: ForwardInfo[] = []; async forwardPort(connectionPort: number, portToForward: ForwardedPort): Promise { const currentConnection = this.connectionService.getConnectionFromPort(connectionPort); @@ -36,15 +43,24 @@ export class RemotePortForwardingProviderImpl implements RemotePortForwardingPro const server = createServer(socket => { currentConnection?.forwardOut(socket, portToForward.port); }).listen(portToForward.port, portToForward.address); - this.forwardedPorts.set(portToForward.port, server); + + currentConnection.onDidDisconnect(() => { + this.portRemoved(portToForward); + }); + + RemotePortForwardingProviderImpl.forwardedPorts.push({ connection: currentConnection, port: portToForward, server }); } async portRemoved(forwardedPort: ForwardedPort): Promise { - const proxy = this.forwardedPorts.get(forwardedPort.port); - if (proxy) { - proxy.close(); - this.forwardedPorts.delete(forwardedPort.port); + const forwardInfo = RemotePortForwardingProviderImpl.forwardedPorts.find(info => info.port.port === forwardedPort.port); + if (forwardInfo) { + forwardInfo.server.close(); + RemotePortForwardingProviderImpl.forwardedPorts.splice(RemotePortForwardingProviderImpl.forwardedPorts.indexOf(forwardInfo), 1); } } + async getForwardedPorts(): Promise { + return Array.from(RemotePortForwardingProviderImpl.forwardedPorts) + .map(forwardInfo => ({ ...forwardInfo.port, editing: false })); + } } From c1e941efc8c43d15cefe1b329d9c9a36ba52c75d Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 29 May 2024 17:00:38 +0200 Subject: [PATCH 242/441] Fix quick pick separators from plugins (#13740) --- .../src/main/browser/quick-open-main.ts | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index 2a7f262bb66a1..39398d457438b 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -35,6 +35,7 @@ import { QuickInputButtonHandle, QuickInputService, QuickPickItem, + QuickPickItemOrSeparator, codiconArray } from '@theia/core/lib/browser'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; @@ -46,10 +47,11 @@ import { UriComponents } from '../../common/uri-components'; import { URI } from '@theia/core/shared/vscode-uri'; import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; import { isUriComponents } from '@theia/monaco-editor-core/esm/vs/base/common/uri'; +import { QuickPickSeparator } from '@theia/core'; export interface QuickInputSession { input: QuickInput; - handlesToItems: Map; + handlesToItems: Map; } interface IconPath { @@ -63,7 +65,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { private proxy: QuickOpenExt; private delegate: MonacoQuickInputService; private readonly items: Record = {}; @@ -80,7 +82,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } async $show(instance: number, options: TransferQuickPickOptions, token: CancellationToken): Promise { - const contents = new Promise((resolve, reject) => { + const contents = new Promise((resolve, reject) => { this.items[instance] = { resolve, reject }; }); @@ -92,7 +94,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { this.proxy.$onItemSelected(Number.parseInt((el).id!)); } }, - activeItem: activeItem ? this.toQuickPickItem(activeItem) : undefined + activeItem: this.isItem(activeItem) ? this.toQuickPickItem(activeItem) : undefined }; const result = await this.delegate.pick(contents, transformedOptions, token); @@ -105,6 +107,10 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { return undefined; } + private isItem(item?: TransferQuickPickItem): item is TransferQuickPickItem & { kind: 'item' } { + return item?.kind === 'item'; + } + private normalizeIconPath(path: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon | undefined): { iconPath?: IconPath iconClasses?: string[] @@ -127,10 +133,17 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } private toQuickPickItem(item: undefined): undefined; - private toQuickPickItem(item: TransferQuickPickItem): QuickPickItem; - private toQuickPickItem(item: TransferQuickPickItem | undefined): QuickPickItem | undefined { + private toQuickPickItem(item: TransferQuickPickItem & { kind: 'item' }): QuickPickItem; + private toQuickPickItem(item: TransferQuickPickItem & { kind: 'separator' }): QuickPickSeparator; + private toQuickPickItem(item: TransferQuickPickItem): QuickPickItemOrSeparator; + private toQuickPickItem(item: TransferQuickPickItem | undefined): QuickPickItemOrSeparator | undefined { if (!item) { return undefined; + } else if (item.kind === 'separator') { + return { + type: 'separator', + label: item.label + }; } return { ...this.normalizeIconPath(item.iconPath), @@ -309,7 +322,7 @@ export class QuickOpenMainImpl implements QuickOpenMain, Disposable { } } else if (param === 'items') { handlesToItems.clear(); - const items: QuickPickItem[] = []; + const items: QuickPickItemOrSeparator[] = []; params[param].forEach((transferItem: TransferQuickPickItem) => { const item = this.toQuickPickItem(transferItem); items.push(item); From 50f75fbae8e027fa246578c0a7e978c6aeb8cda6 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 30 May 2024 09:43:51 +0200 Subject: [PATCH 243/441] Bump API version to 1.89.1 (#13738) fixes #13693 Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 4 +++- dev-packages/application-package/src/api.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 022f9a37570d5..b8f79af9a3761 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,14 @@ ## 1.50.0 +- [application-package] bumped the default supported API from `1.88.1` to `1.89.1` [#13738](https://github.com/eclipse-theia/theia/pull/13738) - contributed on behalf of STMicroelectronics - [plugin] Support WindowState active API [#13718](https://github.com/eclipse-theia/theia/pull/13718) - contributed on behalf of STMicroelectronics +- [plugin] updated `DropMetada` and `documentPaste` proposed API for 1.89 compatibility [#13733](https://github.com/eclipse-theia/theia/pull/13733) - contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_1.50.0) - [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. -- [plugin] updated `DropMetada` and `documentPaste` proposed API for 1.89 compatibility [#13733](https://github.com/eclipse-theia/theia/pull/13733) - contributed on behalf of STMicroelectronics + ## v1.49.0 - 04/29/2024 diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index 52408c20aead5..ccd0a285cd711 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.88.1'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.89.1'; From 255231ac94a00da2d2dc641c1a6bd80599b2458f Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 30 May 2024 12:49:10 +0200 Subject: [PATCH 244/441] Notebooks: Aligned scroll into view behaviour with vscode (#13742) * Fixed focus loss of the notebook editor widget when a cell editor was unfocused Signed-off-by: Jonah Iden * aligned the scroll into view behaviour with vscode Signed-off-by: Jonah Iden * inserting new cell scrolls Signed-off-by: Jonah Iden * fixed delete still scrolling Signed-off-by: Jonah Iden * dont scroll into cell when click into text Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../service/notebook-context-manager.ts | 3 +-- .../src/browser/view-model/notebook-model.ts | 16 ++++++++--- .../src/browser/view/notebook-cell-editor.tsx | 6 ++--- .../browser/view/notebook-cell-list-view.tsx | 27 ++++++++++++++----- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 7638bdc979556..97db41fcae7de 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -88,6 +88,7 @@ export class NotebookContextManager { // Cell Selection realted keys this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); widget.model?.onDidChangeSelectedCell(e => { + this.selectedCellChanged(e.cell); this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!e); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_CELL_FOCUSED])); }); @@ -100,8 +101,6 @@ export class NotebookContextManager { } })); - widget.model?.onDidChangeSelectedCell(e => this.selectedCellChanged(e)); - widget.onDidChangeOutputInputFocus(focus => { this.scopedStore.setContext(NOTEBOOK_OUTPUT_INPUT_FOCUSED, focus); this.onDidChangeContextEmitter.fire(this.createContextKeyChangedEvent([NOTEBOOK_OUTPUT_INPUT_FOCUSED])); diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 9fa632f3d33e4..5099cee977e46 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -56,6 +56,11 @@ export interface NotebookModelProps { serializer: NotebookSerializer; } +export interface SelectedCellChangeEvent { + cell: NotebookCellModel | undefined; + scrollIntoView: boolean; +} + @injectable() export class NotebookModel implements Saveable, Disposable { @@ -74,7 +79,7 @@ export class NotebookModel implements Saveable, Disposable { protected readonly onContentChangedEmitter = new Emitter(); readonly onContentChanged = this.onContentChangedEmitter.event; - protected readonly onDidChangeSelectedCellEmitter = new Emitter(); + protected readonly onDidChangeSelectedCellEmitter = new Emitter(); readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event; protected readonly onDidDisposeEmitter = new Emitter(); @@ -246,10 +251,10 @@ export class NotebookModel implements Saveable, Disposable { this.undoRedoService.redo(this.uri); } - setSelectedCell(cell: NotebookCellModel): void { + setSelectedCell(cell: NotebookCellModel, scrollIntoView?: boolean): void { if (this.selectedCell !== cell) { this.selectedCell = cell; - this.onDidChangeSelectedCellEmitter.fire(cell); + this.onDidChangeSelectedCellEmitter.fire({ cell, scrollIntoView: scrollIntoView ?? true }); } } @@ -285,9 +290,12 @@ export class NotebookModel implements Saveable, Disposable { if (cell) { this.cellDirtyChanged(cell, true); } + + let scrollIntoView = true; switch (edit.editType) { case CellEditType.Replace: this.replaceCells(edit.index, edit.count, edit.cells, computeUndoRedo); + scrollIntoView = edit.cells.length > 0; break; case CellEditType.Output: { if (edit.append) { @@ -330,7 +338,7 @@ export class NotebookModel implements Saveable, Disposable { // if selected cell is affected update it because it can potentially have been replaced if (cell === this.selectedCell) { - this.setSelectedCell(this.cells[Math.min(cellIndex, this.cells.length - 1)]); + this.setSelectedCell(this.cells[Math.min(cellIndex, this.cells.length - 1)], scrollIntoView); } } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index a356c9c0421a9..6e5e3711b1a55 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -78,8 +78,8 @@ export class CellEditor extends React.Component { this.editor?.setLanguage(language); })); - this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(cell => { - if (cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => { + if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { this.props.notebookContextManager.context?.focus(); } })); @@ -129,7 +129,7 @@ export class CellEditor extends React.Component { })); this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => { this.props.notebookContextManager.onDidEditorTextFocus(true); - this.props.notebookModel.setSelectedCell(cell); + this.props.notebookModel.setSelectedCell(cell, false); })); this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { this.props.notebookContextManager.onDidEditorTextFocus(false); diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 065a2a19a2edb..8fcc4ce0b8384 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -37,6 +37,7 @@ interface CellListProps { interface NotebookCellListState { selectedCell?: NotebookCellModel; + scrollIntoView: boolean; dragOverIndicator: { cell: NotebookCellModel, position: 'top' | 'bottom' } | undefined; } @@ -48,17 +49,29 @@ export class NotebookCellListView extends React.Component { if (e.newCellIds && e.newCellIds.length > 0) { - this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]) }); + this.setState({ + ...this.state, + selectedCell: this.props.notebookModel.cells.find(model => model.handle === e.newCellIds![e.newCellIds!.length - 1]), + scrollIntoView: true + }); } else { - this.setState({ ...this.state, selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell) }); + this.setState({ + ...this.state, + selectedCell: this.props.notebookModel.cells.find(cell => cell === this.state.selectedCell), + scrollIntoView: false + }); } })); - this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(cell => { - this.setState({ ...this.state, selectedCell: cell }); + this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(e => { + this.setState({ + ...this.state, + selectedCell: e.cell, + scrollIntoView: e.scrollIntoView + }); })); } @@ -80,13 +93,13 @@ export class NotebookCellListView extends React.Component { this.setState({ ...this.state, selectedCell: cell }); - this.props.notebookModel.setSelectedCell(cell); + this.props.notebookModel.setSelectedCell(cell, false); }} onDragStart={e => this.onDragStart(e, index, cell)} onDragOver={e => this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} draggable={true} - ref={ref => cell === this.state.selectedCell && ref?.scrollIntoView({ block: 'nearest' })}> + ref={ref => cell === this.state.selectedCell && this.state.scrollIntoView && ref?.scrollIntoView({ block: 'nearest' })}>
    {this.renderCellContent(cell, index)} From a17c637aab7d7a515f5bcd3c256c9053c1ec64cf Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 30 May 2024 14:19:51 +0200 Subject: [PATCH 245/441] Improve VSCode tab API (#13730) Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- .../browser/frontend-application-module.ts | 3 ++ .../src/browser/shell}/view-column-service.ts | 10 ++--- .../browser/plugin-ext-frontend-module.ts | 3 -- .../src/main/browser/tabs/tabs-main.ts | 45 +++++++++++++++---- .../src/main/browser/webviews-main.ts | 2 +- packages/plugin-ext/src/plugin/tabs.ts | 9 +++- 6 files changed, 52 insertions(+), 20 deletions(-) rename packages/{plugin-ext/src/main/browser => core/src/browser/shell}/view-column-service.ts (92%) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index bc21b7277c135..3c35877a40668 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -141,6 +141,7 @@ import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './s import { LanguageIconLabelProvider } from './language-icon-provider'; import { bindTreePreferences } from './tree'; import { OpenWithService } from './open-with-service'; +import { ViewColumnService } from './shell/view-column-service'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -462,4 +463,6 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(FrontendApplicationContribution).toService(StylingService); bind(SecondaryWindowHandler).toSelf().inSingletonScope(); + + bind(ViewColumnService).toSelf().inSingletonScope(); }); diff --git a/packages/plugin-ext/src/main/browser/view-column-service.ts b/packages/core/src/browser/shell/view-column-service.ts similarity index 92% rename from packages/plugin-ext/src/main/browser/view-column-service.ts rename to packages/core/src/browser/shell/view-column-service.ts index a44a18b6482f3..16db95bc190d8 100644 --- a/packages/plugin-ext/src/main/browser/view-column-service.ts +++ b/packages/core/src/browser/shell/view-column-service.ts @@ -14,11 +14,11 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; -import { Emitter, Event } from '@theia/core/lib/common/event'; -import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; -import { toArray } from '@theia/core/shared/@phosphor/algorithm'; -import { TabBar, Widget } from '@theia/core/shared/@phosphor/widgets'; +import { injectable, inject } from 'inversify'; +import { Emitter, Event } from '../../common/event'; +import { ApplicationShell } from './application-shell'; +import { toArray } from '@phosphor/algorithm'; +import { TabBar, Widget } from '@phosphor/widgets'; @injectable() export class ViewColumnService { diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 10c4a6f29b79c..dc8d2bfdaf5c8 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -47,7 +47,6 @@ import { PluginDebugService } from './debug/plugin-debug-service'; import { DebugService } from '@theia/debug/lib/common/debug-service'; import { PluginSharedStyle } from './plugin-shared-style'; import { SelectionProviderCommandContribution } from './selection-provider-command'; -import { ViewColumnService } from './view-column-service'; import { ViewContextKeyService } from './view/view-context-key-service'; import { PluginViewWidget, PluginViewWidgetIdentifier } from './view/plugin-view-widget'; import { TreeViewWidgetOptions, VIEW_ITEM_CONTEXT_MENU, PluginTree, TreeViewWidget, PluginTreeModel } from './view/tree-view-widget'; @@ -244,8 +243,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(PluginDebugSessionContributionRegistry).toSelf().inSingletonScope(); rebind(DebugSessionContributionRegistry).toService(PluginDebugSessionContributionRegistry); - bind(ViewColumnService).toSelf().inSingletonScope(); - bind(CommentsService).to(PluginCommentService).inSingletonScope(); bind(CommentingRangeDecorator).toSelf().inSingletonScope(); bind(CommentsContribution).toSelf().inSingletonScope(); diff --git a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts index 5375409373fb8..bd8f83885aad4 100644 --- a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts +++ b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts @@ -24,6 +24,8 @@ import { MonacoDiffEditor } from '@theia/monaco/lib/browser/monaco-diff-editor'; import { toUriComponents } from '../hierarchy/hierarchy-types-converters'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { DisposableCollection } from '@theia/core'; +import { NotebookEditorWidget } from '@theia/notebook/lib/browser'; +import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service'; interface TabInfo { tab: TabDto; @@ -46,6 +48,14 @@ export class TabsMainImpl implements TabsMain, Disposable { private currentActiveGroup: TabGroupDto; private tabGroupChanged: boolean = false; + private viewColumnService: ViewColumnService; + + private readonly defaultTabGroup: TabGroupDto = { + groupId: 0, + tabs: [], + isActive: true, + viewColumn: 0 + }; constructor( rpc: RPCProtocol, @@ -54,6 +64,7 @@ export class TabsMainImpl implements TabsMain, Disposable { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TABS_EXT); this.applicationShell = container.get(ApplicationShell); + this.viewColumnService = container.get(ViewColumnService); this.createTabsModel(); const tabBars = this.applicationShell.mainPanel.tabBars(); @@ -100,15 +111,20 @@ export class TabsMainImpl implements TabsMain, Disposable { } protected createTabsModel(): void { + if (this.applicationShell.mainAreaTabBars.length === 0) { + this.proxy.$acceptEditorTabModel([this.defaultTabGroup]); + return; + } const newTabGroupModel = new Map, TabGroupDto>(); this.tabInfoLookup.clear(); this.disposableTabBarListeners.dispose(); - this.applicationShell.mainAreaTabBars.forEach(tabBar => { - this.attachListenersToTabBar(tabBar); - const groupDto = this.createTabGroupDto(tabBar); - tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index })); - newTabGroupModel.set(tabBar, groupDto); - }); + this.applicationShell.mainAreaTabBars + .forEach((tabBar, i) => { + this.attachListenersToTabBar(tabBar); + const groupDto = this.createTabGroupDto(tabBar, i); + tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index })); + newTabGroupModel.set(tabBar, groupDto); + }); if (newTabGroupModel.size > 0 && Array.from(newTabGroupModel.values()).indexOf(this.currentActiveGroup) < 0) { this.currentActiveGroup = this.tabInfoLookup.get(this.applicationShell.mainPanel.currentTitle!)?.group ?? newTabGroupModel.values().next().value; this.currentActiveGroup.isActive = true; @@ -134,15 +150,17 @@ export class TabsMainImpl implements TabsMain, Disposable { return `${groupId}~${tabTitle.owner.id}`; } - protected createTabGroupDto(tabBar: TabBar): TabGroupDto { - const oldDto = this.tabGroupModel.get(tabBar); + protected createTabGroupDto(tabBar: TabBar, index: number): TabGroupDto { + const oldDto = index === 0 ? this.defaultTabGroup : this.tabGroupModel.get(tabBar); const groupId = oldDto?.groupId ?? this.groupIdCounter++; const tabs = tabBar.titles.map(title => this.createTabDto(title, groupId)); + const viewColumn = this.viewColumnService.getViewColumn(tabBar.id) + ?? this.applicationShell.allTabBars.indexOf(tabBar); return { groupId, tabs, isActive: false, - viewColumn: 1 + viewColumn }; } @@ -182,6 +200,12 @@ export class TabsMainImpl implements TabsMain, Disposable { return { kind: TabInputKind.TerminalEditorInput }; + } else if (widget instanceof NotebookEditorWidget) { + return { + kind: TabInputKind.NotebookInput, + notebookType: widget.notebookType, + uri: toUriComponents(widget.model?.uri.toString() ?? '') + }; } return { kind: TabInputKind.UnknownInput }; @@ -232,6 +256,9 @@ export class TabsMainImpl implements TabsMain, Disposable { } const oldTabDto = tabInfo.tab; const newTabDto = this.createTabDto(title, tabInfo.group.groupId); + if (!oldTabDto.isActive && newTabDto.isActive) { + this.currentActiveGroup.tabs.filter(tab => tab.isActive).forEach(tab => tab.isActive = false); + } if (newTabDto.isActive && !tabInfo.group.isActive) { tabInfo.group.isActive = true; this.currentActiveGroup.isActive = false; diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index cf0f319950ba4..862010101dbf1 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -23,7 +23,7 @@ import { ViewBadge, WebviewOptions, WebviewPanelOptions, WebviewPanelShowOptions import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; import { WebviewWidget, WebviewWidgetIdentifier } from './webview/webview'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; -import { ViewColumnService } from './view-column-service'; +import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; import { JSONExt } from '@theia/core/shared/@phosphor/coreutils'; import { Mutable } from '@theia/core/lib/common/types'; diff --git a/packages/plugin-ext/src/plugin/tabs.ts b/packages/plugin-ext/src/plugin/tabs.ts index a1a4761632371..80a263b778bf3 100644 --- a/packages/plugin-ext/src/plugin/tabs.ts +++ b/packages/plugin-ext/src/plugin/tabs.ts @@ -325,8 +325,13 @@ export class TabsExtImpl implements TabsExt { this.activeGroupId = activeTabGroupId; } } - this.onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); - this.onDidChangeTabs.fire({ opened: tabsOpened, changed: [], closed: [] }); + + if (closed.length > 0 || opened.length > 0 || changed.length > 0) { + this.onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); + } + if (tabsOpened.length > 0) { + this.onDidChangeTabs.fire({ opened: tabsOpened, changed: [], closed: [] }); + } } $acceptTabGroupUpdate(groupDto: TabGroupDto): void { From cb71d7810e28ad44dd2a544f866871e752f4d6d6 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 30 May 2024 15:08:21 +0200 Subject: [PATCH 246/441] fix notebook cell divider size (#13745) Signed-off-by: Jonah Iden --- packages/notebook/src/browser/style/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index cdfdb60f934de..447e3fc1fbe9b 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -171,7 +171,7 @@ } .theia-notebook-cell-divider { - height: 20px; + height: 25px; width: 100%; } From 6327286c918e7defc648c018df0d610baa0301c8 Mon Sep 17 00:00:00 2001 From: Alexandra Buzila Date: Thu, 30 May 2024 15:43:51 +0200 Subject: [PATCH 247/441] Fix performance issues in terminal (#13735) Do not search for matches in the workspace when creating links in the terminal, as this impacts performance in large workspaces. Instead, search for a match only when the user wants to open a link. If the match can't be found, open the quick input service and let the user select from a list of possible matches. Additionally, the LocalFileLinkProvider no longer matches single words, to avoid polluting the terminal with links. Contributed on behalf of STMicroelectronics --- .../browser/terminal-file-link-provider.ts | 116 +++++++++++------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/packages/terminal/src/browser/terminal-file-link-provider.ts b/packages/terminal/src/browser/terminal-file-link-provider.ts index d067a83a2a2fb..d534a9eb9cf47 100644 --- a/packages/terminal/src/browser/terminal-file-link-provider.ts +++ b/packages/terminal/src/browser/terminal-file-link-provider.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { OS, Path } from '@theia/core'; +import { OS, Path, QuickInputService } from '@theia/core'; import { OpenerService } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { inject, injectable } from '@theia/core/shared/inversify'; @@ -29,6 +29,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser'; export class FileLinkProvider implements TerminalLinkProvider { @inject(OpenerService) protected readonly openerService: OpenerService; + @inject(QuickInputService) protected readonly quickInputService: QuickInputService; @inject(FileService) protected fileService: FileService; @inject(FileSearchService) protected searchService: FileSearchService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -45,48 +46,11 @@ export class FileLinkProvider implements TerminalLinkProvider { length: match.length, handle: () => this.open(match, terminal) }); - } else { - const searchTerm = await this.extractPath(match); - const fileUri = await this.isValidWorkspaceFile(searchTerm, terminal); - if (fileUri) { - const position = await this.extractPosition(match); - links.push({ - startIndex: regExp.lastIndex - match.length, - length: match.length, - handle: () => this.openURI(fileUri, position) - }); - } } } return links; } - protected async isValidWorkspaceFile(searchTerm: string | undefined, terminal: TerminalWidget): Promise { - if (!searchTerm) { - return undefined; - } - const cwd = await this.getCwd(terminal); - // remove any leading ./, ../ etc. as they can't be searched - searchTerm = searchTerm.replace(/^(\.+[\\/])+/, ''); - const workspaceRoots = this.workspaceService.tryGetRoots().map(root => root.resource.toString()); - // try and find a matching file in the workspace - const files = (await this.searchService.find(searchTerm, { - rootUris: [cwd.toString(), ...workspaceRoots], - fuzzyMatch: true, - limit: 1 - })); - // checks if the string end in a separator + searchTerm - const regex = new RegExp(`[\\\\|\\/]${searchTerm}$`); - if (files.length && regex.test(files[0])) { - const fileUri = new URI(files[0]); - const valid = await this.isValidFileURI(fileUri); - if (valid) { - return fileUri; - } - - } - } - protected async createRegExp(): Promise { const baseLocalLinkClause = OS.backend.isWindows ? winLocalLinkClause : unixLocalLinkClause; return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); @@ -168,7 +132,7 @@ export class FileLinkProvider implements TerminalLinkProvider { return info; } - const lineAndColumnMatchIndex = OS.backend.isWindows ? winLineAndColumnMatchIndex : unixLineAndColumnMatchIndex; + const lineAndColumnMatchIndex = this.getLineAndColumnMatchIndex(); for (let i = 0; i < lineAndColumnClause.length; i++) { const lineMatchIndex = lineAndColumnMatchIndex + (lineAndColumnClauseGroupCount * i); const rowNumber = matches[lineMatchIndex]; @@ -185,6 +149,9 @@ export class FileLinkProvider implements TerminalLinkProvider { return info; } + protected getLineAndColumnMatchIndex(): number { + return OS.backend.isWindows ? winLineAndColumnMatchIndex : unixLineAndColumnMatchIndex; + } } @injectable() @@ -204,12 +171,75 @@ export class FileDiffPostLinkProvider extends FileLinkProvider { @injectable() export class LocalFileLinkProvider extends FileLinkProvider { override async createRegExp(): Promise { - // match links that might not start with a separator, e.g. 'foo.bar' - const baseLocalLinkClause = OS.backend.isWindows ? - '((' + winPathPrefix + '|(' + winExcludedPathCharactersClause + ')+)(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)*)' - : '((' + pathPrefix + '|(' + excludedPathCharactersClause + ')+)(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)*)'; + // match links that might not start with a separator, e.g. 'foo.bar', but don't match single words e.g. 'foo' + const baseLocalUnixLinkClause = + '((' + pathPrefix + '|' + + '(' + excludedPathCharactersClause + '+(' + pathSeparatorClause + '|' + '\\.' + ')' + excludedPathCharactersClause + '+))' + + '(' + pathSeparatorClause + '(' + excludedPathCharactersClause + ')+)*)'; + + const baseLocalWindowsLinkClause = + '((' + winPathPrefix + '|' + + '(' + winExcludedPathCharactersClause + '+(' + winPathSeparatorClause + '|' + '\\.' + ')' + winExcludedPathCharactersClause + '+))' + + '(' + winPathSeparatorClause + '(' + winExcludedPathCharactersClause + ')+)*)'; + + const baseLocalLinkClause = OS.backend.isWindows ? baseLocalWindowsLinkClause : baseLocalUnixLinkClause; return new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`, 'g'); } + + override async provideLinks(line: string, terminal: TerminalWidget): Promise { + const links: TerminalLink[] = []; + const regExp = await this.createRegExp(); + let regExpResult: RegExpExecArray | null; + while (regExpResult = regExp.exec(line)) { + const match = regExpResult[0]; + const searchTerm = await this.extractPath(match); + if (searchTerm) { + links.push({ + startIndex: regExp.lastIndex - match.length, + length: match.length, + handle: async () => { + const fileUri = await this.isValidWorkspaceFile(searchTerm, terminal); + if (fileUri) { + const position = await this.extractPosition(match); + this.openURI(fileUri, position); + } else { + this.quickInputService.open(match); + } + } + }); + } + } + return links; + } + + protected override getLineAndColumnMatchIndex(): number { + return OS.backend.isWindows ? 14 : 12; + } + + protected async isValidWorkspaceFile(searchTerm: string | undefined, terminal: TerminalWidget): Promise { + if (!searchTerm) { + return undefined; + } + const cwd = await this.getCwd(terminal); + // remove any leading ./, ../ etc. as they can't be searched + searchTerm = searchTerm.replace(/^(\.+[\\/])+/, ''); + const workspaceRoots = this.workspaceService.tryGetRoots().map(root => root.resource.toString()); + // try and find a matching file in the workspace + const files = (await this.searchService.find(searchTerm, { + rootUris: [cwd.toString(), ...workspaceRoots], + fuzzyMatch: true, + limit: 1 + })); + // checks if the string ends in a separator + searchTerm + const regex = new RegExp(`[\\\\|\\/]${searchTerm}$`); + if (files.length && regex.test(files[0])) { + const fileUri = new URI(files[0]); + const valid = await this.isValidFileURI(fileUri); + if (valid) { + return fileUri; + } + } + } } // The following regular expressions are taken from: From 1fc03457df88b577e170c5dd7802f5f04d2d5da3 Mon Sep 17 00:00:00 2001 From: Vladimir Piskarev Date: Fri, 31 May 2024 11:26:49 +0300 Subject: [PATCH 248/441] Fix an API test failure on macOS (#13732) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this fix, Monaco API KeybindingService.resolveKeybinding test was failing on macOS with ``` AssertionError: expected { label: '⌃⇧⌥⌘K', …(6) } to deeply equal { label: '⌃⇧⌥⌘K', …(7) } + expected - actual { "WYSIWYG": true "ariaLabel": "⌃⇧⌥⌘K" + "chord": false "dispatchParts": [ "ctrl+shift+alt+meta+K" ] "electronAccelerator": "Ctrl+Shift+Alt+Cmd+K" ``` The `chord: false` was removed from the expected object for other platforms as part of #13217. This fix removes it from the expected object for macOS. --- examples/api-tests/src/monaco-api.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/api-tests/src/monaco-api.spec.js b/examples/api-tests/src/monaco-api.spec.js index 862069df957da..438d10ea53bf5 100644 --- a/examples/api-tests/src/monaco-api.spec.js +++ b/examples/api-tests/src/monaco-api.spec.js @@ -87,7 +87,6 @@ describe('Monaco API', async function () { electronAccelerator: 'Ctrl+Shift+Alt+Cmd+K', userSettingsLabel: 'ctrl+shift+alt+cmd+K', WYSIWYG: true, - chord: false, parts: [new ResolvedChord( true, true, From de59d81ede932828e7b9425d000624c16a1a40b9 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Fri, 31 May 2024 15:27:12 +0200 Subject: [PATCH 249/441] Update nls metadata for VSCode API 1.89.0 (#13743) --- .../browser/common-frontend-contribution.ts | 2 +- packages/core/src/browser/core-preferences.ts | 2 +- .../core/src/common/i18n/nls.metadata.json | 19964 +++++++++------- .../window/electron-window-preferences.ts | 2 +- .../editor-generated-preference-schema.ts | 277 +- .../browser/problem/problem-preferences.ts | 2 +- .../src/browser/navigator-widget.tsx | 2 +- .../browser/search-in-workspace-widget.tsx | 4 +- .../src/browser/terminal-preferences.ts | 2 +- .../src/browser/vsx-extension.tsx | 2 +- 10 files changed, 11653 insertions(+), 8606 deletions(-) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 89a0532c05d45..aa7d7bd85e299 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -1408,7 +1408,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi const items = [...itemsByTheme.light, ...itemsByTheme.dark, ...itemsByTheme.hc, ...itemsByTheme.hcLight]; this.quickInputService?.showQuickPick(items, { - placeholder: nls.localizeByDefault('Select Color Theme (Up/Down Keys to Preview)'), + placeholder: nls.localizeByDefault('Select Color Theme'), activeItem: items.find(item => item.id === resetTo), onDidChangeSelection: (_, selectedItems) => { resetTo = undefined; diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 8f54f5419f645..04678d4888cb5 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -216,7 +216,7 @@ export const corePreferenceSchema: PreferenceSchema = { enum: ['dark', 'light', 'hc-theia'], enumItemLabels: ['Dark (Theia)', 'Light (Theia)', 'High Contrast (Theia)'], default: DefaultTheme.defaultForOSTheme(FrontendApplicationConfigProvider.get().defaultTheme), - description: nls.localizeByDefault('Specifies the color theme used in the workbench.') + description: nls.localizeByDefault('Specifies the color theme used in the workbench when {0} is not enabled.', '`#window.autoDetectColorScheme#`') }, 'workbench.iconTheme': { type: ['string'], diff --git a/packages/core/src/common/i18n/nls.metadata.json b/packages/core/src/common/i18n/nls.metadata.json index d2f3eae547d4f..6afd015f8d03f 100644 --- a/packages/core/src/common/i18n/nls.metadata.json +++ b/packages/core/src/common/i18n/nls.metadata.json @@ -3,9 +3,6 @@ "vs/platform/terminal/node/ptyHostMain": [ "ptyHost" ], - "vs/code/node/cliProcessMain": [ - "cli" - ], "vs/code/electron-main/main": [ "mainLog", "secondInstanceAdmin", @@ -22,8 +19,8 @@ ] } ], - "vs/code/node/sharedProcess/sharedProcessMain": [ - "sharedLog" + "vs/code/node/cliProcessMain": [ + "cli" ], "vs/code/electron-sandbox/processExplorer/processExplorerMain": [ "name", @@ -36,16 +33,10 @@ "copyAll", "debug" ], - "vs/workbench/electron-sandbox/desktop.main": [ - "join.closeStorage" + "vs/code/node/sharedProcess/sharedProcessMain": [ + "sharedLog" ], "vs/workbench/electron-sandbox/desktop.contribution": [ - "newTab", - "showPreviousTab", - "showNextWindowTab", - "moveWindowTabToNewWindow", - "mergeAllWindowTabs", - "toggleWindowTabsBar", { "key": "miExit", "comment": [ @@ -54,6 +45,7 @@ }, "application.shellEnvironmentResolutionTimeout", "windowConfigurationTitle", + "confirmSaveUntitledWorkspace", "window.openWithoutArgumentsInNewWindow.on", "window.openWithoutArgumentsInNewWindow.off", "openWithoutArgumentsInNewWindow", @@ -64,7 +56,18 @@ "window.reopenFolders.none", "restoreWindows", "restoreFullscreen", - "zoomLevel", + { + "comment": [ + "{0} will be a setting name rendered as a link" + ], + "key": "zoomLevel" + }, + { + "comment": [ + "{0} will be a setting name rendered as a link" + ], + "key": "zoomPerWindow" + }, "window.newWindowDimensions.default", "window.newWindowDimensions.inherit", "window.newWindowDimensions.offset", @@ -74,6 +77,10 @@ "closeWhenEmpty", "window.doubleClickIconToClose", "titleBarStyle", + "window.customTitleBarVisibility.auto", + "window.customTitleBarVisibility.windowed", + "window.customTitleBarVisibility.never", + "window.customTitleBarVisibility", "dialogStyle", "window.nativeTabs", "window.nativeFullScreen", @@ -96,7 +103,16 @@ "argv.disableChromiumSandbox", "argv.useInMemorySecretStorage", "argv.force-renderer-accessibility", - "argv.passwordStore" + "argv.passwordStore", + "newTab", + "showPreviousTab", + "showNextWindowTab", + "moveWindowTabToNewWindow", + "mergeAllWindowTabs", + "toggleWindowTabsBar" + ], + "vs/workbench/electron-sandbox/desktop.main": [ + "join.closeStorage" ], "vs/workbench/services/textfile/electron-sandbox/nativeTextFileService": [ "join.textFiles" @@ -116,6 +132,7 @@ "&& denotes a mnemonic" ] }, + "doNotAskAgain", "workspaceOpenedMessage", "workspaceOpenedDetail", "restartExtensionHost.reason" @@ -145,14 +162,14 @@ "local", "remote" ], + "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ + "join.workingCopyBackups" + ], "vs/workbench/services/integrity/electron-sandbox/integrityService": [ "integrity.prompt", "integrity.moreInformation", "integrity.dontShowAgain" ], - "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ - "join.workingCopyBackups" - ], "vs/workbench/services/extensions/electron-sandbox/nativeExtensionService": [ "extensionService.versionMismatchCrash", "relaunch", @@ -168,36 +185,36 @@ "installResolver", "install", "resolverExtensionNotFound", - "restartExtensionHost", - "restartExtensionHost.reason" + "restartExtensionHost.reason", + "restartExtensionHost" + ], + "vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService": [ + "backupErrorDetails" + ], + "vs/workbench/contrib/localization/electron-sandbox/localization.contribution": [ + "updateLocale", + "changeAndRestart", + "neverAgain" ], "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ + "miShare", "revealInWindows", "revealInMac", "openContainer", - "miShare", "filesCategory" ], - "vs/workbench/contrib/localization/electron-sandbox/localization.contribution": [ - "updateLocale", - "changeAndRestart", - "neverAgain" + "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ + "runtimeExtension" ], "vs/workbench/contrib/issue/electron-sandbox/issue.contribution": [ - { - "key": "reportPerformanceIssue", - "comment": [ - "Here, 'issue' means problem or bug" - ] - }, - "openProcessExplorer", + "tasksQuickAccessPlaceholder", + "openIssueReporter", { "key": "miOpenProcessExplorerer", "comment": [ "&& denotes a mnemonic" ] }, - "stopTracing", "stopTracing.message", { "key": "stopTracing.button", @@ -206,16 +223,33 @@ ] }, "stopTracing.title", - "stopTracing.detail" - ], - "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ - "runtimeExtension" + "stopTracing.detail", + { + "key": "reportPerformanceIssue", + "comment": [ + "Here, 'issue' means problem or bug" + ] + }, + "openProcessExplorer", + "stopTracing" ], "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ "wslFeatureInstalled", "remote", "remote.downloadExtensionsLocally" ], + "vs/workbench/services/themes/electron-sandbox/themes.contribution": [ + "window.systemColorTheme.default", + "window.systemColorTheme.auto", + "window.systemColorTheme.light", + "window.systemColorTheme.dark", + { + "key": "window.systemColorTheme", + "comment": [ + "{0} and {1} will become links to other settings." + ] + } + ], "vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution": [ "no backups", "download sync activity complete", @@ -225,6 +259,21 @@ "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ "experimental.rendererProfiling" ], + "vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution": [ + "terminalConfigurationTitle", + "terminal.explorerKind.integrated", + "terminal.explorerKind.external", + "terminal.explorerKind.both", + "explorer.openInTerminalKind", + "terminal.sourceControlRepositoriesKind.integrated", + "terminal.sourceControlRepositoriesKind.external", + "terminal.sourceControlRepositoriesKind.both", + "sourceControlRepositories.openInTerminalKind", + "terminal.external.windowsExec", + "terminal.external.osxExec", + "terminal.external.linuxExec", + "globalConsoleAction" + ], "vs/workbench/contrib/tasks/electron-sandbox/taskService": [ "TaskSystem.runningTask", { @@ -241,23 +290,10 @@ ] } ], - "vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution": [ - "globalConsoleAction", - "terminalConfigurationTitle", - "terminal.explorerKind.integrated", - "terminal.explorerKind.external", - "terminal.explorerKind.both", - "explorer.openInTerminalKind", - "terminal.sourceControlRepositoriesKind.integrated", - "terminal.sourceControlRepositoriesKind.external", - "terminal.sourceControlRepositoriesKind.both", - "sourceControlRepositories.openInTerminalKind", - "terminal.external.windowsExec", - "terminal.external.osxExec", - "terminal.external.linuxExec" + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contribution": [ + "name" ], "vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution": [ - "remoteTunnel.category", "remoteTunnel.actions.turnOn", "remoteTunnel.actions.turnOff", "remoteTunnel.actions.showLog", @@ -342,7 +378,8 @@ "manage.tunnelName", "remoteTunnelAccess.machineName", "remoteTunnelAccess.machineNameRegex", - "remoteTunnelAccess.preventSleep" + "remoteTunnelAccess.preventSleep", + "remoteTunnel.category" ], "vs/base/common/platform": [ { @@ -376,6 +413,7 @@ "installExtension", "install prerelease", "uninstallExtension", + "updateExtensions", "experimentalApis", "version", "verbose", @@ -400,6 +438,14 @@ "unknownVersion", "unknownCommit" ], + "vs/platform/log/common/log": [ + "trace", + "debug", + "info", + "warn", + "error", + "off" + ], "vs/platform/terminal/node/ptyService": [ "terminal-history-restored" ], @@ -454,7 +500,10 @@ "wrappingStrategy.simple", "wrappingStrategy.advanced", "wrappingStrategy", - "codeActions", + "editor.lightbulb.enabled.off", + "editor.lightbulb.enabled.onCode", + "editor.lightbulb.enabled.on", + "enabled", "editor.stickyScroll.enabled", "editor.stickyScroll.maxLineCount", "editor.stickyScroll.defaultModel", @@ -479,6 +528,9 @@ "minimap.scale", "minimap.renderCharacters", "minimap.maxColumn", + "minimap.showRegionSectionHeaders", + "minimap.showMarkSectionHeaders", + "minimap.sectionHeaderFontSize", "padding.top", "padding.bottom", "parameterHints.enabled", @@ -520,8 +572,17 @@ "inlineSuggest.enabled", "inlineSuggest.showToolbar.always", "inlineSuggest.showToolbar.onHover", + "inlineSuggest.showToolbar.never", "inlineSuggest.showToolbar", "inlineSuggest.suppressSuggestions", + "inlineSuggest.fontFamily", + "inlineEdit.enabled", + "inlineEdit.showToolbar.always", + "inlineEdit.showToolbar.onHover", + "inlineEdit.showToolbar.never", + "inlineEdit.showToolbar", + "inlineEdit.fontFamily", + "inlineEdit.backgroundColoring", "bracketPairColorization.enabled", "bracketPairColorization.independentColorPoolPerBracketType", "editor.guides.bracketPairs.true", @@ -588,6 +649,8 @@ "editor.suggest.showIssues", "selectLeadingAndTrailingWhitespace", "selectSubwords", + "wordSegmenterLocales", + "wordSegmenterLocales", "wrappingIndent.none", "wrappingIndent.same", "wrappingIndent.indent", @@ -677,6 +740,7 @@ "links", "matchBrackets", "mouseWheelScrollSensitivity", + "mouseWheelZoom.mac", "mouseWheelZoom", "multiCursorMergeOverlapping", "multiCursorModifier.ctrlCmd", @@ -692,8 +756,10 @@ "multiCursorPaste.full", "multiCursorPaste", "multiCursorLimit", + "occurrencesHighlight.off", + "occurrencesHighlight.singleFile", + "occurrencesHighlight.multiFile", "occurrencesHighlight", - "multiDocumentOccurrencesHighlight", "overviewRulerBorder", "peekWidgetDefaultFocus.tree", "peekWidgetDefaultFocus.editor", @@ -790,47 +856,33 @@ "error.moreErrors", "error.defaultMessage" ], - "vs/platform/extensionManagement/common/extensionManagement": [ - "extensions", - "preferences" - ], - "vs/platform/extensionManagement/common/extensionManagementCLI": [ - "notFound", - "useId", - "listFromLocation", - "installingExtensionsOnLocation", - "installingExtensions", - "alreadyInstalled-checkAndUpdate", - "alreadyInstalled", - "error while installing extensions", - "installation failed", - "successVsixInstall", - "cancelVsixInstall", - "alreadyInstalled", - "updateMessage", - "installing builtin with version", - "installing builtin ", - "installing with version", - "installing", - "successInstall", - "cancelInstall", - "forceDowngrade", - "builtin", - "forceUninstall", - "uninstalling", - "successUninstallFromLocation", - "successUninstall", - "notInstalleddOnLocation", - "notInstalled" + "vs/code/electron-main/app": [ + "confirmOpenMessageWorkspace", + "confirmOpenMessageFolder", + "confirmOpenMessageFileOrFolder", + { + "key": "open", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "cancel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmOpenDetail", + "doNotAskAgainLocal", + "doNotAskAgainRemote" ], - "vs/platform/extensionManagement/common/extensionsScannerService": [ - "fileReadFail", - "jsonParseFail", - "jsonParseInvalidType", - "jsonsParseReportErrors", - "jsonInvalidFormat", - "jsonsParseReportErrors", - "jsonInvalidFormat" + "vs/platform/environment/node/argvHelper": [ + "multipleValues", + "emptyValue", + "deprecatedArgument", + "unknownSubCommandOption", + "unknownOption", + "gotoValidation" ], "vs/platform/files/common/files": [ "unknownError", @@ -840,17 +892,6 @@ "sizeGB", "sizeTB" ], - "vs/platform/extensionManagement/node/extensionManagementService": [ - "incompatible", - "MarketPlaceDisabled", - "Not a Marketplace extension", - "removeError", - "errorDeleting", - "renameError", - "cannot read", - "restartCode", - "restartCode" - ], "vs/platform/files/common/fileService": [ "invalidPath", "noProviderFound", @@ -889,9 +930,6 @@ "fileMoveCopyErrorNotFound", "fileMoveCopyErrorExists" ], - "vs/platform/languagePacks/common/languagePacks": [ - "currentDisplayLanguage" - ], "vs/platform/request/common/request": [ "request", "httpConfigurationTitle", @@ -907,54 +945,18 @@ "systemCertificates", "systemCertificatesV2" ], - "vs/platform/telemetry/common/telemetryService": [ - "telemetry.telemetryLevelMd", - "telemetry.docsStatement", - "telemetry.docsAndPrivacyStatement", - "telemetry.restart", - "telemetry.crashReports", - "telemetry.errors", - "telemetry.usage", - "telemetry.telemetryLevel.tableDescription", - "telemetry.telemetryLevel.deprecated", - "telemetryConfigurationTitle", - "telemetry.telemetryLevel.default", - "telemetry.telemetryLevel.error", - "telemetry.telemetryLevel.crash", - "telemetry.telemetryLevel.off", - "telemetryConfigurationTitle", - "telemetry.enableTelemetry", - "telemetry.enableTelemetryMd", - "enableTelemetryDeprecated" - ], - "vs/platform/userDataProfile/common/userDataProfile": [ - "defaultProfile" - ], - "vs/code/electron-main/app": [ - { - "key": "open", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "cancel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "confirmOpenMessage", - "confirmOpenDetail", - "doNotAskAgainLocal", - "doNotAskAgainRemote" - ], - "vs/platform/environment/node/argvHelper": [ - "multipleValues", - "emptyValue", - "deprecatedArgument", - "unknownSubCommandOption", - "unknownOption", - "gotoValidation" + "vs/platform/update/common/update.config.contribution": [ + "updateConfigurationTitle", + "updateMode", + "none", + "manual", + "start", + "default", + "updateMode", + "deprecated", + "enableWindowsBackgroundUpdatesTitle", + "enableWindowsBackgroundUpdates", + "showReleaseNotes" ], "vs/platform/dialogs/common/dialogs": [ { @@ -982,18 +984,91 @@ "moreFile", "moreFiles" ], - "vs/platform/update/common/update.config.contribution": [ - "updateConfigurationTitle", - "updateMode", - "none", - "manual", - "start", - "default", - "updateMode", - "deprecated", - "enableWindowsBackgroundUpdatesTitle", - "enableWindowsBackgroundUpdates", - "showReleaseNotes" + "vs/platform/extensionManagement/common/extensionManagement": [ + "extensions", + "preferences" + ], + "vs/platform/extensionManagement/common/extensionManagementCLI": [ + "notFound", + "useId", + "listFromLocation", + "installingExtensionsOnLocation", + "installingExtensions", + "error while installing extensions", + "installation failed", + { + "key": "updateExtensionsQuery", + "comment": [ + "Placeholder is for the count of extensions" + ] + }, + "updateExtensionsNoExtensions", + "updateExtensionsNewVersionsAvailable", + "errorUpdatingExtension", + "successUpdate", + "alreadyInstalled-checkAndUpdate", + "alreadyInstalled", + "alreadyInstalled", + "updateMessage", + "installing builtin with version", + "installing builtin ", + "installing with version", + "installing", + "errorInstallingExtension", + "successInstall", + "successVsixInstall", + "cancelVsixInstall", + "forceDowngrade", + "builtin", + "forceUninstall", + "uninstalling", + "successUninstallFromLocation", + "successUninstall", + "notInstalleddOnLocation", + "notInstalled" + ], + "vs/platform/extensionManagement/common/extensionsScannerService": [ + "fileReadFail", + "jsonParseFail", + "jsonParseInvalidType", + "jsonsParseReportErrors", + "jsonInvalidFormat", + "jsonsParseReportErrors", + "jsonInvalidFormat" + ], + "vs/platform/extensionManagement/node/extensionManagementService": [ + "incompatible", + "MarketPlaceDisabled", + "Not a Marketplace extension", + "removeError", + "errorDeleting", + "renameError", + "cannot read", + "restartCode", + "restartCode" + ], + "vs/platform/languagePacks/common/languagePacks": [ + "currentDisplayLanguage" + ], + "vs/platform/telemetry/common/telemetryService": [ + "telemetry.telemetryLevelMd", + "telemetry.docsStatement", + "telemetry.docsAndPrivacyStatement", + "telemetry.restart", + "telemetry.crashReports", + "telemetry.errors", + "telemetry.usage", + "telemetry.telemetryLevel.tableDescription", + "telemetry.telemetryLevel.deprecated", + "telemetryConfigurationTitle", + "telemetry.telemetryLevel.default", + "telemetry.telemetryLevel.error", + "telemetry.telemetryLevel.crash", + "telemetry.telemetryLevel.off", + "telemetryConfigurationTitle", + "telemetry.enableTelemetry", + "telemetry.enableTelemetryMd", + "enableTelemetryDeprecated" ], "vs/code/electron-sandbox/issue/issueReporterPage": [ "sendSystemInfo", @@ -1024,6 +1099,7 @@ "titleLengthValidation", "details", "descriptionEmptyValidation", + "descriptionTooShortValidation", "show", "extensionData", "show", @@ -1032,6 +1108,9 @@ "show", "show" ], + "vs/platform/userDataProfile/common/userDataProfile": [ + "defaultProfile" + ], "vs/code/electron-sandbox/issue/issueReporterService": [ "hide", "show", @@ -1142,8 +1221,18 @@ "defaultFindMatchTypeSettingKey.contiguous", "defaultFindMatchTypeSettingKey", "expand mode", + "sticky scroll", + "sticky scroll maximum items", "typeNavigationMode2" ], + "vs/platform/markers/common/markers": [ + "sev.error", + "sev.warning", + "sev.info" + ], + "vs/platform/contextkey/browser/contextKeyService": [ + "getContextKeyInfo" + ], "vs/platform/contextkey/common/contextkey": [ "contextkey.parser.error.emptyString", "contextkey.parser.error.emptyString.hint", @@ -1157,22 +1246,6 @@ "contextkey.scanner.errorForLinterWithHint", "contextkey.scanner.errorForLinter" ], - "vs/platform/contextkey/browser/contextKeyService": [ - "getContextKeyInfo" - ], - "vs/platform/markers/common/markers": [ - "sev.error", - "sev.warning", - "sev.info" - ], - "vs/workbench/browser/actions/textInputActions": [ - "undo", - "redo", - "cut", - "copy", - "paste", - "selectAll" - ], "vs/workbench/browser/workbench.contribution": [ "workbench.editor.titleScrollbarSizing.default", "workbench.editor.titleScrollbarSizing.large", @@ -1181,40 +1254,57 @@ "workbench.editor.showTabs.single", "workbench.editor.showTabs.none", "showEditorTabs", - "wrapTabs", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0} will be a setting name rendered as a link" ], - "key": "scrollToSwitchTabs" + "key": "workbench.editor.editorActionsLocation.default" }, - "highlightModifiedTabs", - "decorations.badges", - "decorations.colors", - "workbench.editor.labelFormat.default", - "workbench.editor.labelFormat.short", - "workbench.editor.labelFormat.medium", - "workbench.editor.labelFormat.long", { "comment": [ - "This is the description for a setting. Values surrounded by parenthesis are not to be translated." + "{0} will be a setting name rendered as a link" ], - "key": "tabDescription" + "key": "workbench.editor.editorActionsLocation.titleBar" }, - "workbench.editor.untitled.labelFormat.content", - "workbench.editor.untitled.labelFormat.name", + "workbench.editor.editorActionsLocation.hidden", + "editorActionsLocation", { "comment": [ - "This is the description for a setting. Values surrounded by parenthesis are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], - "key": "untitledLabelFormat" + "key": "wrapTabs" }, { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], - "key": "emptyEditorHint" + "key": "scrollToSwitchTabs" + }, + { + "comment": [ + "{0}, {1} will be a setting name rendered as a link" + ], + "key": "highlightModifiedTabs" }, + "decorations.badges", + "decorations.colors", + "workbench.editor.label.enabled", + "workbench.editor.label.patterns", + "workbench.editor.label.dirname", + "workbench.editor.label.nthdirname", + "workbench.editor.label.filename", + "workbench.editor.label.extname", + "customEditorLabelDescriptionExample", + "workbench.editor.label.template", + "workbench.editor.labelFormat.default", + "workbench.editor.labelFormat.short", + "workbench.editor.labelFormat.medium", + "workbench.editor.labelFormat.long", + "tabDescription", + "workbench.editor.untitled.labelFormat.content", + "workbench.editor.untitled.labelFormat.name", + "untitledLabelFormat", + "workbench.editor.empty.hint", "workbench.editor.languageDetection", "workbench.editor.historyBasedLanguageDetection", "workbench.editor.preferBasedLanguageDetection", @@ -1223,34 +1313,36 @@ "workbench.editor.showLanguageDetectionHints.notebook", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0} will be a setting name rendered as a link" ], - "key": "editorTabCloseButton" + "key": "tabActionLocation" }, + "workbench.editor.tabActionCloseVisibility", + "workbench.editor.tabActionUnpinVisibility", "workbench.editor.tabSizing.fit", "workbench.editor.tabSizing.shrink", "workbench.editor.tabSizing.fixed", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "tabSizing" }, { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "workbench.editor.tabSizingFixedMinWidth" }, { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "workbench.editor.tabSizingFixedMaxWidth" }, { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "workbench.editor.tabHeight" }, @@ -1259,11 +1351,16 @@ "workbench.editor.pinnedTabSizing.shrink", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "pinnedTabSizing" }, - "workbench.editor.pinnedTabsOnSeparateRow", + { + "comment": [ + "{0}, {1} will be a setting name rendered as a link" + ], + "key": "workbench.editor.pinnedTabsOnSeparateRow" + }, "workbench.editor.preventPinnedEditorClose.always", "workbench.editor.preventPinnedEditorClose.onlyKeyboard", "workbench.editor.preventPinnedEditorClose.onlyMouse", @@ -1272,22 +1369,28 @@ "workbench.editor.splitSizingAuto", "workbench.editor.splitSizingDistribute", "workbench.editor.splitSizingSplit", - { - "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." - ], - "key": "splitSizing" - }, + "splitSizing", "splitOnDragAndDrop", + "dragToOpenWindow", "focusRecentEditorAfterClose", "showIcons", "enablePreview", - "enablePreviewFromQuickOpen", - "enablePreviewFromCodeNavigation", + { + "comment": [ + "{0}, {1} will be a setting name rendered as a link" + ], + "key": "enablePreviewFromQuickOpen" + }, + { + "comment": [ + "{0}, {1} will be a setting name rendered as a link" + ], + "key": "enablePreviewFromCodeNavigation" + }, "closeOnFileDelete", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1}, {2}, {3} will be a setting name rendered as a link" ], "key": "editorOpenPositioning" }, @@ -1308,7 +1411,7 @@ "centeredLayoutDynamicWidth", { "comment": [ - "This is the description for a setting. Values surrounded by single quotes are not to be translated." + "{0}, {1} will be a setting name rendered as a link" ], "key": "doubleClickTabToToggleEditorGroupSizes" }, @@ -1349,10 +1452,16 @@ ], "key": "activityBarLocation" }, - "workbench.activityBar.location.side", + "workbench.activityBar.location.default", "workbench.activityBar.location.top", + "workbench.activityBar.location.bottom", "workbench.activityBar.location.hide", - "activityBarIconClickBehavior", + { + "comment": [ + "{0}, {1} will be a setting name rendered as a link" + ], + "key": "activityBarIconClickBehavior" + }, "workbench.activityBar.iconClickBehavior.toggle", "workbench.activityBar.iconClickBehavior.focus", "viewVisibility", @@ -1373,7 +1482,7 @@ { "key": "layoutControlEnabled", "comment": [ - "{0} is a placeholder for a setting identifier." + "{0}, {1} is a placeholder for a setting identifier." ] }, "layoutcontrol.type.menu", @@ -1398,6 +1507,8 @@ "remoteName", "dirty", "focusedView", + "activeRepositoryName", + "activeRepositoryBranchName", "separator", "windowConfigurationTitle", "window.titleSeparator", @@ -1405,7 +1516,7 @@ { "key": "window.commandCenter", "comment": [ - "{0} is a placeholder for a setting identifier." + "{0}, {1} is a placeholder for a setting identifier." ] }, "window.menuBarVisibility.classic", @@ -1417,7 +1528,7 @@ { "key": "window.menuBarVisibility.compact", "comment": [ - "{0} is a placeholder for a setting identifier." + "{0}, {1} is a placeholder for a setting identifier." ] }, "menuBarVisibility.mac", @@ -1442,6 +1553,7 @@ "window.confirmBeforeClose.never", "confirmBeforeCloseWeb", "confirmBeforeClose", + "problems.visibility", "zenModeConfigurationTitle", "zenMode.fullScreen", "zenMode.centerLayout", @@ -1455,115 +1567,123 @@ "zenMode.restore", "zenMode.silentNotifications" ], + "vs/workbench/browser/actions/textInputActions": [ + "undo", + "redo", + "cut", + "copy", + "paste", + "selectAll" + ], + "vs/workbench/browser/actions/developerActions": [ + "storageLogDialogMessage", + "storageLogDialogDetails", + "largeStorageItemDetail", + "global", + "profile", + "workspace", + "machine", + "user", + "removeLargeStorageEntriesPickerButton", + "removeLargeStorageEntriesPickerPlaceholder", + "removeLargeStorageEntriesPickerDescriptionNoEntries", + "removeLargeStorageEntriesConfirmRemove", + "removeLargeStorageEntriesConfirmRemoveDetail", + { + "key": "removeLargeStorageEntriesButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "screencastModeConfigurationTitle", + "screencastMode.location.verticalPosition", + "screencastMode.fontSize", + "screencastMode.keyboardOptions.description", + "screencastMode.keyboardOptions.showKeys", + "screencastMode.keyboardOptions.showKeybindings", + "screencastMode.keyboardOptions.showCommands", + "screencastMode.keyboardOptions.showCommandGroups", + "screencastMode.keyboardOptions.showSingleEditorCursorMoves", + "screencastMode.keyboardOverlayTimeout", + "screencastMode.mouseIndicatorColor", + "screencastMode.mouseIndicatorSize", + "inspect context keys", + "toggle screencast mode", + { + "key": "logStorage", + "comment": [ + "A developer only action to log the contents of the storage for the current window." + ] + }, + { + "key": "logWorkingCopies", + "comment": [ + "A developer only action to log the working copies that exist." + ] + }, + "removeLargeStorageDatabaseEntries", + "startTrackDisposables", + "snapshotTrackedDisposables", + "stopTrackDisposables" + ], "vs/workbench/browser/actions/helpActions": [ - "keybindingsReference", { "key": "miKeyboardShortcuts", "comment": [ "&& denotes a mnemonic" ] }, - "openVideoTutorialsUrl", { "key": "miVideoTutorials", "comment": [ "&& denotes a mnemonic" ] }, - "openTipsAndTricksUrl", { "key": "miTipsAndTricks", "comment": [ "&& denotes a mnemonic" ] }, - "openDocumentationUrl", { "key": "miDocumentation", "comment": [ "&& denotes a mnemonic" ] }, - "newsletterSignup", - "openYouTubeUrl", { "key": "miYouTube", "comment": [ "&& denotes a mnemonic" ] }, - "openUserVoiceUrl", { "key": "miUserVoice", "comment": [ "&& denotes a mnemonic" ] }, - "openLicenseUrl", { "key": "miLicense", "comment": [ "&& denotes a mnemonic" ] }, - "openPrivacyStatement", { "key": "miPrivacyStatement", "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/browser/actions/developerActions": [ - "inspect context keys", - "toggle screencast mode", - { - "key": "logStorage", - "comment": [ - "A developer only action to log the contents of the storage for the current window." - ] - }, - "storageLogDialogMessage", - "storageLogDialogDetails", - { - "key": "logWorkingCopies", - "comment": [ - "A developer only action to log the working copies that exist." - ] }, - "removeLargeStorageDatabaseEntries", - "largeStorageItemDetail", - "global", - "profile", - "workspace", - "machine", - "user", - "removeLargeStorageEntriesPickerButton", - "removeLargeStorageEntriesPickerPlaceholder", - "removeLargeStorageEntriesPickerDescriptionNoEntries", - "removeLargeStorageEntriesConfirmRemove", - "removeLargeStorageEntriesConfirmRemoveDetail", - { - "key": "removeLargeStorageEntriesButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "startTrackDisposables", - "snapshotTrackedDisposables", - "stopTrackDisposables", - "screencastModeConfigurationTitle", - "screencastMode.location.verticalPosition", - "screencastMode.fontSize", - "screencastMode.keyboardOptions.description", - "screencastMode.keyboardOptions.showKeys", - "screencastMode.keyboardOptions.showKeybindings", - "screencastMode.keyboardOptions.showCommands", - "screencastMode.keyboardOptions.showCommandGroups", - "screencastMode.keyboardOptions.showSingleEditorCursorMoves", - "screencastMode.keyboardOverlayTimeout", - "screencastMode.mouseIndicatorColor", - "screencastMode.mouseIndicatorSize" + "keybindingsReference", + "openVideoTutorialsUrl", + "openTipsAndTricksUrl", + "openDocumentationUrl", + "newsletterSignup", + "openYouTubeUrl", + "openUserVoiceUrl", + "openLicenseUrl", + "openPrivacyStatement" ], "vs/workbench/browser/actions/layoutActions": [ "menuBarIcon", @@ -1582,21 +1702,15 @@ "fullScreenIcon", "centerLayoutIcon", "zenModeIcon", - "closeSidebar", - "toggleActivityBar", - "toggleCenteredLayout", { "key": "miToggleCenteredLayout", "comment": [ "&& denotes a mnemonic" ] }, - "moveSidebarRight", - "moveSidebarLeft", "toggleSidebarPosition", "moveSidebarRight", "moveSidebarLeft", - "toggleSidebarPosition", "cofigureLayoutIcon", "configureLayout", "move side bar right", @@ -1617,7 +1731,6 @@ "&& denotes a mnemonic" ] }, - "toggleEditor", { "key": "miShowEditorArea", "comment": [ @@ -1630,7 +1743,6 @@ "&& denotes a mnemonic" ] }, - "toggleSidebar", "primary sidebar", { "key": "primary sidebar mnemonic", @@ -1642,26 +1754,21 @@ "compositePart.hideSideBarLabel", "toggleSideBar", "toggleSideBar", - "toggleStatusbar", { "key": "miStatusbar", "comment": [ "&& denotes a mnemonic" ] }, - "hideEditorTabs", - "showMultipleEditorTabs", - "showSingleEditorTab", "tabBar", - "toggleSeparatePinnedEditorTabs", - "toggleZenMode", + "tabBar", + "editorActionsPosition", { "key": "miToggleZenMode", "comment": [ "&& denotes a mnemonic" ] }, - "toggleMenuBar", { "key": "miMenuBar", "comment": [ @@ -1669,13 +1776,10 @@ ] }, "miMenuBarNoMnemonic", - "resetViewLocations", - "moveView", "sidebarContainer", "panelContainer", "secondarySideBarContainer", "moveFocusedView.selectView", - "moveFocusedView", "moveFocusedView.error.noFocusedView", "moveFocusedView.error.nonMovableView", "moveFocusedView.selectDestination", @@ -1696,14 +1800,7 @@ "sidebar", "panel", "secondarySideBar", - "resetFocusedViewLocation", "resetFocusedView.error.noFocusedView", - "increaseViewSize", - "increaseEditorWidth", - "increaseEditorHeight", - "decreaseViewSize", - "decreaseEditorWidth", - "decreaseEditorHeight", "selectToHide", "selectToShow", "active", @@ -1722,14 +1819,56 @@ "fullscreen", "zenMode", "centeredLayout", - "customizeLayout", "toggleVisibility", "sideBarPosition", "panelAlignment", "layoutModes", "customizeLayoutQuickPickTitle", "close", - "restore defaults" + "restore defaults", + "closeSidebar", + "toggleCenteredLayout", + "moveSidebarRight", + "moveSidebarLeft", + "toggleSidebarPosition", + "toggleEditor", + "toggleSidebar", + "toggleStatusbar", + "hideEditorTabs", + "hideEditorTabsDescription", + "hideEditorTabsZenMode", + "hideEditorTabsZenModeDescription", + "showMultipleEditorTabs", + "showMultipleEditorTabsDescription", + "showMultipleEditorTabsZenMode", + "showMultipleEditorTabsZenModeDescription", + "showSingleEditorTab", + "showSingleEditorTabDescription", + "showSingleEditorTabZenMode", + "showSingleEditorTabZenModeDescription", + "moveEditorActionsToTitleBar", + "moveEditorActionsToTitleBarDescription", + "moveEditorActionsToTabBar", + "moveEditorActionsToTabBarDescription", + "hideEditorActons", + "hideEditorActonsDescription", + "showEditorActons", + "showEditorActonsDescription", + "toggleSeparatePinnedEditorTabs", + "toggleSeparatePinnedEditorTabsDescription", + "toggleZenMode", + "toggleMenuBar", + "resetViewLocations", + "moveView", + "moveFocusedView", + "resetFocusedViewLocation", + "increaseViewSize", + "increaseEditorWidth", + "increaseEditorHeight", + "decreaseViewSize", + "decreaseEditorWidth", + "decreaseEditorHeight", + "customizeLayout" ], "vs/workbench/browser/actions/navigationActions": [ "navigateLeft", @@ -1739,6 +1878,16 @@ "focusNextPart", "focusPreviousPart" ], + "vs/workbench/browser/actions/listCommands": [ + { + "key": "mitoggleTreeStickyScroll", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "toggleTreeStickyScrollDescription", + "toggleTreeStickyScroll" + ], "vs/workbench/browser/actions/windowActions": [ "remove", "dirtyRecentlyOpenedFolder", @@ -1756,57 +1905,57 @@ "dirtyFolderConfirmDetail", "recentDirtyWorkspaceAriaLabel", "recentDirtyFolderAriaLabel", - "openRecent", { "key": "miMore", "comment": [ "&& denotes a mnemonic" ] }, - "quickOpenRecent", - "toggleFullScreen", { "key": "miToggleFullScreen", "comment": [ "&& denotes a mnemonic" ] }, - "reloadWindow", - "about", { "key": "miAbout", "comment": [ "&& denotes a mnemonic" ] }, - "newWindow", { "key": "miNewWindow", "comment": [ "&& denotes a mnemonic" ] }, - "blur", "miConfirmClose", { "key": "miOpenRecent", "comment": [ "&& denotes a mnemonic" ] - } + }, + "openRecent", + "quickOpenRecent", + "toggleFullScreen", + "reloadWindow", + "about", + "newWindow", + "blur" + ], + "vs/workbench/browser/actions/workspaceCommands": [ + { + "key": "add", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "addFolderToWorkspaceTitle", + "workspaceFolderPickerPlaceholder", + "addFolderToWorkspace" ], "vs/workbench/browser/actions/workspaceActions": [ - "workspaces", - "openFile", - "openFolder", - "openFolder", - "openFileFolder", - "openWorkspaceAction", - "closeWorkspace", - "openWorkspaceConfigFile", - "globalRemoveFolderFromWorkspace", - "saveWorkspaceAsAction", - "duplicateWorkspaceInNewWindow", { "key": "miOpenFile", "comment": [ @@ -1856,22 +2005,22 @@ "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/browser/actions/workspaceCommands": [ - "addFolderToWorkspace", - { - "key": "add", - "comment": [ - "&& denotes a mnemonic" - ] }, - "addFolderToWorkspaceTitle", - "workspaceFolderPickerPlaceholder" + "workspaces", + "openFile", + "openFolder", + "openFolder", + "openFileFolder", + "openWorkspaceAction", + "closeWorkspace", + "openWorkspaceConfigFile", + "globalRemoveFolderFromWorkspace", + "saveWorkspaceAsAction", + "duplicateWorkspaceInNewWindow" ], "vs/workbench/browser/actions/quickAccessActions": [ - "quickOpen", "quickOpenWithModes", + "quickOpen", "quickNavigateNext", "quickNavigatePrevious", "quickSelectNext", @@ -1892,14 +2041,25 @@ "menus.debugCallstackContext", "menus.debugVariablesContext", "menus.debugToolBar", + "menus.notebookVariablesContext", "menus.home", "menus.opy", "menus.scmTitle", "menus.scmSourceControl", + "menus.scmSourceControlTitle", "menus.resourceStateContext", "menus.resourceFolderContext", "menus.resourceGroupContext", "menus.changeTitle", + "menus.input", + "menus.incomingChanges", + "menus.incomingChangesContext", + "menus.outgoingChanges", + "menus.outgoingChangesContext", + "menus.incomingChangesAllChangesContext", + "menus.incomingChangesHistoryItemContext", + "menus.outgoingChangesAllChangesContext", + "menus.outgoingChangesHistoryItemContext", "menus.statusBarRemoteIndicator", "menus.terminalContext", "menus.terminalTabContext", @@ -1913,14 +2073,17 @@ "comment.title", "comment.actions", "comment.commentContext", + "commentsView.threadActions", "notebook.toolbar", "notebook.kernelSource", "notebook.cell.title", "notebook.cell.execute", "interactive.toolbar", "interactive.cell.title", + "issue.reporter", "testing.item.context", "testing.item.gutter.title", + "testing.item.result.title", "testing.message.context.title", "testing.message.content.title", "menus.extensionContext", @@ -1933,9 +2096,13 @@ "webview.context", "menus.share", "inlineCompletions.actions", + "inlineEdit.actions", "merge.toolbar", "editorLineNumberContext", "menus.mergeEditorResult", + "menus.multiDiffEditorResource", + "menus.diffEditorGutterToolBarMenus", + "menus.diffEditorGutterToolBarMenus", "requirestring", "optstring", "optstring", @@ -1999,7 +2166,12 @@ "dupe.command", "unsupported.submenureference", "missing.submenu", - "submenuItem.duplicate" + "submenuItem.duplicate", + "command name", + "command title", + "keyboard shortcuts", + "menuContexts", + "commands" ], "vs/workbench/api/common/configurationExtensionPoint": [ "vscode.extension.contributes.configuration.title", @@ -2043,10 +2215,11 @@ "workspaceConfig.extensions.description", "workspaceConfig.remoteAuthority", "workspaceConfig.transient", - "unknownWorkspaceProperty" - ], - "vs/workbench/browser/parts/editor/editorParts": [ - "groupLabel" + "unknownWorkspaceProperty", + "setting name", + "description", + "default", + "settings" ], "vs/workbench/api/browser/viewsExtensionPoint": [ { @@ -2073,6 +2246,7 @@ "vscode.extension.contributes.view.initialState.hidden", "vscode.extension.contributes.view.initialState.collapsed", "vscode.extension.contributs.view.size", + "vscode.extension.contributes.view.accessibilityHelpContent", "vscode.extension.contributes.view.id", "vscode.extension.contributes.view.name", "vscode.extension.contributes.view.when", @@ -2102,7 +2276,15 @@ "optstring", "optstring", "optstring", - "optenum" + "optenum", + "view container id", + "view container title", + "view container location", + "view id", + "view name title", + "view container location", + "viewsContainers", + "views" ], "vs/workbench/browser/parts/editor/editor.contribution": [ "textEditor", @@ -2115,22 +2297,34 @@ "allEditorsByAppearanceQuickAccess", "editorQuickAccessPlaceholder", "allEditorsByMostRecentlyUsedQuickAccess", + "lockGroupAction", "unlockGroupAction", "closeGroupAction", "splitUp", "splitDown", "splitLeft", "splitRight", + "newWindow", "toggleLockGroup", "close", "splitUp", "splitDown", "splitLeft", "splitRight", + "moveEditorGroupToNewWindow", + "copyEditorGroupToNewWindow", + "tabBar", + "multipleTabs", + "singleTab", + "hideTabs", "tabBar", "multipleTabs", "singleTab", - "hideTabBar", + "hideTabs", + "editorActionsPosition", + "tabBar", + "titleBar", + "hidden", "close", "closeOthers", "closeRight", @@ -2146,6 +2340,8 @@ "splitRight", "splitInGroup", "joinInGroup", + "moveToNewWindow", + "copyToNewWindow", "inlineView", "showOpenedEditors", "closeAll", @@ -2167,24 +2363,15 @@ "close", "unpin", "close", + "lockEditorGroup", "unlockEditorGroup", "previousChangeIcon", - "nextChangeIcon", - "toggleWhitespace", "navigate.prev.label", + "nextChangeIcon", "navigate.next.label", + "swapDiffSides", + "toggleWhitespace", "ignoreTrimWhitespace.label", - "keepEditor", - "pinEditor", - "unpinEditor", - "closeEditor", - "closePinnedEditor", - "closeEditorsInGroup", - "closeSavedEditors", - "closeOtherEditors", - "closeRightEditors", - "closeEditorGroup", - "reopenWith", { "key": "miReopenClosedEditor", "comment": [ @@ -2204,98 +2391,96 @@ "&& denotes a mnemonic" ] }, - "miSplitEditorUpWithoutMnemonic", { "key": "miSplitEditorUp", "comment": [ "&& denotes a mnemonic" ] }, - "miSplitEditorDownWithoutMnemonic", { "key": "miSplitEditorDown", "comment": [ "&& denotes a mnemonic" ] }, - "miSplitEditorLeftWithoutMnemonic", { "key": "miSplitEditorLeft", "comment": [ "&& denotes a mnemonic" ] }, - "miSplitEditorRightWithoutMnemonic", { "key": "miSplitEditorRight", "comment": [ "&& denotes a mnemonic" ] }, - "miSplitEditorInGroupWithoutMnemonic", { "key": "miSplitEditorInGroup", "comment": [ "&& denotes a mnemonic" ] }, - "miJoinEditorInGroupWithoutMnemonic", { "key": "miJoinEditorInGroup", "comment": [ "&& denotes a mnemonic" ] }, - "miSingleColumnEditorLayoutWithoutMnemonic", + { + "key": "miMoveEditorToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miCopyEditorToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, { "key": "miSingleColumnEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miTwoColumnsEditorLayoutWithoutMnemonic", { "key": "miTwoColumnsEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miThreeColumnsEditorLayoutWithoutMnemonic", { "key": "miThreeColumnsEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miTwoRowsEditorLayoutWithoutMnemonic", { "key": "miTwoRowsEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miThreeRowsEditorLayoutWithoutMnemonic", { "key": "miThreeRowsEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miTwoByTwoGridEditorLayoutWithoutMnemonic", { "key": "miTwoByTwoGridEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miTwoRowsRightEditorLayoutWithoutMnemonic", { "key": "miTwoRowsRightEditorLayout", "comment": [ "&& denotes a mnemonic" ] }, - "miTwoColumnsBottomEditorLayoutWithoutMnemonic", { "key": "miTwoColumnsBottomEditorLayout", "comment": [ @@ -2445,28 +2630,44 @@ "comment": [ "&& denotes a mnemonic" ] - } + }, + "keepEditor", + "pinEditor", + "unpinEditor", + "closeEditor", + "closePinnedEditor", + "closeEditorsInGroup", + "closeSavedEditors", + "closeOtherEditors", + "closeRightEditors", + "closeEditorGroup", + "reopenWith", + "miSplitEditorUpWithoutMnemonic", + "miSplitEditorDownWithoutMnemonic", + "miSplitEditorLeftWithoutMnemonic", + "miSplitEditorRightWithoutMnemonic", + "miSplitEditorInGroupWithoutMnemonic", + "miJoinEditorInGroupWithoutMnemonic", + "moveEditorToNewWindow", + "copyEditorToNewWindow", + "miSingleColumnEditorLayoutWithoutMnemonic", + "miTwoColumnsEditorLayoutWithoutMnemonic", + "miThreeColumnsEditorLayoutWithoutMnemonic", + "miTwoRowsEditorLayoutWithoutMnemonic", + "miThreeRowsEditorLayoutWithoutMnemonic", + "miTwoByTwoGridEditorLayoutWithoutMnemonic", + "miTwoRowsRightEditorLayoutWithoutMnemonic", + "miTwoColumnsBottomEditorLayoutWithoutMnemonic" ], "vs/workbench/browser/parts/banner/bannerPart": [ "focusBanner" ], + "vs/workbench/browser/parts/editor/editorParts": [ + "groupLabel" + ], "vs/workbench/browser/parts/statusbar/statusbarPart": [ "hideStatusBar" ], - "vs/workbench/browser/parts/views/viewsService": [ - "editor", - "show view", - "toggle view", - "show view", - "toggle view", - { - "key": "focus view", - "comment": [ - "{0} indicates the name of the view to be focused." - ] - }, - "resetViewLocation" - ], "vs/platform/undoRedo/common/undoRedoService": [ { "key": "externalRemoval", @@ -2583,22 +2784,8 @@ "&& denotes a mnemonic" ] }, - "installAndHandle", "installDetail", - { - "key": "install and open", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "Installing", - "enableAndHandle", - { - "key": "enableAndReload", - "comment": [ - "&& denotes a mnemonic" - ] - }, + "openUri", "reloadAndHandle", { "key": "reloadAndOpen", @@ -2606,9 +2793,9 @@ "&& denotes a mnemonic" ] }, + "no", "manage", - "extensions", - "no" + "extensions" ], "vs/workbench/services/keybinding/common/keybindingEditing": [ "errorKeybindingsFileDirty", @@ -2700,7 +2887,13 @@ "vscode.extension.contributes.languages.icon", "vscode.extension.contributes.languages.icon.light", "vscode.extension.contributes.languages.icon.dark", - "invalid", + "language id", + "language name", + "file extensions", + "grammar", + "snippets", + "languages", + "invalid", "invalid.empty", "require.id", "opt.extensions", @@ -2750,20 +2943,16 @@ "workspace folder", "workspace" ], + "vs/workbench/services/extensionManagement/common/extensionFeaturesManagemetService": [ + "accessExtensionFeature", + "accessExtensionFeatureMessage", + "allow", + "disallow" + ], "vs/workbench/services/notification/common/notificationService": [ "neverShowAgain", "neverShowAgain" ], - "vs/workbench/services/userDataProfile/browser/userDataProfileManagement": [ - "reload message when updated", - "reload message when removed", - "reload message when removed", - "cannotRenameDefaultProfile", - "cannotDeleteDefaultProfile", - "switch profile", - "reload message", - "reload button" - ], "vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService": [ "profile import error", "resolving uri", @@ -2870,6 +3059,16 @@ "export profile title", "profile name required" ], + "vs/workbench/services/userDataProfile/browser/userDataProfileManagement": [ + "reload message when updated", + "reload message when removed", + "reload message when removed", + "cannotRenameDefaultProfile", + "cannotDeleteDefaultProfile", + "switch profile", + "reload message", + "reload button" + ], "vs/workbench/services/remote/common/remoteExplorerService": [ "getStartedWalkthrough.id", "RemoteHelpInformationExtPoint", @@ -2909,6 +3108,20 @@ "hideView", "resetViewLocation" ], + "vs/workbench/services/views/browser/viewsService": [ + "editor", + "show view", + "toggle view", + "show view", + "toggle view", + { + "key": "focus view", + "comment": [ + "{0} indicates the name of the view to be focused." + ] + }, + "resetViewLocation" + ], "vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService": [ "no authentication providers", "no account", @@ -2962,19 +3175,11 @@ "sign in using account" ], "vs/workbench/services/authentication/browser/authenticationService": [ - "authentication.id", - "authentication.label", - { - "key": "authenticationExtensionPoint", - "comment": [ - "'Contributes' means adds here" - ] - }, - "authentication.Placeholder", "authentication.missingId", "authentication.missingLabel", - "authentication.idConflict", - "loading", + "authentication.idConflict" + ], + "vs/workbench/services/authentication/browser/authenticationExtensionsService": [ "sign in", "confirmAuthenticationAccess", { @@ -3060,34 +3265,15 @@ ] } ], - "vs/workbench/contrib/performance/browser/performance.contribution": [ - "show.label", - "cycles", - "insta.trace", - "emitter" - ], "vs/workbench/contrib/preferences/browser/preferences.contribution": [ "settingsEditor2", "keybindingsEditor", - "openSettings2", - "openUserSettingsJson", - "openApplicationSettingsJson", - "preferences", - "settings", { "key": "miOpenSettings", "comment": [ "&& denotes a mnemonic" ] }, - "openSettings2", - "openGlobalSettings", - "openRawDefaultSettings", - "openWorkspaceSettings", - "openAccessibilitySettings", - "openWorkspaceSettingsFile", - "openFolderSettings", - "openFolderSettingsFile", "openFolderSettings", { "key": "miOpenOnlineSettings", @@ -3095,43 +3281,63 @@ "&& denotes a mnemonic" ] }, - "filterUntrusted", { "key": "miOpenTelemetrySettings", "comment": [ "&& denotes a mnemonic" ] }, + "settings.focusFile", + "settings.focusFile", + "settings.focusSettingsList", + "settings.focusSettingControl", + "keyboardShortcuts", + "keyboardShortcuts", + "clear", + "clearHistory", + { + "key": "miPreferences", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "openSettings2", + "openUserSettingsJson", + "openApplicationSettingsJson", + "preferences", + "settings", + "openSettings2", + "workbench.action.openSettingsJson.description", + "openGlobalSettings", + "openRawDefaultSettings", + "openWorkspaceSettings", + "openAccessibilitySettings", + "openWorkspaceSettingsFile", + "openFolderSettings", + "openFolderSettingsFile", + "filterUntrusted", "openRemoteSettings", "openRemoteSettingsJSON", "settings.focusSearch", "settings.clearResults", - "settings.focusFile", - "settings.focusFile", - "settings.focusSettingsList", "settings.focusSettingsTOC", - "settings.focusSettingControl", "settings.showContextMenu", "settings.focusLevelUp", "preferences", "openGlobalKeybindings", - "keyboardShortcuts", - "keyboardShortcuts", "openDefaultKeybindingsFile", "openGlobalKeybindingsFile", "showDefaultKeybindings", "showExtensionKeybindings", "showUserKeybindings", - "clear", - "clearHistory", "defineKeybinding.start", - "openSettingsJson", - { - "key": "miPreferences", - "comment": [ - "&& denotes a mnemonic" - ] - } + "openSettingsJson" + ], + "vs/workbench/contrib/performance/browser/performance.contribution": [ + "show.label", + "cycles", + "insta.trace", + "emitter" ], "vs/workbench/contrib/chat/browser/chat.contribution": [ "interactiveSessionConfigurationTitle", @@ -3140,9 +3346,11 @@ "interactiveSession.editor.fontWeight", "interactiveSession.editor.wordWrap", "interactiveSession.editor.lineHeight", + "chat.experimental.implicitContext", "chat", "chat", - "clear" + "clear", + "file" ], "vs/workbench/contrib/notebook/browser/notebook.contribution": [ "notebook.editorOptions.experimentalCustomization", @@ -3166,7 +3374,10 @@ "insertToolbarLocation.both", "insertToolbarLocation.hidden", "notebook.globalToolbar.description", - "notebook.stickyScroll.description", + "notebook.stickyScrollEnabled.description", + "notebook.stickyScrollMode.description", + "notebook.stickyScrollMode.flat", + "notebook.stickyScrollMode.indented", "notebook.consolidatedOutputButton.description", "notebook.showFoldingControls.description", "showFoldingControls.always", @@ -3176,6 +3387,8 @@ "notebook.consolidatedRunButton.description", "notebook.globalToolbarShowLabel", "notebook.textOutputLineLimit", + "notebook.disableOutputFilePathLinks", + "notebook.minimalErrorRendering", "notebook.markup.fontSize", "notebook.interactiveWindow.collapseCodeCells", "notebook.outputLineHeight", @@ -3184,6 +3397,7 @@ "notebook.outputScrolling", "notebook.outputWordWrap", "notebook.formatOnSave", + "notebook.insertFinalNewline", "notebook.codeActionsOnSave", "explicit", "never", @@ -3197,45 +3411,28 @@ "notebook.scrolling.revealNextCellOnExecute.fullCell.description", "notebook.scrolling.revealNextCellOnExecute.firstLine.description", "notebook.scrolling.revealNextCellOnExecute.none.description", - "notebook.scrolling.anchorToFocusedCell.description", - "notebook.scrolling.anchorToFocusedCell.auto.description", - "notebook.scrolling.anchorToFocusedCell.on.description", - "notebook.scrolling.anchorToFocusedCell.off.description" - ], - "vs/workbench/contrib/testing/browser/testing.contribution": [ - "test", - { - "key": "miViewTesting", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "noTestProvidersRegistered", - "searchForAdditionalTestExtensions", - "testResultsPanelName", - "testResultsPanelName", - "testExplorer" - ], - "vs/workbench/contrib/logs/common/logs.contribution": [ - "setDefaultLogLevel", - "remote name", - "show window log" + "notebook.cellChat", + "notebook.cellGenerate", + "notebook.VariablesView.description", + "notebook.cellFailureDiagnostics", + "notebook.backup.sizeLimit" ], "vs/workbench/contrib/interactive/browser/interactive.contribution": [ - "interactiveWindow", "interactive.open", + "interactiveScrollToTop", + "interactiveScrollToBottom", + "interactive.activeCodeBorder", + "interactive.inactiveCodeBorder", + "interactiveWindow.alwaysScrollOnNewCell", + "interactiveWindow.promptToSaveOnClose", + "interactiveWindow", "interactive.open", "interactive.execute", "interactive.input.clear", "interactive.history.previous", "interactive.history.next", - "interactiveScrollToTop", - "interactiveScrollToBottom", "interactive.input.focus", - "interactive.history.focus", - "interactive.activeCodeBorder", - "interactive.inactiveCodeBorder", - "interactiveWindow.alwaysScrollOnNewCell" + "interactive.history.focus" ], "vs/workbench/contrib/quickaccess/browser/quickAccess.contribution": [ "helpQuickAccessPlaceholder", @@ -3272,18 +3469,78 @@ "commandPalette", "commandPalette" ], + "vs/workbench/contrib/testing/browser/testing.contribution": [ + { + "key": "miViewTesting", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "noTestProvidersRegistered", + "searchForAdditionalTestExtensions", + "test", + "testResultsPanelName", + "testResultsPanelName", + "testExplorer", + "testCoverage" + ], + "vs/workbench/contrib/files/browser/explorerViewlet": [ + "explorerViewIcon", + "openEditorsIcon", + { + "key": "miViewExplorer", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "openFolder", + "addAFolder", + "openRecent", + { + "key": "noWorkspaceHelp", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change" + ] + }, + { + "key": "noFolderHelpWeb", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change" + ] + }, + { + "key": "remoteNoFolderHelp", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change" + ] + }, + { + "key": "noFolderButEditorsHelp", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change" + ] + }, + { + "key": "noFolderHelp", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change" + ] + }, + "folders", + "explore", + "explore" + ], + "vs/workbench/contrib/logs/common/logs.contribution": [ + "remote name", + "setDefaultLogLevel", + "show window log" + ], "vs/workbench/contrib/files/browser/fileActions.contribution": [ "copyPath", "copyRelativePath", "revealInSideBar", "acceptLocalChanges", "revertLocalChanges", - "copyPathOfActive", - "copyRelativePathOfActive", - "saveAllInGroup", - "saveFiles", - "revert", - "compareActiveWithSaved", "openToSide", "reopenWith", "revert", @@ -3350,53 +3607,54 @@ "&& denotes a mnemonic" ] }, + "copyPathOfActive", + "copyRelativePathOfActive", + "saveAllInGroup", + "saveFiles", + "revert", + "compareActiveWithSaved", + "compareActiveWithSavedMeta", "newFolderDescription" ], - "vs/workbench/contrib/files/browser/explorerViewlet": [ - "explorerViewIcon", - "openEditorsIcon", + "vs/workbench/contrib/bulkEdit/browser/bulkEditService": [ + "summary.0", + "summary.nm", + "summary.n0", + "summary.textFiles", + "workspaceEdit", + "workspaceEdit", + "nothing", + "closeTheWindow.message", { - "key": "miViewExplorer", + "key": "closeTheWindow", "comment": [ "&& denotes a mnemonic" ] }, - "openFolder", - "addAFolder", - "openRecent", - { - "key": "noWorkspaceHelp", - "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" - ] - }, + "changeWorkspace.message", { - "key": "noFolderHelpWeb", + "key": "changeWorkspace", "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" + "&& denotes a mnemonic" ] }, + "reloadTheWindow.message", { - "key": "remoteNoFolderHelp", + "key": "reloadTheWindow", "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" + "&& denotes a mnemonic" ] }, + "quit.message", { - "key": "noFolderButEditorsHelp", + "key": "quit", "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" + "&& denotes a mnemonic" ] }, - { - "key": "noFolderHelp", - "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change" - ] - }, - "folders", - "explore", - "explore" + "areYouSureQuiteBulkEdit.detail", + "fileOperation", + "refactoring.autoSave" ], "vs/workbench/contrib/files/browser/files.contribution": [ "textFileEditor", @@ -3428,6 +3686,7 @@ "eol", "useTrash", "trimTrailingWhitespace", + "trimTrailingWhitespaceInRegexAndStrings", "insertFinalNewline", "trimFinalNewlines", { @@ -3466,6 +3725,18 @@ ], "key": "autoSaveDelay" }, + { + "comment": [ + "This is the description for a setting. Values surrounded by single quotes are not to be translated." + ], + "key": "autoSaveWorkspaceFilesOnly" + }, + { + "comment": [ + "This is the description for a setting. Values surrounded by single quotes are not to be translated." + ], + "key": "autoSaveWhenNoErrors" + }, "watcherExclude", "watcherInclude", "defaultLanguage", @@ -3531,6 +3802,7 @@ "explorer.autoRevealExclude.when", "enableDragAndDrop", "confirmDragAndDrop", + "confirmPasteNative", "confirmDelete", "enableUndo", "confirmUndo", @@ -3567,46 +3839,6 @@ "fileNestingPatterns", "fileNesting.description" ], - "vs/workbench/contrib/bulkEdit/browser/bulkEditService": [ - "summary.0", - "summary.nm", - "summary.n0", - "summary.textFiles", - "workspaceEdit", - "workspaceEdit", - "nothing", - "closeTheWindow.message", - { - "key": "closeTheWindow", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "changeWorkspace.message", - { - "key": "changeWorkspace", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "reloadTheWindow.message", - { - "key": "reloadTheWindow", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "quit.message", - { - "key": "quit", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "areYouSureQuiteBulkEdit.detail", - "fileOperation", - "refactoring.autoSave" - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution": [ "overlap", "detail", @@ -3693,6 +3925,9 @@ "search.searchEditor.doubleClickBehaviour.goToLocation", "search.searchEditor.doubleClickBehaviour.openLocationToSide", "search.searchEditor.doubleClickBehaviour", + "search.searchEditor.singleClickBehaviour.default", + "search.searchEditor.singleClickBehaviour.peekDefinition", + "search.searchEditor.singleClickBehaviour", { "key": "search.searchEditor.reusePriorSearchConfiguration", "comment": [ @@ -3712,14 +3947,77 @@ "scm.defaultViewMode.tree", "scm.defaultViewMode.list", "search.defaultViewMode", + "search.quickAccess.preserveInput", "search.experimental.closedNotebookResults", - "search.experimental.quickAccess.preserveInput", "search", "search" ], + "vs/workbench/contrib/search/browser/searchView": [ + "searchCanceled", + "moreSearch", + "searchScope.includes", + "placeholder.includes", + "searchScope.excludes", + "placeholder.excludes", + "replaceAll.confirmation.title", + { + "key": "replaceAll.confirm.button", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "replaceAll.occurrence.file.message", + "removeAll.occurrence.file.message", + "replaceAll.occurrence.files.message", + "removeAll.occurrence.files.message", + "replaceAll.occurrences.file.message", + "removeAll.occurrences.file.message", + "replaceAll.occurrences.files.message", + "removeAll.occurrences.files.message", + "removeAll.occurrence.file.confirmation.message", + "replaceAll.occurrence.file.confirmation.message", + "removeAll.occurrence.files.confirmation.message", + "replaceAll.occurrence.files.confirmation.message", + "removeAll.occurrences.file.confirmation.message", + "replaceAll.occurrences.file.confirmation.message", + "removeAll.occurrences.files.confirmation.message", + "replaceAll.occurrences.files.confirmation.message", + "emptySearch", + "searchPathNotFoundError", + "noOpenEditorResultsIncludesExcludes", + "noOpenEditorResultsIncludes", + "noOpenEditorResultsExcludes", + "noOpenEditorResultsFound", + "noResultsIncludesExcludes", + "noResultsIncludes", + "noResultsExcludes", + "noResultsFound", + "rerunSearch.message", + "rerunSearchInAll.message", + "openSettings.message", + "openSettings.learnMore", + "ariaSearchResultsStatus", + "searchMaxResultsWarning", + "forTerm", + "useIgnoresAndExcludesDisabled", + "excludes.enable", + "useExcludesAndIgnoreFilesDescription", + "onlyOpenEditors", + "openEditors.disable", + "disableOpenEditors", + "openInEditor.tooltip", + "openInEditor.message", + "search.file.result", + "search.files.result", + "search.file.results", + "search.files.results", + "searchWithoutFolder", + "openFolder" + ], "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ "searchEditor", "promptOpenWith.searchEditor.displayName", + "search.openNewEditor", "search", "searchEditor.deleteResultBlock", "search.openNewSearchEditor", @@ -3736,8 +4034,7 @@ "searchEditor.action.toggleSearchEditorContextLines", "searchEditor.action.increaseSearchEditorContextLines", "searchEditor.action.decreaseSearchEditorContextLines", - "searchEditor.action.selectAllSearchEditorMatches", - "search.openNewEditor" + "searchEditor.action.selectAllSearchEditorMatches" ], "vs/workbench/contrib/sash/browser/sash.contribution": [ "sashSize", @@ -3745,7 +4042,6 @@ ], "vs/workbench/contrib/scm/browser/scm.contribution": [ "sourceControlViewIcon", - "source control", "no open repo", "no open repo in an untrusted workspace", "manageWorkspaceTrustAction", @@ -3795,6 +4091,8 @@ "autoReveal", "inputFontFamily", "inputFontSize", + "inputMaxLines", + "inputMinLines", "alwaysShowRepository", "scm.repositoriesSortOrder.discoveryTime", "scm.repositoriesSortOrder.name", @@ -3802,81 +4100,28 @@ "repositoriesSortOrder", "providersVisible", "showActionButton", - "showSyncView", + "showInputActionButton", + "scm.showIncomingChanges.always", + "scm.showIncomingChanges.never", + "scm.showIncomingChanges.auto", + "scm.showIncomingChanges", + "scm.showOutgoingChanges.always", + "scm.showOutgoingChanges.never", + "scm.showOutgoingChanges.auto", + "scm.showOutgoingChanges", + "scm.showChangesSummary", + "scm.workingSets.enabled", + "scm.workingSets.default.empty", + "scm.workingSets.default.current", + "scm.workingSets.default", "scm accept", "scm view next commit", "scm view previous commit", "open in external terminal", "open in integrated terminal", "source control", - "source control repositories", - "source control sync" - ], - "vs/workbench/contrib/search/browser/searchView": [ - "searchCanceled", - "moreSearch", - "searchScope.includes", - "placeholder.includes", - "searchScope.excludes", - "placeholder.excludes", - "replaceAll.confirmation.title", - { - "key": "replaceAll.confirm.button", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "replaceAll.occurrence.file.message", - "removeAll.occurrence.file.message", - "replaceAll.occurrence.files.message", - "removeAll.occurrence.files.message", - "replaceAll.occurrences.file.message", - "removeAll.occurrences.file.message", - "replaceAll.occurrences.files.message", - "removeAll.occurrences.files.message", - "removeAll.occurrence.file.confirmation.message", - "replaceAll.occurrence.file.confirmation.message", - "removeAll.occurrence.files.confirmation.message", - "replaceAll.occurrence.files.confirmation.message", - "removeAll.occurrences.file.confirmation.message", - "replaceAll.occurrences.file.confirmation.message", - "removeAll.occurrences.files.confirmation.message", - "replaceAll.occurrences.files.confirmation.message", - "emptySearch", - "searchPathNotFoundError", - "noOpenEditorResultsIncludesExcludes", - "noOpenEditorResultsIncludes", - "noOpenEditorResultsExcludes", - "noOpenEditorResultsFound", - "noResultsIncludesExcludes", - "noResultsIncludes", - "noResultsExcludes", - "noResultsFound", - "rerunSearch.message", - "rerunSearchInAll.message", - "openSettings.message", - "openSettings.learnMore", - "ariaSearchResultsStatus", - "searchMaxResultsWarning", - "forTerm", - "useIgnoresAndExcludesDisabled", - "excludes.enable", - "useExcludesAndIgnoreFilesDescription", - "onlyOpenEditors", - "openEditors.disable", - "disableOpenEditors", - "openInEditor.tooltip", - "openInEditor.message", - "search.file.result", - "search.files.result", - "search.file.results", - "search.files.results", - "searchWithoutFolder", - "openFolder" - ], - "vs/workbench/contrib/debug/browser/debugEditorContribution": [ - "editor.inlineValuesForeground", - "editor.inlineValuesBackground" + "source control", + "source control repositories" ], "vs/workbench/contrib/debug/browser/debug.contribution": [ "debugCategory", @@ -3889,9 +4134,10 @@ "copyStackTrace", "viewMemory", "setValue", - "copyValue", - "copyAsExpression", - "addToWatchExpressions", + "breakWhenValueIsRead", + "breakWhenValueChanges", + "breakWhenValueIsAccessed", + "viewMemory", "breakWhenValueIsRead", "breakWhenValueChanges", "breakWhenValueIsAccessed", @@ -3998,12 +4244,28 @@ ], "key": "allowBreakpointsEverywhere" }, + { + "comment": [ + "This is the description for a setting" + ], + "key": "gutterMiddleClickAction" + }, + "debug.gutterMiddleClickAction.logpoint", + "debug.gutterMiddleClickAction.conditionalBreakpoint", + "debug.gutterMiddleClickAction.triggeredBreakpoint", + "debug.gutterMiddleClickAction.none", { "comment": [ "This is the description for a setting" ], "key": "openExplorerOnEnd" }, + { + "comment": [ + "This is the description for a setting" + ], + "key": "closeReadonlyTabsOnEnd" + }, { "comment": [ "This is the description for a setting" @@ -4105,6 +4367,7 @@ "SetNextStatement", "inlineBreakpoint", "run", + "runMenu", { "comment": [ "Debug is a noun in this context, not a verb." @@ -4128,7 +4391,12 @@ "topStackFrameLineHighlight", "focusedStackFrameLineHighlight" ], + "vs/workbench/contrib/debug/browser/debugEditorContribution": [ + "editor.inlineValuesForeground", + "editor.inlineValuesBackground" + ], "vs/workbench/contrib/debug/browser/breakpointEditorContribution": [ + "breakpointHelper", "logPoint", "breakpoint", "breakpointHasConditionDisabled", @@ -4176,6 +4444,7 @@ "addBreakpoint", "addConditionalBreakpoint", "addLogPoint", + "addTriggeredBreakpoint", "runToLine", "debugIcon.breakpointForeground", "debugIcon.breakpointDisabledForeground", @@ -4183,6 +4452,48 @@ "debugIcon.breakpointCurrentStackframeForeground", "debugIcon.breakpointStackframeForeground" ], + "vs/workbench/contrib/markers/browser/markers.contribution": [ + "markersViewIcon", + { + "key": "miMarker", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "viewAsTree", + "viewAsTable", + "show errors", + "problems", + "show warnings", + "problems", + "show infos", + "problems", + "show active file", + "problems", + "show excluded files", + "problems", + "focusProblemsList", + "focusProblemsFilter", + "problems", + "problems", + "clearFiltersText", + "problems", + "collapseAll", + "status.problems", + "status.problemsVisibilityOff", + "status.problemsVisibility", + "totalErrors", + "totalWarnings", + "totalInfos", + "noProblems", + "manyProblems", + "totalProblems", + "copyMarker", + "copyMessage", + "copyMessage", + "show multiline", + "show singleline" + ], "vs/workbench/contrib/debug/browser/debugViewlet": [ { "key": "miOpenConfigurations", @@ -4197,96 +4508,13 @@ ] }, "debugPanel", - "startAdditionalSession" - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ - "name", - "diffAlgorithm.legacy", - "diffAlgorithm.advanced" - ], - "vs/workbench/contrib/debug/browser/repl": [ - { - "key": "workbench.debug.filter.placeholder", - "comment": [ - "Text in the brackets after e.g. is not localizable" - ] - }, - "showing filtered repl lines", - "debugConsole", - "startDebugFirst", - { - "key": "actions.repl.acceptInput", - "comment": [ - "Apply input from the debug console input box" - ] - }, - "repl.action.filter", - "actions.repl.copyAll", - "selectRepl", - "clearRepl", - "collapse", - "paste", - "copyAll", - "copy" - ], - "vs/workbench/contrib/markers/browser/markers.contribution": [ - "markersViewIcon", - { - "key": "miMarker", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "viewAsTree", - "viewAsTable", - "toggle errors", - "problems", - "errors", - "toggle warnings", - "problems", - "warnings", - "toggle infos", - "problems", - "Infos", - "toggle active file", - "problems", - "Active File", - "toggle Excluded Files", - "problems", - "Excluded Files", - "copyMarker", - "copyMessage", - "copyMessage", - "focusProblemsList", - "focusProblemsFilter", - "show multiline", - "problems", - "show singleline", - "problems", - "clearFiltersText", - "problems", - "collapseAll", - "status.problems", - "totalErrors", - "totalWarnings", - "totalInfos", - "noProblems", - "manyProblems", - "totalProblems" - ], - "vs/workbench/contrib/commands/common/commands.contribution": [ - "runCommands", - "runCommands.description", - "runCommands.commands", - "runCommands.invalidArgs", - "runCommands.noCommandsToRun" - ], - "vs/workbench/contrib/url/browser/url.contribution": [ - "openUrl", - "urlToOpen", - "workbench.trustedDomains.promptInTrustedWorkspace" + "startAdditionalSession", + "openLaunchConfigDescription" ], "vs/workbench/contrib/comments/browser/comments.contribution": [ + "collapseAll", + "expandAll", + "reply", "commentsConfigurationTitle", "openComments", "comments.openPanel.deprecated", @@ -4299,42 +4527,35 @@ "comments.visible", "comments.maxHeight", "collapseOnResolve", - "intro", - "introWidget", - "introWidgetNoKb", - "commentCommands", - "escape", - "next", - "nextNoKb", - "previous", - "previousNoKb", - "nextCommentThreadKb", - "nextCommentThreadNoKb", - "previousCommentThreadKb", - "previousCommentThreadNoKb", - "addComment", - "addCommentNoKb", - "submitComment", - "submitCommentNoKb" + "totalUnresolvedComments" ], - "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ - "webview.editor.label" + "vs/workbench/contrib/commands/common/commands.contribution": [ + "runCommands.description", + "runCommands.commands", + "runCommands.invalidArgs", + "runCommands.noCommandsToRun", + "runCommands" + ], + "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ + "name", + "diffAlgorithm.legacy", + "diffAlgorithm.advanced" + ], + "vs/workbench/contrib/url/browser/url.contribution": [ + "urlToOpen", + "workbench.trustedDomains.promptInTrustedWorkspace", + "openUrl" ], "vs/workbench/contrib/webview/browser/webview.contribution": [ "cut", "copy", "paste" ], + "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ + "webview.editor.label" + ], "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ - { - "key": "remote", - "comment": [ - "Remote as in remote machine" - ] - }, "installed", - "select and install local extensions", - "install remote in local", "searchExtensions", "extensionFoundInSection", "extensionFound", @@ -4348,6 +4569,14 @@ "extensionsToReload", "malicious warning", "reloadNow", + { + "key": "remote", + "comment": [ + "Remote as in remote machine" + ] + }, + "select and install local extensions", + "install remote in local", "popularExtensions", "recommendedExtensions", "enabledExtensions", @@ -4390,6 +4619,8 @@ "selectOutput", "outputScrollOff", "outputScrollOn", + "logLevel.label", + "logLevelDefault.label", "extensionLogs", "selectlog", "logFile", @@ -4402,51 +4633,28 @@ "output", "clearOutput.label", "toggleAutoScroll", - "openActiveLogOutputFile", + "openActiveOutputFile", + "openActiveOutputFileInNewWindow", "showLogs", "openLogFile" ], - "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ - "scopedConsoleAction.Integrated", - "scopedConsoleAction.external", - "scopedConsoleAction.wt" - ], - "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ - "relaunchSettingMessage", - "relaunchSettingMessageWeb", - "relaunchSettingDetail", - "relaunchSettingDetailWeb", - { - "key": "restart", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "restartWeb", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "restartExtensionHost.reason" - ], "vs/workbench/contrib/extensions/browser/extensions.contribution": [ "manageExtensionsQuickAccessPlaceholder", "manageExtensionsHelp", "extension", - "extensions", { "key": "miViewExtensions", "comment": [ "&& denotes a mnemonic" ] }, - "extensionsConfigurationTitle", "all", "enabled", + "selected", "none", "extensions.autoUpdate.true", "extensions.autoUpdate.enabled", + "extensions.autoUpdate.selected", "extensions.autoUpdate.false", "extensions.autoUpdate", "extensionsCheckUpdates", @@ -4467,12 +4675,15 @@ "extensions.supportUntrustedWorkspaces.supported", "extensions.supportUntrustedWorkspaces.version", "extensionsDeferredStartupFinishedActivation", + "extensionsInQuickAccess", "notFound", "workbench.extensions.installExtension.description", "workbench.extensions.installExtension.arg.decription", "workbench.extensions.installExtension.option.installOnlyNewlyAddedFromExtensionPackVSIX", "workbench.extensions.installExtension.option.installPreReleaseVersion", "workbench.extensions.installExtension.option.donotSync", + "workbench.extensions.installExtension.option.justification", + "workbench.extensions.installExtension.option.enable", "workbench.extensions.installExtension.option.context", "notFound", "workbench.extensions.uninstallExtension.description", @@ -4491,25 +4702,13 @@ ] }, "showExtensions", - "focusExtensions", - "installExtensions", - "showRecommendedKeymapExtensionsShort", "importKeyboardShortcutsFroms", - "showLanguageExtensionsShort", - "checkForUpdates", "noUpdatesAvailable", "configure auto updating extensions", "configureExtensionsAutoUpdate.all", "configureExtensionsAutoUpdate.enabled", + "configureExtensionsAutoUpdate.selected", "configureExtensionsAutoUpdate.none", - "updateAll", - "disableAutoUpdate", - "enableAutoUpdate", - "enableAll", - "enableAllWorkspace", - "disableAll", - "disableAllWorkspace", - "InstallFromVSIX", "installFromVSIX", { "key": "installButton", @@ -4521,30 +4720,20 @@ "InstallVSIXAction.successReload", "InstallVSIXAction.success", "InstallVSIXAction.reloadNow", - "installExtensionFromLocation", "installFromLocation", "install button", "installFromLocationPlaceHolder", "installFromLocation", "filterExtensions", - "showFeaturedExtensions", "featured filter", - "showPopularExtensions", "most popular filter", - "showRecommendedExtensions", "most popular recommended", - "recentlyPublishedExtensions", "recently published filter", "filter by category", - "showBuiltInExtensions", "builtin filter", - "extensionUpdates", "extension updates filter", - "showWorkspaceUnsupportedExtensions", "workspace unsupported filter", - "showEnabledExtensions", "enabled filter", - "showDisabledExtensions", "disabled filter", "sorty by", "sort by installs", @@ -4552,18 +4741,49 @@ "sort by name", "sort by published date", "sort by update date", - "clearExtensionsSearchResults", - "refreshExtension", "installWorkspaceRecommendedExtensions", - "show pre-release version", - "show released version", - "workbench.extensions.action.copyExtension", + "enablePreRleaseLabel", + "disablePreRleaseLabel", "extensionInfoName", "extensionInfoId", "extensionInfoDescription", "extensionInfoVersion", "extensionInfoPublisher", "extensionInfoVSMarketplaceLink", + "extensions", + "extensions", + "extensions", + "extensions", + "extensions", + "extensions", + "focusExtensions", + "installExtensions", + "showRecommendedKeymapExtensionsShort", + "showLanguageExtensionsShort", + "checkForUpdates", + "updateAll", + "disableAutoUpdate", + "enableAutoUpdate", + "enableAll", + "enableAllWorkspace", + "disableAll", + "disableAllWorkspace", + "InstallFromVSIX", + "installExtensionFromLocation", + "showFeaturedExtensions", + "showPopularExtensions", + "showRecommendedExtensions", + "recentlyPublishedExtensions", + "showBuiltInExtensions", + "extensionUpdates", + "showWorkspaceUnsupportedExtensions", + "showEnabledExtensions", + "showDisabledExtensions", + "clearExtensionsSearchResults", + "refreshExtension", + "show pre-release version", + "show released version", + "workbench.extensions.action.copyExtension", "workbench.extensions.action.copyExtensionId", "workbench.extensions.action.configure", "workbench.extensions.action.configureKeybindings", @@ -4574,14 +4794,33 @@ "workbench.extensions.action.addExtensionToWorkspaceRecommendations", "workbench.extensions.action.removeExtensionFromWorkspaceRecommendations", "workbench.extensions.action.addToWorkspaceRecommendations", - "extensions", "workbench.extensions.action.addToWorkspaceFolderRecommendations", - "extensions", "workbench.extensions.action.addToWorkspaceIgnoredRecommendations", - "extensions", - "workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations", - "extensions", - "extensions" + "workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations" + ], + "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ + "scopedConsoleAction.Integrated", + "scopedConsoleAction.external", + "scopedConsoleAction.wt" + ], + "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ + "relaunchSettingMessage", + "relaunchSettingMessageWeb", + "relaunchSettingDetail", + "relaunchSettingDetailWeb", + { + "key": "restart", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "restartWeb", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "restartExtensionHost.reason" ], "vs/workbench/contrib/tasks/browser/task.contribution": [ "building", @@ -4630,18 +4869,6 @@ "&& denotes a mnemonic" ] }, - "workbench.action.tasks.openWorkspaceFileTasks", - "ShowLogAction.label", - "RunTaskAction.label", - "ReRunTaskAction.label", - "RestartTaskAction.label", - "ShowTasksAction.label", - "TerminateAction.label", - "BuildAction.label", - "TestAction.label", - "ConfigureDefaultBuildTask.label", - "ConfigureDefaultTestTask.label", - "workbench.action.tasks.openUserTasks", "userTasks", "tasksQuickAccessPlaceholder", "tasksQuickAccessHelp", @@ -4664,7 +4891,20 @@ "task.saveBeforeRun", "task.saveBeforeRun.always", "task.saveBeforeRun.never", - "task.SaveBeforeRun.prompt" + "task.SaveBeforeRun.prompt", + "task.verboseLogging", + "workbench.action.tasks.openWorkspaceFileTasks", + "ShowLogAction.label", + "RunTaskAction.label", + "ReRunTaskAction.label", + "RestartTaskAction.label", + "ShowTasksAction.label", + "TerminateAction.label", + "BuildAction.label", + "TestAction.label", + "ConfigureDefaultBuildTask.label", + "ConfigureDefaultTestTask.label", + "workbench.action.tasks.openUserTasks" ], "vs/workbench/contrib/remote/common/remote.contribution": [ "invalidWorkspaceMessage", @@ -4675,8 +4915,6 @@ "&& denotes a mnemonic" ] }, - "triggerReconnect", - "pauseSocketWriting", "ui", "workspace", "remote", @@ -4687,6 +4925,7 @@ "remote.autoForwardPortsSource.process", "remote.autoForwardPortsSource.output", "remote.autoForwardPortsSource.hybrid", + "remote.autoForwardPortFallback", "remote.forwardOnClick", "remote.portsAttributes.port", "remote.portsAttributes.notify", @@ -4716,11 +4955,42 @@ "remote.portsAttributes.requireLocalPort", "remote.portsAttributes.protocol", "remote.portsAttributes.defaults", - "remote.localPortHost" + "remote.localPortHost", + "triggerReconnect", + "pauseSocketWriting" ], - "vs/workbench/contrib/snippets/browser/snippets.contribution": [ - "editor.snippets.codeActions.enabled", - "snippetSchema.json.prefix", + "vs/workbench/contrib/debug/browser/repl": [ + { + "key": "workbench.debug.filter.placeholder", + "comment": [ + "Text in the brackets after e.g. is not localizable" + ] + }, + "showing filtered repl lines", + "debugConsole", + "startDebugFirst", + { + "key": "actions.repl.acceptInput", + "comment": [ + "Apply input from the debug console input box" + ] + }, + "repl.action.filter", + "actions.repl.copyAll", + "selectRepl", + "collapse", + "paste", + "copyAll", + "copy", + "clearRepl", + "clearRepl.descriotion" + ], + "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ + "toggleKeybindingsLog" + ], + "vs/workbench/contrib/snippets/browser/snippets.contribution": [ + "editor.snippets.codeActions.enabled", + "snippetSchema.json.prefix", "snippetSchema.json.isFileTemplate", "snippetSchema.json.body", "snippetSchema.json.description", @@ -4730,9 +5000,6 @@ "snippetSchema.json", "snippetSchema.json.scope" ], - "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ - "toggleKeybindingsLog" - ], "vs/workbench/contrib/folding/browser/folding.contribution": [ "null", "nullFormatterDescription", @@ -4754,40 +5021,6 @@ "read.title", "stop.title" ], - "vs/workbench/contrib/update/browser/update.contribution": [ - "showReleaseNotes", - { - "key": "mshowReleaseNotes", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "update.noReleaseNotesOnline", - "checkForUpdates", - "downloadUpdate", - "installUpdate", - "restartToUpdate", - "openDownloadPage", - "applyUpdate", - "pickUpdate", - { - "key": "updateButton", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/contrib/surveys/browser/nps.contribution": [ - "surveyQuestion", - "takeSurvey", - "remindLater", - "neverAgain" - ], - "vs/workbench/contrib/surveys/browser/ces.contribution": [ - "cesSurveyQuestion", - "giveFeedback", - "remindLater" - ], "vs/workbench/contrib/themes/browser/themes.contribution": [ "manageExtensionIcon", "themes.selectMarketplaceTheme", @@ -4795,29 +5028,36 @@ "installExtension.confirm", "installExtension.button.ok", "installing extensions", - "selectTheme.label", + "themes.selectTheme.darkScheme", + "themes.selectTheme.lightScheme", + "themes.selectTheme.darkHC", + "themes.selectTheme.lightHC", + "themes.selectTheme.default", + "themes.configure.switchingEnabled", + "themes.configure.switchingDisabled", "installColorThemes", "browseColorThemes", - "themes.selectTheme", "themes.category.light", "themes.category.dark", "themes.category.hc", - "selectIconTheme.label", "installIconThemes", "themes.selectIconTheme", "fileIconThemeCategory", "noIconThemeLabel", "noIconThemeDesc", - "selectProductIconTheme.label", "installProductIconThemes", "browseProductIconThemes", "themes.selectProductIconTheme", "productIconThemeCategory", "defaultProductIconThemeLabel", "manage extension", - "generateColorTheme.label", - "toggleLightDarkThemes.label", - "browseColorThemeInMarketPlace.label", + { + "key": "cannotToggle", + "comment": [ + "{0} is a setting name" + ] + }, + "goToSetting", "themes", { "key": "miSelectTheme", @@ -4844,7 +5084,56 @@ "comment": [ "{0} is the name of the new default theme" ] - } + }, + "selectTheme.label", + "selectIconTheme.label", + "selectProductIconTheme.label", + "generateColorTheme.label", + "toggleLightDarkThemes.label", + "browseColorThemeInMarketPlace.label" + ], + "vs/workbench/contrib/update/browser/update.contribution": [ + { + "key": "mshowReleaseNotes", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "update.noReleaseNotesOnline", + { + "key": "mshowReleaseNotes", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "releaseNotesFromFileNone", + "pickUpdate", + { + "key": "updateButton", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "showReleaseNotes", + "showReleaseNotesCurrentFile", + "developerCategory", + "checkForUpdates", + "downloadUpdate", + "installUpdate", + "restartToUpdate", + "openDownloadPage", + "applyUpdate" + ], + "vs/workbench/contrib/surveys/browser/nps.contribution": [ + "surveyQuestion", + "takeSurvey", + "remindLater", + "neverAgain" + ], + "vs/workbench/contrib/surveys/browser/ces.contribution": [ + "cesSurveyQuestion", + "giveFeedback", + "remindLater" ], "vs/workbench/contrib/surveys/browser/languageSurveys.contribution": [ "helpUs", @@ -4853,13 +5142,9 @@ "neverAgain" ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution": [ - "miWelcome", "welcome", - "welcome", - "welcome.goBack", "welcome.markStepComplete", "welcome.markStepInomplete", - "welcome.showAllWalkthroughs", "pickWalkthroughs", "workspacePlatform", "workbench.welcomePage.walkthroughs.openOnInstall", @@ -4893,9 +5178,20 @@ ], "key": "workbench.startupEditor.welcomePageInEmptyWorkbench" }, + { + "comment": [ + "This is the description for a setting. Values surrounded by single quotes are not to be translated." + ], + "key": "workbench.startupEditor.terminal" + }, "workbench.startupEditor", "deprecationMessage", - "workbench.welcomePage.preferReducedMotion" + "workbench.welcomePage.preferReducedMotion", + "miWelcome", + "minWelcomeDescription", + "welcome", + "welcome.goBack", + "welcome.showAllWalkthroughs" ], "vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution": [ "walkThrough.editor.label", @@ -4908,15 +5204,15 @@ ], "vs/workbench/contrib/welcomeViews/common/newFile.contribution": [ "Built-In", - "Create", - "welcome.newFile", "newFileTitle", "newFilePlaceholder", "file", "notebook", "change keybinding", "miNewFileWithName", - "miNewFile2" + "miNewFile2", + "Create", + "welcome.newFile" ], "vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution": [ "editorHasCallHierarchyProvider", @@ -4924,13 +5220,13 @@ "callHierarchyDirection", "no.item", "error", + "showIncomingCallsIcons", + "showOutgoingCallsIcon", + "close", "title", "title.incoming", - "showIncomingCallsIcons", "title.outgoing", - "showOutgoingCallsIcon", - "title.refocus", - "close" + "title.refocus" ], "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ "editorHasTypeHierarchyProvider", @@ -4938,11 +5234,11 @@ "typeHierarchyDirection", "no.item", "error", + "close", "title", "title.supertypes", "title.subtypes", - "title.refocusTypeHierarchy", - "close" + "title.refocusTypeHierarchy" ], "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ "document" @@ -4989,8 +5285,8 @@ "status.autoDetectLanguage", "langDetection.name", "langDetection.aria", - "detectlang", - "noDetection" + "noDetection", + "detectlang" ], "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ "langStatus.name", @@ -5002,6 +5298,24 @@ "name.pattern", "reset" ], + "vs/workbench/contrib/authentication/browser/authentication.contribution": [ + "authentication.id", + "authentication.label", + { + "key": "authenticationExtensionPoint", + "comment": [ + "'Contributes' means adds here" + ] + }, + "authenticationlabel", + "authenticationid", + "authentication", + "authentication.Placeholder", + "authentication.missingId", + "authentication.missingLabel", + "authentication.idConflict", + "loading" + ], "vs/workbench/contrib/userDataSync/browser/userDataSync.contribution": [ { "key": "local too many requests - reload", @@ -5039,19 +5353,12 @@ "filterTimeline" ], "vs/workbench/contrib/editSessions/browser/editSessions.contribution": [ - "continue working on", - "continue edit session in local folder", - "show log", "continueOn.installAdditional", "resuming working changes window", "autoStoreWorkingChanges", "check for pending cloud changes", "store working changes", - "show cloud changes", "store your working changes", - "resume latest cloud changes", - "resume cloud changes", - "store working changes in cloud", "storing working changes", "checkingForWorkingChanges", "no cloud changes", @@ -5091,16 +5398,33 @@ "continueOnCloudChanges.promptForAuth", "continueOnCloudChanges.off", "continueOnCloudChanges", - "cloudChangesPartialMatchesEnabled" + "cloudChangesPartialMatchesEnabled", + "continue working on", + "continue edit session in local folder", + "show log", + "show cloud changes", + "resume latest cloud changes", + "resume cloud changes", + "store working changes in cloud" ], "vs/workbench/contrib/workspaces/browser/workspaces.contribution": [ - "workspaceFound", + { + "key": "foundWorkspace", + "comment": [ + "{Locked=\"]({1})\"}" + ] + }, "openWorkspace", - "workspacesFound", + { + "key": "foundWorkspaces", + "comment": [ + "{Locked=\"]({0})\"}" + ] + }, "selectWorkspace", "selectToOpen", - "openWorkspace", - "alreadyOpen" + "alreadyOpen", + "openWorkspace" ], "vs/workbench/contrib/workspace/browser/workspace.contribution": [ "openLooseFileWorkspaceDetails", @@ -5177,7 +5501,6 @@ "restrictedModeBannerMessageWindow", "restrictedModeBannerMessageFolder", "restrictedModeBannerMessageWorkspace", - "status.ariaTrustedWindow", "status.ariaUntrustedWindow", { "key": "status.tooltipUntrustedWindow2", @@ -5185,7 +5508,6 @@ "[abc]({n}) are links. Only translate `features are disabled` and `window is not trusted`. Do not change brackets and parentheses or {n}" ] }, - "status.ariaTrustedFolder", "status.ariaUntrustedFolder", { "key": "status.tooltipUntrustedFolder2", @@ -5193,7 +5515,6 @@ "[abc]({n}) are links. Only translate `features are disabled` and `folder is not trusted`. Do not change brackets and parentheses or {n}" ] }, - "status.ariaTrustedWorkspace", "status.ariaUntrustedWorkspace", { "key": "status.tooltipUntrustedWorkspace2", @@ -5202,10 +5523,8 @@ ] }, "status.WorkspaceTrust", + "untrusted", "workspaceTrustEditor", - "workspacesCategory", - "configureWorkspaceTrustSettings", - "manageWorkspaceTrust", "workspace.trust.description", "workspace.trust.startupPrompt.description", "workspace.trust.startupPrompt.always", @@ -5219,193 +5538,67 @@ "workspace.trust.untrustedFiles.prompt", "workspace.trust.untrustedFiles.open", "workspace.trust.untrustedFiles.newWindow", - "workspace.trust.emptyWindow.description" + "workspace.trust.emptyWindow.description", + "workspacesCategory", + "configureWorkspaceTrustSettings", + "manageWorkspaceTrust" + ], + "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ + "bracketPairColorizer.notification", + "bracketPairColorizer.notification.action.uninstall", + "bracketPairColorizer.notification.action.enableNative", + "bracketPairColorizer.notification.action.showMoreInfo" ], "vs/workbench/contrib/share/browser/share.contribution": [ - "share", "generating link", "shareTextSuccess", "shareSuccess", "close", "open link", - "experimental.share.enabled" - ], - "vs/workbench/contrib/audioCues/browser/audioCues.contribution": [ - "audioCues.enabled.auto", - "audioCues.enabled.on", - "audioCues.enabled.off", - "audioCues.volume", - "audioCues.debouncePositionChanges", - "audioCues.lineHasBreakpoint", - "audioCues.lineHasInlineSuggestion", - "audioCues.lineHasError", - "audioCues.lineHasFoldedArea", - "audioCues.lineHasWarning", - "audioCues.onDebugBreak", - "audioCues.noInlayHints", - "audioCues.taskCompleted", - "audioCues.taskFailed", - "audioCues.terminalCommandFailed", - "audioCues.terminalQuickFix", - "audioCues.diffLineInserted", - "audioCues.diffLineDeleted", - "audioCues.diffLineModified", - "audioCues.notebookCellCompleted", - "audioCues.notebookCellFailed", - "audioCues.chatRequestSent", - "audioCues.chatResponsePending", - "audioCues.chatResponseReceived", - "audioCues.clear", - "audioCues.save", - "audioCues.save.userGesture", - "audioCues.save.always", - "audioCues.save.never", - "audioCues.format", - "audioCues.format.userGesture", - "audioCues.format.always", - "audioCues.format.never" + "experimental.share.enabled", + "share" ], "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution": [ - "workbench.accounts.showEntitlements" + "workbench.accounts.showEntitlements", + "workbench.chat.showWelcomeView" ], - "vs/workbench/browser/workbench": [ - "loaderErrorNative" + "vs/workbench/electron-sandbox/actions/developerActions": [ + "toggleDevTools", + "configureRuntimeArguments", + "reloadWindowWithExtensionsDisabled", + "openUserDataFolder" ], - "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ - "bracketPairColorizer.notification", - "bracketPairColorizer.notification.action.uninstall", - "bracketPairColorizer.notification.action.enableNative", - "bracketPairColorizer.notification.action.showMoreInfo" - ], - "vs/workbench/electron-sandbox/window": [ - "restart", - "configure", - "learnMore", - "keychainWriteError", - "troubleshooting", - "runningTranslated", - "downloadArmBuild", - "proxyAuthRequired", - { - "key": "loginButton", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "username", - "password", - "proxyDetail", - "rememberCredentials", - "quitMessageMac", - "quitMessage", - "closeWindowMessage", - { - "key": "quitButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "exitButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "closeWindowButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "doNotAskAgain", - "shutdownErrorDetail", - "willShutdownDetail", - "shutdownErrorClose", - "shutdownErrorQuit", - "shutdownErrorReload", - "shutdownErrorLoad", - "shutdownTitleClose", - "shutdownTitleQuit", - "shutdownTitleReload", - "shutdownTitleLoad", - "shutdownForceClose", - "shutdownForceQuit", - "shutdownForceReload", - "shutdownForceLoad", - "loaderCycle", - "runningAsRoot", - "appRootWarning.banner", - "macoseolmessage", - "learnMore", - "resolveShellEnvironment", - "learnMore" - ], - "vs/platform/workspace/common/workspace": [ - "codeWorkspace" - ], - "vs/workbench/services/configuration/browser/configurationService": [ - "configurationDefaults.description", - "experimental", - "setting description" - ], - "vs/workbench/services/log/electron-sandbox/logService": [ - "rendererLog" - ], - "vs/platform/workspace/common/workspaceTrust": [ - "trusted", - "untrusted" - ], - "vs/workbench/services/userDataProfile/common/userDataProfile": [ - "defaultProfileIcon", - "profiles", - "profile" - ], - "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ - "devTools", - "directUrl", - "connectionError" - ], - "vs/workbench/electron-sandbox/actions/developerActions": [ - "toggleDevTools", - "configureRuntimeArguments", - "reloadWindowWithExtensionsDisabled", - "openUserDataFolder" - ], - "vs/platform/configuration/common/configurationRegistry": [ - "defaultLanguageConfigurationOverrides.title", - "defaultLanguageConfiguration.description", - "overrideSettings.defaultDescription", - "overrideSettings.errorMessage", - "overrideSettings.defaultDescription", - "overrideSettings.errorMessage", - "config.property.empty", - "config.property.languageDefault", - "config.property.duplicate", - "config.policy.duplicate" + "vs/platform/configuration/common/configurationRegistry": [ + "defaultLanguageConfigurationOverrides.title", + "defaultLanguageConfiguration.description", + "overrideSettings.defaultDescription", + "overrideSettings.errorMessage", + "overrideSettings.defaultDescription", + "overrideSettings.errorMessage", + "config.property.empty", + "config.property.languageDefault", + "config.property.duplicate", + "config.policy.duplicate" ], "vs/workbench/electron-sandbox/actions/windowActions": [ - "closeWindow", { "key": "miCloseWindow", "comment": [ "&& denotes a mnemonic" ] }, - "zoomIn", { "key": "miZoomIn", "comment": [ "&& denotes a mnemonic" ] }, - "zoomOut", { "key": "miZoomOut", "comment": [ "&& denotes a mnemonic" ] }, - "zoomReset", { "key": "miZoomReset", "comment": [ @@ -5414,18 +5607,17 @@ }, "close", "close", - "switchWindowPlaceHolder", + "windowGroup", "windowDirtyAriaLabel", "current", + "current", + "switchWindowPlaceHolder", + "closeWindow", + "zoomIn", + "zoomOut", + "zoomReset", "switchWindow", - "quickSwitchWindow", - "splitWindow", - { - "key": "miSplitWindow", - "comment": [ - "&& denotes a mnemonic" - ] - } + "quickSwitchWindow" ], "vs/platform/contextkey/common/contextkeys": [ "isMac", @@ -5439,11 +5631,11 @@ "inputFocus" ], "vs/workbench/electron-sandbox/actions/installActions": [ + "successIn", + "successFrom", "shellCommand", "install", - "successIn", - "uninstall", - "successFrom" + "uninstall" ], "vs/workbench/common/contextkeys": [ "workbenchState", @@ -5453,6 +5645,7 @@ "virtualWorkspace", "temporaryWorkspace", "isFullscreen", + "isAuxiliaryWindowFocusedContext", "embedderIdentifier", "activeEditorIsDirty", "activeEditorIsNotPreview", @@ -5460,6 +5653,7 @@ "activeEditorIsLastInGroup", "activeEditorIsPinned", "activeEditorIsReadonly", + "activeCompareEditorCanSwap", "activeEditorCanToggleReadonly", "activeEditorCanRevert", "activeEditor", @@ -5473,13 +5667,15 @@ "activeEditorGroupLast", "activeEditorGroupLocked", "multipleEditorGroups", + "editorPartMultipleEditorGroups", + "editorPartEditorGroupMaximized", + "isAuxiliaryEditorPart", "editorIsOpen", "inZenMode", - "isCenteredLayout", + "isMainEditorCenteredLayout", "splitEditorsVertically", - "editorAreaVisible", + "mainEditorAreaVisible", "editorTabsVisible", - "editorGroupMaximized", "sideBarVisible", "sideBarFocus", "activeViewlet", @@ -5514,10 +5710,86 @@ "applicationConfigurationTitle", "workbenchConfigurationTitle", "securityConfigurationTitle", + "problemsConfigurationTitle", "security.allowedUNCHosts.patternErrorMessage", "security.allowedUNCHosts", "security.restrictUNCAccess" ], + "vs/workbench/electron-sandbox/window": [ + "restart", + "configure", + "learnMore", + "keychainWriteError", + "troubleshooting", + "runningTranslated", + "downloadArmBuild", + "proxyAuthRequired", + { + "key": "loginButton", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "username", + "password", + "proxyDetail", + "rememberCredentials", + "shutdownErrorDetail", + "willShutdownDetail", + "shutdownErrorClose", + "shutdownErrorQuit", + "shutdownErrorReload", + "shutdownErrorLoad", + "shutdownTitleClose", + "shutdownTitleQuit", + "shutdownTitleReload", + "shutdownTitleLoad", + "shutdownForceClose", + "shutdownForceQuit", + "shutdownForceReload", + "shutdownForceLoad", + "loaderCycle", + "runningAsRoot", + "appRootWarning.banner", + "macoseolmessage", + "learnMore", + "resolveShellEnvironment", + "learnMore", + "zoomOut", + "zoomIn", + "zoomReset", + "zoomResetLabel", + "zoomSettings", + "status.windowZoom", + "zoomNumber" + ], + "vs/workbench/browser/workbench": [ + "loaderErrorNative" + ], + "vs/workbench/services/configuration/browser/configurationService": [ + "configurationDefaults.description", + "experimental", + "setting description" + ], + "vs/platform/workspace/common/workspace": [ + "codeWorkspace" + ], + "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ + "devTools", + "directUrl", + "connectionError" + ], + "vs/workbench/services/log/electron-sandbox/logService": [ + "rendererLog" + ], + "vs/workbench/services/files/electron-sandbox/diskFileSystemProvider": [ + "fileWatcher" + ], + "vs/workbench/services/userDataProfile/common/userDataProfile": [ + "defaultProfileIcon", + "profile", + "profiles" + ], "vs/workbench/browser/parts/dialogs/dialogHandler": [ "aboutDetail", { @@ -5543,6 +5815,31 @@ }, "okButton" ], + "vs/workbench/services/textfile/browser/textFileService": [ + "textFileCreate.source", + "textFileOverwrite.source", + "textFileModelDecorations", + "readonlyAndDeleted", + "readonly", + "deleted", + "fileBinaryError", + "confirmOverwrite", + "overwriteIrreversible", + { + "key": "replaceButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmMakeWriteable", + "confirmMakeWriteableDetail", + { + "key": "makeWriteableButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/workbench/services/dialogs/browser/abstractFileDialogService": [ "saveChangesDetail", "saveChangesMessage", @@ -5575,30 +5872,8 @@ "allFiles", "noExt" ], - "vs/workbench/services/textfile/browser/textFileService": [ - "textFileCreate.source", - "textFileOverwrite.source", - "textFileModelDecorations", - "readonlyAndDeleted", - "readonly", - "deleted", - "fileBinaryError", - "confirmOverwrite", - "overwriteIrreversible", - { - "key": "replaceButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "confirmMakeWriteable", - "confirmMakeWriteableDetail", - { - "key": "makeWriteableButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - } + "vs/workbench/services/extensionManagement/common/extensionManagement": [ + "extensionsConfigurationTitle" ], "vs/workbench/common/theme": [ "tabActiveBackground", @@ -5621,6 +5896,7 @@ "tabActiveUnfocusedBorderTop", "tabHoverBorder", "tabUnfocusedHoverBorder", + "tabDragAndDropBorder", "tabActiveModifiedBorder", "tabInactiveModifiedBorder", "unfocusedActiveModifiedBorder", @@ -5651,6 +5927,11 @@ "panelSectionHeaderForeground", "panelSectionHeaderBorder", "panelSectionBorder", + "panelStickyScrollBackground", + "panelStickyScrollBorder", + "panelStickyScrollShadow", + "outputViewBackground", + "outputViewStickyScrollBackground", "banner.background", "banner.foreground", "banner.iconForeground", @@ -5688,6 +5969,12 @@ "activityBarDragAndDropBorder", "activityBarBadgeBackground", "activityBarBadgeForeground", + "activityBarTop", + "activityBarTopActiveFocusBorder", + "activityBarTopActiveBackground", + "activityBarTopInActiveForeground", + "activityBarTopDragAndDropBorder", + "activityBarTopBackground", "profileBadgeBackground", "profileBadgeForeground", "statusBarItemHostBackground", @@ -5703,11 +5990,16 @@ "sideBarBackground", "sideBarForeground", "sideBarBorder", + "sideBarTitleBackground", "sideBarTitleForeground", "sideBarDragAndDropBackground", "sideBarSectionHeaderBackground", "sideBarSectionHeaderForeground", "sideBarSectionHeaderBorder", + "sideBarActivityBarTopBorder", + "sideBarStickyScrollBackground", + "sideBarStickyScrollBorder", + "sideBarStickyScrollShadow", "titleBarActiveForeground", "titleBarInactiveForeground", "titleBarActiveBackground", @@ -5738,228 +6030,6 @@ "windowActiveBorder", "windowInactiveBorder" ], - "vs/platform/theme/common/colorRegistry": [ - "foreground", - "disabledForeground", - "errorForeground", - "descriptionForeground", - "iconForeground", - "focusBorder", - "contrastBorder", - "activeContrastBorder", - "selectionBackground", - "textSeparatorForeground", - "textLinkForeground", - "textLinkActiveForeground", - "textPreformatForeground", - "textPreformatBackground", - "textBlockQuoteBackground", - "textBlockQuoteBorder", - "textCodeBlockBackground", - "widgetShadow", - "widgetBorder", - "inputBoxBackground", - "inputBoxForeground", - "inputBoxBorder", - "inputBoxActiveOptionBorder", - "inputOption.hoverBackground", - "inputOption.activeBackground", - "inputOption.activeForeground", - "inputPlaceholderForeground", - "inputValidationInfoBackground", - "inputValidationInfoForeground", - "inputValidationInfoBorder", - "inputValidationWarningBackground", - "inputValidationWarningForeground", - "inputValidationWarningBorder", - "inputValidationErrorBackground", - "inputValidationErrorForeground", - "inputValidationErrorBorder", - "dropdownBackground", - "dropdownListBackground", - "dropdownForeground", - "dropdownBorder", - "buttonForeground", - "buttonSeparator", - "buttonBackground", - "buttonHoverBackground", - "buttonBorder", - "buttonSecondaryForeground", - "buttonSecondaryBackground", - "buttonSecondaryHoverBackground", - "badgeBackground", - "badgeForeground", - "scrollbarShadow", - "scrollbarSliderBackground", - "scrollbarSliderHoverBackground", - "scrollbarSliderActiveBackground", - "progressBarBackground", - "editorError.background", - "editorError.foreground", - "errorBorder", - "editorWarning.background", - "editorWarning.foreground", - "warningBorder", - "editorInfo.background", - "editorInfo.foreground", - "infoBorder", - "editorHint.foreground", - "hintBorder", - "sashActiveBorder", - "editorBackground", - "editorForeground", - "editorStickyScrollBackground", - "editorStickyScrollHoverBackground", - "editorWidgetBackground", - "editorWidgetForeground", - "editorWidgetBorder", - "editorWidgetResizeBorder", - "pickerBackground", - "pickerForeground", - "pickerTitleBackground", - "pickerGroupForeground", - "pickerGroupBorder", - "keybindingLabelBackground", - "keybindingLabelForeground", - "keybindingLabelBorder", - "keybindingLabelBottomBorder", - "editorSelectionBackground", - "editorSelectionForeground", - "editorInactiveSelection", - "editorSelectionHighlight", - "editorSelectionHighlightBorder", - "editorFindMatch", - "findMatchHighlight", - "findRangeHighlight", - "editorFindMatchBorder", - "findMatchHighlightBorder", - "findRangeHighlightBorder", - "searchEditor.queryMatch", - "searchEditor.editorFindMatchBorder", - "search.resultsInfoForeground", - "hoverHighlight", - "hoverBackground", - "hoverForeground", - "hoverBorder", - "statusBarBackground", - "activeLinkForeground", - "editorInlayHintForeground", - "editorInlayHintBackground", - "editorInlayHintForegroundTypes", - "editorInlayHintBackgroundTypes", - "editorInlayHintForegroundParameter", - "editorInlayHintBackgroundParameter", - "editorLightBulbForeground", - "editorLightBulbAutoFixForeground", - "diffEditorInserted", - "diffEditorRemoved", - "diffEditorInsertedLines", - "diffEditorRemovedLines", - "diffEditorInsertedLineGutter", - "diffEditorRemovedLineGutter", - "diffEditorOverviewInserted", - "diffEditorOverviewRemoved", - "diffEditorInsertedOutline", - "diffEditorRemovedOutline", - "diffEditorBorder", - "diffDiagonalFill", - "diffEditor.unchangedRegionBackground", - "diffEditor.unchangedRegionForeground", - "diffEditor.unchangedCodeBackground", - "listFocusBackground", - "listFocusForeground", - "listFocusOutline", - "listFocusAndSelectionOutline", - "listActiveSelectionBackground", - "listActiveSelectionForeground", - "listActiveSelectionIconForeground", - "listInactiveSelectionBackground", - "listInactiveSelectionForeground", - "listInactiveSelectionIconForeground", - "listInactiveFocusBackground", - "listInactiveFocusOutline", - "listHoverBackground", - "listHoverForeground", - "listDropBackground", - "highlight", - "listFocusHighlightForeground", - "invalidItemForeground", - "listErrorForeground", - "listWarningForeground", - "listFilterWidgetBackground", - "listFilterWidgetOutline", - "listFilterWidgetNoMatchesOutline", - "listFilterWidgetShadow", - "listFilterMatchHighlight", - "listFilterMatchHighlightBorder", - "treeIndentGuidesStroke", - "treeInactiveIndentGuidesStroke", - "tableColumnsBorder", - "tableOddRowsBackgroundColor", - "listDeemphasizedForeground", - "checkbox.background", - "checkbox.select.background", - "checkbox.foreground", - "checkbox.border", - "checkbox.select.border", - "quickInput.list.focusBackground deprecation", - "quickInput.listFocusForeground", - "quickInput.listFocusIconForeground", - "quickInput.listFocusBackground", - "menuBorder", - "menuForeground", - "menuBackground", - "menuSelectionForeground", - "menuSelectionBackground", - "menuSelectionBorder", - "menuSeparatorBackground", - "toolbarHoverBackground", - "toolbarHoverOutline", - "toolbarActiveBackground", - "snippetTabstopHighlightBackground", - "snippetTabstopHighlightBorder", - "snippetFinalTabstopHighlightBackground", - "snippetFinalTabstopHighlightBorder", - "breadcrumbsFocusForeground", - "breadcrumbsBackground", - "breadcrumbsFocusForeground", - "breadcrumbsSelectedForeground", - "breadcrumbsSelectedBackground", - "mergeCurrentHeaderBackground", - "mergeCurrentContentBackground", - "mergeIncomingHeaderBackground", - "mergeIncomingContentBackground", - "mergeCommonHeaderBackground", - "mergeCommonContentBackground", - "mergeBorder", - "overviewRulerCurrentContentForeground", - "overviewRulerIncomingContentForeground", - "overviewRulerCommonContentForeground", - "overviewRulerFindMatchForeground", - "overviewRulerSelectionHighlightForeground", - "minimapFindMatchHighlight", - "minimapSelectionOccurrenceHighlight", - "minimapSelectionHighlight", - "minimapInfo", - "overviewRuleWarning", - "minimapError", - "minimapBackground", - "minimapForegroundOpacity", - "minimapSliderBackground", - "minimapSliderHoverBackground", - "minimapSliderActiveBackground", - "problemsErrorIconForeground", - "problemsWarningIconForeground", - "problemsInfoIconForeground", - "chartsForeground", - "chartsLines", - "chartsRed", - "chartsBlue", - "chartsYellow", - "chartsOrange", - "chartsGreen", - "chartsPurple" - ], "vs/base/common/actions": [ "submenu.empty" ], @@ -6005,10 +6075,10 @@ "&& denotes a mnemonic" ] }, - "extensionInstallWorkspaceTrustMessage", "extensionInstallWorkspaceTrustButton", "extensionInstallWorkspaceTrustContinueButton", "extensionInstallWorkspaceTrustManageButton", + "extensionInstallWorkspaceTrustMessage", "VS Code for Web", "limited support", { @@ -6024,7 +6094,8 @@ ] }, "non web extensions detail", - "non web extensions" + "non web extensions", + "main.notFound" ], "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService": [ "notFoundReleaseExtension", @@ -6034,6 +6105,15 @@ "backupTrackerBackupFailed", "backupTrackerConfirmFailed", "backupErrorDetails", + { + "key": "ok", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "shutdownForceClose", + "shutdownForceQuit", + "shutdownForceReload", "backupBeforeShutdownMessage", "backupBeforeShutdownDetail", "saveBeforeShutdown", @@ -6069,13 +6149,27 @@ "extensionStopVetoDetailsMany", "extensionService.autoRestart", "extensionService.crash", - "restart" + "restart", + "activation" ], "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ "extensionCache.invalid", "reloadWindow" ], + "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ + "extensionHost.startupFailDebug", + "extensionHost.startupFail", + "reloadWindow", + "join.extensionDevelopment" + ], + "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ + "showLanguagePackExtensions", + "searchMarketplace", + "installAndRestartMessage", + "installAndRestart" + ], "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService": [ + "lifecycleVeto", "unableToOpenWindowError", "unableToOpenWindow", "unableToOpenWindowDetail", @@ -6086,9 +6180,19 @@ ] } ], - "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ - "openLogsFolder", - "openExtensionLogsFolder" + "vs/workbench/contrib/localization/common/localization.contribution": [ + "vscode.extension.contributes.localizations", + "vscode.extension.contributes.localizations.languageId", + "vscode.extension.contributes.localizations.languageName", + "vscode.extension.contributes.localizations.languageNameLocalized", + "vscode.extension.contributes.localizations.translations", + "vscode.extension.contributes.localizations.translations.id", + "vscode.extension.contributes.localizations.translations.id.pattern", + "vscode.extension.contributes.localizations.translations.path", + "language id", + "localizations language name", + "localizations localized language name", + "localizations" ], "vs/editor/common/editorContextKeys": [ "editorTextFocus", @@ -6097,9 +6201,17 @@ "editorReadonly", "inDiffEditor", "isEmbeddedDiffEditor", + "inMultiDiffEditor", + "multiDiffEditorAllCollapsed", + "diffEditorHasChanges", "comparingMovedCode", "accessibleDiffViewerVisible", "diffEditorRenderSideBySideInlineBreakpointReached", + "diffEditorInlineMode", + "diffEditorOriginalWritable", + "diffEditorModifiedWritable", + "diffEditorOriginalUri", + "diffEditorModifiedUri", "editorColumnSelection", "editorHasSelection", "editorHasMultipleSelections", @@ -6131,65 +6243,31 @@ "editorHasMultipleDocumentFormattingProvider", "editorHasMultipleDocumentSelectionFormattingProvider" ], - "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ - "extensionHost.startupFailDebug", - "extensionHost.startupFail", - "reloadWindow", - "join.extensionDevelopment" - ], "vs/workbench/common/editor": [ "promptOpenWith.defaultEditor.displayName", "builtinProviderDisplayName", "openLargeFile", "configureEditorLargeFileConfirmation" ], - "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ - "showLanguagePackExtensions", - "searchMarketplace", - "installAndRestartMessage", - "installAndRestart" - ], - "vs/workbench/contrib/localization/common/localization.contribution": [ - "vscode.extension.contributes.localizations", - "vscode.extension.contributes.localizations.languageId", - "vscode.extension.contributes.localizations.languageName", - "vscode.extension.contributes.localizations.languageNameLocalized", - "vscode.extension.contributes.localizations.translations", - "vscode.extension.contributes.localizations.translations.id", - "vscode.extension.contributes.localizations.translations.id.pattern", - "vscode.extension.contributes.localizations.translations.path" - ], "vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard": [ "actions.pasteSelectionClipboard" ], "vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate": [ "startDebugTextMate" ], - "vs/workbench/contrib/issue/common/issue.contribution": [ - { - "key": "reportIssueInEnglish", - "comment": [ - "Translate this to \"Report Issue in English\" in all languages please!" - ] - }, - { - "key": "miReportIssue", - "comment": [ - "&& denotes a mnemonic", - "Translate this to \"Report Issue in English\" in all languages please!" - ] - } + "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ + "openLogsFolder", + "openExtensionLogsFolder" + ], + "vs/workbench/browser/editor": [ + "preview", + "pinned" ], "vs/workbench/contrib/extensions/electron-sandbox/runtimeExtensionsEditor": [ "extensionHostProfileStart", "stopExtensionHostProfileStart", "saveExtensionHostProfile", - "saveprofile.dialogTitle", - "saveprofile.saveButton" - ], - "vs/workbench/browser/editor": [ - "preview", - "pinned" + "saveprofile.dialogTitle" ], "vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction": [ "debugExtensionHost", @@ -6208,6 +6286,7 @@ "cleanUpExtensionsFolder" ], "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ + "runtimeExtensionEditorLabelIcon", "extensionsInputName" ], "vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService": [ @@ -6229,6 +6308,21 @@ "unresponsive-exthost", "show" ], + "vs/workbench/contrib/issue/common/issue.contribution": [ + { + "key": "miReportIssue", + "comment": [ + "&& denotes a mnemonic", + "Translate this to \"Report Issue in English\" in all languages please!" + ] + }, + { + "key": "reportIssueInEnglish", + "comment": [ + "Translate this to \"Report Issue in English\" in all languages please!" + ] + } + ], "vs/workbench/contrib/terminal/common/terminal": [ "vscode.extension.contributes.terminal", "vscode.extension.contributes.terminal.profiles", @@ -6238,6 +6332,11 @@ "vscode.extension.contributes.terminal.types.icon.light", "vscode.extension.contributes.terminal.types.icon.dark" ], + "vs/workbench/contrib/issue/browser/issueQuickAccess": [ + "reportExtensionMarketplace", + "extensions", + "contributedIssuePage" + ], "vs/workbench/services/dialogs/browser/simpleFileDialog": [ "openLocalFile", "saveLocalFile", @@ -6288,6 +6387,84 @@ "Variable", "symbolAriaLabel" ], + "vs/workbench/services/themes/common/themeConfiguration": [ + { + "key": "colorTheme", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "colorThemeError", + { + "key": "preferredDarkColorTheme", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "colorThemeError", + { + "key": "preferredLightColorTheme", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "colorThemeError", + { + "key": "preferredHCDarkColorTheme", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "colorThemeError", + { + "key": "preferredHCLightColorTheme", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "colorThemeError", + { + "key": "detectColorScheme", + "comment": [ + "{0} and {1} will become links to other settings." + ] + }, + "workbenchColors", + "iconTheme", + "noIconThemeLabel", + "noIconThemeDesc", + "iconThemeError", + "productIconTheme", + "defaultProductIconThemeLabel", + "defaultProductIconThemeDesc", + "productIconThemeError", + { + "key": "autoDetectHighContrast", + "comment": [ + "{0} and {1} will become links to other settings." + ] + }, + "editorColors.comments", + "editorColors.strings", + "editorColors.keywords", + "editorColors.numbers", + "editorColors.types", + "editorColors.functions", + "editorColors.variables", + "editorColors.textMateRules", + "editorColors.semanticHighlighting", + "editorColors.semanticHighlighting.deprecationMessage", + { + "key": "editorColors.semanticHighlighting.deprecationMessageMarkdown", + "comment": [ + "{0} will become a link to another setting." + ] + }, + "editorColors", + "editorColors.semanticHighlighting.enabled", + "editorColors.semanticHighlighting.rules", + "semanticTokenColors" + ], "vs/workbench/services/userDataSync/common/userDataSync": [ "settings", "keybindings", @@ -6298,20 +6475,8 @@ "profiles", "workspace state label", "syncViewIcon", - "download sync activity title", - "sync category" - ], - "vs/workbench/contrib/tasks/common/tasks": [ - "tasks.taskRunningContext", - "tasksCategory", - "TaskDefinition.missingRequiredProperty" - ], - "vs/workbench/contrib/tasks/common/taskService": [ - "tasks.customExecutionSupported", - "tasks.shellExecutionSupported", - "tasks.taskCommandsRegistered", - "tasks.processExecutionSupported", - "tasks.serverlessWebContext" + "sync category", + "download sync activity title" ], "vs/workbench/contrib/performance/electron-sandbox/startupProfiler": [ "prof.message", @@ -6332,12 +6497,6 @@ ] } ], - "vs/workbench/common/views": [ - "views log", - "defaultViewIcon", - "duplicateId", - "treeView.notRegistered" - ], "vs/workbench/contrib/terminal/common/terminalContextKey": [ "terminalFocusContextKey", "terminalFocusInAnyContextKey", @@ -6356,45 +6515,18 @@ "inTerminalRunCommandPickerContextKey", "terminalShellIntegrationEnabled" ], - "vs/platform/audioCues/browser/audioCueService": [ - "audioCues.lineHasError.name", - "audioCues.lineHasWarning.name", - "audioCues.lineHasFoldedArea.name", - "audioCues.lineHasBreakpoint.name", - "audioCues.lineHasInlineSuggestion.name", - "audioCues.terminalQuickFix.name", - "audioCues.onDebugBreak.name", - "audioCues.noInlayHints", - "audioCues.taskCompleted", - "audioCues.taskFailed", - "audioCues.terminalCommandFailed", - "audioCues.terminalBell", - "audioCues.notebookCellCompleted", - "audioCues.notebookCellFailed", - "audioCues.diffLineInserted", - "audioCues.diffLineDeleted", - "audioCues.diffLineModified", - "audioCues.chatRequestSent", - "audioCues.chatResponseReceived", - "audioCues.chatResponsePending", - "audioCues.clear", - "audioCues.save", - "audioCues.format" - ], - "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ - "openToolsLabel", - "iframeWebviewAlert" - ], - "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ - "revealInWindows", - "revealInMac", - "openContainer" + "vs/workbench/contrib/tasks/common/taskService": [ + "tasks.customExecutionSupported", + "tasks.shellExecutionSupported", + "tasks.taskCommandsRegistered", + "tasks.processExecutionSupported", + "tasks.serverlessWebContext" ], - "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ - "mergeEditor", - "merge.dev.openState", - "mergeEditor.enterJSON", - "merge.dev.openSelectionInTemporaryMergeEditor" + "vs/workbench/common/views": [ + "views log", + "defaultViewIcon", + "duplicateId", + "treeView.notRegistered" ], "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ "TerminalTaskSystem.unknownError", @@ -6440,9 +6572,15 @@ "reuseTerminal" ], "vs/workbench/contrib/tasks/browser/abstractTaskService": [ - "ConfigureTaskRunnerAction.label", "tasks", "TaskService.pickBuildTaskForLabel", + "taskEvent", + "TaskService.skippingReconnection", + "TaskService.notConnecting", + "TaskService.reconnecting", + "TaskService.reconnected", + "TaskService.noTasks", + "TaskService.reconnectingTasks", "runTask.arg", "runTask.label", "runTask.type", @@ -6451,6 +6589,15 @@ "showOutput", "TaskServer.folderIgnored", "TaskService.providerUnavailable", + "taskService.gettingCachedTasks", + "taskService.getSavedTasks", + "taskService.getSavedTasks.reading", + "taskService.getSavedTasks.error", + "taskService.getSavedTasks.resolved", + "taskService.getSavedTasks.unresolved", + "taskService.removePersistentTask", + "taskService.setPersistentTask", + "savePersistentTask", "TaskService.noTestTask1", "TaskService.noTestTask2", "TaskService.noBuildTask1", @@ -6473,7 +6620,12 @@ "&& denotes a mnemonic" ] }, - "saveBeforeRun.dontSave", + { + "key": "saveBeforeRun.dontSave", + "comment": [ + "&& denotes a mnemonic" + ] + }, "TaskSystem.activeSame.noBackground", "terminateTask", "restartTask", @@ -6493,7 +6645,6 @@ "TaskSystem.versionWorkspaceFile", "TasksSystem.locationUserConfig", "TaskSystem.versionSettings", - "TaskSystem.workspaceFolderError", "TaskSystem.configurationErrors", "taskService.ignoreingFolder", "TaskSystem.invalidTaskJson", @@ -6537,25 +6688,125 @@ "taskService.upgradeVersion", "taskService.upgradeVersionPlural", "taskService.openDiff", - "taskService.openDiffs" + "taskService.openDiffs", + "ConfigureTaskRunnerAction.label" + ], + "vs/platform/accessibilitySignal/browser/accessibilitySignalService": [ + "accessibilitySignals.positionHasError.name", + "accessibility.signals.positionHasError", + "accessibilitySignals.positionHasWarning.name", + "accessibility.signals.positionHasWarning", + "accessibilitySignals.lineHasError.name", + "accessibility.signals.lineHasError", + "accessibilitySignals.lineHasWarning.name", + "accessibility.signals.lineHasWarning", + "accessibilitySignals.lineHasFoldedArea.name", + "accessibility.signals.lineHasFoldedArea", + "accessibilitySignals.lineHasBreakpoint.name", + "accessibility.signals.lineHasBreakpoint", + "accessibilitySignals.lineHasInlineSuggestion.name", + "accessibilitySignals.terminalQuickFix.name", + "accessibility.signals.terminalQuickFix", + "accessibilitySignals.onDebugBreak.name", + "accessibility.signals.onDebugBreak", + "accessibilitySignals.noInlayHints", + "accessibility.signals.noInlayHints", + "accessibilitySignals.taskCompleted", + "accessibility.signals.taskCompleted", + "accessibilitySignals.taskFailed", + "accessibility.signals.taskFailed", + "accessibilitySignals.terminalCommandFailed", + "accessibility.signals.terminalCommandFailed", + "accessibilitySignals.terminalBell", + "accessibility.signals.terminalBell", + "accessibilitySignals.notebookCellCompleted", + "accessibility.signals.notebookCellCompleted", + "accessibilitySignals.notebookCellFailed", + "accessibility.signals.notebookCellFailed", + "accessibilitySignals.diffLineInserted", + "accessibilitySignals.diffLineDeleted", + "accessibilitySignals.diffLineModified", + "accessibilitySignals.chatRequestSent", + "accessibility.signals.chatRequestSent", + "accessibilitySignals.chatResponseReceived", + "accessibilitySignals.progress", + "accessibility.signals.progress", + "accessibilitySignals.clear", + "accessibility.signals.clear", + "accessibilitySignals.save", + "accessibility.signals.save", + "accessibilitySignals.format", + "accessibility.signals.format", + "accessibilitySignals.voiceRecordingStarted", + "accessibilitySignals.voiceRecordingStopped" + ], + "vs/workbench/contrib/tasks/common/tasks": [ + "tasks.taskRunningContext", + "TaskDefinition.missingRequiredProperty", + "tasksCategory" + ], + "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ + "openToolsDescription", + "iframeWebviewAlert", + "openToolsLabel" + ], + "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ + "mergeEditor.enterJSON", + "mergeEditor", + "merge.dev.openState", + "merge.dev.openSelectionInTemporaryMergeEditor" + ], + "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ + "revealInWindows", + "revealInMac", + "openContainer" + ], + "vs/workbench/contrib/multiDiffEditor/browser/actions": [ + "goToFile", + "collapseAllDiffs", + "ExpandAllDiffs" + ], + "vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver": [ + "viewChanges" + ], + "vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions": [ + "holdForSpeech" + ], + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput": [ + "name", + { + "key": "files", + "comment": [ + "the number of files being shown" + ] + } ], "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions": [ "voiceChatGettingReady", "voiceChatInProgress", "quickVoiceChatInProgress", "inlineVoiceChatInProgress", + "terminalVoiceChatInProgress", "voiceChatInViewInProgress", "voiceChatInEditorInProgress", "listening", + "confirmInstallDetail", + "voice.keywordActivation.off", + "voice.keywordActivation.chatInView", + "voice.keywordActivation.quickChat", + "voice.keywordActivation.inlineChat", + "voice.keywordActivation.chatInContext", + "voice.keywordActivation", + "keywordActivation.status.name", + "keywordActivation.status.active", + "keywordActivation.status.inactive", "workbench.action.chat.voiceChatInView.label", + "workbench.action.chat.holdToVoiceChatInChatView.label", "workbench.action.chat.inlineVoiceChat", "workbench.action.chat.quickVoiceChat.label", "workbench.action.chat.startVoiceChat.label", + "workbench.action.chat.startVoiceChat.label", "workbench.action.chat.stopListening.label", - "workbench.action.chat.stopListeningInChatView.label", - "workbench.action.chat.stopListeningInChatEditor.label", - "workbench.action.chat.stopListeningInQuickChat.label", - "workbench.action.chat.stopListeningInInlineChat.label", "workbench.action.chat.stopListeningAndSubmit.label" ], "vs/workbench/api/common/extHostTelemetry": [ @@ -6580,6 +6831,10 @@ "worker", "local" ], + "vs/workbench/api/common/extHostLanguageModels": [ + "chatAccessWithJustification", + "chatAccess" + ], "vs/platform/terminal/node/terminalProcess": [ "launchFail.cwdNotDirectory", "launchFail.cwdDoesNotExist", @@ -6589,93 +6844,34 @@ "vs/workbench/api/node/extHostDebugService": [ "debug.terminal.title" ], - "vs/platform/extensions/common/extensionValidator": [ - "extensionDescription.publisher", - "extensionDescription.name", - "extensionDescription.version", - "extensionDescription.engines", - "extensionDescription.engines.vscode", - "extensionDescription.extensionDependencies", - "extensionDescription.activationEvents1", - "extensionDescription.activationEvents2", - "extensionDescription.extensionKind", - "extensionDescription.main1", - "extensionDescription.main2", - "extensionDescription.browser1", - "extensionDescription.browser2", - "notSemver", - "versionSyntax", - "versionSpecificity1", - "versionSpecificity2", - "versionMismatch" + "vs/platform/dialogs/electron-main/dialogMainService": [ + "open", + "openFolder", + "openFile", + "openWorkspaceTitle", + { + "key": "openWorkspace", + "comment": [ + "&& denotes a mnemonic" + ] + } ], - "vs/platform/extensionManagement/common/extensionNls": [ - "missingNLSKey" + "vs/platform/shell/node/shellEnv": [ + "resolveShellEnvTimeout", + "resolveShellEnvError", + "resolveShellEnvExitError" ], - "vs/base/common/jsonErrorMessages": [ - "error.invalidSymbol", - "error.invalidNumberFormat", - "error.propertyNameExpected", - "error.valueExpected", - "error.colonExpected", - "error.commaExpected", - "error.closeBraceExpected", - "error.closeBracketExpected", - "error.endOfFileExpected" + "vs/platform/externalTerminal/node/externalTerminalService": [ + "console.title", + "mac.terminal.script.failed", + "mac.terminal.type.not.supported", + "press.any.key", + "linux.term.failed", + "ext.term.app.not.found" ], - "vs/base/node/zip": [ - "invalid file", - "incompleteExtract", - "notFound" - ], - "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ - "MarketPlaceDisabled", - "malicious extension", - "notFoundDeprecatedReplacementExtension", - "incompatible platform", - "notFoundReleaseExtension", - "notFoundCompatibleDependency", - "singleDependentError", - "twoDependentsError", - "multipleDependentsError", - "singleIndirectDependentError", - "twoIndirectDependentsError", - "multipleIndirectDependentsError" - ], - "vs/platform/extensionManagement/node/extensionManagementUtil": [ - "invalidManifest" - ], - "vs/platform/files/common/io": [ - "fileTooLargeError" - ], - "vs/platform/shell/node/shellEnv": [ - "resolveShellEnvTimeout", - "resolveShellEnvError", - "resolveShellEnvExitError" - ], - "vs/platform/dialogs/electron-main/dialogMainService": [ - "open", - "openFolder", - "openFile", - "openWorkspaceTitle", - { - "key": "openWorkspace", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/platform/files/electron-main/diskFileSystemProviderServer": [ - "binFailed", - "trashFailed" - ], - "vs/platform/externalTerminal/node/externalTerminalService": [ - "console.title", - "mac.terminal.script.failed", - "mac.terminal.type.not.supported", - "press.any.key", - "linux.term.failed", - "ext.term.app.not.found" + "vs/platform/files/electron-main/diskFileSystemProviderServer": [ + "binFailed", + "trashFailed" ], "vs/platform/issue/electron-main/issueMainService": [ "local", @@ -6728,6 +6924,20 @@ "sourceMissing" ], "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ + { + "key": "clearButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "cancel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmClearRecentsMessage", + "confirmClearDetail", "newWindow", "newWindowDesc", "recentFoldersAndWorkspaces", @@ -6778,9 +6988,84 @@ "confirmOpenDetail", "doNotAskAgain" ], + "vs/platform/files/common/io": [ + "fileTooLargeError" + ], + "vs/platform/extensions/common/extensionValidator": [ + "extensionDescription.publisher", + "extensionDescription.name", + "extensionDescription.version", + "extensionDescription.engines", + "extensionDescription.engines.vscode", + "extensionDescription.extensionDependencies", + "extensionDescription.activationEvents1", + "extensionDescription.activationEvents2", + "extensionDescription.extensionKind", + "extensionDescription.main1", + "extensionDescription.main2", + "extensionDescription.browser1", + "extensionDescription.browser2", + "notSemver", + "versionSyntax", + "versionSpecificity1", + "versionSpecificity2", + "versionMismatch" + ], + "vs/base/common/jsonErrorMessages": [ + "error.invalidSymbol", + "error.invalidNumberFormat", + "error.propertyNameExpected", + "error.valueExpected", + "error.colonExpected", + "error.commaExpected", + "error.closeBraceExpected", + "error.closeBracketExpected", + "error.endOfFileExpected" + ], + "vs/platform/extensionManagement/common/extensionNls": [ + "missingNLSKey" + ], + "vs/base/node/zip": [ + "invalid file", + "incompleteExtract", + "notFound" + ], + "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ + "MarketPlaceDisabled", + "malicious extension", + "notFoundDeprecatedReplacementExtension", + "incompatible platform", + "notFoundReleaseExtension", + "notFoundCompatibleDependency", + "singleDependentError", + "twoDependentsError", + "multipleDependentsError", + "singleIndirectDependentError", + "twoIndirectDependentsError", + "multipleIndirectDependentsError" + ], + "vs/platform/extensionManagement/node/extensionManagementUtil": [ + "invalidManifest" + ], "vs/base/browser/ui/button/button": [ "button dropdown more actions" ], + "vs/platform/theme/common/iconRegistry": [ + "iconDefinition.fontId", + "iconDefinition.fontCharacter", + "widgetClose", + "previousChangeIcon", + "nextChangeIcon" + ], + "vs/base/browser/ui/tree/abstractTree": [ + "filter", + "fuzzySearch", + "type to filter", + "type to search", + "type to search", + "close", + "not found" + ], "vs/base/common/date": [ "date.fromNow.in", "date.fromNow.now", @@ -6835,7 +7120,16 @@ "date.fromNow.years.singular.fullWord", "date.fromNow.years.singular", "date.fromNow.years.plural.fullWord", - "date.fromNow.years.plural" + "date.fromNow.years.plural", + "duration.ms.full", + "duration.ms", + "duration.s.full", + "duration.s", + "duration.m.full", + "duration.m", + "duration.h.full", + "duration.h", + "duration.d" ], "vs/platform/userDataSync/common/keybindingsSync": [ "errorInvalidSettings", @@ -6862,22 +7156,6 @@ "session expired", "turned off machine" ], - "vs/base/browser/ui/tree/abstractTree": [ - "filter", - "fuzzySearch", - "type to filter", - "type to search", - "type to search", - "close", - "not found" - ], - "vs/platform/theme/common/iconRegistry": [ - "iconDefinition.fontId", - "iconDefinition.fontCharacter", - "widgetClose", - "previousChangeIcon", - "nextChangeIcon" - ], "vs/editor/common/core/editorColorRegistry": [ "lineHighlight", "lineHighlightBorderBox", @@ -6887,6 +7165,10 @@ "symbolHighlightBorder", "caret", "editorCursorBackground", + "editorMultiCursorPrimaryForeground", + "editorMultiCursorPrimaryBackground", + "editorMultiCursorSecondaryForeground", + "editorMultiCursorSecondaryBackground", "editorWhitespaces", "editorLineNumbers", "editorIndentGuides", @@ -6947,31 +7229,6 @@ "editorUnicodeHighlight.border", "editorUnicodeHighlight.background" ], - "vs/editor/browser/coreCommands": [ - "stickydesc", - "stickydesc", - "removedCursor" - ], - "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ - "toggleCollapseUnchangedRegions", - "toggleShowMovedCodeBlocks", - "toggleUseInlineViewWhenSpaceIsLimited", - "useInlineViewWhenSpaceIsLimited", - "showMoves", - "diffEditor", - "switchSide", - "exitCompareMove", - "collapseAllUnchangedRegions", - "showAllUnchangedRegions", - "accessibleDiffViewer", - "editor.action.accessibleDiffViewer.next", - "Open Accessible Diff Viewer", - "editor.action.accessibleDiffViewer.prev" - ], - "vs/editor/browser/widget/codeEditorWidget": [ - "cursors.maximum", - "goToSetting" - ], "vs/platform/contextkey/common/scanner": [ "contextkey.scanner.hint.didYouMean1", "contextkey.scanner.hint.didYouMean2", @@ -6979,6 +7236,18 @@ "contextkey.scanner.hint.didYouForgetToOpenOrCloseQuote", "contextkey.scanner.hint.didYouForgetToEscapeSlash" ], + "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ + "useInlineViewWhenSpaceIsLimited", + "showMoves", + "revertHunk", + "revertSelection", + "Open Accessible Diff Viewer" + ], + "vs/editor/browser/coreCommands": [ + "stickydesc", + "stickydesc", + "removedCursor" + ], "vs/editor/contrib/anchorSelect/browser/anchorSelect": [ "selectionAnchor", "anchorSet", @@ -6997,7 +7266,8 @@ "comment": [ "&& denotes a mnemonic" ] - } + }, + "smartSelect.selectToBracketDescription" ], "vs/editor/contrib/caretOperations/browser/caretOperations": [ "caret.moveLeft", @@ -7025,11 +7295,6 @@ "actions.clipboard.copyLabel", "actions.clipboard.copyLabel", "actions.clipboard.copyLabel", - "copy as", - "copy as", - "share", - "share", - "share", { "key": "miPaste", "comment": [ @@ -7039,7 +7304,16 @@ "actions.clipboard.pasteLabel", "actions.clipboard.pasteLabel", "actions.clipboard.pasteLabel", - "actions.clipboard.copyWithSyntaxHighlightingLabel" + "actions.clipboard.copyWithSyntaxHighlightingLabel", + "copy as", + "copy as", + "share", + "share", + "share" + ], + "vs/editor/browser/widget/codeEditor/codeEditorWidget": [ + "cursors.maximum", + "goToSetting" ], "vs/editor/contrib/codeAction/browser/codeActionContributions": [ "showCodeActionHeaders", @@ -7049,8 +7323,25 @@ "showLensOnLine", "placeHolder" ], + "vs/editor/contrib/comment/browser/comment": [ + "comment.line", + { + "key": "miToggleLineComment", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "comment.line.add", + "comment.line.remove", + "comment.block", + { + "key": "miToggleBlockComment", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], "vs/editor/contrib/colorPicker/browser/standaloneColorPickerActions": [ - "showOrFocusStandaloneColorPicker", { "key": "mishowOrFocusStandaloneColorPicker", "comment": [ @@ -7068,25 +7359,15 @@ "comment": [ "Action that inserts color with standalone color picker" ] - } - ], - "vs/editor/contrib/comment/browser/comment": [ - "comment.line", - { - "key": "miToggleLineComment", - "comment": [ - "&& denotes a mnemonic" - ] }, - "comment.line.add", - "comment.line.remove", - "comment.block", - { - "key": "miToggleBlockComment", - "comment": [ - "&& denotes a mnemonic" - ] - } + "showOrFocusStandaloneColorPicker", + "showOrFocusStandaloneColorPickerDescription", + "hideColorPickerDescription", + "insertColorWithStandaloneColorPickerDescription" + ], + "vs/editor/contrib/cursorUndo/browser/cursorUndo": [ + "cursor.undo", + "cursor.redo" ], "vs/editor/contrib/contextmenu/browser/contextmenu": [ "context.minimap.minimap", @@ -7100,36 +7381,10 @@ "context.minimap.slider.always", "action.showContextMenu.label" ], - "vs/editor/contrib/cursorUndo/browser/cursorUndo": [ - "cursor.undo", - "cursor.redo" - ], "vs/editor/contrib/dropOrPasteInto/browser/copyPasteContribution": [ + "pasteAs.kind", "pasteAs", - "pasteAs.id" - ], - "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution": [ - "defaultProviderDescription" - ], - "vs/editor/contrib/folding/browser/folding": [ - "unfoldAction.label", - "unFoldRecursivelyAction.label", - "foldAction.label", - "toggleFoldAction.label", - "foldRecursivelyAction.label", - "foldAllBlockComments.label", - "foldAllMarkerRegions.label", - "unfoldAllMarkerRegions.label", - "foldAllExcept.label", - "unfoldAllExcept.label", - "foldAllAction.label", - "unfoldAllAction.label", - "gotoParentFold.label", - "gotoPreviousFold.label", - "gotoNextFold.label", - "createManualFoldRange.label", - "removeManualFoldingRanges.label", - "foldLevelAction.label" + "pasteAsText" ], "vs/editor/contrib/find/browser/findController": [ "too.large.for.replaceall", @@ -7140,10 +7395,6 @@ "&& denotes a mnemonic" ] }, - "actions.find.isRegexOverride", - "actions.find.wholeWordOverride", - "actions.find.matchCaseOverride", - "actions.find.preserveCaseOverride", "startFindWithArgsAction", "startFindWithSelectionAction", "findNextMatchAction", @@ -7163,33 +7414,55 @@ ] } ], + "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution": [ + "defaultProviderDescription" + ], "vs/editor/contrib/fontZoom/browser/fontZoom": [ "EditorFontZoomIn.label", "EditorFontZoomOut.label", "EditorFontZoomReset.label" ], + "vs/editor/contrib/folding/browser/folding": [ + "unfoldAction.label", + "unFoldRecursivelyAction.label", + "foldAction.label", + "toggleFoldAction.label", + "foldRecursivelyAction.label", + "foldAllBlockComments.label", + "foldAllMarkerRegions.label", + "unfoldAllMarkerRegions.label", + "foldAllExcept.label", + "unfoldAllExcept.label", + "foldAllAction.label", + "unfoldAllAction.label", + "gotoParentFold.label", + "gotoPreviousFold.label", + "gotoNextFold.label", + "createManualFoldRange.label", + "removeManualFoldingRanges.label", + "foldLevelAction.label" + ], "vs/editor/contrib/format/browser/formatActions": [ "formatDocument.label", "formatSelection.label" ], + "vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition": [ + "multipleResults" + ], "vs/editor/contrib/gotoSymbol/browser/goToCommands": [ "peek.submenu", "def.title", "noResultWord", "generic.noResults", - "actions.goToDecl.label", { "key": "miGotoDefinition", "comment": [ "&& denotes a mnemonic" ] }, - "actions.goToDeclToSide.label", - "actions.previewDecl.label", "decl.title", "decl.noResultWord", "decl.generic.noResults", - "actions.goToDeclaration.label", { "key": "miGotoDeclaration", "comment": [ @@ -7198,32 +7471,26 @@ }, "decl.noResultWord", "decl.generic.noResults", - "actions.peekDecl.label", "typedef.title", "goToTypeDefinition.noResultWord", "goToTypeDefinition.generic.noResults", - "actions.goToTypeDefinition.label", { "key": "miGotoTypeDefinition", "comment": [ "&& denotes a mnemonic" ] }, - "actions.peekTypeDefinition.label", "impl.title", "goToImplementation.noResultWord", "goToImplementation.generic.noResults", - "actions.goToImplementation.label", { "key": "miGotoImplementation", "comment": [ "&& denotes a mnemonic" ] }, - "actions.peekImplementation.label", "references.no", "references.noGeneric", - "goToReferences.label", { "key": "miGotoReference", "comment": [ @@ -7231,15 +7498,22 @@ ] }, "ref.title", - "references.action.label", "ref.title", - "label.generic", "generic.title", "generic.noResult", - "ref.title" - ], - "vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition": [ - "multipleResults" + "ref.title", + "actions.goToDecl.label", + "actions.goToDeclToSide.label", + "actions.previewDecl.label", + "actions.goToDeclaration.label", + "actions.peekDecl.label", + "actions.goToTypeDefinition.label", + "actions.peekTypeDefinition.label", + "actions.goToImplementation.label", + "actions.peekImplementation.label", + "goToReferences.label", + "references.action.label", + "label.generic" ], "vs/editor/contrib/gotoError/browser/gotoError": [ "markerAction.next.label", @@ -7261,72 +7535,6 @@ ] } ], - "vs/editor/contrib/hover/browser/hover": [ - { - "key": "showOrFocusHover", - "comment": [ - "Label for action that will trigger the showing/focusing of a hover in the editor.", - "If the hover is not visible, it will show the hover.", - "This allows for users to show the hover without using the mouse.", - "If the hover is already visible, it will take focus." - ] - }, - { - "key": "showDefinitionPreviewHover", - "comment": [ - "Label for action that will trigger the showing of definition preview hover in the editor.", - "This allows for users to show the definition preview hover without using the mouse." - ] - }, - { - "key": "scrollUpHover", - "comment": [ - "Action that allows to scroll up in the hover widget with the up arrow when the hover widget is focused." - ] - }, - { - "key": "scrollDownHover", - "comment": [ - "Action that allows to scroll down in the hover widget with the up arrow when the hover widget is focused." - ] - }, - { - "key": "scrollLeftHover", - "comment": [ - "Action that allows to scroll left in the hover widget with the left arrow when the hover widget is focused." - ] - }, - { - "key": "scrollRightHover", - "comment": [ - "Action that allows to scroll right in the hover widget with the right arrow when the hover widget is focused." - ] - }, - { - "key": "pageUpHover", - "comment": [ - "Action that allows to page up in the hover widget with the page up command when the hover widget is focused." - ] - }, - { - "key": "pageDownHover", - "comment": [ - "Action that allows to page down in the hover widget with the page down command when the hover widget is focused." - ] - }, - { - "key": "goToTopHover", - "comment": [ - "Action that allows to go to the top of the hover widget with the home command when the hover widget is focused." - ] - }, - { - "key": "goToBottomHover", - "comment": [ - "Action that allows to go to the bottom in the hover widget with the end command when the hover widget is focused." - ] - } - ], "vs/editor/contrib/indentation/browser/indentation": [ "indentationToSpaces", "indentationToTabs", @@ -7344,7 +7552,15 @@ "changeTabDisplaySize", "detectIndentation", "editor.reindentlines", - "editor.reindentselectedlines" + "editor.reindentselectedlines", + "indentationToSpacesDescription", + "indentationToTabsDescription", + "indentUsingTabsDescription", + "indentUsingSpacesDescription", + "changeTabDisplaySizeDescription", + "detectIndentationDescription", + "editor.reindentlinesDescription", + "editor.reindentselectedlinesDescription" ], "vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace": [ "InPlaceReplaceAction.previous.label", @@ -7407,6 +7623,7 @@ "editor.transformToTitlecase", "editor.transformToSnakecase", "editor.transformToCamelcase", + "editor.transformToPascalcase", "editor.transformToKebabcase" ], "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ @@ -7492,7 +7709,9 @@ "rename.failedApply", "rename.failed", "rename.label", - "enablePreview" + "enablePreview", + "focusNextRenameSuggestion", + "focusPreviousRenameSuggestion" ], "vs/editor/contrib/smartSelect/browser/smartSelect": [ "smartSelect.expand", @@ -7516,9 +7735,6 @@ "hasPrevTabstop", "next" ], - "vs/editor/contrib/tokenization/browser/tokenization": [ - "forceRetokenize" - ], "vs/editor/contrib/suggest/browser/suggestController": [ "aria.alert.snippet", "suggest.trigger.label", @@ -7531,21 +7747,26 @@ "detail.less", "suggest.reset.label" ], + "vs/editor/contrib/tokenization/browser/tokenization": [ + "forceRetokenize" + ], "vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode": [ + "toggle.tabMovesFocus.on", + "toggle.tabMovesFocus.off", { "key": "toggle.tabMovesFocus", "comment": [ "Turn on/off use of tab key for moving focus around VS Code" ] }, - "toggle.tabMovesFocus.on", - "toggle.tabMovesFocus.off" + "tabMovesFocusDescriptions" ], "vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter": [ "warningIcon", "unicodeHighlighting.thisDocumentHasManyNonBasicAsciiUnicodeCharacters", "unicodeHighlighting.thisDocumentHasManyAmbiguousUnicodeCharacters", "unicodeHighlighting.thisDocumentHasManyInvisibleUnicodeCharacters", + "unicodeHighlight.configureUnicodeHighlightOptions", "unicodeHighlight.characterIsAmbiguousASCII", "unicodeHighlight.characterIsAmbiguous", "unicodeHighlight.characterIsInvisible", @@ -7564,8 +7785,7 @@ "action.unicodeHighlight.showExcludeOptions", "unicodeHighlight.excludeInvisibleCharFromBeingHighlighted", "unicodeHighlight.excludeCharFromBeingHighlighted", - "unicodeHighlight.allowCommonCharactersInLanguage", - "unicodeHighlight.configureUnicodeHighlightOptions" + "unicodeHighlight.allowCommonCharactersInLanguage" ], "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ "unusualLineTerminators.title", @@ -7579,14 +7799,14 @@ }, "unusualLineTerminators.ignore" ], + "vs/editor/contrib/wordOperations/browser/wordOperations": [ + "deleteInsideWord" + ], "vs/editor/contrib/wordHighlighter/browser/wordHighlighter": [ "wordHighlight.next.label", "wordHighlight.previous.label", "wordHighlight.trigger.label" ], - "vs/editor/contrib/wordOperations/browser/wordOperations": [ - "deleteInsideWord" - ], "vs/editor/contrib/readOnlyMessage/browser/contribution": [ "editor.simple.readonly", "editor.readonly" @@ -7611,12 +7831,12 @@ "tabFocusModeOffMsg", "tabFocusModeOffMsgNoKb", "showAccessibilityHelpAction", - "saveAudioCueDisabled", - "saveAudioCueAlways", - "saveAudioCueUserGesture", - "formatAudioCueDisabled", - "formatAudioCueAlways", - "formatAudioCueUserGesture", + "listSignalSoundsCommand", + "listAnnouncementsCommand", + "quickChatCommand", + "quickChatCommandNoKb", + "startInlineChatCommand", + "startInlineChatCommandNoKb", "inspectTokens", "gotoLineActionLabel", "helpQuickAccess", @@ -7629,74 +7849,6 @@ "toggleHighContrast", "bulkEditServiceSummary" ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ - "toggleAuxiliaryIconRight", - "toggleAuxiliaryIconRightOn", - "toggleAuxiliaryIconLeft", - "toggleAuxiliaryIconLeftOn", - "toggleAuxiliaryBar", - "secondary sidebar", - { - "key": "secondary sidebar mnemonic", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "focusAuxiliaryBar", - "toggleSecondarySideBar", - "toggleSecondarySideBar", - "hideAuxiliaryBar" - ], - "vs/workbench/browser/parts/panel/panelActions": [ - "maximizeIcon", - "restoreIcon", - "closeIcon", - "togglePanelOffIcon", - "togglePanelOnIcon", - "togglePanelVisibility", - "toggle panel", - { - "key": "toggle panel mnemonic", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "focusPanel", - "focusPanel", - "positionPanelLeft", - "positionPanelLeftShort", - "positionPanelRight", - "positionPanelRightShort", - "positionPanelBottom", - "positionPanelBottomShort", - "alignPanelLeft", - "alignPanelLeftShort", - "alignPanelRight", - "alignPanelRightShort", - "alignPanelCenter", - "alignPanelCenterShort", - "alignPanelJustify", - "alignPanelJustifyShort", - "positionPanel", - "alignPanel", - "previousPanelView", - "nextPanelView", - "toggleMaximizedPanel", - "maximizePanel", - "minimizePanel", - "panelMaxNotSupported", - "closePanel", - "closeSecondarySideBar", - "togglePanel", - "hidePanel", - "movePanelToSecondarySideBar", - "movePanelToSecondarySideBar", - "moveSidePanelToPanel", - "moveSidePanelToPanel" - ], - "vs/workbench/browser/quickaccess": [ - "inQuickOpen" - ], "vs/workbench/api/common/jsonValidationExtensionPoint": [ "contributes.jsonValidation", "contributes.jsonValidation.fileMatch", @@ -7706,22 +7858,10 @@ "invalid.url", "invalid.path.1", "invalid.url.fileschema", - "invalid.url.schema" - ], - "vs/workbench/services/themes/common/iconExtensionPoint": [ - "contributes.icons", - "contributes.icon.id", - "contributes.icon.id.format", - "contributes.icon.description", - "contributes.icon.default.fontPath", - "contributes.icon.default.fontCharacter", - "contributes.icon.default", - "invalid.icons.configuration", - "invalid.icons.id.format", - "invalid.icons.description", - "invalid.icons.default.fontPath.extension", - "invalid.icons.default.fontPath.path", - "invalid.icons.default" + "invalid.url.schema", + "fileMatch", + "schema", + "jsonValidation" ], "vs/workbench/services/themes/common/colorExtensionPoint": [ "contributes.color", @@ -7739,7 +7879,28 @@ "invalid.description", "invalid.defaults", "invalid.defaults.highContrast", - "invalid.defaults.highContrastLight" + "invalid.defaults.highContrastLight", + "id", + "description", + "defaultDark", + "defaultLight", + "defaultHC", + "colors" + ], + "vs/workbench/services/themes/common/iconExtensionPoint": [ + "contributes.icons", + "contributes.icon.id", + "contributes.icon.id.format", + "contributes.icon.description", + "contributes.icon.default.fontPath", + "contributes.icon.default.fontCharacter", + "contributes.icon.default", + "invalid.icons.configuration", + "invalid.icons.id.format", + "invalid.icons.description", + "invalid.icons.default.fontPath.extension", + "invalid.icons.default.fontPath.path", + "invalid.icons.default" ], "vs/workbench/services/themes/common/tokenClassificationExtensionPoint": [ "contributes.semanticTokenTypes", @@ -7767,7 +7928,7 @@ "invalid.semanticTokenScopes.scopes.value", "invalid.semanticTokenScopes.scopes.selector" ], - "vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint": [ + "vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint": [ "parseErrors", "formatError", "schema.openBracket", @@ -7846,6 +8007,9 @@ "vscode.extension.contributes.statusBarItems", "invalid" ], + "vs/workbench/api/browser/mainThreadLanguageModels": [ + "languageModelsAccountId" + ], "vs/workbench/api/browser/mainThreadCLICommands": [ "cannot be installed" ], @@ -7915,7 +8079,6 @@ "label" ], "vs/workbench/api/browser/mainThreadMessageService": [ - "extensionSource", "defaultSource", "manageExtension", "cancel", @@ -7965,34 +8128,89 @@ "remote.tunnelsView.elevationButton" ], "vs/workbench/api/browser/mainThreadAuthentication": [ - "noTrustedExtensions", - "manageTrustedExtensions.cancel", + "signedOut", + "confirmRelogin", + "confirmLogin", { - "key": "accountLastUsedDate", + "key": "allow", "comment": [ - "The placeholder {0} is a string with time information, such as \"3 days ago\"" + "&& denotes a mnemonic" ] }, - "notUsed", - "manageTrustedExtensions", - "manageExtensions", - "signOutMessage", - "signOutMessageSimple", + "learnMore" + ], + "vs/workbench/browser/parts/titlebar/windowTitle": [ + "userIsAdmin", + "userIsSudo", + "devExtensionWindowTitlePrefix" + ], + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ + "toggleAuxiliaryIconRight", + "toggleAuxiliaryIconRightOn", + "toggleAuxiliaryIconLeft", + "toggleAuxiliaryIconLeftOn", + "secondary sidebar", { - "key": "signOut", + "key": "secondary sidebar mnemonic", "comment": [ "&& denotes a mnemonic" ] }, - "signedOut", - "confirmRelogin", - "confirmLogin", + "toggleSecondarySideBar", + "toggleSecondarySideBar", + "toggleAuxiliaryBar", + "focusAuxiliaryBar", + "hideAuxiliaryBar" + ], + "vs/workbench/browser/parts/panel/panelActions": [ + "maximizeIcon", + "restoreIcon", + "closeIcon", + "togglePanelOffIcon", + "togglePanelOnIcon", + "toggle panel", { - "key": "allow", + "key": "toggle panel mnemonic", "comment": [ "&& denotes a mnemonic" ] - } + }, + "focusPanel", + "positionPanelLeftShort", + "positionPanelRightShort", + "positionPanelBottomShort", + "alignPanelLeftShort", + "alignPanelRightShort", + "alignPanelCenterShort", + "alignPanelJustifyShort", + "positionPanel", + "alignPanel", + "maximizePanel", + "minimizePanel", + "panelMaxNotSupported", + "togglePanel", + "togglePanelVisibility", + "focusPanel", + "positionPanelLeft", + "positionPanelRight", + "positionPanelBottom", + "alignPanelLeft", + "alignPanelRight", + "alignPanelCenter", + "alignPanelJustify", + "previousPanelView", + "nextPanelView", + "toggleMaximizedPanel", + "closePanel", + "closeSecondarySideBar", + "hidePanel", + "movePanelToSecondarySideBar", + "movePanelToSecondarySideBar", + "moveSidePanelToPanel", + "moveSidePanelToPanel" + ], + "vs/workbench/browser/quickaccess": [ + "inQuickOpen" ], "vs/workbench/services/extensions/common/extensionsRegistry": [ "ui", @@ -8037,6 +8255,7 @@ "vscode.extension.activationEvents.onTerminalProfile", "vscode.extension.activationEvents.onTerminalQuickFixRequest", "vscode.extension.activationEvents.onWalkthrough", + "vscode.extension.activationEvents.onIssueReporterOpened", "vscode.extension.activationEvents.star", "vscode.extension.badges", "vscode.extension.badges.url", @@ -8081,11 +8300,6 @@ "vscode.extension.pricing", "product.extensionEnabledApiProposals" ], - "vs/workbench/browser/parts/titlebar/windowTitle": [ - "userIsAdmin", - "userIsSudo", - "devExtensionWindowTitlePrefix" - ], "vs/workbench/browser/parts/views/treeView": [ "no-dataprovider", "treeView.enableCollapseAll", @@ -8095,6 +8309,14 @@ "treeView.toggleCollapseAll", "command-error" ], + "vs/workbench/browser/parts/views/viewPaneContainer": [ + "views", + "viewMoveUp", + "viewMoveLeft", + "viewMoveDown", + "viewMoveRight", + "viewsMove" + ], "vs/workbench/contrib/debug/common/debug": [ "debugType", "debugConfigurationType", @@ -8118,10 +8340,13 @@ "watchItemType", "canViewMemory", "breakpointItemType", + "breakpointItemIsDataBytes", + "breakpointHasModes", "breakpointSupportsCondition", "loadedScriptsSupported", "loadedScriptsItemType", "focusedSessionIsAttach", + "focusedSessionIsNoDebug", "stepBackSupported", "restartFrameSupported", "stackFrameSupportsRestart", @@ -8132,6 +8357,7 @@ "debugExtensionsAvailable", "debugProtocolVariableMenuContext", "debugSetVariableSupported", + "debugSetDataBreakpointAddressSupported", "debugSetExpressionSupported", "breakWhenValueChangesSupported", "breakWhenValueIsAccessedSupported", @@ -8140,6 +8366,12 @@ "suspendDebuggeeSupported", "variableEvaluateNamePresent", "variableIsReadonly", + "variableValue", + "variableType", + "variableInterfaces", + "variableName", + "variableLanguage", + "variableExtensionId", "exceptionWidgetVisible", "multiSessionRepl", "multiSessionDebug", @@ -8150,14 +8382,6 @@ "debuggerDisabled", "internalConsoleOptions" ], - "vs/workbench/browser/parts/views/viewPaneContainer": [ - "views", - "viewMoveUp", - "viewMoveLeft", - "viewMoveDown", - "viewMoveRight", - "viewsMove" - ], "vs/workbench/contrib/files/common/files": [ "explorerViewletVisible", "foldersViewVisible", @@ -8177,12 +8401,14 @@ "vs/workbench/contrib/remote/browser/remoteExplorer": [ "remoteNoPorts", "noRemoteNoPorts", - "ports", "1forwardedPort", "nForwardedPorts", "remote.forwardedPorts.statusbarTextNone", "remote.forwardedPorts.statusbarTooltip", "status.forwardedPorts", + "remote.autoForwardPortsSource.fallback", + "remote.autoForwardPortsSource.fallback.switchBack", + "remote.autoForwardPortsSource.fallback.showPortSourceSetting", "remote.tunnelsView.automaticForward", { "key": "remote.tunnelsView.notificationLink2", @@ -8192,17 +8418,18 @@ }, "remote.tunnelsView.elevationMessage", "remote.tunnelsView.makePublic", - "remote.tunnelsView.elevationButton" + "remote.tunnelsView.elevationButton", + "ports" ], "vs/workbench/common/editor/sideBySideEditorInput": [ "sideBySideLabels" ], - "vs/workbench/browser/parts/editor/sideBySideEditor": [ - "sideBySideEditor" - ], "vs/workbench/common/editor/diffEditorInput": [ "sideBySideLabels" ], + "vs/workbench/browser/parts/editor/sideBySideEditor": [ + "sideBySideEditor" + ], "vs/workbench/browser/parts/editor/textDiffEditor": [ "textDiffEditor", "fileTooLargeForHeapErrorWithSize", @@ -8252,7 +8479,6 @@ "currentProblem", "currentProblem", "showLanguageExtensions", - "changeMode", "noEditor", "languageDescription", "languageDescriptionConfigured", @@ -8263,11 +8489,9 @@ "pickLanguage", "currentAssociation", "pickLanguageToConfigure", - "changeEndOfLine", "noEditor", "noWritableCodeEditor", "pickEndOfLine", - "changeEncoding", "noEditor", "noEditor", "noFileEditor", @@ -8276,7 +8500,18 @@ "pickAction", "guessedEncoding", "pickEncodingForReopen", - "pickEncodingForSave" + "pickEncodingForSave", + "changeMode", + "changeEndOfLine", + "changeEncoding" + ], + "vs/workbench/browser/parts/editor/diffEditorCommands": [ + "compare", + "compare", + "compare.nextChange", + "compare.previousChange", + "toggleInlineView", + "swapDiffSides" ], "vs/workbench/browser/parts/editor/editorCommands": [ "editorCommand.activeEditorMove.description", @@ -8285,10 +8520,6 @@ "editorCommand.activeEditorCopy.description", "editorCommand.activeEditorCopy.arg.name", "editorCommand.activeEditorCopy.arg.description", - "compare.nextChange", - "compare.previousChange", - "toggleInlineView", - "compare", "splitEditorInGroup", "joinEditorInGroup", "toggleJoinEditorInGroup", @@ -8300,14 +8531,107 @@ "lockEditorGroup", "unlockEditorGroup" ], + "vs/editor/browser/editorExtensions": [ + { + "key": "miUndo", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "undo", + { + "key": "miRedo", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "redo", + { + "key": "miSelectAll", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "selectAll" + ], "vs/workbench/browser/parts/editor/editorActions": [ + "splitEditorGroupUp", + "splitEditorGroupDown", + "closeEditor", + "unpinEditor", + "closeOneEditor", + "navigateForward", + { + "key": "miForward", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "navigateBack", + { + "key": "miBack", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmClearRecentsMessage", + "confirmClearDetail", + { + "key": "clearButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "confirmClearEditorHistoryMessage", + "confirmClearDetail", + { + "key": "clearButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "splitEditorToLeftGroup", + { + "key": "miMoveEditorToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miCopyEditorToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miMoveEditorGroupToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miCopyEditorGroupToNewWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miRestoreEditorsToMainWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miNewEmptyEditorWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, "splitEditor", "splitEditorOrthogonal", "splitEditorGroupLeft", "splitEditorGroupRight", "splitEditorGroupUp", - "splitEditorGroupUp", - "splitEditorGroupDown", "splitEditorGroupDown", "joinTwoGroups", "joinAllGroups", @@ -8321,9 +8645,6 @@ "focusRightGroup", "focusAboveGroup", "focusBelowGroup", - "closeEditor", - "unpinEditor", - "closeOneEditor", "revertAndCloseActiveEditor", "closeEditorsToTheLeft", "closeAllEditors", @@ -8339,6 +8660,7 @@ "duplicateActiveGroupUp", "duplicateActiveGroupDown", "minimizeOtherEditorGroups", + "minimizeOtherEditorGroupsHideSidebar", "evenEditorGroups", "toggleEditorWidths", "maximizeEditorHideSidebar", @@ -8350,21 +8672,7 @@ "firstEditorInGroup", "lastEditorInGroup", "navigateForward", - "navigateForward", - { - "key": "miForward", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "navigateBack", "navigateBack", - { - "key": "miBack", - "comment": [ - "&& denotes a mnemonic" - ] - }, "navigatePrevious", "navigateForwardInEdits", "navigateBackInEdits", @@ -8376,14 +8684,6 @@ "navigateToLastNavigationLocation", "reopenClosedEditor", "clearRecentFiles", - "confirmClearRecentsMessage", - "confirmClearDetail", - { - "key": "clearButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, "showEditorsInActiveGroup", "showAllEditors", "showAllEditorsByMostRecentlyUsed", @@ -8397,14 +8697,6 @@ "openNextRecentlyUsedEditorInGroup", "openPreviousRecentlyUsedEditorInGroup", "clearEditorHistory", - "confirmClearEditorHistoryMessage", - "confirmClearDetail", - { - "key": "clearButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, "moveEditorLeft", "moveEditorRight", "moveEditorToPreviousGroup", @@ -8420,7 +8712,6 @@ "splitEditorToAboveGroup", "splitEditorToBelowGroup", "splitEditorToLeftGroup", - "splitEditorToLeftGroup", "splitEditorToRightGroup", "splitEditorToFirstGroup", "splitEditorToLastGroup", @@ -8438,36 +8729,22 @@ "newGroupBelow", "toggleEditorType", "reopenTextEditor", - "popEditorOut", - { - "key": "miPopEditorOut", - "comment": [ - "&& denotes a mnemonic" - ] - } + "moveEditorToNewWindow", + "copyEditorToNewWindow", + "moveEditorGroupToNewWindow", + "copyEditorGroupToNewWindow", + "restoreEditorsToMainWindow", + "newEmptyEditorWindow" ], - "vs/editor/browser/editorExtensions": [ - { - "key": "miUndo", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "undo", - { - "key": "miRedo", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "redo", - { - "key": "miSelectAll", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "selectAll" + "vs/workbench/browser/parts/editor/editorConfiguration": [ + "interactiveWindow", + "markdownPreview", + "simpleBrowser", + "livePreview", + "workbench.editor.autoLockGroups", + "workbench.editor.defaultBinaryEditor", + "editor.editorAssociations", + "editorLargeFileSizeConfirmation" ], "vs/workbench/browser/parts/editor/editorQuickAccess": [ "noViewResults", @@ -8476,13 +8753,11 @@ "entryAriaLabelDirty", "closeEditor" ], - "vs/workbench/browser/parts/editor/editorConfiguration": [ - "interactiveWindow", - "markdownPreview", - "workbench.editor.autoLockGroups", - "workbench.editor.defaultBinaryEditor", - "editor.editorAssociations", - "editorLargeFileSizeConfirmation" + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ + "activity bar position", + "move second side bar left", + "move second side bar right", + "hide second side bar" ], "vs/workbench/browser/parts/panel/panelPart": [ "panel position", @@ -8490,27 +8765,18 @@ "hidePanel" ], "vs/workbench/browser/parts/sidebar/sidebarPart": [ - "manage", - "accounts" - ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ - "move second side bar left", - "move second side bar right", - "hide second side bar" - ], - "vs/workbench/browser/parts/statusbar/statusbarActions": [ - "hide", - "focusStatusBar" + "toggleActivityBar" ], "vs/platform/actions/common/menuResetAction": [ "title" ], "vs/platform/actions/common/menuService": [ - "hide.label" + "hide.label", + "configure keybinding" ], - "vs/base/browser/ui/icons/iconSelectBox": [ - "iconSelect.placeholder", - "iconSelect.noResults" + "vs/workbench/browser/parts/statusbar/statusbarActions": [ + "hide", + "focusStatusBar" ], "vs/base/browser/ui/dialog/dialog": [ "ok", @@ -8520,12 +8786,14 @@ "dialogPendingMessage", "dialogClose" ], - "vs/workbench/services/preferences/common/preferencesEditorInput": [ - "settingsEditor2InputName" - ], "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ + "keybindingsEditorLabelIcon", "keybindingsInputName" ], + "vs/workbench/services/preferences/common/preferencesEditorInput": [ + "settingsEditorLabelIcon", + "settingsEditor2InputName" + ], "vs/workbench/services/preferences/common/preferencesModels": [ "commonlyUsed", "defaultKeybindingsHeader" @@ -8533,6 +8801,9 @@ "vs/workbench/services/textfile/common/textFileEditorModel": [ "textFileCreate.source" ], + "vs/workbench/services/editor/common/editorResolverService": [ + "editor.editorAssociations" + ], "vs/base/common/keybindingLabels": [ { "key": "ctrlKey", @@ -8661,9 +8932,6 @@ "missing.chord", "missing.chord" ], - "vs/workbench/services/editor/common/editorResolverService": [ - "editor.editorAssociations" - ], "vs/workbench/services/themes/common/colorThemeData": [ "error.cannotparsejson", "error.invalidformat", @@ -8749,25 +9017,6 @@ "schema.supportsSemanticHighlighting", "schema.semanticTokenColors" ], - "vs/workbench/services/themes/common/themeExtensionPoints": [ - "vscode.extension.contributes.themes", - "vscode.extension.contributes.themes.id", - "vscode.extension.contributes.themes.label", - "vscode.extension.contributes.themes.uiTheme", - "vscode.extension.contributes.themes.path", - "vscode.extension.contributes.iconThemes", - "vscode.extension.contributes.iconThemes.id", - "vscode.extension.contributes.iconThemes.label", - "vscode.extension.contributes.iconThemes.path", - "vscode.extension.contributes.productIconThemes", - "vscode.extension.contributes.productIconThemes.id", - "vscode.extension.contributes.productIconThemes.label", - "vscode.extension.contributes.productIconThemes.path", - "reqarray", - "reqpath", - "reqid", - "invalid.path.1" - ], "vs/workbench/services/themes/browser/productIconThemeData": [ "error.parseicondefs", "defaultTheme", @@ -8782,79 +9031,6 @@ "error.icon.font", "error.icon.fontCharacter" ], - "vs/workbench/services/themes/common/themeConfiguration": [ - "colorTheme", - "colorThemeError", - { - "key": "preferredDarkColorTheme", - "comment": [ - "{0} will become a link to another setting." - ] - }, - "colorThemeError", - { - "key": "preferredLightColorTheme", - "comment": [ - "{0} will become a link to another setting." - ] - }, - "colorThemeError", - { - "key": "preferredHCDarkColorTheme", - "comment": [ - "{0} will become a link to another setting." - ] - }, - "colorThemeError", - { - "key": "preferredHCLightColorTheme", - "comment": [ - "{0} will become a link to another setting." - ] - }, - "colorThemeError", - { - "key": "detectColorScheme", - "comment": [ - "{0} and {1} will become links to other settings." - ] - }, - "workbenchColors", - "iconTheme", - "noIconThemeLabel", - "noIconThemeDesc", - "iconThemeError", - "productIconTheme", - "defaultProductIconThemeLabel", - "defaultProductIconThemeDesc", - "productIconThemeError", - { - "key": "autoDetectHighContrast", - "comment": [ - "{0} and {1} will become links to other settings." - ] - }, - "editorColors.comments", - "editorColors.strings", - "editorColors.keywords", - "editorColors.numbers", - "editorColors.types", - "editorColors.functions", - "editorColors.variables", - "editorColors.textMateRules", - "editorColors.semanticHighlighting", - "editorColors.semanticHighlighting.deprecationMessage", - { - "key": "editorColors.semanticHighlighting.deprecationMessageMarkdown", - "comment": [ - "{0} will become a link to another setting." - ] - }, - "editorColors", - "editorColors.semanticHighlighting.enabled", - "editorColors.semanticHighlighting.rules", - "semanticTokenColors" - ], "vs/workbench/services/themes/common/productIconThemeSchema": [ "schema.id", "schema.id.formatError", @@ -8865,12 +9041,34 @@ "schema.font-style", "schema.iconDefinitions" ], + "vs/workbench/services/themes/common/themeExtensionPoints": [ + "vscode.extension.contributes.themes", + "vscode.extension.contributes.themes.id", + "vscode.extension.contributes.themes.label", + "vscode.extension.contributes.themes.uiTheme", + "vscode.extension.contributes.themes.path", + "vscode.extension.contributes.iconThemes", + "vscode.extension.contributes.iconThemes.id", + "vscode.extension.contributes.iconThemes.label", + "vscode.extension.contributes.iconThemes.path", + "vscode.extension.contributes.productIconThemes", + "vscode.extension.contributes.productIconThemes.id", + "vscode.extension.contributes.productIconThemes.label", + "vscode.extension.contributes.productIconThemes.path", + "color themes", + "file icon themes", + "product icon themes", + "themes", + "reqarray", + "reqpath", + "reqid", + "invalid.path.1" + ], "vs/workbench/services/extensionManagement/browser/extensionBisect": [ "I cannot reproduce", "This is Bad", "bisect.singular", "bisect.plural", - "title.start", "msg.start", "detail.start", { @@ -8879,7 +9077,6 @@ "&& denotes a mnemonic" ] }, - "title.isBad", "done.msg", "done.detail2", "done.msg", @@ -8918,6 +9115,8 @@ "&& denotes a mnemonic" ] }, + "title.start", + "title.isBad", "title.stop" ], "vs/workbench/services/userDataProfile/browser/settingsResource": [ @@ -8945,9 +9144,6 @@ "vs/workbench/services/userDataProfile/common/userDataProfileIcons": [ "settingsViewBarIcon" ], - "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ - "saveParticipants" - ], "vs/workbench/services/remote/common/tunnelModel": [ "tunnel.forwardedPortsViewEnabled", "tunnel.source.user", @@ -8955,9 +9151,18 @@ "remote.localPortMismatch.single", "tunnel.staticallyForwarded" ], - "vs/workbench/services/hover/browser/hoverWidget": [ + "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ + "saveParticipants" + ], + "vs/workbench/services/views/common/viewContainerModel": [ + "showViewsLog" + ], + "vs/editor/browser/services/hoverService/hoverWidget": [ "hoverhint" ], + "vs/editor/browser/services/hoverService/updatableHoverWidget": [ + "iconLabel.loading" + ], "vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl": [ "alreadyDebugging", "stop", @@ -8977,12 +9182,6 @@ "defineKeybinding.existing", "defineKeybinding.chordsTo" ], - "vs/workbench/contrib/performance/browser/perfviewEditor": [ - "name" - ], - "vs/workbench/contrib/speech/common/speechService": [ - "hasSpeechProvider" - ], "vs/editor/contrib/suggest/browser/suggest": [ "suggestWidgetHasSelection", "suggestWidgetDetailsVisible", @@ -8993,6 +9192,11 @@ "suggestionInsertMode", "suggestionCanResolve" ], + "vs/workbench/contrib/preferences/browser/preferencesActions": [ + "languageDescriptionConfigured", + "pickLanguage", + "configureLanguageBasedSettings" + ], "vs/workbench/contrib/preferences/browser/keybindingsEditor": [ "recordKeysLabel", "sortByPrecedeneLabel", @@ -9042,11 +9246,6 @@ "settingsFilter", "preferencesOpenSettings" ], - "vs/workbench/contrib/preferences/browser/preferencesActions": [ - "configureLanguageBasedSettings", - "languageDescriptionConfigured", - "pickLanguage" - ], "vs/workbench/contrib/preferences/common/preferencesContribution": [ "splitSettingsEditorLabel", "enableNaturalLanguageSettingsSearch", @@ -9068,160 +9267,311 @@ "turnOnSyncButton", "lastSyncedLabel" ], - "vs/workbench/contrib/chat/browser/actions/chatActions": [ - "chat.category", - "quickChat", - { - "key": "actions.chat.acceptInput", - "comment": [ - "Apply input from the chat input box" - ] - }, - { - "key": "actions.chat.submitSecondaryAgent", - "comment": [ - "Send input from the chat input box to the secondary agent" - ] - }, - "interactiveSession.clearHistory.label", - "actions.interactiveSession.focus", - "interactiveSession.focusInput.label", - "interactiveSession.open", - "interactiveSession.history.label", - "interactiveSession.history.delete", - "interactiveSession.history.pick" + "vs/workbench/contrib/performance/browser/perfviewEditor": [ + "name" ], - "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ - "interactive.copyCodeBlock.label", - "interactive.insertCodeBlock.label", - "interactive.insertIntoNewFile.label", - "interactive.runInTerminal.label", - "interactive.nextCodeBlock.label", - "interactive.previousCodeBlock.label" + "vs/workbench/contrib/speech/common/speechService": [ + "hasSpeechProvider", + "speechToTextInProgress", + "speechLanguage.da-DK", + "speechLanguage.de-DE", + "speechLanguage.en-AU", + "speechLanguage.en-CA", + "speechLanguage.en-GB", + "speechLanguage.en-IE", + "speechLanguage.en-IN", + "speechLanguage.en-NZ", + "speechLanguage.en-US", + "speechLanguage.es-ES", + "speechLanguage.es-MX", + "speechLanguage.fr-CA", + "speechLanguage.fr-FR", + "speechLanguage.hi-IN", + "speechLanguage.it-IT", + "speechLanguage.ja-JP", + "speechLanguage.ko-KR", + "speechLanguage.nl-NL", + "speechLanguage.pt-PT", + "speechLanguage.pt-BR", + "speechLanguage.ru-RU", + "speechLanguage.sv-SE", + "speechLanguage.tr-TR", + "speechLanguage.zh-CN", + "speechLanguage.zh-HK", + "speechLanguage.zh-TW" + ], + "vs/workbench/contrib/speech/browser/speechService": [ + "vscode.extension.contributes.speechProvider", + "speechProviderName", + "speechProviderDescription" ], - "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ - "interactive.copyAll.label", - "interactive.copyItem.label" + "vs/workbench/contrib/accessibility/browser/accessibleView": [ + "symbolLabel", + "symbolLabelAria", + "disableAccessibilityHelp", + "openDoc", + "exit", + "ariaAccessibleViewActionsBottom", + "ariaAccessibleViewActions", + "accessibility-help", + "accessible-view", + "accessible-view-hint", + "accessibility-help-hint", + "accessibleHelpToolbar", + "accessibleViewToolbar", + "toolbar", + "intro", + "insertAtCursor", + "insertAtCursorNoKb", + "insertIntoNewFile", + "insertIntoNewFileNoKb", + "runInTerminal", + "runInTerminalNoKb", + "accessibleViewNextPreviousHint", + "chatAccessibleViewNextPreviousHintNoKb", + "acessibleViewDisableHint", + "accessibleViewDisableHintNoKb", + "goToSymbolHint", + "goToSymbolHintNoKb", + "acessibleViewHint", + "acessibleViewHintNoKbEither", + "accessibleViewSymbolQuickPickPlaceholder", + "accessibleViewSymbolQuickPickTitle" ], - "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ + "vs/workbench/contrib/accessibility/browser/accessibleViewContributions": [ + "notification.accessibleViewSrc", + "notification.accessibleView", + "clearNotification", + "clearNotification" + ], + "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ + "editor.action.accessibleViewNext", + "editor.action.accessibleViewNextCodeBlock", + "editor.action.accessibleViewPreviousCodeBlock", + "editor.action.accessibleViewPrevious", + "editor.action.accessibleViewGoToSymbol", + "editor.action.accessibilityHelp", + "editor.action.accessibleView", + "editor.action.accessibleViewDisableHint", + "editor.action.accessibleViewAcceptInlineCompletionAction" + ], + "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ + "chat.newChat.label", + "chat.newChat.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatActions": [ + "interactiveSession.history.delete", + "interactiveSession.history.pick", + "chat.category", + "openChat", + "chat.history.label", + "interactiveSession.open", + "interactiveSession.clearHistory.label", + "chat.clear.label", + "actions.interactiveSession.focus", + "interactiveSession.focusInput.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ + "interactive.copyCodeBlock.label", + "interactive.insertCodeBlock.label", + "interactive.insertIntoNewFile.label", + "interactive.runInTerminal.label", + "interactive.nextCodeBlock.label", + "interactive.previousCodeBlock.label", + "interactive.compare.apply" + ], + "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ + "interactive.copyAll.label", + "interactive.copyItem.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ "interactive.submit.label", + { + "key": "actions.chat.submitSecondaryAgent", + "comment": [ + "Send input from the chat input box to the secondary agent" + ] + }, + "chat.newChat.label", "interactive.cancel.label" ], + "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ + "interactive.nextFileTree.label", + "interactive.previousFileTree.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ + "chat.file.label", + "chat.export.label", + "chat.import.label" + ], + "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ + "chat.openInEditor.label", + "chat.openInNewWindow.label", + "interactiveSession.openInSidebar.label" + ], "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ - "chat.openInChatView.label", - "chat.closeQuickChat.label", - "quickChat", "toggle.desc", "toggle.query", "toggle.isPartialQuery", "toggle.query", + "chat.openInChatView.label", + "chat.closeQuickChat.label", + "chat.launchInlineChat.label", + "quickChat", "interactiveSession.open" ], "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ + "reunmenu", "interactive.helpful.label", "interactive.unhelpful.label", + "interactive.reportIssueForBug.label", "interactive.insertIntoNotebook.label", - "chat.remove.label" - ], - "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ - "vscode.extension.contributes.interactiveSession", - "vscode.extension.contributes.interactiveSession.id", - "vscode.extension.contributes.interactiveSession.label", - "vscode.extension.contributes.interactiveSession.icon", - "vscode.extension.contributes.interactiveSession.when", - "chat.viewContainer.label" + "chat.remove.label", + "chat.rerun.label", + "chat.rerunWithoutCommandDetection.label" ], - "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ - "chat.file.label", - "chat.export.label", - "chat.import.label" + "vs/workbench/contrib/chat/browser/chat": [ + "generating" ], "vs/workbench/contrib/chat/browser/chatEditorInput": [ + "chatEditorLabelIcon", "chatEditorName" ], - "vs/workbench/contrib/chat/common/chatServiceImpl": [ - "emptyResponse" - ], - "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ - "chat.openInEditor.label", - "interactiveSession.openInEditor.label", - "interactiveSession.openInSidebar.label" - ], - "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ - "interactiveSession.clear.label", - "interactiveSession.clear.label", - "interactiveSession.clear.label" - ], - "vs/workbench/contrib/accessibility/browser/accessibleView": [ - "symbolLabel", - "symbolLabelAria", - "disableAccessibilityHelp", - "openDoc", - "exit", - "ariaAccessibleViewActionsBottom", - "ariaAccessibleViewActions", - "accessibility-help", - "accessible-view", - "accessible-view-hint", - "accessibility-help-hint", - "accessibleHelpToolbar", - "accessibleViewToolbar", - "toolbar", - "intro", - "accessibleViewNextPreviousHint", - "chatAccessibleViewNextPreviousHintNoKb", - "acessibleViewDisableHint", - "accessibleViewDisableHintNoKb", - "goToSymbolHint", - "goToSymbolHintNoKb", - "acessibleViewHint", - "acessibleViewHintNoKbEither", - "accessibleViewSymbolQuickPickPlaceholder", - "accessibleViewSymbolQuickPickTitle" - ], - "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ - "notification.accessibleViewSrc", - "notification.accessibleView", - "clearNotification", - "clearNotification" - ], - "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ - "editor.action.accessibleViewNext", - "editor.action.accessibleViewPrevious", - "editor.action.accessibleViewGoToSymbol", - "editor.action.accessibilityHelp", - "editor.action.accessibleView", - "editor.action.accessibleViewDisableHint", - "editor.action.accessibleViewAcceptInlineCompletionAction" - ], - "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ - "interactive.nextFileTree.label", - "interactive.previousFileTree.label" - ], "vs/workbench/contrib/chat/common/chatContextKeys": [ - "interactiveSessionResponseHasProviderId", "interactiveSessionResponseVote", + "chatSessionResponseDetectedAgentOrCommand", + "chatResponseSupportsIssueReporting", "chatResponseFiltered", "interactiveSessionRequestInProgress", "chatResponse", "chatRequest", + "chatEditApplied", "interactiveInputHasText", + "interactiveInputHasFocus", "inInteractiveInput", "inChat", - "hasChatProvider" + "chatIsEnabled" ], - "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ - "pickFileLabel" + "vs/workbench/contrib/chat/common/chatServiceImpl": [ + "chatFailErrorMessage", + "action.showExtension", + "emptyResponse" + ], + "vs/workbench/contrib/chat/common/languageModelStats": [ + "Language Models", + "languageModels" + ], + "vs/workbench/contrib/chat/browser/chatParticipantContributions": [ + "vscode.extension.contributes.chatParticipant", + "chatParticipantId", + "chatParticipantName", + "chatParticipantDescription", + "chatParticipantIsDefaultDescription", + "chatCommandSticky", + "chatCommandsDescription", + "chatCommand", + "chatCommandDescription", + "chatCommandWhen", + "chatCommandSampleRequest", + "chatCommandSticky", + "defaultImplicitVariables", + "chatLocationsDescription", + "chat.viewContainer.label" ], "vs/workbench/contrib/chat/common/chatColors": [ "chat.requestBorder", + "chat.requestBackground", "chat.slashCommandBackground", "chat.slashCommandForeground", "chat.avatarBackground", "chat.avatarForeground" ], - "vs/workbench/contrib/notebook/common/notebookEditorInput": [ - "vetoExtHostRestart" + "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ + "pickFileLabel" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ + "create.fail", + "welcome.2", + "welcome.1", + "empty", + "err.apply", + "err.discard", + "savehint" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ + "startInlineChat", + "arrowUp", + "arrowDown", + "discard", + "discardMenu", + "discard", + "undo.clipboard", + "undo.newfile", + "apply2", + "cancel", + "close", + "configure", + "label", + "viewInChat", + "run", + "unstash", + "cat", + "focus", + "showChanges", + "apply1", + "moveToNextHunk", + "moveToPreviousHunk", + "copyRecordings" + ], + "vs/workbench/contrib/inlineChat/common/inlineChat": [ + "inlineChatHasProvider", + "inlineChatVisible", + "inlineChatFocused", + "inlineChatResponseFocused", + "inlineChatEmpty", + "inlineChatInnerCursorFirst", + "inlineChatInnerCursorLast", + "inlineChatInnerCursorStart", + "inlineChatInnerCursorEnd", + "inlineChatOuterCursorPosition", + "inlineChatHasActiveRequest", + "inlineChatHasStashedSession", + "inlineChatResponseType", + "inlineChatResponseTypes", + "inlineChatDidEdit", + "inlineChatUserDidEdit", + "inlineChatLastFeedbackKind", + "inlineChatSupportIssueReporting", + "inlineChatDocumentChanged", + "inlineChatChangeHasDiff", + "inlineChatChangeShowsDiff", + "inlineChat.background", + "inlineChat.border", + "inlineChat.shadow", + "inlineChat.regionHighlight", + "inlineChatInput.border", + "inlineChatInput.focusBorder", + "inlineChatInput.placeholderForeground", + "inlineChatInput.background", + "inlineChatDiff.inserted", + "editorOverviewRuler.inlineChatInserted", + "editorOverviewRuler.inlineChatInserted", + "inlineChatDiff.removed", + "editorOverviewRuler.inlineChatRemoved", + "mode", + "mode.live", + "mode.preview", + "finishOnType", + "acceptedOrDiscardBeforeSave", + "holdToSpeech", + "accessibleDiffView", + "accessibleDiffView.auto", + "accessibleDiffView.on", + "accessibleDiffView.off" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl": [ + "inlineChat", + "inlineChat.N" ], "vs/workbench/contrib/notebook/browser/notebookEditor": [ "fail.noEditor", @@ -9229,8 +9579,13 @@ "notebookOpenEnableMissingViewType", "notebookOpenInstallMissingViewType", "notebookOpenAsText", + "notebookTooLargeForHeapErrorWithSize", + "notebookTooLargeForHeapErrorWithoutSize", "notebookOpenInTextEditor" ], + "vs/workbench/contrib/notebook/common/notebookEditorInput": [ + "vetoExtHostRestart" + ], "vs/workbench/contrib/notebook/browser/services/notebookServiceImpl": [ "notebookOpenInstallMissingViewType" ], @@ -9240,13 +9595,16 @@ "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ "notebookRunTrust" ], + "vs/editor/common/languages/modesRegistry": [ + "plainText.alias" + ], "vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl": [ "disableOtherKeymapsConfirmation", "yes", "no" ], - "vs/editor/common/languages/modesRegistry": [ - "plainText.alias" + "vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl": [ + "workbench.notebook.clearNotebookKernelsMRUCache" ], "vs/workbench/contrib/comments/browser/commentReply": [ "reply", @@ -9254,9 +9612,6 @@ "reply", "reply" ], - "vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl": [ - "workbench.notebook.clearNotebookKernelsMRUCache" - ], "vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl": [ "renderChannelName" ], @@ -9278,34 +9633,14 @@ "notebook.cell.insertCodeCellBelowAndFocusContainer", "notebook.changeCellType" ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables": [ + "notebookVariables" + ], "vs/workbench/contrib/notebook/browser/controller/coreActions": [ - "notebookActions.category", "notebookMenu.insertCell", "notebookMenu.cellTitle", - "miShare" - ], - "vs/workbench/contrib/notebook/browser/controller/executeActions": [ - "notebookActions.renderMarkdown", - "notebookActions.executeNotebook", - "notebookActions.executeNotebook", - "notebookActions.execute", - "notebookActions.execute", - "notebookActions.executeAbove", - "notebookActions.executeBelow", - "notebookActions.executeAndFocusContainer", - "notebookActions.executeAndFocusContainer", - "notebookActions.cancel", - "notebookActions.cancel", - "notebookActions.executeAndSelectBelow", - "notebookActions.executeAndInsertBelow", - "notebookActions.cancelNotebook", - "notebookActions.interruptNotebook", - "revealRunningCell", - "revealRunningCell", - "revealRunningCellShort", - "revealLastFailedCell", - "revealLastFailedCell", - "revealLastFailedCellShort" + "miShare", + "notebookActions.category" ], "vs/workbench/contrib/notebook/browser/controller/insertCellActions": [ "notebookActions.insertCodeCellAbove", @@ -9333,39 +9668,117 @@ "notebookActions.menu.insertMarkdown", "notebookActions.menu.insertMarkdown.tooltip" ], - "vs/workbench/contrib/notebook/browser/controller/layoutActions": [ - "workbench.notebook.layout.select.label", - "workbench.notebook.layout.configure.label", - "workbench.notebook.layout.configure.label", - "customizeNotebook", - "notebook.toggleLineNumbers", - "notebook.showLineNumbers", - "notebook.toggleCellToolbarPosition", - "notebook.toggleBreadcrumb", - "notebook.saveMimeTypeOrder", - "notebook.placeholder", - "saveTarget.machine", - "saveTarget.workspace", - "workbench.notebook.layout.webview.reset.label" - ], - "vs/workbench/contrib/notebook/browser/controller/editActions": [ - "notebookActions.editCell", - "notebookActions.quitEdit", - "notebookActions.deleteCell", - "confirmDeleteButton", - "confirmDeleteButtonMessage", - "doNotAskAgain", - "clearCellOutputs", - "clearAllCellsOutputs", - "changeLanguage", - "changeLanguage", - "languageDescription", - "languageDescriptionConfigured", - "autoDetect", - "languagesPicks", + "vs/workbench/contrib/notebook/browser/controller/sectionActions": [ + { + "key": "mirunCell", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "runCell", + { + "key": "mirunCellsInSection", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "runCellsInSection", + { + "key": "mifoldSection", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "foldSection", + { + "key": "miexpandSection", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "expandSection", + "runCell", + "runCellsInSection", + "foldSection", + "expandSection" + ], + "vs/workbench/contrib/notebook/browser/controller/executeActions": [ + "notebookActions.renderMarkdown", + "notebookActions.executeNotebook", + "notebookActions.executeNotebook", + "notebookActions.execute", + "notebookActions.execute", + "notebookActions.executeAbove", + "notebookActions.executeBelow", + "notebookActions.executeAndFocusContainer", + "notebookActions.executeAndFocusContainer", + "notebookActions.cancel", + "notebookActions.cancel", + "notebookActions.executeAndSelectBelow", + "notebookActions.executeAndInsertBelow", + "revealRunningCellShort", + "revealRunningCell", + "revealRunningCell", + "revealRunningCell", + "revealLastFailedCell", + "revealLastFailedCell", + "revealLastFailedCellShort", + "notebookActions.cancelNotebook", + "notebookActions.interruptNotebook" + ], + "vs/workbench/contrib/notebook/browser/controller/layoutActions": [ + "notebook.showLineNumbers", + "notebook.placeholder", + "saveTarget.machine", + "saveTarget.workspace", + { + "key": "mitoggleNotebookStickyScroll", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "notebookStickyScroll", + { + "key": "mitoggleNotebookStickyScroll", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "workbench.notebook.layout.select.label", + "workbench.notebook.layout.configure.label", + "workbench.notebook.layout.configure.label", + "customizeNotebook", + "notebook.toggleLineNumbers", + "notebook.toggleCellToolbarPosition", + "notebook.toggleBreadcrumb", + "notebook.saveMimeTypeOrder", + "workbench.notebook.layout.webview.reset.label", + "toggleStickyScroll" + ], + "vs/workbench/contrib/notebook/browser/controller/editActions": [ + "notebookActions.editCell", + "notebookActions.quitEdit", + "notebookActions.deleteCell", + "confirmDeleteButton", + "confirmDeleteButtonMessage", + "doNotAskAgain", + "clearCellOutputs", + "clearAllCellsOutputs", + "changeLanguage", + "changeLanguage", + "languageDescription", + "languageDescriptionConfigured", + "autoDetect", + "languagesPicks", "pickLanguageToConfigure", + "noDetection", + "noNotebookEditor", + "noWritableCodeEditor", + "indentConvert", + "indentView", + "pickAction", "detectLanguage", - "noDetection" + "selectNotebookIndentation" ], "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ "notebookActions.copyOutput" @@ -9375,22 +9788,23 @@ "unfold.cell", "fold.cell" ], + "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ + "notebookActions.hideFind", + "notebookActions.findInNotebook" + ], "vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard": [ "notebookActions.copy", "notebookActions.cut", "notebookActions.paste", "notebookActions.pasteAbove", + "notebook.cell.output.selectAll", "toggleNotebookClipboardLog" ], - "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ - "notebookActions.hideFind", - "notebookActions.findInNotebook" - ], "vs/workbench/contrib/notebook/browser/contrib/format/formatting": [ - "format.title", "label", "formatCell.label", - "formatCells.label" + "formatCells.label", + "format.title" ], "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants": [ "notebookFormatSave.formatting", @@ -9414,7 +9828,19 @@ "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions": [ "notebook.toggleCellToolbarPosition" ], + "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ + "outline.showMarkdownHeadersOnly", + "outline.showCodeCells", + "outline.showCodeCellSymbols", + "breadcrumbs.showCodeCells", + "notebook.gotoSymbols.showAllSymbols", + "filter", + "toggleShowMarkdownHeadersOnly", + "toggleCodeCells", + "toggleCodeCellSymbols" + ], "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow": [ + "notebook.cell.webviewHandledEvents", "cursorMoveDown", "cursorMoveUp", "focusFirstCell", @@ -9428,11 +9854,6 @@ "cursorPageDownSelect", "notebook.navigation.allowNavigateToSurroundingCells" ], - "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ - "outline.showCodeCells", - "breadcrumbs.showCodeCells", - "notebook.gotoSymbols.showAllSymbols" - ], "vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile": [ "setProfileTitle" ], @@ -9447,7 +9868,8 @@ "notebook.cell.status.pending", "notebook.cell.status.executing", "notebook.cell.statusBar.timerTooltip.reportIssueFootnote", - "notebook.cell.statusBar.timerTooltip" + "notebook.cell.statusBar.timerTooltip", + "notebook.cell.status.diagnostic" ], "vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar": [ "notebook.info", @@ -9457,9 +9879,18 @@ "kernel.select.label", "notebook.activeCellStatusName", "notebook.multiActiveCellIndicator", - "notebook.singleActiveCellIndicator" + "notebook.singleActiveCellIndicator", + "notebook.indentation", + "selectNotebookIndentation" + ], + "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ + "workbench.notebook.toggleLayoutTroubleshoot", + "workbench.notebook.inspectLayout", + "workbench.notebook.clearNotebookEdtitorTypeCache" ], "vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands": [ + "notebookActions.toggleOutputs", + "cellCommands.quickFix.noneMessage", "notebookActions.moveCellUp", "notebookActions.moveCellDown", "notebookActions.copyCellUp", @@ -9475,170 +9906,228 @@ "notebookActions.collapseCellOutput", "notebookActions.expandCellOutput", "notebookActions.toggleOutputs", - "notebookActions.toggleOutputs", "notebookActions.collapseAllCellInput", "notebookActions.expandAllCellInput", "notebookActions.collapseAllCellOutput", "notebookActions.expandAllCellOutput", - "notebookActions.toggleScrolling" + "notebookActions.toggleScrolling", + "notebookActions.cellFailureActions" ], "vs/workbench/contrib/notebook/browser/diff/notebookDiffActions": [ - "notebook.diff.switchToText", "notebook.diff.cell.revertMetadata", "notebook.diff.cell.switchOutputRenderingStyleToText", "notebook.diff.cell.revertOutputs", "notebook.diff.cell.revertInput", - "notebook.diff.showOutputs", - "notebook.diff.showMetadata", "notebook.diff.action.previous.title", "notebook.diff.action.next.title", "notebook.diff.ignoreMetadata", - "notebook.diff.ignoreOutputs" + "notebook.diff.ignoreOutputs", + "notebook.diff.switchToText", + "notebook.diff.showOutputs", + "notebook.diff.showMetadata" ], - "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ - "workbench.notebook.toggleLayoutTroubleshoot", - "workbench.notebook.inspectLayout", - "workbench.notebook.clearNotebookEdtitorTypeCache" + "vs/editor/contrib/peekView/browser/peekView": [ + "inReferenceSearchEditor", + "label.close", + "peekViewTitleBackground", + "peekViewTitleForeground", + "peekViewTitleInfoForeground", + "peekViewBorder", + "peekViewResultsBackground", + "peekViewResultsMatchForeground", + "peekViewResultsFileForeground", + "peekViewResultsSelectionBackground", + "peekViewResultsSelectionForeground", + "peekViewEditorBackground", + "peekViewEditorGutterBackground", + "peekViewEditorStickScrollBackground", + "peekViewResultsMatchHighlight", + "peekViewEditorMatchHighlight", + "peekViewEditorMatchHighlightBorder" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ - "welcome.1", - "welcome.2", - "create.fail", - "create.fail.detail", - "welcome.1", - "default.placeholder", - "default.placeholder.history", - "thinking", - "editResponseMessage", - "empty", - "empty", - "err.apply", - "err.discard" + "vs/workbench/contrib/interactive/browser/interactiveEditor": [ + "interactiveInputPlaceHolder" ], - "vs/workbench/contrib/inlineChat/common/inlineChat": [ - "inlineChatHasProvider", - "inlineChatVisible", - "inlineChatFocused", - "inlineChatResponseFocused", - "inlineChatEmpty", - "inlineChatInnerCursorFirst", - "inlineChatInnerCursorLast", - "inlineChatInnerCursorStart", - "inlineChatInnerCursorEnd", - "inlineChatMarkdownMessageCropState", - "inlineChatOuterCursorPosition", - "inlineChatHasActiveRequest", - "inlineChatHasStashedSession", - "inlineChatResponseType", - "inlineChatResponseTypes", - "inlineChatDidEdit", - "inlineChatUserDidEdit", - "inlineChatLastFeedbackKind", - "inlineChatDocumentChanged", - "inlineChat.background", - "inlineChat.border", - "inlineChat.shadow", - "inlineChat.regionHighlight", - "inlineChatInput.border", - "inlineChatInput.focusBorder", - "inlineChatInput.placeholderForeground", - "inlineChatInput.background", - "inlineChatDiff.inserted", - "inlineChatDiff.removed", - "mode", - "mode.livePreview", - "mode.preview", - "mode.live", - "showDiff", - "showGutterIcon" + "vs/workbench/contrib/notebook/browser/notebookIcons": [ + "selectKernelIcon", + "executeIcon", + "executeAboveIcon", + "executeBelowIcon", + "stopIcon", + "deleteCellIcon", + "executeAllIcon", + "editIcon", + "stopEditIcon", + "moveUpIcon", + "moveDownIcon", + "clearIcon", + "splitCellIcon", + "successStateIcon", + "errorStateIcon", + "pendingStateIcon", + "executingStateIcon", + "collapsedIcon", + "expandedIcon", + "openAsTextIcon", + "revertIcon", + "renderOutputIcon", + "mimetypeIcon", + "copyIcon", + "previousChangeIcon", + "nextChangeIcon", + "variablesViewIcon" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ - "run", - "unstash", - "cat", - "accept", - "rerun", - "rerunShort", - "stop", - "arrowUp", - "arrowDown", - "focus", - "previousFromHistory", - "nextFromHistory", - "discardMenu", - "discard", - "undo.clipboard", - "undo.newfile", - "feedback.helpful", - "feedback.unhelpful", - "showDiff", - { - "key": "miShowDiff", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "showDiff2", - { - "key": "miShowDiff2", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "apply1", - "apply2", - "cancel", - "copyRecordings", - "label", - "viewInChat", - "expandMessage", - "contractMessage" + "vs/platform/quickinput/browser/helpQuickAccess": [ + "helpPickAriaLabel" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations": [ - "startInlineChatIcon", - "toggleShowGutterIcon" + "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ + "noViewResults", + "views", + "panels", + "secondary side bar", + "terminalTitle", + "terminals", + "debugConsoles", + "channels", + "openView", + "quickOpenView" ], "vs/workbench/contrib/files/browser/fileConstants": [ + "removeFolderFromWorkspace", "saveAs", "save", "saveWithoutFormatting", "saveAll", - "removeFolderFromWorkspace", "newUntitledFile" ], - "vs/workbench/contrib/testing/browser/testingProgressUiService": [ - "testProgress.runningInitial", - "testProgress.running", + "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ + "noCommandResults", + "configure keybinding", + "askXInChat", + "commandWithCategory", + "confirmClearMessage", + "confirmClearDetail", + { + "key": "clearButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "showTriggerActions", + "clearCommandHistory" + ], + "vs/workbench/contrib/testing/browser/codeCoverageDecorations": [ + "testing.toggleInlineCoverage", + "coverage.branches", + "coverage.branchNotCovered", + "coverage.branchCoveredYes", + "coverage.branchCovered", + "coverage.declExecutedNo", + "coverage.declExecutedCount", + "coverage.declExecutedYes", + "coverage.toggleInline" + ], + "vs/workbench/contrib/testing/browser/testCoverageBars": [ + "statementCoverage", + "functionCoverage", + "branchCoverage" + ], + "vs/workbench/contrib/testing/browser/icons": [ + "testViewIcon", + "testingResultsIcon", + "testingRunIcon", + "testingRerunIcon", + "testingRunAllIcon", + "testingDebugAllIcon", + "testingDebugIcon", + "testingCoverageIcon", + "testingRunAllWithCoverageIcon", + "testingCancelIcon", + "filterIcon", + "hiddenIcon", + "testingShowAsList", + "testingShowAsTree", + "testingUpdateProfiles", + "testingRefreshTests", + "testingTurnContinuousRunOn", + "testingTurnContinuousRunOff", + "testingTurnContinuousRunIsOn", + "testingCancelRefreshTests", + "testingCoverage", + "testingWasCovered", + "testingMissingBranch", + "testingErrorIcon", + "testingFailedIcon", + "testingPassedIcon", + "testingQueuedIcon", + "testingSkippedIcon", + "testingUnsetIcon" + ], + "vs/workbench/contrib/testing/browser/testCoverageView": [ + "functionsWithoutCoverage", + "loadingCoverageDetails", + "testCoverageItemLabel", + "testCoverageTreeLabel", + "testing.coverageSortByLocation", + "testing.coverageSortByLocationDescription", + "testing.coverageSortByCoverage", + "testing.coverageSortByCoverageDescription", + "testing.coverageSortByName", + "testing.coverageSortByNameDescription", + "testing.coverageSortPlaceholder", + "testing.changeCoverageSort" + ], + "vs/workbench/contrib/testing/browser/testingDecorations": [ + "peekTestOutout", + "expected.title", + "actual.title", + "testing.gutterMsg.contextMenu", + "testing.gutterMsg.debug", + "testing.gutterMsg.coverage", + "testing.gutterMsg.run", + "run test", + "debug test", + "coverage test", + "testing.runUsing", + "peek failure", + "reveal test", + "run all test", + "run all test with coverage", + "debug all test", + "testOverflowItems", + "selectTestToRun" + ], + "vs/workbench/contrib/testing/browser/testingProgressUiService": [ + "testProgress.runningInitial", + "testProgress.running", "testProgressWithSkip.running", "testProgress.completed", "testProgressWithSkip.completed" ], - "vs/workbench/contrib/testing/browser/testingOutputPeek": [ - "testing.markdownPeekError", - "testOutputTitle", - "testingOutputExpected", - "testingOutputActual", - "runNoOutputForPast", - "runNoOutput", - "close", - "testUnnamedTask", - "messageMoreLinesN", - "messageMoreLines1", - "testingPeekLabel", - "testing.showResultOutput", - "testing.showResultOutput", - "testing.reRunLastRun", - "testing.debugLastRun", - "testing.showResultOutput", - "testing.revealInExplorer", - "run test", - "debug test", - "testing.goToFile", - "testing.goToError", - "testing.goToNextMessage", - "testing.goToPreviousMessage", - "testing.openMessageInEditor", - "testing.toggleTestingPeekHistory" + "vs/workbench/contrib/testing/browser/testingExplorerView": [ + "defaultTestProfile", + "selectDefaultConfigs", + "configureTestProfiles", + "noResults", + "testingContinuousBadge", + "testingCountBadgePassed", + "testingCountBadgeSkipped", + "testingCountBadgeFailed", + "testingNoTest", + "testingFindExtension", + { + "key": "testing.treeElementLabelDuration", + "comment": [ + "{0} is the original label in testing.treeElementLabel, {1} is a duration" + ] + }, + { + "key": "testing.treeElementLabelOutdated", + "comment": [ + "{0} is the original label in testing.treeElementLabel" + ] + }, + "testExplorer" ], "vs/workbench/contrib/testing/browser/testingViewPaneContainer": [ "testing" @@ -9661,6 +10150,7 @@ "testing.defaultGutterClickAction", "testing.defaultGutterClickAction.run", "testing.defaultGutterClickAction.debug", + "testing.defaultGutterClickAction.coverage", "testing.defaultGutterClickAction.contextMenu", "testing.gutterEnabled", "testing.saveBeforeTest", @@ -9669,22 +10159,49 @@ "testing.openTesting.openOnTestFailure", "testing.openTesting.openExplorerOnTestStart", "testing.openTesting", - "testing.alwaysRevealTestOnStateChange" + "testing.alwaysRevealTestOnStateChange", + "testing.ShowCoverageInExplorer", + "testing.displayedCoveragePercent", + "testing.displayedCoveragePercent.totalCoverage", + "testing.displayedCoveragePercent.statement", + "testing.displayedCoveragePercent.minimum", + "testing.coverageBarThresholds" ], - "vs/workbench/contrib/testing/browser/testingDecorations": [ - "peekTestOutout", - "expected.title", - "actual.title", - "testing.gutterMsg.contextMenu", - "testing.gutterMsg.debug", - "testing.gutterMsg.run", + "vs/workbench/contrib/testing/browser/testingOutputPeek": [ + "testing.markdownPeekError", + "testOutputTitle", + "testingOutputExpected", + "testingOutputActual", + "caseNoOutput", + "runNoOutput", + "runNoOutputForPast", + "openTestCoverage", + "closeTestCoverage", + "testUnnamedTask", + "messageMoreLinesN", + "messageMoreLines1", + "testingPeekLabel", + "testing.showResultOutput", + "testing.showResultOutput", + "testing.reRunLastRun", + "testing.debugLastRun", + "testing.showResultOutput", + "testing.revealInExplorer", "run test", "debug test", - "testing.runUsing", - "peek failure", - "reveal test", - "run all test", - "debug all test" + "testing.goToFile", + "testing.goToError", + "close", + "testing.goToNextMessage", + "testing.goToNextMessage.description", + "testing.goToPreviousMessage", + "testing.goToPreviousMessage.description", + "testing.openMessageInEditor", + "testing.toggleTestingPeekHistory", + "testing.toggleTestingPeekHistory.description" + ], + "vs/workbench/contrib/testing/common/testingContentProvider": [ + "runNoOutout" ], "vs/workbench/contrib/testing/common/testServiceImpl": [ "testTrust", @@ -9692,35 +10209,6 @@ "testTrust", "testError" ], - "vs/workbench/contrib/testing/common/testingContentProvider": [ - "runNoOutout" - ], - "vs/workbench/contrib/testing/browser/icons": [ - "testViewIcon", - "testingResultsIcon", - "testingRunIcon", - "testingRerunIcon", - "testingRunAllIcon", - "testingDebugAllIcon", - "testingDebugIcon", - "testingCancelIcon", - "filterIcon", - "hiddenIcon", - "testingShowAsList", - "testingShowAsTree", - "testingUpdateProfiles", - "testingRefreshTests", - "testingTurnContinuousRunOn", - "testingTurnContinuousRunOff", - "testingTurnContinuousRunIsOn", - "testingCancelRefreshTests", - "testingErrorIcon", - "testingFailedIcon", - "testingPassedIcon", - "testingQueuedIcon", - "testingSkippedIcon", - "testingUnsetIcon" - ], "vs/workbench/contrib/testing/common/testingContextKeys": [ "testing.canRefresh", "testing.isRefreshing", @@ -9733,44 +10221,54 @@ "testing.supportsContinuousRun", "testing.isParentRunningContinuously", "testing.activeEditorHasTests", + "testing.isTestCoverageOpen", "testing.peekItemType", "testing.controllerId", "testing.testId", "testing.testItemHasUri", "testing.testItemIsHidden", "testing.testMessage", - "testing.testResultOutdated" + "testing.testResultOutdated", + "testing.testResultState" ], "vs/workbench/contrib/testing/browser/testingConfigurationUi": [ "testConfigurationUi.pick", "updateTestConfiguration" ], "vs/workbench/contrib/testing/browser/testExplorerActions": [ + "testing.toggleContinuousRunOff", + "configureProfile", + "testing.noProfiles", + "testing.selectContinuousProfiles", + "discoveringTests", + "noTestProvider", + "noDebugTestProvider", + "noCoverageTestProvider", + "noTestsAtCursor", + "noTests", + "noTestsInFile", + "testing.noCoverage", + "runSelectedTests", + "debugSelectedTests", + "coverageSelectedTests", "hideTest", "unhideTest", "unhideAllTests", "debug test", + "run with cover test", "testing.runUsing", "run test", "testing.selectDefaultTestProfiles", "testing.toggleContinuousRunOn", - "testing.toggleContinuousRunOff", "testing.startContinuousRunUsing", "testing.configureProfile", - "configureProfile", "testing.stopContinuous", - "testing.noProfiles", - "testing.selectContinuousProfiles", "testing.startContinuous", "getSelectedProfiles", "getExplorerSelection", - "runSelectedTests", - "debugSelectedTests", - "discoveringTests", "runAllTests", - "noTestProvider", "debugAllTests", - "noDebugTestProvider", + "runAllWithCoverage", "testing.cancelRun", "testing.viewAsList", "testing.viewAsTree", @@ -9781,24 +10279,57 @@ "testing.collapseAll", "testing.clearResults", "testing.editFocusedTest", - "noTestsAtCursor", "testing.runAtCursor", "testing.debugAtCursor", - "noTestsInFile", + "testing.coverageAtCursor", "testing.runCurrentFile", "testing.debugCurrentFile", + "testing.coverageCurrentFile", "testing.reRunFailTests", "testing.debugFailTests", "testing.reRunLastRun", "testing.debugLastRun", + "testing.coverageLastRun", "testing.searchForTestExtension", "testing.openOutputPeek", "testing.toggleInlineTestOutput", "testing.refreshTests", - "testing.cancelTestRefresh" + "testing.cancelTestRefresh", + "testing.clearCoverage", + "testing.openCoverage" + ], + "vs/workbench/contrib/files/browser/views/emptyView": [ + "noWorkspace" + ], + "vs/workbench/contrib/files/browser/views/explorerView": [ + "explorerSection", + "createNewFile", + "createNewFolder", + "refreshExplorer", + "refreshExplorerMetadata", + "collapseExplorerFolders", + "collapseExplorerFoldersMetadata" + ], + "vs/workbench/contrib/files/browser/views/openEditorsView": [ + "dirtyCounter", + "openEditors", + { + "key": "miToggleEditorLayout", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "openEditors", + "comment": [ + "Open is an adjective" + ] + }, + "flipLayout", + "miToggleEditorLayoutWithoutMnemonic", + "newUntitledFile" ], "vs/workbench/contrib/logs/common/logsActions": [ - "setLogLevel", "all", "extensionLogs", "loggers", @@ -9806,127 +10337,14 @@ "selectLogLevelFor", "selectLogLevel", "resetLogLevel", - "trace", - "debug", - "info", - "warn", - "err", - "off", "default", - "openSessionLogFile", "current", "sessions placeholder", - "log placeholder" - ], - "vs/editor/contrib/peekView/browser/peekView": [ - "inReferenceSearchEditor", - "label.close", - "peekViewTitleBackground", - "peekViewTitleForeground", - "peekViewTitleInfoForeground", - "peekViewBorder", - "peekViewResultsBackground", - "peekViewResultsMatchForeground", - "peekViewResultsFileForeground", - "peekViewResultsSelectionBackground", - "peekViewResultsSelectionForeground", - "peekViewEditorBackground", - "peekViewEditorGutterBackground", - "peekViewEditorStickScrollBackground", - "peekViewResultsMatchHighlight", - "peekViewEditorMatchHighlight", - "peekViewEditorMatchHighlightBorder" - ], - "vs/workbench/contrib/testing/browser/testingExplorerView": [ - "defaultTestProfile", - "selectDefaultConfigs", - "configureTestProfiles", - "noResults", - "testingContinuousBadge", - "testingCountBadgePassed", - "testingCountBadgeSkipped", - "testingCountBadgeFailed", - "testingNoTest", - "testingFindExtension", - { - "key": "testing.treeElementLabelDuration", - "comment": [ - "{0} is the original label in testing.treeElementLabel, {1} is a duration" - ] - }, - { - "key": "testing.treeElementLabelOutdated", - "comment": [ - "{0} is the original label in testing.treeElementLabel" - ] - }, - "testExplorer" - ], - "vs/workbench/contrib/interactive/browser/interactiveEditor": [ - "interactiveInputPlaceHolder" - ], - "vs/platform/quickinput/browser/helpQuickAccess": [ - "helpPickAriaLabel" - ], - "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ - "noViewResults", - "views", - "panels", - "secondary side bar", - "terminalTitle", - "terminals", - "debugConsoles", - "channels", - "openView", - "quickOpenView" - ], - "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ - "noCommandResults", - "configure keybinding", - "askXInChat", - "commandWithCategory", - "showTriggerActions", - "clearCommandHistory", - "confirmClearMessage", - "confirmClearDetail", - { - "key": "clearButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/contrib/notebook/browser/notebookIcons": [ - "selectKernelIcon", - "executeIcon", - "executeAboveIcon", - "executeBelowIcon", - "stopIcon", - "deleteCellIcon", - "executeAllIcon", - "editIcon", - "stopEditIcon", - "moveUpIcon", - "moveDownIcon", - "clearIcon", - "splitCellIcon", - "successStateIcon", - "errorStateIcon", - "pendingStateIcon", - "executingStateIcon", - "collapsedIcon", - "expandedIcon", - "openAsTextIcon", - "revertIcon", - "renderOutputIcon", - "mimetypeIcon", - "copyIcon", - "previousChangeIcon", - "nextChangeIcon" + "log placeholder", + "setLogLevel", + "openSessionLogFile" ], "vs/workbench/contrib/files/browser/fileActions": [ - "newFile", - "newFolder", "rename", "delete", "copyFile", @@ -10009,26 +10427,29 @@ "confirmDeleteMessageFile", "confirmOverwrite", "replaceButtonLabel", - "globalCompareFile", "saveAllInGroup", "closeGroup", - "focusFilesExplorer", - "showInExplorer", - "openFileInNewWindow", "openFileToShowInNewWindow.unsupportedschema", "emptyFileNameError", "fileNameStartsWithSlashError", "fileNameExistsError", "invalidFileNameError", "fileNameWhitespaceWarning", - "compareNewUntitledTextFiles", - "compareWithClipboard", "clipboardComparisonLabel", "retry", "createBulkEdit", "creatingBulkEdit", "renameBulkEdit", "renamingBulkEdit", + "confirmMultiPasteNative", + "confirmPasteNative", + "doNotAskAgain", + { + "key": "pasteButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, "fileIsAncestor", { "key": "movingBulkEdit", @@ -10054,6 +10475,7 @@ "Placeholder will be replaced by the name of the file moved." ] }, + "fileDeleted", { "key": "copyingBulkEdit", "comment": [ @@ -10078,13 +10500,26 @@ "Placeholder will be replaced by the name of the file copied." ] }, - "fileDeleted", + "newFile", + "newFolder", + "globalCompareFile", + "compareFileWithMeta", + "toggleAutoSave", + "toggleAutoSaveDescription", + "focusFilesExplorer", + "focusFilesExplorerMetadata", + "showInExplorer", + "showInExplorerMetadata", + "openFileInEmptyWorkspace", + "openFileInEmptyWorkspaceMetadata", + "compareNewUntitledTextFiles", + "compareNewUntitledTextFilesMeta", + "compareWithClipboard", + "compareWithClipboardMeta", "setActiveEditorReadonlyInSession", "setActiveEditorWriteableInSession", "toggleActiveEditorReadonlyInSession", - "resetActiveEditorReadonlyInSession", - "toggleAutoSave", - "toggleAutoSaveDescription" + "resetActiveEditorReadonlyInSession" ], "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ "userGuide", @@ -10127,32 +10562,6 @@ "genericRevertError", "newFileCommand.saveLabel" ], - "vs/workbench/contrib/files/browser/views/explorerView": [ - "explorerSection", - "createNewFile", - "createNewFolder", - "refreshExplorer", - "collapseExplorerFolders" - ], - "vs/workbench/contrib/files/browser/views/openEditorsView": [ - "dirtyCounter", - "openEditors", - "flipLayout", - "miToggleEditorLayoutWithoutMnemonic", - { - "key": "miToggleEditorLayout", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "newUntitledFile", - { - "key": "openEditors", - "comment": [ - "Open is an adjective" - ] - } - ], "vs/workbench/contrib/files/browser/editors/binaryFileEditor": [ "binaryFileEditor" ], @@ -10170,11 +10579,11 @@ "detectIndentation", "trimAutoWhitespace", "largeFileOptimizations", + "wordBasedSuggestions.off", + "wordBasedSuggestions.currentDocument", + "wordBasedSuggestions.matchingDocuments", + "wordBasedSuggestions.allDocuments", "wordBasedSuggestions", - "wordBasedSuggestionsMode.currentDocument", - "wordBasedSuggestionsMode.matchingDocuments", - "wordBasedSuggestionsMode.allDocuments", - "wordBasedSuggestionsMode", "semanticHighlighting.true", "semanticHighlighting.false", "semanticHighlighting.configuredByTheme", @@ -10196,6 +10605,7 @@ "renderSideBySideInlineBreakpoint", "useInlineViewWhenSpaceIsLimited", "renderMarginRevertIcon", + "renderGutterMenu", "ignoreTrimWhitespace", "renderIndicators", "codeLens", @@ -10230,15 +10640,7 @@ "cancel", "empty.msg", "conflict.1", - "conflict.N", - "edt.title.del", - "rename", - "create", - "edt.title.2", - "edt.title.1" - ], - "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ - "default" + "conflict.N" ], "vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess": [ "cannotRunGotoLine", @@ -10249,7 +10651,6 @@ ], "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess": [ "empty", - "gotoSymbol", { "key": "miGotoSymbolInEditor", "comment": [ @@ -10258,11 +10659,13 @@ }, "gotoSymbolQuickAccessPlaceholder", "gotoSymbolQuickAccess", - "gotoSymbolByCategoryQuickAccess" + "gotoSymbolByCategoryQuickAccess", + "gotoSymbol" ], "vs/workbench/contrib/search/browser/anythingQuickAccess": [ "noAnythingResults", "recentlyOpenedSeparator", + "recentlyOpenedSeparator", "fileAndSymbolResultsSeparator", "fileResultsSeparator", "helpPickAriaLabel", @@ -10284,6 +10687,7 @@ ], "vs/workbench/contrib/search/browser/searchIcons": [ "searchDetailsIcon", + "searchSeeMoreIcon", "searchShowContextIcon", "searchHideReplaceIcon", "searchShowReplaceIcon", @@ -10299,7 +10703,14 @@ "searchStopIcon", "searchViewIcon", "searchNewEditorIcon", - "searchOpenInFile" + "searchOpenInFile", + "searchSparkleFilled", + "searchSparkleEmpty" + ], + "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ + "noSymbolResults", + "openToSide", + "openToBottom" ], "vs/workbench/contrib/search/browser/searchWidget": [ "search.action.replaceAll.disabled.label", @@ -10311,15 +10722,13 @@ "label.Replace", "search.replace.placeHolder" ], - "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ - "noSymbolResults", - "openToSide", - "openToBottom" - ], "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess": [ "QuickSearchSeeMoreFiles", "QuickSearchOpenInFile", - "QuickSearchMore" + "QuickSearchMore", + "showMore", + "enterSearchTerm", + "noAnythingResults" ], "vs/workbench/contrib/search/browser/searchActionsCopy": [ "copyMatchLabel", @@ -10327,10 +10736,7 @@ "copyAllLabel" ], "vs/workbench/contrib/search/browser/searchActionsFind": [ - "restrictResultsToFolder", - "excludeFolderFromSearch", - "revealInSideBar", - "findInFiles", + "search.expandRecursively", { "key": "miFindInFiles", "comment": [ @@ -10339,6 +10745,10 @@ }, "findInFiles.description", "findInFiles.args", + "restrictResultsToFolder", + "excludeFolderFromSearch", + "revealInSideBar", + "findInFiles", "findInFolder", "findInWorkspace" ], @@ -10367,6 +10777,16 @@ "file.replaceAll.label", "file.replaceAll.label" ], + "vs/workbench/contrib/search/browser/searchActionsSymbol": [ + "showTriggerActions", + { + "key": "miGotoSymbolInWorkspace", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "showTriggerActions" + ], "vs/workbench/contrib/search/browser/searchActionsTopBar": [ "clearSearchHistoryLabel", "CancelSearchAction.label", @@ -10377,22 +10797,50 @@ "ViewAsTreeAction.label", "ViewAsListAction.label" ], - "vs/workbench/contrib/search/browser/searchActionsSymbol": [ - "showTriggerActions", - "showTriggerActions", - { - "key": "miGotoSymbolInWorkspace", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], "vs/workbench/contrib/search/browser/searchActionsTextQuickAccess": [ "quickTextSearch" ], + "vs/workbench/browser/parts/views/viewPane": [ + "viewPaneContainerExpandedIcon", + "viewPaneContainerCollapsedIcon", + "viewToolbarAriaLabel", + "viewAccessibilityHelp" + ], + "vs/workbench/browser/labels": [ + "notebookCellLabel", + "notebookCellLabel" + ], "vs/workbench/contrib/search/browser/searchActionsBase": [ "search" ], + "vs/workbench/contrib/search/browser/patternInputWidget": [ + "defaultLabel", + "onlySearchInOpenEditors", + "useExcludesAndIgnoreFilesDescription" + ], + "vs/workbench/contrib/search/browser/searchMessage": [ + "unable to open trust", + "unable to open" + ], + "vs/workbench/contrib/search/browser/searchResultsView": [ + "searchFolderMatch.other.label", + "searchFolderMatch.other.label", + "searchFileMatches", + "searchFileMatch", + "searchMatches", + "searchMatch", + "lineNumStr", + "numLinesStr", + "search", + "folderMatchAriaLabel", + "otherFilesAriaLabel", + "fileMatchAriaLabel", + "replacePreviewResultAria", + "searchResultAria" + ], + "vs/workbench/services/search/common/queryBuilder": [ + "search.noWorkspaceWithName" + ], "vs/workbench/contrib/searchEditor/browser/searchEditor": [ "moreSearch", "searchScope.includes", @@ -10404,10 +10852,9 @@ "searchEditor", "textInputBoxBorder" ], - "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ - "searchTitle.withQuery", - "searchTitle.withQuery", - "searchTitle" + "vs/workbench/contrib/scm/browser/activity": [ + "status.scm", + "scmPendingChangesBadge" ], "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ "changes", @@ -10439,20 +10886,54 @@ "minimapGutterDeletedBackground", "overviewRulerModifiedForeground", "overviewRulerAddedForeground", - "overviewRulerDeletedForeground" + "overviewRulerDeletedForeground", + "diffAdded", + "diffModified", + "diffDeleted" ], - "vs/workbench/contrib/scm/browser/activity": [ - "status.scm", - "scmPendingChangesBadge" + "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ + "searchEditorLabelIcon", + "searchTitle.withQuery", + "searchTitle.withQuery", + "searchTitle" ], "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ "source control" ], + "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ + "scm" + ], + "vs/workbench/contrib/workspace/common/workspace": [ + "workspaceTrustEnabledCtx", + "workspaceTrustedCtx" + ], "vs/workbench/contrib/scm/browser/scmViewPane": [ + "scm.historyItemAdditionsForeground", + "scm.historyItemDeletionsForeground", + "scm.historyItemStatisticsBorder", + "scm.historyItemSelectedStatisticsBorder", + "fileChanged", + "filesChanged", + "insertion", + "insertions", + "deletion", + "deletions", "scm", "input", "sortAction", + "scmChanges", "repositories", + "incomingChanges", + "incomingChanges", + "always", + "auto", + "never", + "outgoingChanges", + "outgoingChanges", + "always", + "auto", + "never", + "showChangesSummary", "setListViewMode", "setTreeViewMode", "repositorySortByDiscoveryTime", @@ -10463,96 +10944,18 @@ "sortChangesByStatus", "collapse all", "expand all", - "label.close" - ], - "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ - "scm" - ], - "vs/workbench/contrib/workspace/common/workspace": [ - "workspaceTrustEnabledCtx", - "workspaceTrustedCtx" - ], - "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ - "scmSync", - "incoming", - "outgoing", - "refresh", - "setListViewMode", - "setTreeViewMode" - ], - "vs/workbench/browser/parts/views/viewPane": [ - "viewPaneContainerExpandedIcon", - "viewPaneContainerCollapsedIcon", - "viewToolbarAriaLabel" - ], - "vs/workbench/contrib/search/browser/patternInputWidget": [ - "defaultLabel", - "onlySearchInOpenEditors", - "useExcludesAndIgnoreFilesDescription" - ], - "vs/workbench/contrib/search/browser/searchMessage": [ - "unable to open trust", - "unable to open" - ], - "vs/workbench/contrib/search/browser/searchResultsView": [ - "searchFolderMatch.other.label", - "searchFolderMatch.other.label", - "searchFileMatches", - "searchFileMatch", - "searchMatches", - "searchMatch", - "lineNumStr", - "numLinesStr", - "search", - "folderMatchAriaLabel", - "otherFilesAriaLabel", - "fileMatchAriaLabel", - "replacePreviewResultAria", - "searchResultAria" - ], - "vs/workbench/services/search/common/queryBuilder": [ - "search.noWorkspaceWithName" - ], - "vs/workbench/contrib/debug/browser/debugHover": [ - { - "key": "quickTip", - "comment": [ - "\"switch to editor language hover\" means to show the programming language hover widget instead of the debug hover" - ] - }, - "treeAriaLabel", - { - "key": "variableAriaLabel", - "comment": [ - "Do not translate placeholders. Placeholders are name and value of a variable." - ] - } - ], - "vs/workbench/contrib/debug/browser/exceptionWidget": [ - "debugExceptionWidgetBorder", - "debugExceptionWidgetBackground", - "exceptionThrownWithId", - "exceptionThrown", - "close" - ], - "vs/workbench/contrib/debug/common/debugModel": [ - "invalidVariableAttributes", - "startDebugFirst", - "notAvailable", - { - "key": "pausedOn", - "comment": [ - "indicates reason for program being paused" - ] - }, - "paused", - { - "key": "running", - "comment": [ - "indicates state" - ] - }, - "breakpointDirtydHover" + "scmInputMoreActions", + "scmInputCancelAction", + "label.close", + "syncSeparatorHeader", + "syncSeparatorHeaderAriaLabel", + "syncIncomingSeparatorHeader", + "syncIncomingSeparatorHeaderAriaLabel", + "syncOutgoingSeparatorHeader", + "syncOutgoingSeparatorHeaderAriaLabel", + "incomingChangesAriaLabel", + "outgoingChangesAriaLabel", + "allChanges" ], "vs/workbench/contrib/debug/browser/breakpointsView": [ "unverifiedExceptionBreakpoint", @@ -10563,12 +10966,17 @@ "read", "write", "access", + "expressionAndHitCount", "functionBreakpointPlaceholder", "functionBreakPointInputAriaLabel", "functionBreakpointExpressionPlaceholder", "functionBreakPointExpresionAriaLabel", "functionBreakpointHitCountPlaceholder", "functionBreakPointHitCountAriaLabel", + "dataBreakpointExpressionPlaceholder", + "dataBreakPointExpresionAriaLabel", + "dataBreakpointHitCountPlaceholder", + "dataBreakPointHitCountAriaLabel", "exceptionBreakpointAriaLabel", "exceptionBreakpointPlaceholder", "breakpoints", @@ -10590,43 +10998,60 @@ "logMessage", "expression", "hitCount", + "triggeredBy", "breakpoint", - "addFunctionBreakpoint", { "key": "miFunctionBreakpoint", "comment": [ "&& denotes a mnemonic" ] }, - "activateBreakpoints", + "dataBreakpointError", + "dataBreakpointAccessType", + "dataBreakpointMemoryRangePrompt", + "dataBreakpointMemoryRangePlaceholder", + "dataBreakpointAddrFormat", + "dataBreakpointAddrStartEnd", + { + "key": "miDataBreakpoint", + "comment": [ + "&& denotes a mnemonic" + ] + }, "removeBreakpoint", - "removeAllBreakpoints", { "key": "miRemoveAllBreakpoints", "comment": [ "&& denotes a mnemonic" ] }, - "enableAllBreakpoints", { "key": "miEnableAllBreakpoints", "comment": [ "&& denotes a mnemonic" ] }, - "disableAllBreakpoints", { "key": "miDisableAllBreakpoints", "comment": [ "&& denotes a mnemonic" ] }, - "reapplyAllBreakpoints", "editCondition", "editCondition", "editHitCount", "editBreakpoint", - "editHitCount" + "editHitCount", + "editMode", + "selectBreakpointMode", + "addFunctionBreakpoint", + "addDataBreakpointOnAddress", + "editDataBreakpointOnAddress", + "activateBreakpoints", + "removeAllBreakpoints", + "enableAllBreakpoints", + "disableAllBreakpoints", + "reapplyAllBreakpoints" ], "vs/workbench/contrib/debug/browser/callStackView": [ { @@ -10701,44 +11126,10 @@ "debugIcon.continueForeground", "debugIcon.stepBackForeground" ], - "vs/workbench/contrib/debug/browser/debugCommands": [ - "debug", - "restartDebug", - "stepOverDebug", - "stepIntoDebug", - "stepIntoTargetDebug", - "stepOutDebug", - "pauseDebug", - "disconnect", - "disconnectSuspend", - "stop", - "continueDebug", - "focusSession", - "selectAndStartDebugging", - "openLaunchJson", - "startDebug", - "startWithoutDebugging", - "nextDebugConsole", - "prevDebugConsole", - "openLoadedScript", - "callStackTop", - "callStackBottom", - "callStackUp", - "callStackDown", - "selectDebugConsole", - "selectDebugSession", - "chooseLocation", - "noExecutableCode", - "jumpToCursor", - "editor.debug.action.stepIntoTargets.none", - "addConfiguration", - "addInlineBreakpoint" - ], "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ "workbench.action.debug.startDebug" ], "vs/workbench/contrib/debug/browser/debugEditorActions": [ - "toggleBreakpointAction", { "key": "miToggleBreakpoint", "comment": [ @@ -10759,6 +11150,13 @@ "&& denotes a mnemonic" ] }, + "triggerByBreakpointEditorAction", + { + "key": "miTriggerByBreakpoint", + "comment": [ + "&& denotes a mnemonic" + ] + }, "EditBreakpointEditorAction", { "key": "miEditBreakpoint", @@ -10766,14 +11164,12 @@ "&& denotes a mnemonic" ] }, - "openDisassemblyView", { "key": "miDisassemblyView", "comment": [ "&& denotes a mnemonic" ] }, - "toggleDisassemblyViewSourceCode", { "key": "mitogglesource", "comment": [ @@ -10791,6 +11187,10 @@ "goToNextBreakpoint", "goToPreviousBreakpoint", "closeExceptionWidget", + "toggleBreakpointAction", + "openDisassemblyView", + "toggleDisassemblyViewSourceCode", + "toggleDisassemblyViewSourceCodeDescription", "runToCursor", "evaluateInDebugConsole", "addToWatch" @@ -10806,6 +11206,7 @@ "debugBreakpoint", "debugBreakpointDisabled", "debugBreakpointUnverified", + "debugBreakpointPendingOnTrigger", "debugBreakpointFunction", "debugBreakpointFunctionDisabled", "debugBreakpointFunctionUnverified", @@ -10846,6 +11247,7 @@ "watchExpressionRemove", "watchExpressionsAdd", "watchExpressionsAddFuncBreakpoint", + "watchExpressionsAddDataBreakpoint", "breakpointsRemoveAll", "breakpointsActivate", "debugConsoleEvaluationInput", @@ -10911,6 +11313,42 @@ "breakpointAdded", "breakpointRemoved" ], + "vs/workbench/contrib/debug/browser/debugCommands": [ + "openLaunchJson", + "chooseLocation", + "noExecutableCode", + "jumpToCursor", + "editor.debug.action.stepIntoTargets.none", + "addInlineBreakpoint", + "debug", + "restartDebug", + "stepOverDebug", + "stepIntoDebug", + "stepIntoTargetDebug", + "stepOutDebug", + "pauseDebug", + "disconnect", + "disconnectSuspend", + "stop", + "continueDebug", + "focusSession", + "selectAndStartDebugging", + "startDebug", + "startWithoutDebugging", + "nextDebugConsole", + "prevDebugConsole", + "openLoadedScript", + "callStackTop", + "callStackBottom", + "callStackUp", + "callStackDown", + "copyAsExpression", + "copyValue", + "addToWatchExpressions", + "selectDebugConsole", + "selectDebugSession", + "addConfiguration" + ], "vs/workbench/contrib/debug/browser/debugStatus": [ "status.debug", "debugTarget", @@ -10930,19 +11368,6 @@ "instructionBytes", "instructionText" ], - "vs/workbench/contrib/debug/browser/loadedScriptsView": [ - "loadedScriptsSession", - { - "comment": [ - "Debug is a noun in this context, not a verb." - ], - "key": "loadedScriptsAriaLabel" - }, - "loadedScriptsRootFolderAriaLabel", - "loadedScriptsSessionAriaLabel", - "loadedScriptsFolderAriaLabel", - "loadedScriptsSourceAriaLabel" - ], "vs/workbench/contrib/debug/browser/statusbarColorProvider": [ "statusBarDebuggingBackground", "statusBarDebuggingForeground", @@ -10951,6 +11376,9 @@ ], "vs/workbench/contrib/debug/browser/variablesView": [ "variableValueAriaLabel", + "removeVisualizer", + "variableValueAriaLabel", + "useVisualizer", "variablesAriaTreeLabel", "variableScopeAriaLabel", { @@ -10960,11 +11388,50 @@ ] }, "viewMemory.prompt", - "cancel", - "install", - "viewMemory.install.progress", "collapse" ], + "vs/workbench/contrib/debug/browser/loadedScriptsView": [ + "loadedScriptsSession", + { + "comment": [ + "Debug is a noun in this context, not a verb." + ], + "key": "loadedScriptsAriaLabel" + }, + "loadedScriptsRootFolderAriaLabel", + "loadedScriptsSessionAriaLabel", + "loadedScriptsFolderAriaLabel", + "loadedScriptsSourceAriaLabel", + "collapse" + ], + "vs/workbench/contrib/debug/browser/welcomeView": [ + { + "key": "openAFileWhichCanBeDebugged", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change", + "{Locked=\"](command:{0})\"}" + ] + }, + "runAndDebugAction", + "detectThenRunAndDebug", + { + "key": "customizeRunAndDebug", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change", + "{Locked=\"](command:{0})\"}" + ] + }, + { + "key": "customizeRunAndDebugOpenFolder", + "comment": [ + "Please do not translate the word \"command\", it is part of our internal syntax which must not change", + "Please do not translate \"launch.json\", it is the specific configuration file name", + "{Locked=\"](command:{0})\"}" + ] + }, + "allDebuggersDisabled", + "run" + ], "vs/workbench/contrib/debug/browser/watchExpressionsView": [ "typeNewValue", "watchExpressionInputAriaLabel", @@ -10997,34 +11464,53 @@ } ], "vs/workbench/contrib/debug/common/disassemblyViewInput": [ + "disassemblyEditorLabelIcon", "disassemblyInputName" ], - "vs/workbench/contrib/debug/browser/welcomeView": [ + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariableCommands": [ + "copyWorkspaceVariableValue", + "executeNotebookVariableProvider" + ], + "vs/workbench/contrib/debug/browser/debugHover": [ { - "key": "openAFileWhichCanBeDebugged", + "key": "quickTip", "comment": [ - "Please do not translate the word \"command\", it is part of our internal syntax which must not change", - "{Locked=\"](command:{0})\"}" + "\"switch to editor language hover\" means to show the programming language hover widget instead of the debug hover" ] }, - "runAndDebugAction", - "detectThenRunAndDebug", + "treeAriaLabel", { - "key": "customizeRunAndDebug", + "key": "variableAriaLabel", "comment": [ - "Please do not translate the word \"command\", it is part of our internal syntax which must not change", - "{Locked=\"](command:{0})\"}" + "Do not translate placeholders. Placeholders are name and value of a variable." + ] + } + ], + "vs/workbench/contrib/debug/browser/exceptionWidget": [ + "debugExceptionWidgetBorder", + "debugExceptionWidgetBackground", + "exceptionThrownWithId", + "exceptionThrown", + "close" + ], + "vs/workbench/contrib/debug/common/debugModel": [ + "invalidVariableAttributes", + "startDebugFirst", + "notAvailable", + { + "key": "pausedOn", + "comment": [ + "indicates reason for program being paused" ] }, + "paused", { - "key": "customizeRunAndDebugOpenFolder", + "key": "running", "comment": [ - "Please do not translate the word \"commmand\", it is part of our internal syntax which must not change", - "{Locked=\"](command:{0})\"}" + "indicates state" ] }, - "allDebuggersDisabled", - "run" + "breakpointDirtydHover" ], "vs/workbench/contrib/debug/browser/breakpointWidget": [ "breakpointWidgetLogMessagePlaceholder", @@ -11033,101 +11519,14 @@ "expression", "hitCount", "logMessage", - "breakpointType" - ], - "vs/platform/actions/browser/menuEntryActionViewItem": [ - "titleAndKb", - "titleAndKb", - "titleAndKbAndAlt" - ], - "vs/workbench/contrib/debug/browser/debugActionViewItems": [ - "debugLaunchConfigurations", - "noConfigurations", - "addConfigTo", - "addConfiguration", - "debugSession" - ], - "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ - "mergeEditor", - "merge.dev.copyState", - "mergeEditor.name", - "mergeEditor.noActiveMergeEditor", - "mergeEditor.name", - "mergeEditor.successfullyCopiedMergeEditorContents", - "merge.dev.saveContentsToFolder", - "mergeEditor.name", - "mergeEditor.noActiveMergeEditor", - "mergeEditor.selectFolderToSaveTo", - "mergeEditor.name", - "mergeEditor.successfullySavedMergeEditorContentsToFolder", - "merge.dev.loadContentsFromFolder", - "mergeEditor.selectFolderToSaveTo" - ], - "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ - "title", - "layout.mixed", - "layout.column", - "showNonConflictingChanges", - "layout.showBase", - "layout.showBaseTop", - "layout.showBaseCenter", - "mergeEditor", - "openfile", - "merge.goToNextUnhandledConflict", - "merge.goToPreviousUnhandledConflict", - "merge.toggleCurrentConflictFromLeft", - "merge.toggleCurrentConflictFromRight", - "mergeEditor.compareInput1WithBase", - "mergeEditor.compareWithBase", - "mergeEditor.compareInput2WithBase", - "mergeEditor.compareWithBase", - "merge.openBaseEditor", - "merge.acceptAllInput1", - "merge.acceptAllInput2", - "mergeEditor.resetResultToBaseAndAutoMerge", - "mergeEditor.resetResultToBaseAndAutoMerge.short", - "mergeEditor.resetChoice", - "mergeEditor.acceptMerge", - "mergeEditor.acceptMerge.unhandledConflicts.message", - "mergeEditor.acceptMerge.unhandledConflicts.detail", - { - "key": "mergeEditor.acceptMerge.unhandledConflicts.accept", - "comment": [ - "&& denotes a mnemonic" - ] - } - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ - "name" - ], - "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ - "mergeEditor" - ], - "vs/platform/history/browser/contextScopedHistoryWidget": [ - "suggestWidgetVisible" - ], - "vs/workbench/contrib/debug/browser/linkDetector": [ - "followForwardedLink", - "followLink", - "fileLinkWithPathMac", - "fileLinkWithPath", - "fileLinkMac", - "fileLink" - ], - "vs/workbench/contrib/debug/browser/replViewer": [ - "debugConsole", - "replVariableAriaLabel", - { - "key": "occurred", - "comment": [ - "Front will the value of the debug console element. Placeholder will be replaced by a number which represents occurrance count." - ] - }, - "replRawObjectAriaLabel", - "replGroup" - ], - "vs/workbench/contrib/debug/common/replModel": [ - "consoleCleared" + "triggeredBy", + "breakpointType", + "bpMode", + "noTriggerByBreakpoint", + "triggerByLoading", + "noBpSource", + "selectBreakpoint", + "ok" ], "vs/workbench/contrib/markers/browser/markersView": [ "showing filtered problems", @@ -11137,7 +11536,6 @@ ], "vs/workbench/contrib/markers/browser/messages": [ "problems.view.toggle.label", - "problems.view.focus.label", "problems.panel.configuration.title", "problems.panel.configuration.autoreveal", "problems.panel.configuration.viewMode", @@ -11182,24 +11580,279 @@ "problems.tree.aria.label.marker.nosource", "problems.tree.aria.label.relatedinfo.message", "errors.warnings.show.label", + "problems.view.focus.label", "markers.panel.title.problems" ], - "vs/workbench/browser/parts/views/viewFilter": [ - "more filters" - ], "vs/workbench/contrib/markers/browser/markersFileDecorations": [ "label", "tooltip.1", "tooltip.N", "markers.showOnFile" ], + "vs/workbench/browser/parts/views/viewFilter": [ + "more filters" + ], + "vs/platform/actions/browser/menuEntryActionViewItem": [ + "titleAndKb", + "titleAndKb", + "titleAndKbAndAlt" + ], + "vs/workbench/contrib/debug/browser/debugActionViewItems": [ + "debugLaunchConfigurations", + "noConfigurations", + "addConfigTo", + "addConfiguration", + "debugSession" + ], + "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ + "commentsCountReplies", + "commentsCountReply", + "commentCount", + "imageWithLabel", + "image", + "outdated", + "commentLine", + "commentRange", + "lastReplyFrom", + "comments.view.title" + ], + "vs/workbench/contrib/comments/browser/commentsView": [ + "comments.filter.placeholder", + "comments.filter.ariaLabel", + "showing filtered results", + "acessibleViewHint", + "acessibleViewHintNoKbOpen", + "resourceWithCommentLabelOutdated", + "resourceWithCommentLabel", + "resourceWithCommentLabelFileOutdated", + "resourceWithCommentLabelFile", + "resourceWithRepliesLabel", + "replyCount", + "rootCommentsLabel", + "resourceWithCommentThreadsLabel" + ], + "vs/workbench/contrib/comments/browser/commentsController": [ + "commentRange", + "commentRangeStart", + "hasCommentRangesKb", + "hasCommentRangesNoKb", + "hasCommentRanges", + "pickCommentService" + ], + "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ + "accessibility.announcement.deprecationMessage", + "accessibilityConfigurationTitle", + "sound.enabled.auto", + "sound.enabled.on", + "sound.enabled.off", + "announcement.enabled.auto", + "announcement.enabled.off", + "verbosity.terminal.description", + "verbosity.diffEditor.description", + "verbosity.chat.description", + "verbosity.interactiveEditor.description", + "verbosity.inlineCompletions.description", + "verbosity.keybindingsEditor.description", + "verbosity.notebook", + "verbosity.hover", + "verbosity.notification", + "verbosity.emptyEditorHint", + "verbosity.comments", + "verbosity.diffEditorActive", + "announcement.save", + "announcement.save.userGesture", + "announcement.save.always", + "announcement.save.never", + "announcement.clear", + "announcement.format", + "announcement.format.userGesture", + "announcement.format.always", + "announcement.format.never", + "announcement.breakpoint", + "announcement.error", + "announcement.warning", + "announcement.foldedArea", + "announcement.terminalQuickFix", + "announcement.terminalBell", + "announcement.terminalCommandFailed", + "announcement.taskFailed", + "announcement.taskCompleted", + "announcement.chatRequestSent", + "announcement.progress", + "announcement.noInlayHints", + "announcement.lineHasBreakpoint", + "announcement.notebookCellCompleted", + "announcement.notebookCellFailed", + "announcement.onDebugBreak", + "terminal.integrated.accessibleView.closeOnKeyPress", + "accessibility.signals.sounds.volume", + "accessibility.signals.debouncePositionChanges", + "accessibility.signals.lineHasBreakpoint", + "accessibility.signals.lineHasBreakpoint.sound", + "accessibility.signals.lineHasBreakpoint.announcement", + "accessibility.signals.lineHasInlineSuggestion", + "accessibility.signals.lineHasInlineSuggestion.sound", + "accessibility.signals.lineHasError", + "accessibility.signals.lineHasError.sound", + "accessibility.signals.lineHasError.announcement", + "accessibility.signals.lineHasFoldedArea", + "accessibility.signals.lineHasFoldedArea.sound", + "accessibility.signals.lineHasFoldedArea.announcement", + "accessibility.signals.lineHasWarning", + "accessibility.signals.lineHasWarning.sound", + "accessibility.signals.lineHasWarning.announcement", + "accessibility.signals.positionHasError", + "accessibility.signals.positionHasError.sound", + "accessibility.signals.positionHasError.announcement", + "accessibility.signals.positionHasWarning", + "accessibility.signals.positionHasWarning.sound", + "accessibility.signals.positionHasWarning.announcement", + "accessibility.signals.onDebugBreak", + "accessibility.signals.onDebugBreak.sound", + "accessibility.signals.onDebugBreak.announcement", + "accessibility.signals.noInlayHints", + "accessibility.signals.noInlayHints.sound", + "accessibility.signals.noInlayHints.announcement", + "accessibility.signals.taskCompleted", + "accessibility.signals.taskCompleted.sound", + "accessibility.signals.taskCompleted.announcement", + "accessibility.signals.taskFailed", + "accessibility.signals.taskFailed.sound", + "accessibility.signals.taskFailed.announcement", + "accessibility.signals.terminalCommandFailed", + "accessibility.signals.terminalCommandFailed.sound", + "accessibility.signals.terminalCommandFailed.announcement", + "accessibility.signals.terminalQuickFix", + "accessibility.signals.terminalQuickFix.sound", + "accessibility.signals.terminalQuickFix.announcement", + "accessibility.signals.terminalBell", + "accessibility.signals.terminalBell.sound", + "accessibility.signals.terminalBell.announcement", + "accessibility.signals.diffLineInserted", + "accessibility.signals.sound", + "accessibility.signals.diffLineModified", + "accessibility.signals.diffLineModified.sound", + "accessibility.signals.diffLineDeleted", + "accessibility.signals.diffLineDeleted.sound", + "accessibility.signals.notebookCellCompleted", + "accessibility.signals.notebookCellCompleted.sound", + "accessibility.signals.notebookCellCompleted.announcement", + "accessibility.signals.notebookCellFailed", + "accessibility.signals.notebookCellFailed.sound", + "accessibility.signals.notebookCellFailed.announcement", + "accessibility.signals.chatRequestSent", + "accessibility.signals.chatRequestSent.sound", + "accessibility.signals.chatRequestSent.announcement", + "accessibility.signals.progress", + "accessibility.signals.progress.sound", + "accessibility.signals.progress.announcement", + "accessibility.signals.chatResponseReceived", + "accessibility.signals.chatResponseReceived.sound", + "accessibility.signals.voiceRecordingStarted", + "accessibility.signals.voiceRecordingStarted.sound", + "accessibility.signals.voiceRecordingStopped", + "accessibility.signals.voiceRecordingStopped.sound", + "accessibility.signals.clear", + "accessibility.signals.clear.sound", + "accessibility.signals.clear.announcement", + "accessibility.signals.save", + "accessibility.signals.save.sound", + "accessibility.signals.save.sound.userGesture", + "accessibility.signals.save.sound.always", + "accessibility.signals.save.sound.never", + "accessibility.signals.save.announcement", + "accessibility.signals.save.announcement.userGesture", + "accessibility.signals.save.announcement.always", + "accessibility.signals.save.announcement.never", + "accessibility.signals.format", + "accessibility.signals.format.sound", + "accessibility.signals.format.userGesture", + "accessibility.signals.format.always", + "accessibility.signals.format.never", + "accessibility.signals.format.announcement", + "accessibility.signals.format.announcement.userGesture", + "accessibility.signals.format.announcement.always", + "accessibility.signals.format.announcement.never", + "dimUnfocusedEnabled", + "dimUnfocusedOpacity", + "accessibility.hideAccessibleView", + "voice.speechTimeout", + "voice.speechLanguage", + "speechLanguage.auto" + ], + "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ + "comments.nextCommentingRange", + "comments.previousCommentingRange", + "comments.toggleCommenting", + "comments.addCommand.error", + "comments.addCommand", + "comments.collapseAll", + "comments.expandAll", + "comments.expandUnresolved" + ], + "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ + "mergeEditor.name", + "mergeEditor.noActiveMergeEditor", + "mergeEditor.name", + "mergeEditor.successfullyCopiedMergeEditorContents", + "mergeEditor.name", + "mergeEditor.noActiveMergeEditor", + "mergeEditor.selectFolderToSaveTo", + "mergeEditor.name", + "mergeEditor.successfullySavedMergeEditorContentsToFolder", + "mergeEditor.selectFolderToSaveTo", + "mergeEditor", + "merge.dev.copyState", + "merge.dev.saveContentsToFolder", + "merge.dev.loadContentsFromFolder" + ], + "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ + "name" + ], + "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ + "mergeEditor.compareWithBase", + "mergeEditor.compareWithBase", + "mergeEditor.resetResultToBaseAndAutoMerge.short", + "mergeEditor.acceptMerge.unhandledConflicts.message", + "mergeEditor.acceptMerge.unhandledConflicts.detail", + { + "key": "mergeEditor.acceptMerge.unhandledConflicts.accept", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "title", + "layout.mixed", + "layout.column", + "showNonConflictingChanges", + "layout.showBase", + "layout.showBaseTop", + "layout.showBaseCenter", + "mergeEditor", + "openfile", + "merge.goToNextUnhandledConflict", + "merge.goToPreviousUnhandledConflict", + "merge.toggleCurrentConflictFromLeft", + "merge.toggleCurrentConflictFromRight", + "mergeEditor.compareInput1WithBase", + "mergeEditor.compareInput2WithBase", + "merge.openBaseEditor", + "merge.acceptAllInput1", + "merge.acceptAllInput2", + "mergeEditor.resetResultToBaseAndAutoMerge", + "mergeEditor.resetChoice", + "mergeEditor.acceptMerge" + ], + "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ + "mergeEditor" + ], "vs/workbench/contrib/url/browser/trustedDomains": [ - "trustedDomain.manageTrustedDomain", "trustedDomain.trustDomain", "trustedDomain.trustAllPorts", "trustedDomain.trustSubDomain", "trustedDomain.trustAllDomains", - "trustedDomain.manageTrustedDomains" + "trustedDomain.manageTrustedDomains", + "trustedDomain.manageTrustedDomain" ], "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ "openExternalLinkAt", @@ -11222,27 +11875,6 @@ ] } ], - "vs/workbench/contrib/comments/common/commentContextKeys": [ - "hasCommentingRange", - "editorHasCommentingRange", - "hasCommentingProvider", - "commentThreadIsEmpty", - "commentIsEmpty", - "comment", - "commentThread", - "commentController", - "commentFocused" - ], - "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ - "comments.nextCommentingRange", - "comments.previousCommentingRange", - "comments.toggleCommenting", - "comments.addCommand.error", - "comments.addCommand", - "comments.collapseAll", - "comments.expandAll", - "comments.expandUnresolved" - ], "vs/workbench/contrib/webviewPanel/browser/webviewCommands": [ "editor.action.webvieweditor.showFind", "editor.action.webvieweditor.hideFind", @@ -11256,6 +11888,11 @@ "vs/workbench/contrib/customEditor/common/customEditor": [ "context.customEditor" ], + "vs/workbench/contrib/customEditor/browser/customEditorInput": [ + "editorUnsupportedInWindow", + "reopenInOriginalWindow", + "editorCannotMove" + ], "vs/workbench/contrib/externalUriOpener/common/configuration": [ "externalUriOpeners", "externalUriOpeners.uri", @@ -11269,21 +11906,9 @@ "selectOpenerPlaceHolder" ], "vs/workbench/contrib/extensions/common/extensionsInput": [ + "extensionsEditorLabelIcon", "extensionsInputName" ], - "vs/workbench/contrib/extensions/browser/extensionsViews": [ - "extensions", - "offline error", - "error", - "no extensions found", - "suggestProxyError", - "open user settings", - "no local extensions", - "extension.arialabel.verifiedPublisher", - "extension.arialabel.publisher", - "extension.arialabel.deprecated", - "extension.arialabel.rating" - ], "vs/workbench/contrib/extensions/browser/extensionsIcons": [ "extensionsViewIcon", "manageExtensionIcon", @@ -11310,19 +11935,38 @@ "trustIcon", "activationtimeIcon" ], - "vs/platform/dnd/browser/dnd": [ - "fileTooLarge" - ], - "vs/workbench/contrib/extensions/browser/extensionsActions": [ - "VS Code for Web", - "cannot be installed", - { - "key": "more information", - "comment": [ - "&& denotes a mnemonic" - ] + "vs/workbench/contrib/extensions/browser/extensionsViews": [ + "extensions", + "offline error", + "error", + "no extensions found", + "suggestProxyError", + "open user settings", + "no local extensions", + "extension.arialabel.verifiedPublisher", + "extension.arialabel.publisher", + "extension.arialabel.deprecated", + "extension.arialabel.rating" + ], + "vs/platform/dnd/browser/dnd": [ + "fileTooLarge" + ], + "vs/platform/actions/browser/toolbar": [ + "hide", + "resetThisMenu" + ], + "vs/workbench/contrib/extensions/browser/extensionsActions": [ + "VS Code for Web", + "cannot be installed", + { + "key": "more information", + "comment": [ + "&& denotes a mnemonic" + ] }, "close", + "install prerelease", + "cancel", "signature verification failed", "install anyway", "cancel", @@ -11352,6 +11996,7 @@ "install confirmation", "installExtensionStart", "installExtensionComplete", + "install workspace version", "install pre-release", "install pre-release version", "install", @@ -11378,17 +12023,22 @@ "update", "updateExtensionStart", "updateExtensionComplete", - "ignoreUpdates", - "ignoreExtensionUpdate", + "enableAutoUpdate", + "disableAutoUpdate", + "toggleAutoUpdatesForPublisherLabel", + "ignoreExtensionUpdatePublisher", + "enableAutoUpdate", + "disableAutoUpdate", "migrateExtension", "migrate to", "migrate", "manage", "manage", - "switch to pre-release version", - "switch to pre-release version tooltip", - "switch to release version", - "switch to release version tooltip", + "togglePreRleaseLabel", + "togglePreRleaseDisableLabel", + "togglePreRleaseDisableTooltip", + "switchToPreReleaseLabel", + "switchToPreReleaseTooltip", "install another version", "no versions", "pre-release", @@ -11404,17 +12054,14 @@ "disableGloballyActionToolTip", "enableAction", "disableAction", - "reloadAction", - "reload required", + "reload window", + "restart extensions", + "restart product", + "update product", "current", - "workbench.extensions.action.setColorTheme", "select color theme", - "workbench.extensions.action.setFileIconTheme", "select file icon theme", - "workbench.extensions.action.setProductIconTheme", "select product icon theme", - "workbench.extensions.action.setDisplayLanguage", - "workbench.extensions.action.clearLanguage", "showRecommendedExtension", "installRecommendedExtension", "ignoreExtensionRecommendation", @@ -11456,6 +12103,7 @@ "learn more", "Cannot be enabled", "learn more", + "manage access", "Install language pack also in remote server", "Install language pack also locally", "enabled remotely", @@ -11491,71 +12139,16 @@ "extensionButtonSeparator", "extensionButtonProminentBackground", "extensionButtonProminentForeground", - "extensionButtonProminentHoverBackground" - ], - "vs/workbench/contrib/terminal/browser/terminal.contribution": [ - "tasksQuickAccessPlaceholder", - "tasksQuickAccessHelp", - { - "key": "miToggleIntegratedTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "terminal", - "terminal" - ], - "vs/workbench/contrib/terminal/browser/terminalView": [ - "terminal.useMonospace", - "terminal.monospaceOnly", - "terminals", - "terminalConnectingLabel" - ], - "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ - "workbench.action.terminal.focusAccessibleBuffer", - "workbench.action.terminal.accessibleBufferGoToNextCommand", - "workbench.action.terminal.accessibleBufferGoToPreviousCommand" - ], - "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ - "workbench.action.terminal.showTextureAtlas", - "workbench.action.terminal.writeDataToTerminal", - "workbench.action.terminal.writeDataToTerminal.prompt", - "workbench.action.terminal.restartPtyHost" - ], - "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ - "workbench.action.terminal.showEnvironmentContributions", - "envChanges", - "extension", - "ScopedEnvironmentContributionInfo" - ], - "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ - "workbench.action.terminal.focusFind", - "workbench.action.terminal.hideFind", - "workbench.action.terminal.toggleFindRegex", - "workbench.action.terminal.toggleFindWholeWord", - "workbench.action.terminal.toggleFindCaseSensitive", - "workbench.action.terminal.findNext", - "workbench.action.terminal.findPrevious", - "workbench.action.terminal.searchWorkspace" - ], - "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ - "workbench.action.terminal.openDetectedLink", - "workbench.action.terminal.openLastUrlLink", - "workbench.action.terminal.openLastLocalFileLink" - ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ - "workbench.action.terminal.showQuickFixes" - ], - "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ - "app.extensions.json.title", - "app.extensions.json.recommendations", - "app.extension.identifier.errorMessage", - "app.extensions.json.unwantedRecommendations", - "app.extension.identifier.errorMessage" + "extensionButtonProminentHoverBackground", + "enableAutoUpdateLabel", + "workbench.extensions.action.setColorTheme", + "workbench.extensions.action.setFileIconTheme", + "workbench.extensions.action.setProductIconTheme", + "workbench.extensions.action.setDisplayLanguage", + "workbench.extensions.action.clearLanguage" ], "vs/workbench/contrib/extensions/browser/extensionEditor": [ "extension version", - "preRelease", "name", "preview", "preview", @@ -11563,18 +12156,18 @@ "publisher", "install count", "rating", + "workspace extension", + "local extension", "details", "detailstooltip", - "contributions", - "contributionstooltip", + "features", + "featurestooltip", "changelog", "changelogtooltip", "dependencies", "dependenciestooltip", "extensionpack", "extensionpacktooltip", - "runtimeStatus", - "runtimeStatus description", "noReadme", "Readme title", "extension pack", @@ -11582,6 +12175,7 @@ "Readme title", "categories", "Marketplace", + "issues", "repository", "license", "resources", @@ -11592,86 +12186,17 @@ "id", "noChangelog", "Changelog title", - "noContributions", - "noContributions", "noDependencies", - "activation reason", - "startup", - "activation time", - "activatedBy", - "not yet activated", - "uncaught errors", - "messages", - "noStatus", - "settings", - "setting name", - "description", - "default", - "debuggers", - "debugger name", - "debugger type", - "viewContainers", - "view container id", - "view container title", - "view container location", - "views", - "view id", - "view name", - "view location", - "localizations", - "localizations language id", - "localizations language name", - "localizations localized language name", - "customEditors", - "customEditors view type", - "customEditors priority", - "customEditors filenamePattern", - "codeActions", - "codeActions.title", - "codeActions.kind", - "codeActions.description", - "codeActions.languages", - "authentication", - "authentication.label", - "authentication.id", - "colorThemes", - "iconThemes", - "productThemes", - "colors", - "colorId", - "description", - "defaultDark", - "defaultLight", - "defaultHC", - "JSON Validation", - "fileMatch", - "schema", - "commands", - "command name", - "command title", - "keyboard shortcuts", - "menuContexts", - "languages", - "language id", - "language name", - "file extensions", - "grammar", - "snippets", - "activation events", - "Notebooks", - "Notebook id", - "Notebook name", - "NotebookRenderers", - "Notebook renderer name", - "Notebook mimetypes", "find", "find next", "find previous" ], - "vs/workbench/contrib/extensions/common/extensionsUtils": [ - "disableOtherKeymapsConfirmation", - "yes", - "no" + "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ + "app.extensions.json.title", + "app.extensions.json.recommendations", + "app.extension.identifier.errorMessage", + "app.extensions.json.unwantedRecommendations", + "app.extension.identifier.errorMessage" ], "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ "activation" @@ -11683,6 +12208,11 @@ "reload", "no missing deps" ], + "vs/workbench/contrib/extensions/common/extensionsUtils": [ + "disableOtherKeymapsConfirmation", + "yes", + "no" + ], "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ "type", "searchFor", @@ -11690,7 +12220,6 @@ "manage" ], "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService": [ - "neverShowAgain", "ignoreExtensionRecommendations", "ignoreAll", "no", @@ -11711,40 +12240,13 @@ "Placeholder string is the name of the software that is installed." ] }, + "donotShowAgain", + "donotShowAgainExtension", + "donotShowAgainExtensionSingle", "install", "install and do no sync", "show recommendations" ], - "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ - "Manifest is not found", - "postUninstallTooltip", - "postUpdateTooltip", - "enable locally", - "enable remote", - "postEnableTooltip", - "postEnableTooltip", - "postDisableTooltip", - "postEnableTooltip", - "postEnableTooltip", - "malicious", - "incompatible", - "uninstallingExtension", - "not found", - "installing extension", - "installing named extension", - "disable all", - "singleDependentError", - "twoDependentsError", - "multipleDependentsError" - ], - "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ - "exampleExtension" - ], - "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ - "deprecated extensions", - "showDeprecated", - "neverShowAgain" - ], "vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor": [ { "key": "starActivation", @@ -11790,73 +12292,223 @@ "extensionActivating", "unresponsive.title", "errors", + "requests count", + "session requests count", + "requests count title", "runtimeExtensions", "copy id", "disable workspace", "disable", "showRuntimeExtensions" ], - "vs/workbench/contrib/tasks/common/problemMatcher": [ - "ProblemPatternParser.problemPattern.missingRegExp", - "ProblemPatternParser.loopProperty.notLast", - "ProblemPatternParser.problemPattern.kindProperty.notFirst", - "ProblemPatternParser.problemPattern.missingProperty", - "ProblemPatternParser.problemPattern.missingLocation", - "ProblemPatternParser.invalidRegexp", - "ProblemPatternSchema.regexp", - "ProblemPatternSchema.kind", - "ProblemPatternSchema.file", - "ProblemPatternSchema.location", - "ProblemPatternSchema.line", - "ProblemPatternSchema.column", - "ProblemPatternSchema.endLine", - "ProblemPatternSchema.endColumn", - "ProblemPatternSchema.severity", - "ProblemPatternSchema.code", - "ProblemPatternSchema.message", - "ProblemPatternSchema.loop", - "NamedProblemPatternSchema.name", - "NamedMultiLineProblemPatternSchema.name", - "NamedMultiLineProblemPatternSchema.patterns", - "ProblemPatternExtPoint", - "ProblemPatternRegistry.error", - "ProblemPatternRegistry.error", - "ProblemMatcherParser.noProblemMatcher", - "ProblemMatcherParser.noProblemPattern", - "ProblemMatcherParser.noOwner", - "ProblemMatcherParser.noFileLocation", - "ProblemMatcherParser.unknownSeverity", - "ProblemMatcherParser.noDefinedPatter", - "ProblemMatcherParser.noIdentifier", - "ProblemMatcherParser.noValidIdentifier", - "ProblemMatcherParser.problemPattern.watchingMatcher", - "ProblemMatcherParser.invalidRegexp", - "WatchingPatternSchema.regexp", - "WatchingPatternSchema.file", - "PatternTypeSchema.name", - "PatternTypeSchema.description", - "ProblemMatcherSchema.base", - "ProblemMatcherSchema.owner", - "ProblemMatcherSchema.source", - "ProblemMatcherSchema.severity", - "ProblemMatcherSchema.applyTo", - "ProblemMatcherSchema.fileLocation", - "ProblemMatcherSchema.background", - "ProblemMatcherSchema.background.activeOnStart", - "ProblemMatcherSchema.background.beginsPattern", - "ProblemMatcherSchema.background.endsPattern", - "ProblemMatcherSchema.watching.deprecated", - "ProblemMatcherSchema.watching", - "ProblemMatcherSchema.watching.activeOnStart", - "ProblemMatcherSchema.watching.beginsPattern", - "ProblemMatcherSchema.watching.endsPattern", - "LegacyProblemMatcherSchema.watchedBegin.deprecated", - "LegacyProblemMatcherSchema.watchedBegin", - "LegacyProblemMatcherSchema.watchedEnd.deprecated", - "LegacyProblemMatcherSchema.watchedEnd", - "NamedProblemMatcherSchema.name", - "NamedProblemMatcherSchema.label", - "ProblemMatcherExtPoint", + "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ + "restartExtensionHost.reason" + ], + "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ + "exampleExtension" + ], + "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ + "deprecated extensions", + "showDeprecated", + "neverShowAgain" + ], + "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ + "Manifest is not found", + "restart", + "reload", + "restart extensions", + "postUninstallTooltip", + "postUpdateDownloadTooltip", + "postUpdateUpdateTooltip", + "postUpdateRestartTooltip", + "postUpdateTooltip", + "enable locally", + "enable remote", + "postEnableTooltip", + "postEnableTooltip", + "postDisableTooltip", + "postEnableTooltip", + "postEnableTooltip", + "malicious", + "not found version", + "not found", + { + "key": "installButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "installButtonLabelWithAction", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "open", + "installExtensionTitle", + "installExtensionMessage", + "installVSIXMessage", + "sync extension", + "unknown", + "enableExtensionTitle", + "enableExtensionMessage", + { + "key": "enableButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "enableButtonLabelWithAction", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "incompatible", + "uninstallingExtension", + "installing named extension", + "installing extension", + "disable all", + "singleDependentError", + "twoDependentsError", + "multipleDependentsError" + ], + "vs/workbench/contrib/terminal/browser/terminal.contribution": [ + "tasksQuickAccessPlaceholder", + "tasksQuickAccessHelp", + { + "key": "miToggleIntegratedTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "terminal", + "terminal" + ], + "vs/workbench/contrib/terminal/browser/terminalView": [ + "terminal.useMonospace", + "terminal.monospaceOnly", + "terminals", + "terminalConnectingLabel" + ], + "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ + "workbench.action.terminal.writeDataToTerminal.prompt", + "terminalDevMode", + "workbench.action.terminal.showTextureAtlas", + "workbench.action.terminal.writeDataToTerminal", + "workbench.action.terminal.restartPtyHost" + ], + "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ + "envChanges", + "extension", + "ScopedEnvironmentContributionInfo", + "workbench.action.terminal.showEnvironmentContributions" + ], + "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ + "workbench.action.terminal.focusAccessibleBuffer", + "workbench.action.terminal.accessibleBufferGoToNextCommand", + "workbench.action.terminal.accessibleBufferGoToPreviousCommand", + "workbench.action.terminal.scrollToBottomAccessibleView", + "workbench.action.terminal.scrollToTopAccessibleView" + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ + "workbench.action.terminal.openDetectedLink", + "workbench.action.terminal.openLastUrlLink", + "workbench.action.terminal.openLastUrlLink.description", + "workbench.action.terminal.openLastLocalFileLink" + ], + "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ + "workbench.action.terminal.showQuickFixes" + ], + "vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution": [ + "fontZoomIn", + "fontZoomOut", + "fontZoomReset" + ], + "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ + "workbench.action.terminal.focusFind", + "workbench.action.terminal.hideFind", + "workbench.action.terminal.toggleFindRegex", + "workbench.action.terminal.toggleFindWholeWord", + "workbench.action.terminal.toggleFindCaseSensitive", + "workbench.action.terminal.findNext", + "workbench.action.terminal.findPrevious", + "workbench.action.terminal.searchWorkspace" + ], + "vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution": [ + "workbench.action.terminal.selectPrevSuggestion", + "workbench.action.terminal.selectPrevPageSuggestion", + "workbench.action.terminal.selectNextSuggestion", + "workbench.action.terminal.selectNextPageSuggestion", + "workbench.action.terminal.acceptSelectedSuggestion", + "workbench.action.terminal.hideSuggestWidget" + ], + "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ + "workbench.action.tasks.manageAutomaticRunning", + "workbench.action.tasks.allowAutomaticTasks", + "workbench.action.tasks.disallowAutomaticTasks" + ], + "vs/workbench/contrib/tasks/common/problemMatcher": [ + "ProblemPatternParser.problemPattern.missingRegExp", + "ProblemPatternParser.loopProperty.notLast", + "ProblemPatternParser.problemPattern.kindProperty.notFirst", + "ProblemPatternParser.problemPattern.missingProperty", + "ProblemPatternParser.problemPattern.missingLocation", + "ProblemPatternParser.invalidRegexp", + "ProblemPatternSchema.regexp", + "ProblemPatternSchema.kind", + "ProblemPatternSchema.file", + "ProblemPatternSchema.location", + "ProblemPatternSchema.line", + "ProblemPatternSchema.column", + "ProblemPatternSchema.endLine", + "ProblemPatternSchema.endColumn", + "ProblemPatternSchema.severity", + "ProblemPatternSchema.code", + "ProblemPatternSchema.message", + "ProblemPatternSchema.loop", + "NamedProblemPatternSchema.name", + "NamedMultiLineProblemPatternSchema.name", + "NamedMultiLineProblemPatternSchema.patterns", + "ProblemPatternExtPoint", + "ProblemPatternRegistry.error", + "ProblemPatternRegistry.error", + "ProblemMatcherParser.noProblemMatcher", + "ProblemMatcherParser.noProblemPattern", + "ProblemMatcherParser.noOwner", + "ProblemMatcherParser.noFileLocation", + "ProblemMatcherParser.unknownSeverity", + "ProblemMatcherParser.noDefinedPatter", + "ProblemMatcherParser.noIdentifier", + "ProblemMatcherParser.noValidIdentifier", + "ProblemMatcherParser.problemPattern.watchingMatcher", + "ProblemMatcherParser.invalidRegexp", + "WatchingPatternSchema.regexp", + "WatchingPatternSchema.file", + "PatternTypeSchema.name", + "PatternTypeSchema.description", + "ProblemMatcherSchema.base", + "ProblemMatcherSchema.owner", + "ProblemMatcherSchema.source", + "ProblemMatcherSchema.severity", + "ProblemMatcherSchema.applyTo", + "ProblemMatcherSchema.fileLocation", + "ProblemMatcherSchema.background", + "ProblemMatcherSchema.background.activeOnStart", + "ProblemMatcherSchema.background.beginsPattern", + "ProblemMatcherSchema.background.endsPattern", + "ProblemMatcherSchema.watching.deprecated", + "ProblemMatcherSchema.watching", + "ProblemMatcherSchema.watching.activeOnStart", + "ProblemMatcherSchema.watching.beginsPattern", + "ProblemMatcherSchema.watching.endsPattern", + "LegacyProblemMatcherSchema.watchedBegin.deprecated", + "LegacyProblemMatcherSchema.watchedBegin", + "LegacyProblemMatcherSchema.watchedEnd.deprecated", + "LegacyProblemMatcherSchema.watchedEnd", + "NamedProblemMatcherSchema.name", + "NamedProblemMatcherSchema.label", + "ProblemMatcherExtPoint", "msCompile", "lessCompile", "gulp-tsc", @@ -11866,10 +12518,15 @@ "eslint-stylish", "go" ], - "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ - "workbench.action.tasks.manageAutomaticRunning", - "workbench.action.tasks.allowAutomaticTasks", - "workbench.action.tasks.disallowAutomaticTasks" + "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ + "JsonSchema.version.deprecated", + "JsonSchema.version", + "JsonSchema._runner", + "JsonSchema.runner", + "JsonSchema.windows", + "JsonSchema.mac", + "JsonSchema.linux", + "JsonSchema.shell" ], "vs/workbench/contrib/tasks/common/jsonSchema_v2": [ "JsonSchema.shell", @@ -11955,19 +12612,6 @@ "JsonSchema.mac", "JsonSchema.linux" ], - "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ - "restartExtensionHost.reason" - ], - "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ - "JsonSchema.version.deprecated", - "JsonSchema.version", - "JsonSchema._runner", - "JsonSchema.runner", - "JsonSchema.windows", - "JsonSchema.mac", - "JsonSchema.linux", - "JsonSchema.shell" - ], "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ "noTaskResults", "TaskService.pickRunTask" @@ -11991,7 +12635,6 @@ "pickRemoteExtension", "remotehelp", "remote.explorer", - "remote.explorer", "reconnectionWaitOne", "reconnectionWaitMany", "reconnectNow", @@ -12005,19 +12648,16 @@ "&& denotes a mnemonic" ] }, - "remote.help" + "remote.help", + "remote.explorer" ], "vs/workbench/contrib/remote/browser/remoteIndicator": [ - "remote.category", - "remote.showMenu", - "remote.close", { "key": "miCloseRemote", "comment": [ "&& denotes a mnemonic" ] }, - "remote.install", "host.open", "host.open", "host.reconnecting", @@ -12050,7 +12690,30 @@ "reloadWindow", "closeVirtualWorkspace.title", "remoteActions", - "remote.startActions.installingExtension" + "remote.startActions.installingExtension", + "remote.showExtensionRecommendations", + "remote.category", + "remote.showMenu", + "remote.close", + "remote.install" + ], + "vs/workbench/contrib/remote/browser/remoteConnectionHealth": [ + "unsupportedGlibcWarning", + { + "key": "allow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "learnMore", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "remember", + "unsupportedGlibcBannerLearnMore", + "unsupportedGlibcWarning.banner" ], "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ "expandAbbreviationAction", @@ -12062,15 +12725,19 @@ } ], "vs/workbench/contrib/codeEditor/browser/accessibility/accessibility": [ - "toggleScreenReaderMode" + "toggleScreenReaderMode", + "toggleScreenReaderModeDescription" ], "vs/workbench/contrib/codeEditor/browser/diffEditorHelper": [ "hintWhitespace", "hintTimeout", "removeTimeout", + "msg3", + "switchSidesNoKb", + "msg5", "msg1", "msg2", - "msg3" + "msg4" ], "vs/workbench/contrib/codeEditor/browser/inspectKeybindings": [ "workbench.action.inspectKeyMap", @@ -12090,6 +12757,11 @@ "inspectEditorTokens", "inspectTMScopesWidget.loading" ], + "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ + "gotoLineQuickAccessPlaceholder", + "gotoLineQuickAccess", + "gotoLine" + ], "vs/workbench/contrib/codeEditor/browser/saveParticipants": [ { "key": "formatting2", @@ -12107,51 +12779,46 @@ "codeAction.apply" ], "vs/workbench/contrib/codeEditor/browser/toggleColumnSelection": [ - "toggleColumnSelection", { "key": "miColumnSelection", "comment": [ "&& denotes a mnemonic" ] - } + }, + "toggleColumnSelection" ], "vs/workbench/contrib/codeEditor/browser/toggleMinimap": [ - "toggleMinimap", { "key": "miMinimap", "comment": [ "&& denotes a mnemonic" ] - } - ], - "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ - "gotoLine", - "gotoLineQuickAccessPlaceholder", - "gotoLineQuickAccess" - ], - "vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier": [ - "toggleLocation", - "miMultiCursorAlt", - "miMultiCursorCmd", - "miMultiCursorCtrl" + }, + "toggleMinimap" ], "vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter": [ - "toggleRenderControlCharacters", { "key": "miToggleRenderControlCharacters", "comment": [ "&& denotes a mnemonic" ] - } + }, + "toggleRenderControlCharacters" + ], + "vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier": [ + "miMultiCursorAlt", + "miMultiCursorCmd", + "miMultiCursorCtrl", + "toggleLocation" ], "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ - "toggleRenderWhitespace", { "key": "miToggleRenderWhitespace", "comment": [ "&& denotes a mnemonic" ] - } + }, + "toggleRenderWhitespace" ], "vs/workbench/contrib/codeEditor/browser/toggleWordWrap": [ "editorWordWrap", @@ -12184,16 +12851,50 @@ "defaultHintAriaLabel", "disableHint" ], - "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ - "global.scope", - "global.1", - "detail.label", - "name", - "bad_name1", + "vs/workbench/contrib/codeEditor/browser/dictation/editorDictation": [ + "stopDictationShort1", + "stopDictationShort2", + "voiceCategory", + "startDictation", + "stopDictation" + ], + "vs/platform/history/browser/contextScopedHistoryWidget": [ + "suggestWidgetVisible" + ], + "vs/workbench/contrib/debug/browser/linkDetector": [ + "followForwardedLink", + "followLink", + "fileLinkWithPathMac", + "fileLinkWithPath", + "fileLinkMac", + "fileLink" + ], + "vs/workbench/contrib/debug/browser/replViewer": [ + "debugConsole", + "replVariableAriaLabel", + { + "key": "occurred", + "comment": [ + "Front will the value of the debug console element. Placeholder will be replaced by a number which represents occurrance count." + ] + }, + "replRawObjectAriaLabel", + "replGroup" + ], + "vs/workbench/contrib/debug/common/replModel": [ + "consoleCleared" + ], + "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ + "label" + ], + "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ + "global.scope", + "global.1", + "detail.label", + "name", + "bad_name1", "bad_name2", "bad_name3", - "openSnippet.label", - "userSnippets", { "key": "miOpenSnippets", "comment": [ @@ -12207,23 +12908,23 @@ "group.global", "new.global.sep", "new.global.sep", - "openSnippet.pickLanguage" + "openSnippet.pickLanguage", + "openSnippet.label", + "userSnippets" ], "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ - "label", - "placeholder" - ], - "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ - "snippet.suggestions.label" - ], - "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ + "placeholder", "label" ], "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ + "more", "codeAction", "overflow.start.title", "title" ], + "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ + "snippet.suggestions.label" + ], "vs/workbench/contrib/snippets/browser/snippetsService": [ "invalid.path.0", "invalid.language.0", @@ -12249,10 +12950,11 @@ "vs/workbench/contrib/format/browser/formatActionsMultiple": [ "null", "nullFormatterDescription", - "miss", + "miss.1", + "miss.2", "config.needed", "config.bad", - "miss.1", + "miss", { "key": "do.config", "comment": [ @@ -12333,6 +13035,12 @@ ] } ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ + "builtin", + "developer", + "resetWelcomePageWalkthroughProgress", + "resetGettingStartedProgressDescription" + ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput": [ "getStarted" ], @@ -12340,6 +13048,10 @@ "welcome.displayName", "startupPage.markdownPreviewError" ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ + "gettingStartedUnchecked", + "gettingStartedChecked" + ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted": [ "welcomeAriaLabel", "pickWalkthroughs", @@ -12373,6 +13085,9 @@ "showAll", "close", "closeAriaLabel", + "videos", + "videos-title", + "videos-description", "gettingStarted.allStepsComplete", "gettingStarted.someStepsComplete", "gettingStarted.keyboardTip", @@ -12388,23 +13103,15 @@ ] } ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ - "builtin", - "developer", - "resetWelcomePageWalkthroughProgress" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ - "gettingStartedUnchecked", - "gettingStartedChecked" + "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ + "editorWalkThrough.title", + "editorWalkThrough", + "editorWalkThroughMetadata" ], "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ "walkThrough.unboundCommand", "walkThrough.gitNotFound" ], - "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ - "editorWalkThrough.title", - "editorWalkThrough" - ], "vs/workbench/contrib/welcomeViews/common/viewsWelcomeContribution": [ "ViewsWelcomeExtensionPoint.proposedAPI" ], @@ -12452,14 +13159,38 @@ "sortByName", "sortByKind" ], + "vs/workbench/contrib/authentication/browser/actions/signOutOfAccountAction": [ + "signOutOfAccount", + "signOutMessage", + "signOutMessageSimple", + { + "key": "signOut", + "comment": [ + "&& denotes a mnemonic" + ] + } + ], + "vs/workbench/contrib/authentication/browser/actions/manageTrustedExtensionsForAccountAction": [ + "pickAccount", + "noTrustedExtensions", + "manageTrustedExtensions.cancel", + { + "key": "accountLastUsedDate", + "comment": [ + "The placeholder {0} is a string with time information, such as \"3 days ago\"" + ] + }, + "notUsed", + "trustedExtensionTooltip", + "trustedExtensions", + "manageTrustedExtensions", + "manageExtensions", + "manageTrustedExtensionsForAccount", + "accounts" + ], "vs/workbench/contrib/userDataSync/browser/userDataSync": [ - "stop sync", - "configure sync", - "sync now", "syncing", "synced with time", - "sync settings", - "show synced data", "conflicts detected", "replace remote", "replace local", @@ -12526,12 +13257,10 @@ "default", "insiders", "stable", - "global activity turn on sync", "turnin on sync", "cancel turning on sync", "sign in global", "sign in accounts", - "resolveConflicts_global", "sync is on", "turn off failed", "configure", @@ -12539,40 +13268,23 @@ "show sync log toolrip", "complete merges title", "download sync activity complete", - "workbench.actions.syncData.reset" - ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ - "profiles", - "switchProfile", - "selectProfile", - "edit profile", - "show profile contents", - "export profile", - "export profile in share", - "import profile", - "import from url", - "import from file", - "templates", - "import profile quick pick title", - "import profile placeholder", - "profile import error", - "import profile dialog", - "import profile share", - "save profile as", - "create profile", - "delete profile", - "current", - "delete specific profile", - "pick profile to delete" + "workbench.actions.syncData.reset", + "stop sync", + "configure sync", + "sync now", + "sync settings", + "show synced data", + "global activity turn on sync", + "resolveConflicts_global" ], "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ - "create temporary profile", - "rename profile", "select profile to rename", "profileExists", "current", "rename specific profile", "pick profile to rename", + "create temporary profile", + "rename profile", "mange", "cleanup profile", "reset workspaces" @@ -12582,7 +13294,12 @@ "contributes.codeActions.languages", "contributes.codeActions.kind", "contributes.codeActions.title", - "contributes.codeActions.description" + "contributes.codeActions.description", + "codeActions.title", + "codeActions.kind", + "codeActions.description", + "codeActions.languages", + "codeactions" ], "vs/workbench/contrib/codeActions/common/documentationExtensionPoint": [ "contributes.documentation", @@ -12592,16 +13309,6 @@ "contributes.documentation.refactoring.when", "contributes.documentation.refactoring.command" ], - "vs/workbench/contrib/codeActions/browser/codeActionsContribution": [ - "alwaysSave", - "explicitSave", - "neverSave", - "explicitSaveBoolean", - "neverSaveBoolean", - "codeActionsOnSave.fixAll", - "editor.codeActionsOnSave", - "codeActionsOnSave.generic" - ], "vs/workbench/contrib/timeline/browser/timelinePane": [ "timeline.loadingMore", "timeline.loadMore", @@ -12618,25 +13325,52 @@ "timelineRefresh", "timelinePin", "timelineUnpin", + "timeline", "refresh", "timeline", "timeline.toggleFollowActiveEditorCommand.follow", "timeline", "timeline.toggleFollowActiveEditorCommand.unfollow", - "timeline", "timeline" ], + "vs/workbench/contrib/codeActions/browser/codeActionsContribution": [ + "alwaysSave", + "explicitSave", + "neverSave", + "explicitSaveBoolean", + "neverSaveBoolean", + "codeActionsOnSave.fixAll", + "editor.codeActionsOnSave", + "codeActionsOnSave.generic" + ], "vs/workbench/contrib/localHistory/browser/localHistoryTimeline": [ "localHistory" ], + "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ + "profiles", + "selectProfile", + "import from url", + "import from file", + "templates", + "import profile quick pick title", + "import profile placeholder", + "profile import error", + "import profile dialog", + "current", + "delete specific profile", + "pick profile to delete", + "switchProfile", + "edit profile", + "show profile contents", + "export profile", + "export profile in share", + "import profile", + "import profile share", + "save profile as", + "create profile", + "delete profile" + ], "vs/workbench/contrib/localHistory/browser/localHistoryCommands": [ - "localHistory.category", - "localHistory.compareWithFile", - "localHistory.compareWithPrevious", - "localHistory.selectForCompare", - "localHistory.compareWithSelected", - "localHistory.open", - "localHistory.restore", "localHistoryRestore.source", "confirmRestoreMessage", "confirmRestoreDetail", @@ -12647,14 +13381,10 @@ ] }, "unableToRestore", - "localHistory.restoreViaPicker", "restoreViaPicker.filePlaceholder", "restoreViaPicker.entryPlaceholder", - "localHistory.restoreViaPickerMenu", - "localHistory.rename", "renameLocalHistoryEntryTitle", "renameLocalHistoryPlaceholder", - "localHistory.delete", "confirmDeleteMessage", "confirmDeleteDetail", { @@ -12663,7 +13393,6 @@ "&& denotes a mnemonic" ] }, - "localHistory.deleteAll", "confirmDeleteAllMessage", "confirmDeleteAllDetail", { @@ -12672,21 +13401,30 @@ "&& denotes a mnemonic" ] }, - "localHistory.create", "createLocalHistoryEntryTitle", "createLocalHistoryPlaceholder", "localHistoryEditorLabel", "localHistoryCompareToFileEditorLabel", - "localHistoryCompareToPreviousEditorLabel" + "localHistoryCompareToPreviousEditorLabel", + "localHistory.category", + "localHistory.compareWithFile", + "localHistory.compareWithPrevious", + "localHistory.selectForCompare", + "localHistory.compareWithSelected", + "localHistory.open", + "localHistory.restore", + "localHistory.restoreViaPicker", + "localHistory.restoreViaPickerMenu", + "localHistory.rename", + "localHistory.delete", + "localHistory.deleteAll", + "localHistory.create" ], "vs/workbench/contrib/editSessions/common/editSessions": [ - "cloud changes", "editSessionViewIcon", + "cloud changes", "cloud changes" ], - "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ - "cloudChangesLog" - ], "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ "choose account read placeholder", "choose account placeholder", @@ -12699,6 +13437,9 @@ "sign out of cloud changes clear data prompt", "delete all cloud changes" ], + "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ + "cloudChangesLog" + ], "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ "noStoredChanges", "storeWorkingChangesTitle", @@ -12715,6 +13456,21 @@ "cloud changes", "open file" ], + "vs/workbench/contrib/accessibilitySignals/browser/commands": [ + "accessibility.sound.help.description", + "sounds.help.settings", + "sounds.help.placeholder", + "accessibility.announcement.help.description", + "announcement.help.settings", + "announcement.help.placeholder", + "announcement.help.placeholder.disabled", + "signals.sound.help", + "accessibility.announcement.help" + ], + "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ + "workspaceTrustEditorLabelIcon", + "workspaceTrustEditorInputName" + ], "vs/workbench/contrib/workspace/browser/workspaceTrustEditor": [ "shieldIcon", "checkListIcon", @@ -12819,39 +13575,6 @@ "untrustedFolderReason", "trustedForcedReason" ], - "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ - "workspaceTrustEditorInputName" - ], - "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ - "accessibilityConfigurationTitle", - "verbosity.terminal.description", - "verbosity.diffEditor.description", - "verbosity.chat.description", - "verbosity.interactiveEditor.description", - "verbosity.inlineCompletions.description", - "verbosity.keybindingsEditor.description", - "verbosity.notebook", - "verbosity.hover", - "verbosity.notification", - "verbosity.emptyEditorHint", - "verbosity.comments", - "alert.save", - "alert.save.userGesture", - "alert.save.always", - "alert.save.never", - "alert.format", - "alert.format.userGesture", - "alert.format.always", - "alert.format.never", - "dimUnfocusedEnabled", - "dimUnfocusedOpacity", - "accessibility.hideAccessibleView" - ], - "vs/workbench/contrib/accessibility/browser/accessibleNotificationService": [ - "cleared", - "saved", - "formatted" - ], "vs/workbench/contrib/accessibility/browser/accessibilityStatus": [ "screenReaderDetectedExplanation.question", "screenReaderDetectedExplanation.answerYes", @@ -12859,27 +13582,157 @@ "screenReaderDetected", "status.editor.screenReaderMode" ], - "vs/workbench/contrib/audioCues/browser/commands": [ - "audioCues.help", - "disabled", - "audioCues.help.settings", - "audioCues.help.placeholder" + "vs/workbench/contrib/comments/browser/commentsAccessibility": [ + "intro", + "introWidget", + "introWidgetNoKb", + "commentCommands", + "escape", + "next", + "nextNoKb", + "previous", + "previousNoKb", + "nextCommentThreadKb", + "nextCommentThreadNoKb", + "previousCommentThreadKb", + "previousCommentThreadNoKb", + "addComment", + "addCommentNoKb", + "submitComment", + "submitCommentNoKb" + ], + "vs/workbench/contrib/accessibilitySignals/browser/openDiffEditorAnnouncement": [ + "openDiffEditorAnnouncement" + ], + "vs/workbench/contrib/accessibility/browser/audioCueConfiguration": [ + "audioCues.enabled.auto", + "audioCues.enabled.on", + "audioCues.enabled.off", + "audioCues.enabled.deprecated", + "audioCues.debouncePositionChanges", + "audioCues.debouncePositionChangesDeprecated", + "audioCues.lineHasBreakpoint", + "audioCues.lineHasInlineSuggestion", + "audioCues.lineHasError", + "audioCues.lineHasFoldedArea", + "audioCues.lineHasWarning", + "audioCues.onDebugBreak", + "audioCues.noInlayHints", + "audioCues.taskCompleted", + "audioCues.taskFailed", + "audioCues.terminalCommandFailed", + "audioCues.terminalQuickFix", + "audioCues.terminalBell", + "audioCues.diffLineInserted", + "audioCues.diffLineDeleted", + "audioCues.diffLineModified", + "audioCues.notebookCellCompleted", + "audioCues.notebookCellFailed", + "audioCues.chatRequestSent", + "audioCues.chatResponsePending", + "audioCues.chatResponseReceived", + "audioCues.clear", + "audioCues.save", + "audioCues.save.userGesture", + "audioCues.save.always", + "audioCues.save.never", + "audioCues.format", + "audioCues.format.userGesture", + "audioCues.format.always", + "audioCues.format.never" + ], + "vs/workbench/contrib/scrollLocking/browser/scrollLocking": [ + "mouseScrolllingLocked", + "mouseLockScrollingEnabled", + { + "key": "miToggleLockedScrolling", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "synchronizeScrolling", + { + "key": "miHoldLockedScrolling", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "toggleLockedScrolling", + "holdLockedScrolling" ], "vs/workbench/contrib/share/browser/shareService": [ "shareProviderCount", "type to filter" ], + "vs/workbench/browser/window": [ + "quitMessageMac", + "quitMessage", + "closeWindowMessage", + { + "key": "quitButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "exitButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "closeWindowButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "doNotAskAgain", + "shutdownError", + "shutdownErrorDetail", + { + "key": "reload", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "unableToOpenExternal", + { + "key": "open", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "learnMore", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "openExternalDialogButtonRetry.v2", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "openExternalDialogDetail.v2", + { + "key": "openExternalDialogButtonInstall.v3", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "openExternalDialogDetailNoInstall", + "openExternalDialogTitle" + ], "vs/workbench/browser/parts/notifications/notificationsCenter": [ "notificationsEmpty", "notifications", "notificationsToolbar", + "turnOnNotifications", + "turnOffNotifications", + "moreSources", "notificationsCenterWidgetAriaLabel" ], - "vs/workbench/browser/parts/notifications/notificationsAlerts": [ - "alertErrorMessage", - "alertWarningMessage", - "alertInfoMessage" - ], "vs/workbench/browser/parts/notifications/notificationsStatus": [ "status.notifications", "status.notifications", @@ -12915,19 +13768,26 @@ }, "status.message" ], - "vs/workbench/browser/parts/notifications/notificationsToasts": [ - "notificationAriaLabel", - "notificationWithSourceAriaLabel" + "vs/workbench/browser/parts/notifications/notificationsAlerts": [ + "alertErrorMessage", + "alertWarningMessage", + "alertInfoMessage" ], "vs/workbench/browser/parts/notifications/notificationsCommands": [ + "selectSources", "notifications", "showNotifications", "hideNotifications", "clearAllNotifications", "acceptNotificationPrimaryAction", "toggleDoNotDisturbMode", + "toggleDoNotDisturbModeBySource", "focusNotificationToasts" ], + "vs/workbench/browser/parts/notifications/notificationsToasts": [ + "notificationAriaLabel", + "notificationWithSourceAriaLabel" + ], "vs/workbench/services/configuration/common/configurationEditing": [ "fsError", "openTasksConfiguration", @@ -12983,307 +13843,597 @@ "vs/workbench/common/editor/textEditorModel": [ "languageAutoDetected" ], - "vs/workbench/browser/parts/titlebar/titlebarPart": [ - "focusTitleBar", - "toggle.commandCenter", - "toggle.layout" - ], - "vs/workbench/services/configurationResolver/common/variableResolver": [ - "canNotResolveFile", - "canNotResolveFolderForFile", - "canNotFindFolder", - "canNotResolveWorkspaceFolderMultiRoot", - "canNotResolveWorkspaceFolder", - "missingEnvVarName", - "configNotFound", - "configNoString", - "missingConfigName", - "extensionNotInstalled", - "missingExtensionName", - "canNotResolveUserHome", - "canNotResolveLineNumber", - "canNotResolveSelectedText", - "noValueForCommand" - ], - "vs/workbench/services/workingCopy/common/workingCopyHistoryTracker": [ - "undoRedo.source" - ], - "vs/workbench/services/extensions/common/extensionHostManager": [ - "measureExtHostLatency" - ], - "vs/workbench/services/extensions/common/extensionsUtil": [ - "overwritingExtension", - "overwritingExtension", - "extensionUnderDevelopment" - ], - "vs/workbench/contrib/localization/common/localizationsActions": [ - "chooseLocale", - "installed", - "available", - "moreInfo", - "clearDisplayLanguage", - "configureLocale", - "configureLocaleDescription" - ], - "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ - "reportExtensionIssue" - ], - "vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions": [ - "cmd.reportOrShow", - "cmd.report", - "attach.title", - "attach.msg", - "cmd.show", - "attach.title", - "attach.msg2" - ], - "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote": [ - "workbench.action.terminal.newLocal" - ], - "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ - "ptyHostStatus", - "ptyHostStatus.short", - "nonResponsivePtyHost", - "ptyHostStatus.ariaLabel" - ], - "vs/workbench/contrib/localHistory/browser/localHistory": [ - "localHistoryIcon", - "localHistoryRestore" - ], - "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ - "taskTerminalStatus.active", - "taskTerminalStatus.succeeded", - "taskTerminalStatus.succeededInactive", - "taskTerminalStatus.errors", - "taskTerminalStatus.errorsInactive", - "taskTerminalStatus.warnings", - "taskTerminalStatus.warningsInactive", - "taskTerminalStatus.infos", - "taskTerminalStatus.infosInactive", - "task.watchFirstError" + "vs/platform/theme/common/colors/chartsColors": [ + "chartsForeground", + "chartsLines", + "chartsRed", + "chartsBlue", + "chartsYellow", + "chartsOrange", + "chartsGreen", + "chartsPurple" ], - "vs/workbench/contrib/tasks/common/taskConfiguration": [ - "ConfigurationParser.invalidCWD", - "ConfigurationParser.inValidArg", - "ConfigurationParser.noShell", - "ConfigurationParser.noName", - "ConfigurationParser.unknownMatcherKind", - "ConfigurationParser.invalidVariableReference", - "ConfigurationParser.noTaskType", - "ConfigurationParser.noTypeDefinition", - "ConfigurationParser.missingType", - "ConfigurationParser.incorrectType", - "ConfigurationParser.notCustom", - "ConfigurationParser.noTaskName", - "taskConfiguration.providerUnavailable", - "taskConfiguration.noCommandOrDependsOn", - "taskConfiguration.noCommand", - { - "key": "TaskParse.noOsSpecificGlobalTasks", - "comment": [ - "\"Task version 2.0.0\" refers to the 2.0.0 version of the task system. The \"version 2.0.0\" is not localizable as it is a json key and value." - ] - } + "vs/platform/theme/common/colors/inputColors": [ + "inputBoxBackground", + "inputBoxForeground", + "inputBoxBorder", + "inputBoxActiveOptionBorder", + "inputOption.hoverBackground", + "inputOption.activeBackground", + "inputOption.activeForeground", + "inputPlaceholderForeground", + "inputValidationInfoBackground", + "inputValidationInfoForeground", + "inputValidationInfoBorder", + "inputValidationWarningBackground", + "inputValidationWarningForeground", + "inputValidationWarningBorder", + "inputValidationErrorBackground", + "inputValidationErrorForeground", + "inputValidationErrorBorder", + "dropdownBackground", + "dropdownListBackground", + "dropdownForeground", + "dropdownBorder", + "buttonForeground", + "buttonSeparator", + "buttonBackground", + "buttonHoverBackground", + "buttonBorder", + "buttonSecondaryForeground", + "buttonSecondaryBackground", + "buttonSecondaryHoverBackground", + "checkbox.background", + "checkbox.select.background", + "checkbox.foreground", + "checkbox.border", + "checkbox.select.border", + "keybindingLabelBackground", + "keybindingLabelForeground", + "keybindingLabelBorder", + "keybindingLabelBottomBorder" ], - "vs/workbench/contrib/tasks/common/taskTemplates": [ - "dotnetCore", - "msbuild", - "externalCommand", - "Maven" + "vs/platform/theme/common/colors/editorColors": [ + "editorBackground", + "editorForeground", + "editorStickyScrollBackground", + "editorStickyScrollHoverBackground", + "editorStickyScrollBorder", + "editorStickyScrollShadow", + "editorWidgetBackground", + "editorWidgetForeground", + "editorWidgetBorder", + "editorWidgetResizeBorder", + "editorError.background", + "editorError.foreground", + "errorBorder", + "editorWarning.background", + "editorWarning.foreground", + "warningBorder", + "editorInfo.background", + "editorInfo.foreground", + "infoBorder", + "editorHint.foreground", + "hintBorder", + "activeLinkForeground", + "editorSelectionBackground", + "editorSelectionForeground", + "editorInactiveSelection", + "editorSelectionHighlight", + "editorSelectionHighlightBorder", + "editorFindMatch", + "findMatchHighlight", + "findRangeHighlight", + "editorFindMatchBorder", + "findMatchHighlightBorder", + "findRangeHighlightBorder", + "hoverHighlight", + "hoverBackground", + "hoverForeground", + "hoverBorder", + "statusBarBackground", + "editorInlayHintForeground", + "editorInlayHintBackground", + "editorInlayHintForegroundTypes", + "editorInlayHintBackgroundTypes", + "editorInlayHintForegroundParameter", + "editorInlayHintBackgroundParameter", + "editorLightBulbForeground", + "editorLightBulbAutoFixForeground", + "editorLightBulbAiForeground", + "snippetTabstopHighlightBackground", + "snippetTabstopHighlightBorder", + "snippetFinalTabstopHighlightBackground", + "snippetFinalTabstopHighlightBorder", + "diffEditorInserted", + "diffEditorRemoved", + "diffEditorInsertedLines", + "diffEditorRemovedLines", + "diffEditorInsertedLineGutter", + "diffEditorRemovedLineGutter", + "diffEditorOverviewInserted", + "diffEditorOverviewRemoved", + "diffEditorInsertedOutline", + "diffEditorRemovedOutline", + "diffEditorBorder", + "diffDiagonalFill", + "diffEditor.unchangedRegionBackground", + "diffEditor.unchangedRegionForeground", + "diffEditor.unchangedCodeBackground", + "widgetShadow", + "widgetBorder", + "toolbarHoverBackground", + "toolbarHoverOutline", + "toolbarActiveBackground", + "breadcrumbsFocusForeground", + "breadcrumbsBackground", + "breadcrumbsFocusForeground", + "breadcrumbsSelectedForeground", + "breadcrumbsSelectedBackground", + "mergeCurrentHeaderBackground", + "mergeCurrentContentBackground", + "mergeIncomingHeaderBackground", + "mergeIncomingContentBackground", + "mergeCommonHeaderBackground", + "mergeCommonContentBackground", + "mergeBorder", + "overviewRulerCurrentContentForeground", + "overviewRulerIncomingContentForeground", + "overviewRulerCommonContentForeground", + "overviewRulerFindMatchForeground", + "overviewRulerSelectionHighlightForeground", + "problemsErrorIconForeground", + "problemsWarningIconForeground", + "problemsInfoIconForeground" ], - "vs/workbench/contrib/tasks/browser/taskQuickPick": [ - "taskQuickPick.showAll", - "configureTaskIcon", - "removeTaskIcon", - "configureTask", - "contributedTasks", - "taskType", - "removeRecent", - "recentlyUsed", - "configured", - "configured", - "TaskQuickPick.changeSettingDetails", - "TaskQuickPick.changeSettingNo", - "TaskService.pickRunTask", - "TaskQuickPick.changeSettingsOptions", - "TaskQuickPick.goBack", - "TaskQuickPick.noTasksForType", - "noProviderForTask" + "vs/platform/theme/common/colors/minimapColors": [ + "minimapFindMatchHighlight", + "minimapSelectionOccurrenceHighlight", + "minimapSelectionHighlight", + "minimapInfo", + "overviewRuleWarning", + "minimapError", + "minimapBackground", + "minimapForegroundOpacity", + "minimapSliderBackground", + "minimapSliderHoverBackground", + "minimapSliderActiveBackground" ], - "vs/workbench/contrib/debug/common/abstractDebugAdapter": [ - "timeout" + "vs/platform/theme/common/colors/listColors": [ + "listFocusBackground", + "listFocusForeground", + "listFocusOutline", + "listFocusAndSelectionOutline", + "listActiveSelectionBackground", + "listActiveSelectionForeground", + "listActiveSelectionIconForeground", + "listInactiveSelectionBackground", + "listInactiveSelectionForeground", + "listInactiveSelectionIconForeground", + "listInactiveFocusBackground", + "listInactiveFocusOutline", + "listHoverBackground", + "listHoverForeground", + "listDropBackground", + "listDropBetweenBackground", + "highlight", + "listFocusHighlightForeground", + "invalidItemForeground", + "listErrorForeground", + "listWarningForeground", + "listFilterWidgetBackground", + "listFilterWidgetOutline", + "listFilterWidgetNoMatchesOutline", + "listFilterWidgetShadow", + "listFilterMatchHighlight", + "listFilterMatchHighlightBorder", + "listDeemphasizedForeground", + "treeIndentGuidesStroke", + "treeInactiveIndentGuidesStroke", + "tableColumnsBorder", + "tableOddRowsBackgroundColor" ], - "vs/workbench/contrib/debug/node/debugAdapter": [ - "debugAdapterBinNotFound", - { - "key": "debugAdapterCannotDetermineExecutable", - "comment": [ - "Adapter executable file not found" - ] - }, - "unableToLaunchDebugAdapter", - "unableToLaunchDebugAdapterNoArgs" + "vs/platform/theme/common/colors/miscColors": [ + "sashActiveBorder", + "badgeBackground", + "badgeForeground", + "scrollbarShadow", + "scrollbarSliderBackground", + "scrollbarSliderHoverBackground", + "scrollbarSliderActiveBackground", + "progressBarBackground" ], - "vs/platform/menubar/electron-main/menubar": [ - { - "key": "miNewWindow", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mFile", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mEdit", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mSelection", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mView", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mGoto", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mRun", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "mTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "mWindow", - { - "key": "mHelp", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "mAbout", - { - "key": "miPreferences", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "mServices", - "mHide", - "mHideOthers", - "mShowAll", - "miQuit", - { - "key": "quit", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "cancel", - "quitMessage", - "mMinimize", - "mZoom", - "mBringToFront", - { - "key": "miSwitchWindow", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "mNewTab", - "mShowPreviousTab", - "mShowNextTab", - "mMoveTabToNewWindow", - "mMergeAllWindows", - "miCheckForUpdates", - "miCheckingForUpdates", - "miDownloadUpdate", - "miDownloadingUpdate", - "miInstallUpdate", - "miInstallingUpdate", - "miRestartToUpdate" + "vs/platform/theme/common/colors/menuColors": [ + "menuBorder", + "menuForeground", + "menuBackground", + "menuSelectionForeground", + "menuSelectionBackground", + "menuSelectionBorder", + "menuSeparatorBackground" ], - "vs/platform/windows/electron-main/windowImpl": [ + "vs/platform/theme/common/colors/baseColors": [ + "foreground", + "disabledForeground", + "errorForeground", + "descriptionForeground", + "iconForeground", + "focusBorder", + "contrastBorder", + "activeContrastBorder", + "selectionBackground", + "textLinkForeground", + "textLinkActiveForeground", + "textSeparatorForeground", + "textPreformatForeground", + "textPreformatBackground", + "textBlockQuoteBackground", + "textBlockQuoteBorder", + "textCodeBlockBackground" + ], + "vs/platform/theme/common/colors/quickpickColors": [ + "pickerBackground", + "pickerForeground", + "pickerTitleBackground", + "pickerGroupForeground", + "pickerGroupBorder", + "quickInput.list.focusBackground deprecation", + "quickInput.listFocusForeground", + "quickInput.listFocusIconForeground", + "quickInput.listFocusBackground" + ], + "vs/platform/theme/common/colors/searchColors": [ + "search.resultsInfoForeground", + "searchEditor.queryMatch", + "searchEditor.editorFindMatchBorder" + ], + "vs/workbench/browser/parts/titlebar/titlebarPart": [ + "ariaLabelTitleActions", + "focusTitleBar" + ], + "vs/workbench/services/configurationResolver/common/variableResolver": [ + "canNotResolveFile", + "canNotResolveFolderForFile", + "canNotFindFolder", + "canNotResolveWorkspaceFolderMultiRoot", + "canNotResolveWorkspaceFolder", + "missingEnvVarName", + "configNotFound", + "configNoString", + "missingConfigName", + "extensionNotInstalled", + "missingExtensionName", + "canNotResolveUserHome", + "canNotResolveLineNumber", + "canNotResolveSelectedText", + "noValueForCommand" + ], + "vs/workbench/services/workingCopy/common/workingCopyHistoryTracker": [ + "undoRedo.source" + ], + "vs/workbench/services/extensions/common/extensionHostManager": [ + "measureExtHostLatency" + ], + "vs/workbench/contrib/localization/common/localizationsActions": [ + "chooseLocale", + "installed", + "available", + "moreInfo", + "configureLocale", + "configureLocaleDescription", + "clearDisplayLanguage" + ], + "vs/workbench/services/extensions/common/extensionsUtil": [ + "overwritingExtension", + "overwritingExtension", + "extensionUnderDevelopment" + ], + "vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions": [ + "cmd.reportOrShow", + "cmd.report", + "attach.title", + "attach.msg", + "cmd.show", + "attach.title", + "attach.msg2" + ], + "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ + "reportExtensionIssue" + ], + "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote": [ + "workbench.action.terminal.newLocal" + ], + "vs/workbench/contrib/terminal/browser/baseTerminalBackend": [ + "ptyHostStatus", + "ptyHostStatus.short", + "nonResponsivePtyHost", + "ptyHostStatus.ariaLabel" + ], + "vs/platform/theme/common/tokenClassificationRegistry": [ + "schema.token.settings", + "schema.token.foreground", + "schema.token.background.warning", + "schema.token.fontStyle", + "schema.fontStyle.error", + "schema.token.fontStyle.none", + "schema.token.bold", + "schema.token.italic", + "schema.token.underline", + "schema.token.strikethrough", + "comment", + "string", + "keyword", + "number", + "regexp", + "operator", + "namespace", + "type", + "struct", + "class", + "interface", + "enum", + "typeParameter", + "function", + "member", + "method", + "macro", + "variable", + "parameter", + "property", + "enumMember", + "event", + "decorator", + "labels", + "declaration", + "documentation", + "static", + "abstract", + "deprecated", + "modification", + "async", + "readonly" + ], + "vs/workbench/contrib/tasks/common/taskConfiguration": [ + "ConfigurationParser.invalidCWD", + "ConfigurationParser.inValidArg", + "ConfigurationParser.noShell", + "ConfigurationParser.noName", + "ConfigurationParser.unknownMatcherKind", + "ConfigurationParser.invalidVariableReference", + "ConfigurationParser.noTaskType", + "ConfigurationParser.noTypeDefinition", + "ConfigurationParser.missingType", + "ConfigurationParser.incorrectType", + "ConfigurationParser.notCustom", + "ConfigurationParser.noTaskName", + "taskConfiguration.providerUnavailable", + "taskConfiguration.noCommandOrDependsOn", + "taskConfiguration.noCommand", { - "key": "reopen", + "key": "TaskParse.noOsSpecificGlobalTasks", "comment": [ - "&& denotes a mnemonic" + "\"Task version 2.0.0\" refers to the 2.0.0 version of the task system. The \"version 2.0.0\" is not localizable as it is a json key and value." ] - }, + } + ], + "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ + "taskTerminalStatus.active", + "taskTerminalStatus.succeeded", + "taskTerminalStatus.succeededInactive", + "taskTerminalStatus.errors", + "taskTerminalStatus.errorsInactive", + "taskTerminalStatus.warnings", + "taskTerminalStatus.warningsInactive", + "taskTerminalStatus.infos", + "taskTerminalStatus.infosInactive", + "task.watchFirstError" + ], + "vs/workbench/contrib/tasks/common/taskTemplates": [ + "dotnetCore", + "msbuild", + "externalCommand", + "Maven" + ], + "vs/workbench/contrib/tasks/browser/taskQuickPick": [ + "taskQuickPick.showAll", + "configureTaskIcon", + "removeTaskIcon", + "configureTask", + "contributedTasks", + "taskType", + "removeRecent", + "recentlyUsed", + "configured", + "configured", + "TaskQuickPick.changeSettingDetails", + "TaskQuickPick.changeSettingNo", + "TaskService.pickRunTask", + "TaskQuickPick.changeSettingsOptions", + "TaskQuickPick.goBack", + "TaskQuickPick.noTasksForType", + "noProviderForTask" + ], + "vs/workbench/contrib/localHistory/browser/localHistory": [ + "localHistoryIcon", + "localHistoryRestore" + ], + "vs/workbench/contrib/multiDiffEditor/browser/icons.contribution": [ + "multiDiffEditorLabelIcon" + ], + "vs/workbench/contrib/debug/common/abstractDebugAdapter": [ + "timeout" + ], + "vs/workbench/contrib/debug/node/debugAdapter": [ + "debugAdapterBinNotFound", { - "key": "close", + "key": "debugAdapterCannotDetermineExecutable", "comment": [ - "&& denotes a mnemonic" + "Adapter executable file not found" ] }, + "unableToLaunchDebugAdapter", + "unableToLaunchDebugAdapterNoArgs" + ], + "vs/platform/menubar/electron-main/menubar": [ { - "key": "wait", + "key": "miNewWindow", "comment": [ "&& denotes a mnemonic" ] }, - "appStalled", - "appStalledDetail", - "doNotRestoreEditors", - "appGone", - "appGoneDetails", { - "key": "reopen", + "key": "mFile", "comment": [ "&& denotes a mnemonic" ] }, { - "key": "newWindow", + "key": "mEdit", "comment": [ "&& denotes a mnemonic" ] }, { - "key": "close", + "key": "mSelection", "comment": [ "&& denotes a mnemonic" ] }, - "appGoneDetailWorkspace", - "appGoneDetailEmptyWindow", - "doNotRestoreEditors", - "hiddenMenuBar" - ], - "vs/platform/terminal/common/terminalPlatformConfiguration": [ - "terminalProfile.args", - "terminalProfile.overrideName", - "terminalProfile.icon", - "terminalProfile.color", - "terminalProfile.env", - "terminalProfile.path", - "terminalAutomationProfile.path", { - "key": "terminal.integrated.profile", + "key": "mView", "comment": [ - "{0} is the platform, {1} is a code block, {2} and {3} are a link start and end" + "&& denotes a mnemonic" ] }, - "terminalIntegratedConfigurationTitle", + { + "key": "mGoto", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mRun", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "mTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "mWindow", + { + "key": "mHelp", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "mAbout", + { + "key": "miPreferences", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "mServices", + "mHide", + "mHideOthers", + "mShowAll", + "miQuit", + { + "key": "quit", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "cancel", + "quitMessage", + "mMinimize", + "mZoom", + "mBringToFront", + { + "key": "miSwitchWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "mNewTab", + "mShowPreviousTab", + "mShowNextTab", + "mMoveTabToNewWindow", + "mMergeAllWindows", + "miCheckForUpdates", + "miCheckingForUpdates", + "miDownloadUpdate", + "miDownloadingUpdate", + "miInstallUpdate", + "miInstallingUpdate", + "miRestartToUpdate" + ], + "vs/platform/windows/electron-main/windowImpl": [ + { + "key": "reopen", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "close", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "wait", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "appStalled", + "appStalledDetail", + "doNotRestoreEditors", + "appGone", + "appGoneDetails", + { + "key": "reopen", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "newWindow", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "close", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "appGoneDetailWorkspace", + "appGoneDetailEmptyWindow", + "doNotRestoreEditors", + "hiddenMenuBar" + ], + "vs/platform/terminal/common/terminalPlatformConfiguration": [ + "terminalProfile.args", + "terminalProfile.overrideName", + "terminalProfile.icon", + "terminalProfile.color", + "terminalProfile.env", + "terminalProfile.path", + "terminalAutomationProfile.path", + { + "key": "terminal.integrated.profile", + "comment": [ + "{0} is the platform, {1} is a code block, {2} and {3} are a link start and end" + ] + }, + "terminalIntegratedConfigurationTitle", "terminal.integrated.automationProfile.linux", "terminal.integrated.automationProfile.osx", "terminal.integrated.automationProfile.windows", @@ -13315,13 +14465,53 @@ "alertWarningMessage", "alertInfoMessage", { - "key": "history.inputbox.hint", + "key": "history.inputbox.hint.suffix.noparens", + "comment": [ + "Text is the suffix of an input field placeholder coming after the action the input field performs, this will be used when the input field ends in a closing parenthesis \")\", for example \"Filter (e.g. text, !exclude)\". The character inserted into the final string is ⇅ to represent the up and down arrow keys." + ] + }, + { + "key": "history.inputbox.hint.suffix.inparens", "comment": [ - "Text will be prefixed with ⇅ plus a single space, then used as a hint where input field keeps history" + "Text is the suffix of an input field placeholder coming after the action the input field performs, this will be used when the input field does NOT end in a closing parenthesis (eg. \"Find\"). The character inserted into the final string is ⇅ to represent the up and down arrow keys." ] }, "clearedInput" ], + "vs/editor/browser/widget/diffEditor/registrations.contribution": [ + "diffEditor.move.border", + "diffEditor.moveActive.border", + "diffEditor.unchangedRegionShadow", + "diffInsertIcon", + "diffRemoveIcon" + ], + "vs/editor/browser/widget/diffEditor/commands": [ + "toggleCollapseUnchangedRegions", + "toggleShowMovedCodeBlocks", + "toggleUseInlineViewWhenSpaceIsLimited", + "diffEditor", + "switchSide", + "exitCompareMove", + "collapseAllUnchangedRegions", + "showAllUnchangedRegions", + "revert", + "accessibleDiffViewer", + "editor.action.accessibleDiffViewer.next", + "editor.action.accessibleDiffViewer.prev" + ], + "vs/editor/contrib/dropOrPasteInto/browser/copyPasteController": [ + "pasteWidgetVisible", + "postPasteWidgetTitle", + "pasteAsError", + "pasteIntoEditorProgress", + "pasteAsPickerPlaceholder", + "pasteAsProgress" + ], + "vs/editor/contrib/codeAction/browser/codeActionController": [ + "editingNewSelection", + "hideMoreActions", + "showMoreActions" + ], "vs/editor/contrib/codeAction/browser/codeActionCommands": [ "args.schema.kind", "args.schema.apply", @@ -13352,12 +14542,8 @@ "autoFix.label", "editor.action.autoFix.noneMessage" ], - "vs/editor/contrib/codeAction/browser/codeActionController": [ - "editingNewSelection", - "hideMoreActions", - "showMoreActions" - ], "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ + "codeActionAutoRun", "preferredcodeActionWithKb", "codeActionWithKb", "codeAction" @@ -13371,35 +14557,15 @@ ] } ], - "vs/editor/contrib/dropOrPasteInto/browser/copyPasteController": [ - "pasteWidgetVisible", - "postPasteWidgetTitle", - "pasteIntoEditorProgress", - "pasteAsPickerPlaceholder", - "pasteAsProgress" - ], "vs/editor/contrib/dropOrPasteInto/browser/defaultProviders": [ - "builtIn", "text.label", "defaultDropProvider.uriList.uris", "defaultDropProvider.uriList.uri", "defaultDropProvider.uriList.paths", "defaultDropProvider.uriList.path", "defaultDropProvider.uriList.relativePaths", - "defaultDropProvider.uriList.relativePath" - ], - "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController": [ - "dropWidgetVisible", - "postDropWidgetTitle", - "dropIntoEditorProgress" - ], - "vs/editor/contrib/folding/browser/foldingDecorations": [ - "foldBackgroundBackground", - "editorGutter.foldingControlForeground", - "foldingExpandedIcon", - "foldingCollapsedIcon", - "foldingManualCollapedIcon", - "foldingManualExpandedIcon" + "defaultDropProvider.uriList.relativePath", + "pasteHtmlLabel" ], "vs/editor/contrib/find/browser/findWidget": [ "findSelectionIcon", @@ -13430,6 +14596,21 @@ "ariaSearchNoResultWithLineNumNoCurrentMatch", "ctrlEnter.keybindingChanged" ], + "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController": [ + "dropWidgetVisible", + "postDropWidgetTitle", + "dropIntoEditorProgress" + ], + "vs/editor/contrib/folding/browser/foldingDecorations": [ + "foldBackgroundBackground", + "editorGutter.foldingControlForeground", + "foldingExpandedIcon", + "foldingCollapsedIcon", + "foldingManualCollapedIcon", + "foldingManualExpandedIcon", + "linesCollapsed", + "linesExpanded" + ], "vs/editor/contrib/inlineCompletions/browser/commands": [ "action.inlineSuggest.showNext", "action.inlineSuggest.showPrevious", @@ -13443,80 +14624,163 @@ "action.inlineSuggest.hide", "action.inlineSuggest.alwaysShowToolbar" ], - "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ - "inlineSuggestionFollows" - ], "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController": [ "showAccessibleViewHint" ], - "vs/editor/contrib/gotoSymbol/browser/peek/referencesController": [ - "referenceSearchVisible", - "labelLoading", - "metaTitle.N" + "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ + "inlineSuggestionFollows" ], - "vs/editor/contrib/gotoSymbol/browser/referencesModel": [ - "aria.oneReference", + "vs/editor/contrib/hover/browser/hoverActions": [ { - "key": "aria.oneReference.preview", + "key": "showOrFocusHover", "comment": [ - "Placeholders are: 0: filename, 1:line number, 2: column number, 3: preview snippet of source code" + "Label for action that will trigger the showing/focusing of a hover in the editor.", + "If the hover is not visible, it will show the hover.", + "This allows for users to show the hover without using the mouse." ] }, - "aria.fileReferences.1", - "aria.fileReferences.N", - "aria.result.0", - "aria.result.1", - "aria.result.n1", - "aria.result.nm" - ], - "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ - "hasSymbols", - "location.kb", - "location" - ], - "vs/editor/contrib/message/browser/messageController": [ - "messageVisible" - ], - "vs/editor/contrib/gotoError/browser/gotoErrorWidget": [ - "Error", - "Warning", - "Info", - "Hint", - "marker aria", - "problems", - "change", - "editorMarkerNavigationError", - "editorMarkerNavigationErrorHeaderBackground", - "editorMarkerNavigationWarning", - "editorMarkerNavigationWarningBackground", - "editorMarkerNavigationInfo", - "editorMarkerNavigationInfoHeaderBackground", - "editorMarkerNavigationBackground" - ], - "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ - "modesContentHover.loading", - "stopped rendering", - "too many characters" - ], - "vs/editor/contrib/hover/browser/markerHoverParticipant": [ - "view problem", - "noQuickFixes", - "checkingForQuickFixes", - "noQuickFixes", - "quick fixes" - ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget": [ - "parameterHintsNextIcon", - "parameterHintsPreviousIcon", + "showOrFocusHover.focus.noAutoFocus", + "showOrFocusHover.focus.focusIfVisible", + "showOrFocusHover.focus.autoFocusImmediately", { - "key": "content", + "key": "showDefinitionPreviewHover", "comment": [ - "A label", - "A keybinding" + "Label for action that will trigger the showing of definition preview hover in the editor.", + "This allows for users to show the definition preview hover without using the mouse." ] }, - "previous", - "next" + { + "key": "scrollUpHover", + "comment": [ + "Action that allows to scroll up in the hover widget with the up arrow when the hover widget is focused." + ] + }, + { + "key": "scrollDownHover", + "comment": [ + "Action that allows to scroll down in the hover widget with the up arrow when the hover widget is focused." + ] + }, + { + "key": "scrollLeftHover", + "comment": [ + "Action that allows to scroll left in the hover widget with the left arrow when the hover widget is focused." + ] + }, + { + "key": "scrollRightHover", + "comment": [ + "Action that allows to scroll right in the hover widget with the right arrow when the hover widget is focused." + ] + }, + { + "key": "pageUpHover", + "comment": [ + "Action that allows to page up in the hover widget with the page up command when the hover widget is focused." + ] + }, + { + "key": "pageDownHover", + "comment": [ + "Action that allows to page down in the hover widget with the page down command when the hover widget is focused." + ] + }, + { + "key": "goToTopHover", + "comment": [ + "Action that allows to go to the top of the hover widget with the home command when the hover widget is focused." + ] + }, + { + "key": "goToBottomHover", + "comment": [ + "Action that allows to go to the bottom in the hover widget with the end command when the hover widget is focused." + ] + }, + { + "key": "increaseHoverVerbosityLevel", + "comment": [ + "Label for action that will increase the hover verbosity level." + ] + }, + { + "key": "decreaseHoverVerbosityLevel", + "comment": [ + "Label for action that will decrease the hover verbosity level." + ] + }, + "showOrFocusHoverDescription", + "showDefinitionPreviewHoverDescription", + "scrollUpHoverDescription", + "scrollDownHoverDescription", + "scrollLeftHoverDescription", + "scrollRightHoverDescription", + "pageUpHoverDescription", + "pageDownHoverDescription", + "goToTopHoverDescription", + "goToBottomHoverDescription" + ], + "vs/editor/contrib/hover/browser/markerHoverParticipant": [ + "view problem", + "noQuickFixes", + "checkingForQuickFixes", + "noQuickFixes", + "quick fixes" + ], + "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ + "increaseHoverVerbosity", + "decreaseHoverVerbosity", + "modesContentHover.loading", + "stopped rendering", + "too many characters", + "increaseVerbosityWithKb", + "increaseVerbosity", + "decreaseVerbosityWithKb", + "decreaseVerbosity" + ], + "vs/editor/contrib/gotoSymbol/browser/peek/referencesController": [ + "referenceSearchVisible", + "labelLoading", + "metaTitle.N" + ], + "vs/editor/contrib/gotoSymbol/browser/referencesModel": [ + "aria.oneReference", + { + "key": "aria.oneReference.preview", + "comment": [ + "Placeholders are: 0: filename, 1:line number, 2: column number, 3: preview snippet of source code" + ] + }, + "aria.fileReferences.1", + "aria.fileReferences.N", + "aria.result.0", + "aria.result.1", + "aria.result.n1", + "aria.result.nm" + ], + "vs/editor/contrib/gotoSymbol/browser/symbolNavigation": [ + "hasSymbols", + "location.kb", + "location" + ], + "vs/editor/contrib/message/browser/messageController": [ + "messageVisible" + ], + "vs/editor/contrib/gotoError/browser/gotoErrorWidget": [ + "Error", + "Warning", + "Info", + "Hint", + "marker aria", + "problems", + "change", + "editorMarkerNavigationError", + "editorMarkerNavigationErrorHeaderBackground", + "editorMarkerNavigationWarning", + "editorMarkerNavigationWarningBackground", + "editorMarkerNavigationInfo", + "editorMarkerNavigationInfoHeaderBackground", + "editorMarkerNavigationBackground" ], "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ "hint.dbl", @@ -13545,18 +14809,21 @@ "hint", "editorHoverWidgetHighlightForeground" ], - "vs/editor/contrib/rename/browser/renameInputField": [ + "vs/editor/contrib/rename/browser/renameWidget": [ "renameInputVisible", - "renameAriaLabel", + "renameInputFocused", { "key": "label", "comment": [ "placeholders are keybindings, e.g \"F2 to Rename, Shift+F2 to Preview\"" ] - } + }, + "renameSuggestionsReceivedAria", + "renameAriaLabel", + "generateRenameSuggestionsButton", + "cancelRenameSuggestionsButton" ], "vs/editor/contrib/stickyScroll/browser/stickyScrollActions": [ - "toggleStickyScroll", { "key": "mitoggleStickyScroll", "comment": [ @@ -13570,13 +14837,15 @@ "&& denotes a mnemonic" ] }, - "focusStickyScroll", { "key": "mifocusStickyScroll", "comment": [ "&& denotes a mnemonic" ] }, + "toggleEditorStickyScroll", + "toggleEditorStickyScroll.description", + "focusStickyScroll", "selectNextStickyScrollLine.title", "selectPreviousStickyScrollLine.title", "goToFocusedStickyScrollLine.title", @@ -13600,49 +14869,25 @@ "label.desc", "ariaCurrenttSuggestionReadDetails" ], - "vs/platform/theme/common/tokenClassificationRegistry": [ - "schema.token.settings", - "schema.token.foreground", - "schema.token.background.warning", - "schema.token.fontStyle", - "schema.fontStyle.error", - "schema.token.fontStyle.none", - "schema.token.bold", - "schema.token.italic", - "schema.token.underline", - "schema.token.strikethrough", - "comment", - "string", - "keyword", - "number", - "regexp", - "operator", - "namespace", - "type", - "struct", - "class", - "interface", - "enum", - "typeParameter", - "function", - "member", - "method", - "macro", - "variable", - "parameter", - "property", - "enumMember", - "event", - "decorator", - "labels", - "declaration", - "documentation", - "static", - "abstract", - "deprecated", - "modification", - "async", - "readonly" + "vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature": [ + "foldUnchanged", + "diff.hiddenLines.top", + "showUnchangedRegion", + "diff.bottom", + "hiddenLines", + "diff.hiddenLines.expandAll" + ], + "vs/workbench/contrib/chat/browser/chatInputPart": [ + "actions.chat.accessibiltyHelp", + "chatInput.accessibilityHelpNoKb", + "chatInput", + "notebook.moreExecuteActionsLabel", + "use", + "chat.submitToSecondaryAgent" + ], + "vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables": [ + "allFiles", + "allFilesDescription" ], "vs/workbench/api/browser/mainThreadWebviews": [ "errorMessage" @@ -13665,42 +14910,9 @@ "vetoExtHostRestart", "defaultEditLabel" ], - "vs/workbench/contrib/comments/browser/commentsView": [ - "comments.filter.placeholder", - "comments.filter.ariaLabel", - "totalUnresolvedComments", - "showing filtered results", - "rootCommentsLabel", - "resourceWithCommentThreadsLabel", - "resourceWithCommentLabel", - "resourceWithCommentLabelFile", - "collapseAll", - "expandAll" - ], - "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ - "commentsCount", - "commentCount", - "imageWithLabel", - "image", - "commentLine", - "commentRange", - "lastReplyFrom", - "comments.view.title" - ], "vs/workbench/contrib/testing/common/testResult": [ "runFinished" ], - "vs/workbench/browser/parts/editor/editorDropTarget": [ - "dropIntoEditorPrompt" - ], - "vs/workbench/browser/parts/editor/editorGroupView": [ - "ariaLabelGroupActions", - "emptyEditorGroup", - "groupLabelLong", - "groupLabel", - "groupAriaLabelLong", - "groupAriaLabel" - ], "vs/base/browser/ui/tree/treeDefaults": [ "collapse all" ], @@ -13745,13 +14957,11 @@ "remote.tunnelsView.portNumberToHigh", "remote.tunnelView.inlineElevationMessage", "remote.tunnelView.alreadyForwarded", - "remote.tunnel.forward", "remote.tunnel.forwardItem", "remote.tunnel.forwardPrompt", "remote.tunnel.forwardError", "remote.tunnel.forwardErrorProvided", "remote.tunnel.closeNoPorts", - "remote.tunnel.close", "remote.tunnel.closePlaceholder", "remote.tunnel.open", "remote.tunnel.openPreview", @@ -13771,7 +14981,9 @@ "tunnelContext.privacyMenu", "tunnelContext.protocolMenu", "portWithRunningProcess.foreground", - "remote.tunnel" + "remote.tunnel", + "remote.tunnel.forward", + "remote.tunnel.close" ], "vs/workbench/contrib/remote/browser/remoteIcons": [ "getStartedIcon", @@ -13800,24 +15012,17 @@ "binaryError", "openAnyway" ], - "vs/workbench/browser/parts/paneCompositePart": [ - "pane.emptyMessage", - "moreActions", - "views" - ], "vs/workbench/browser/parts/activitybar/activitybarPart": [ "menu", "hideMenu", "activity bar position", - "positionActivityBarSide", { - "key": "miSideActivityBar", + "key": "miDefaultActivityBar", "comment": [ "&& denotes a mnemonic" ] }, - "side", - "positionActivityBarTop", + "default", { "key": "miTopActivityBar", "comment": [ @@ -13825,7 +15030,13 @@ ] }, "top", - "hideActivityBar", + { + "key": "miBottomActivityBar", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "bottom", { "key": "miHideActivityBar", "comment": [ @@ -13836,15 +15047,33 @@ "positionActivituBar", "positionActivituBar", "positionActivituBar", + "positionActivityBarDefault", + "positionActivityBarTop", + "positionActivityBarBottom", + "hideActivityBar", "previousSideBarView", "nextSideBarView", "focusActivityBar" ], + "vs/workbench/browser/parts/paneCompositePart": [ + "pane.emptyMessage", + "moreActions", + "views" + ], "vs/workbench/browser/parts/sidebar/sidebarActions": [ "focusSideBar" ], - "vs/base/browser/ui/iconLabel/iconLabelHover": [ - "iconLabel.loading" + "vs/workbench/browser/parts/editor/editorGroupView": [ + "ariaLabelGroupActions", + "emptyEditorGroup", + "groupLabelLong", + "groupLabel", + "groupAriaLabelLong", + "groupAriaLabel", + "moveErrorDetails" + ], + "vs/workbench/browser/parts/editor/editorDropTarget": [ + "dropIntoEditorPrompt" ], "vs/workbench/services/preferences/browser/keybindingsEditorModel": [ "default", @@ -13861,6 +15090,7 @@ "validations.stringIncorrectEnumOptions", "validations.stringIncorrectType", "invalidTypeError", + "regexParsingError", "validations.maxLength", "validations.minLength", "validations.regex", @@ -13896,6 +15126,21 @@ ] } ], + "vs/base/browser/ui/icons/iconSelectBox": [ + "iconSelect.placeholder", + "iconSelect.noResults" + ], + "vs/platform/quickinput/browser/quickInput": [ + "quickInput.back", + "inputModeEntry", + "quickInput.steps", + "quickInputBox.ariaLabel", + "inputModeEntryDescription" + ], + "vs/base/browser/ui/hover/hoverWidget": [ + "acessibleViewHint", + "acessibleViewHintNoKbOpen" + ], "vs/platform/quickinput/browser/quickInputController": [ "quickInput.checkAll", { @@ -13915,10 +15160,6 @@ "quickInput.backWithKeybinding", "quickInput.back" ], - "vs/base/browser/ui/hover/hoverWidget": [ - "acessibleViewHint", - "acessibleViewHintNoKbOpen" - ], "vs/workbench/services/textMate/common/TMGrammars": [ "vscode.extension.contributes.grammars", "vscode.extension.contributes.grammars.language", @@ -13930,6 +15171,9 @@ "vscode.extension.contributes.grammars.balancedBracketScopes", "vscode.extension.contributes.grammars.unbalancedBracketScopes" ], + "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ + "unbound" + ], "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ "userSettings", "userSettingsRemote", @@ -13942,8 +15186,26 @@ "userSettings", "workspaceSettings" ], - "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ - "unbound" + "vs/workbench/contrib/preferences/browser/preferencesRenderers": [ + "editTtile", + "replaceDefaultValue", + "copyDefaultValue", + "unsupportedPolicySetting", + "unsupportLanguageOverrideSetting", + "defaultProfileSettingWhileNonDefaultActive", + "allProfileSettingWhileInNonDefaultProfileSetting", + "unsupportedRemoteMachineSetting", + "unsupportedWindowSetting", + "unsupportedApplicationSetting", + "unsupportedMachineSetting", + "untrustedSetting", + "unknown configuration setting", + "manage workspace trust", + "manage workspace trust", + "unsupportedProperty" + ], + "vs/base/browser/ui/toolbar/toolbar": [ + "moreActions" ], "vs/workbench/contrib/preferences/common/settingsEditorColorRegistry": [ "headerForeground", @@ -13968,47 +15230,6 @@ "settings.rowHoverBackground", "settings.focusedRowBorder" ], - "vs/workbench/contrib/preferences/browser/preferencesRenderers": [ - "editTtile", - "replaceDefaultValue", - "copyDefaultValue", - "unsupportedPolicySetting", - "unsupportLanguageOverrideSetting", - "defaultProfileSettingWhileNonDefaultActive", - "allProfileSettingWhileInNonDefaultProfileSetting", - "unsupportedRemoteMachineSetting", - "unsupportedWindowSetting", - "unsupportedApplicationSetting", - "unsupportedMachineSetting", - "untrustedSetting", - "unknown configuration setting", - "manage workspace trust", - "manage workspace trust", - "unsupportedProperty" - ], - "vs/base/browser/ui/toolbar/toolbar": [ - "moreActions" - ], - "vs/workbench/contrib/preferences/browser/settingsTree": [ - "extensions", - "modified", - "settingsContextMenuTitle", - "newExtensionsButtonLabel", - "editInSettingsJson", - "editLanguageSettingLabel", - "settings.Default", - "modified", - "showExtension", - "resetSettingLabel", - "validationError", - "validationError", - "settings.Modified", - "settings", - "copySettingIdLabel", - "copySettingAsJSONLabel", - "stopSyncingSetting", - "applyToAllProfiles" - ], "vs/workbench/contrib/preferences/browser/settingsLayout": [ "commonlyUsed", "textEditor", @@ -14017,6 +15238,7 @@ "font", "formatting", "diffEditor", + "multiDiffEditor", "minimap", "suggestions", "files", @@ -14030,6 +15252,7 @@ "window", "newWindow", "features", + "accessibility.signals", "accessibility", "fileExplorer", "search", @@ -14045,7 +15268,6 @@ "remote", "timeline", "notebook", - "audioCues", "mergeEditor", "chat", "application", @@ -14084,11 +15306,32 @@ "policySettingsSearch", "policySettingsSearchTooltip" ], + "vs/workbench/contrib/preferences/browser/settingsTree": [ + "extensions", + "modified", + "settingsContextMenuTitle", + "newExtensionsButtonLabel", + "editInSettingsJson", + "editLanguageSettingLabel", + "settings.Default", + "modified", + "showExtension", + "resetSettingLabel", + "validationError", + "validationError", + "settings.Modified", + "settings", + "copySettingIdLabel", + "copySettingAsJSONLabel", + "stopSyncingSetting", + "applyToAllProfiles" + ], "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ "chat.overview", "chat.requestHistory", "chat.inspectResponse", "chat.inspectResponseNoKb", + "chat.followUp", "chat.announcement", "workbench.action.chat.focus", "workbench.action.chat.focusNoKb", @@ -14110,28 +15353,38 @@ "inlineChat.diff", "inlineChat.diffNoKb", "inlineChat.toolbar", - "chat.audioCues" + "chat.signals" + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ + "inlineSuggestionVisible", + "inlineSuggestionHasIndentation", + "inlineSuggestionHasIndentationLessThanTabSize", + "suppressSuggestions" + ], + "vs/workbench/contrib/chat/browser/codeBlockPart": [ + "chat.codeBlockHelp", + "chat.codeBlock.toolbarVerbose", + "chat.codeBlock.toolbar", + "chat.codeBlockLabel", + "vulnerabilitiesPlural", + "vulnerabilitiesSingular", + "chat.codeBlockHelp", + "original", + "modified", + "chat.codeBlock.toolbarVerbose", + "chat.codeBlock.toolbar", + "chat.compareCodeBlockLabel", + "chat.edits.N", + "chat.edits.1", + "interactive.compare.apply.confirm", + "interactive.compare.apply.confirm.detail" ], "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ "notebookActions.joinSelectedCells", "notebookActions.joinSelectedCells.label" ], - "vs/workbench/contrib/chat/browser/chatInputPart": [ - "actions.chat.accessibiltyHelp", - "chatInput.accessibilityHelpNoKb", - "chatInput" - ], - "vs/workbench/contrib/chat/browser/chatListRenderer": [ - "usedAgent", - "usingAgent", - "thinking", - "usedReferencesPlural", - "usedReferencesSingular", - "usedReferencesExpanded", - "usedReferencesCollapsed", + "vs/workbench/contrib/chat/browser/chatAccessibilityProvider": [ "chat", - "commandFollowUpInfo", - "commandFollowUpInfoMany", "singleFileTreeHint", "multiFileTreeHint", "noCodeBlocksHint", @@ -14139,22 +15392,37 @@ "singleCodeBlockHint", "singleCodeBlock", "multiCodeBlockHint", - "multiCodeBlock", - "treeAriaLabel" + "multiCodeBlock" ], - "vs/platform/actions/browser/toolbar": [ - "hide", - "resetThisMenu" + "vs/workbench/contrib/chat/browser/chatListRenderer": [ + "usedAgent", + "usingAgent", + "usedAgent", + "usingAgent", + "usedReferencesPlural", + "usedReferencesSingular", + "usedReferencesExpanded", + "usedReferencesCollapsed", + "commandButtonDisabled", + "editsSummary1", + "editsSummary", + "treeAriaLabel", + "usedReferences" ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ - "inlineSuggestionVisible", - "inlineSuggestionHasIndentation", - "inlineSuggestionHasIndentationLessThanTabSize", - "suppressSuggestions" + "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ + "change.0", + "review.1", + "change.1", + "review.N", + "change.N", + "review" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget": [ + "inlineChatClosed" ], "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView": [ - "notebookActions.selectKernel", - "notebookActions.selectKernel.args" + "notebookActions.selectKernel.args", + "notebookActions.selectKernel" ], "vs/workbench/contrib/notebook/browser/notebookExtensionPoint": [ "contributes.notebook.provider", @@ -14183,12 +15451,31 @@ "contributes.preload.provider", "contributes.preload.provider.viewType", "contributes.preload.entrypoint", - "contributes.preload.localResourceRoots" + "contributes.preload.localResourceRoots", + "Notebook id", + "Notebook name", + "Notebook renderer name", + "Notebook mimetypes", + "notebooks", + "notebookRenderer" + ], + "vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView": [ + "notebook.emptyMarkdownPlaceholder", + { + "key": "notebook.error.rendererNotFound", + "comment": [ + "$0 is a placeholder for the mime type" + ] + }, + { + "key": "notebook.error.rendererFallbacksExhausted", + "comment": [ + "$0 is a placeholder for the mime type" + ] + }, + "webview title" ], "vs/workbench/contrib/notebook/browser/notebookEditorWidget": [ - "notebookTreeAriaLabelHelp", - "notebookTreeAriaLabelHelpNoKb", - "notebookTreeAriaLabel", "notebook.cellBorderColor", "notebook.focusedEditorBorder", "notebookStatusSuccessIcon.foreground", @@ -14214,22 +15501,6 @@ "notebook.cellEditorBackground", "notebook.editorBackground" ], - "vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView": [ - "notebook.emptyMarkdownPlaceholder", - { - "key": "notebook.error.rendererNotFound", - "comment": [ - "$0 is a placeholder for the mime type" - ] - }, - { - "key": "notebook.error.rendererFallbacksExhausted", - "comment": [ - "$0 is a placeholder for the mime type" - ] - }, - "webview title" - ], "vs/workbench/services/workingCopy/common/fileWorkingCopyManager": [ "fileWorkingCopyCreate.source", "fileWorkingCopyReplace.source", @@ -14272,34 +15543,91 @@ "kernels.detecting", "select" ], + "vs/workbench/contrib/comments/common/commentContextKeys": [ + "hasCommentingRange", + "editorHasCommentingRange", + "hasCommentingProvider", + "commentThreadIsEmpty", + "commentIsEmpty", + "comment", + "commentThread", + "commentController", + "commentFocused" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView": [ + "notebook.notebookVariables" + ], + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext": [ + "notebookCellChatFocused", + "notebookChatHasActiveRequest", + "notebookChatUserDidEdit", + "notebookChatOuterFocusPosition" + ], + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController": [ + "default.placeholder", + "welcome.1", + "default.placeholder", + "welcome.1" + ], + "vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions": [ + "indentUsingTabs", + "indentUsingSpaces", + "changeTabDisplaySize", + "convertIndentationToSpaces", + "convertIndentationToTabs", + { + "key": "selectTabWidth", + "comment": [ + "Tab corresponds to the tab key" + ] + }, + "convertIndentation" + ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget": [ "ariaSearchNoResultEmpty", "ariaSearchNoResult", "ariaSearchNoResultWithLineNumNoCurrentMatch" ], + "vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions": [ + "arrowUp", + "arrowDown", + "focusChatWidget", + "focusNextChatWidget", + "apply2", + "apply3", + "discard", + "feedback.helpful", + "feedback.unhelpful", + "feedback.reportIssueForBug", + "notebookActions.menu.insertCodeCellWithChat", + "notebookActions.menu.insertCodeCellWithChat.tooltip", + "notebookActions.menu.insertCodeCellWithChat.tooltip", + "notebookActions.menu.insertCodeCellWithChat", + "notebookActions.menu.insertCodeCellWithChat.tooltip", + "notebookActions.menu.insertCode.ontoolbar", + "notebookActions.menu.insertCode.tooltip", + "focusNotebookChat", + "focusNextCell", + "focusPreviousCell", + "notebook.cell.chat.accept", + "notebook.cell.chat.stop", + "notebook.cell.chat.close", + "apply1", + "notebook.cell.chat.previousFromHistory", + "notebook.cell.chat.nextFromHistory", + "notebookActions.restoreCellprompt" + ], "vs/editor/contrib/codeAction/browser/codeAction": [ "applyCodeActionFailed" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ - "lines.0", - "lines.1", - "lines.N" - ], - "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ - "aria-label", - "original", - "modified", - "inlineChat.accessibilityHelp", - "inlineChat.accessibilityHelpNoKb", - "slashCommandUsed", - "inlineChatClosed" - ], - "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ - "terminal.integrated.copySelection.noSelection", - "yes", - "no", - "dontShowAgain", - "terminal.slowRendering" + "vs/platform/quickinput/browser/commandsQuickAccess": [ + "recentlyUsed", + "suggested", + "commonlyUsed", + "morecCommands", + "suggested", + "commandPickAriaLabelWithKeybinding", + "canNotRun" ], "vs/workbench/contrib/testing/browser/theme": [ "testing.iconFailed", @@ -14310,34 +15638,28 @@ "testing.iconUnset", "testing.iconSkipped", "testing.peekBorder", + "testing.messagePeekBorder", "testing.peekBorder", + "testing.messagePeekHeaderBackground", + "testing.coveredBackground", + "testing.coveredBorder", + "testing.coveredGutterBackground", + "testing.uncoveredBranchBackground", + "testing.uncoveredBackground", + "testing.uncoveredBorder", + "testing.uncoveredGutterBackground", + "testing.coverCountBadgeBackground", + "testing.coverCountBadgeForeground", "testing.message.error.decorationForeground", "testing.message.error.marginBackground", "testing.message.info.decorationForeground", - "testing.message.info.marginBackground" - ], - "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ - "terminal.background", - "terminal.foreground", - "terminalCursor.foreground", - "terminalCursor.background", - "terminal.selectionBackground", - "terminal.inactiveSelectionBackground", - "terminal.selectionForeground", - "terminalCommandDecoration.defaultBackground", - "terminalCommandDecoration.successBackground", - "terminalCommandDecoration.errorBackground", - "terminalOverviewRuler.cursorForeground", - "terminal.border", - "terminal.findMatchBackground", - "terminal.findMatchHighlightBorder", - "terminal.findMatchBorder", - "terminal.findMatchHighlightBackground", - "terminal.findMatchHighlightBorder", - "terminalOverviewRuler.findMatchHighlightForeground", - "terminal.dragAndDropBackground", - "terminal.tab.activeBorder", - "terminal.ansiColor" + "testing.message.info.marginBackground", + "testing.iconErrored.retired", + "testing.iconFailed.retired", + "testing.iconPassed.retired", + "testing.iconQueued.retired", + "testing.iconUnset.retired", + "testing.iconSkipped.retired" ], "vs/workbench/contrib/testing/common/constants": [ "testState.errored", @@ -14369,14 +15691,58 @@ "testing.filters.showExcludedTests", "testing.filters.removeTestExclusions" ], - "vs/platform/quickinput/browser/commandsQuickAccess": [ - "recentlyUsed", - "suggested", - "commonlyUsed", - "morecCommands", - "suggested", - "commandPickAriaLabelWithKeybinding", - "canNotRun" + "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ + "terminal.integrated.copySelection.noSelection" + ], + "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ + "terminal.background", + "terminal.foreground", + "terminalCursor.foreground", + "terminalCursor.background", + "terminal.selectionBackground", + "terminal.inactiveSelectionBackground", + "terminal.selectionForeground", + "terminalCommandDecoration.defaultBackground", + "terminalCommandDecoration.successBackground", + "terminalCommandDecoration.errorBackground", + "terminalOverviewRuler.cursorForeground", + "terminal.border", + "terminal.findMatchBackground", + "terminal.findMatchHighlightBorder", + "terminal.findMatchBorder", + "terminal.findMatchHighlightBackground", + "terminal.findMatchHighlightBorder", + "terminalOverviewRuler.findMatchHighlightForeground", + "terminal.dragAndDropBackground", + "terminal.tab.activeBorder", + "terminal.ansiColor" + ], + "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ + "canNotResolve", + "symbolicLlink", + "unknown", + "label" + ], + "vs/workbench/contrib/files/browser/views/explorerViewer": [ + "treeAriaLabel", + "fileInputAriaLabel", + "confirmRootsMove", + "confirmMultiMove", + "confirmRootMove", + "confirmMove", + "doNotAskAgain", + { + "key": "moveButtonLabel", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "copy", + "copying", + "move", + "moving", + "numberOfFolders", + "numberOfFiles" ], "vs/workbench/contrib/files/browser/fileImportExport": [ "uploadingFiles", @@ -14443,33 +15809,6 @@ ] } ], - "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ - "canNotResolve", - "symbolicLlink", - "unknown", - "label" - ], - "vs/workbench/contrib/files/browser/views/explorerViewer": [ - "treeAriaLabel", - "fileInputAriaLabel", - "confirmRootsMove", - "confirmMultiMove", - "confirmRootMove", - "confirmMove", - "doNotAskAgain", - { - "key": "moveButtonLabel", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "copy", - "copying", - "move", - "moving", - "numberOfFolders", - "numberOfFiles" - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree": [ "bulkEdit", "aria.renameAndEdit", @@ -14488,6 +15827,13 @@ "detail.del", "title" ], + "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ + "default" + ], + "vs/workbench/contrib/search/browser/replaceService": [ + "searchReplace.source", + "fileReplaceChanges" + ], "vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess": [ "cannotRunGotoSymbolWithoutEditor", "cannotRunGotoSymbolWithoutSymbolProvider", @@ -14523,11 +15869,8 @@ "field", "constant" ], - "vs/workbench/contrib/search/browser/replaceService": [ - "searchReplace.source", - "fileReplaceChanges" - ], "vs/workbench/contrib/search/browser/searchFindInput": [ + "aiDescription", "searchFindInputNotebookFilter.label" ], "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ @@ -14549,16 +15892,12 @@ "vs/workbench/contrib/debug/browser/baseDebugView": [ "debug.lazyButton.tooltip" ], - "vs/workbench/contrib/debug/common/debugSource": [ - "unknownSource" - ], - "vs/workbench/contrib/debug/browser/debugSessionPicker": [ - "moveFocusedView.selectView", - "workbench.action.debug.startDebug", - "workbench.action.debug.spawnFrom" - ], - "vs/workbench/contrib/debug/common/loadedScriptsPicker": [ - "moveFocusedView.selectView" + "vs/workbench/contrib/debug/browser/debugConfigurationManager": [ + "editLaunchConfig", + "selectConfiguration", + "DebugConfig.failed", + "workspace", + "user settings" ], "vs/workbench/contrib/debug/browser/debugAdapterManager": [ "debugNoType", @@ -14579,19 +15918,13 @@ "installExt", "selectDebug" ], - "vs/workbench/contrib/debug/browser/debugConfigurationManager": [ - "editLaunchConfig", - "selectConfiguration", - "DebugConfig.failed", - "workspace", - "user settings" - ], "vs/workbench/contrib/debug/browser/debugSession": [ "noDebugAdapter", "noDebugAdapter", "noDebugAdapter", "noDebugAdapter", "noDebugAdapter", + "sessionDoesNotSupporBytesBreakpoints", "noDebugAdapter", "sessionNotReadyForBreakpoints", "noDebugAdapter", @@ -14660,9 +15993,76 @@ "taskNotTrackedWithTaskId", "taskNotTracked" ], + "vs/workbench/contrib/debug/common/debugSource": [ + "unknownSource" + ], + "vs/workbench/contrib/debug/common/loadedScriptsPicker": [ + "moveFocusedView.selectView" + ], + "vs/workbench/contrib/debug/browser/debugSessionPicker": [ + "moveFocusedView.selectView", + "workbench.action.debug.startDebug", + "workbench.action.debug.spawnFrom" + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget": [ + "parameterHintsNextIcon", + "parameterHintsPreviousIcon", + { + "key": "content", + "comment": [ + "A label", + "A keybinding" + ] + }, + "previous", + "next" + ], + "vs/workbench/contrib/markers/browser/markersTreeViewer": [ + "problemsView", + "expandedIcon", + "collapsedIcon", + "single line", + "multi line" + ], + "vs/workbench/contrib/markers/browser/markersTable": [ + "codeColumnLabel", + "messageColumnLabel", + "fileColumnLabel", + "sourceColumnLabel" + ], "vs/base/browser/ui/dropdown/dropdownActionViewItem": [ "moreActions" ], + "vs/workbench/contrib/comments/browser/commentsModel": [ + "noComments" + ], + "vs/workbench/contrib/comments/browser/commentColors": [ + "resolvedCommentIcon", + "unresolvedCommentIcon", + "commentReplyInputBackground", + "resolvedCommentBorder", + "unresolvedCommentBorder", + "commentThreadRangeBackground", + "commentThreadActiveRangeBackground" + ], + "vs/workbench/contrib/comments/browser/commentsViewActions": [ + "focusCommentsList", + "commentsClearFilterText", + "focusCommentsFilter", + "toggle unresolved", + "comments", + "unresolved", + "toggle resolved", + "comments", + "resolved" + ], + "vs/workbench/contrib/comments/browser/commentGlyphWidget": [ + "editorGutterCommentRangeForeground", + "editorOverviewRuler.commentForeground", + "editorOverviewRuler.commentUnresolvedForeground", + "editorGutterCommentGlyphForeground", + "editorGutterCommentUnresolvedGlyphForeground" + ], "vs/workbench/contrib/mergeEditor/common/mergeEditor": [ "is", "isr", @@ -14749,16 +16149,14 @@ }, "noMoreWarn" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ + "base", + "compareWith", + "compareWithTooltip" + ], "vs/workbench/contrib/mergeEditor/browser/view/viewModel": [ "noConflictMessage" ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ - "result", - "mergeEditor.remainingConflicts", - "mergeEditor.remainingConflict", - "goToNextConflict", - "allConflictHandled" - ], "vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView": [ "input1", "input2", @@ -14772,6 +16170,13 @@ "accept.first", "accept.second" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ + "result", + "mergeEditor.remainingConflicts", + "mergeEditor.remainingConflict", + "goToNextConflict", + "allConflictHandled" + ], "vs/workbench/contrib/mergeEditor/browser/view/colors": [ "mergeEditor.change.background", "mergeEditor.change.word.background", @@ -14787,36 +16192,6 @@ "mergeEditor.conflict.input1.background", "mergeEditor.conflict.input2.background" ], - "vs/base/browser/ui/findinput/replaceInput": [ - "defaultLabel", - "label.preserveCaseToggle" - ], - "vs/workbench/contrib/markers/browser/markersTreeViewer": [ - "problemsView", - "expandedIcon", - "collapsedIcon", - "single line", - "multi line" - ], - "vs/workbench/contrib/markers/browser/markersTable": [ - "codeColumnLabel", - "messageColumnLabel", - "fileColumnLabel", - "sourceColumnLabel" - ], - "vs/workbench/contrib/comments/browser/commentsController": [ - "commentRange", - "commentRangeStart", - "hasCommentRangesKb", - "hasCommentRangesNoKb", - "hasCommentRanges", - "pickCommentService" - ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ - "base", - "compareWith", - "compareWithTooltip" - ], "vs/workbench/contrib/customEditor/common/contributedCustomEditors": [ "builtinProviderDisplayName" ], @@ -14824,29 +16199,141 @@ "fileSystemRenameError", "fileSystemNotAllowedError" ], - "vs/workbench/contrib/terminal/browser/terminalActions": [ - "showTerminalTabs", - "workbench.action.terminal.newWorkspacePlaceholder", - "terminalLaunchHelp", - "workbench.action.terminal.newInActiveWorkspace", - "workbench.action.terminal.createTerminalEditor", - "workbench.action.terminal.createTerminalEditor", - "workbench.action.terminal.createTerminalEditorSide", - "workbench.action.terminal.focusPreviousPane", - "workbench.action.terminal.focusNextPane", - "workbench.action.terminal.runRecentCommand", - "workbench.action.terminal.copyLastCommand", - "workbench.action.terminal.goToRecentDirectory", - "workbench.action.terminal.resizePaneLeft", - "workbench.action.terminal.resizePaneRight", - "workbench.action.terminal.resizePaneUp", - "workbench.action.terminal.resizePaneDown", - "workbench.action.terminal.focus.tabsView", - "workbench.action.terminal.focusNext", - "workbench.action.terminal.focusPrevious", + "vs/workbench/contrib/extensions/browser/extensionsViewer": [ + "error", + "Unknown Extension", + "extensions" + ], + "vs/workbench/contrib/extensions/browser/extensionFeaturesTab": [ + "activation", + "uncaught errors", + "messaages", + "last request", + "requests count session", + "requests count total", + "runtime", + "noFeatures", + "extension features list", + "revoked", + "accessExtensionFeature", + "disableAccessExtensionFeatureMessage", + "enableAccessExtensionFeatureMessage", + "revoke", + "grant", + "cancel", + "revoke", + "enable" + ], + "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ + "ratedLabel", + "sponsor", + "remote extension title", + "syncingore.label", + "activation", + "startup", + "sponsor", + "workspace extension", + "local extension", + "publisher verified tooltip", + "updateRequired", + "activation", + "startup", + "uncaught error", + "uncaught errors", + "message", + "messages", + "dependencies", + "Show prerelease version", + "has prerelease", + "recommendationHasBeenIgnored", + "extensionIconStarForeground", + "extensionIconVerifiedForeground", + "extensionPreReleaseForeground", + "extensionIcon.sponsorForeground" + ], + "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ + "exeBasedRecommendation" + ], + "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ + "workspaceRecommendation", + "workspaceRecommendation" + ], + "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ + "fileBasedRecommendation", + "languageName" + ], + "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ + "exeBasedRecommendation" + ], + "vs/workbench/contrib/extensions/browser/webRecommendations": [ + "reason" + ], + "vs/platform/terminal/common/terminalLogService": [ + "terminalLoggerName" + ], + "vs/workbench/contrib/terminal/browser/terminalIcons": [ + "terminalViewIcon", + "renameTerminalIcon", + "killTerminalIcon", + "newTerminalIcon", + "configureTerminalProfileIcon", + "terminalDecorationMark", + "terminalDecorationIncomplete", + "terminalDecorationError", + "terminalDecorationSuccess", + "terminalCommandHistoryRemove", + "terminalCommandHistoryOutput", + "terminalCommandHistoryFuzzySearch" + ], + "vs/workbench/contrib/terminal/browser/terminalActions": [ + "showTerminalTabs", + "workbench.action.terminal.newWorkspacePlaceholder", + "terminalLaunchHelp", + "workbench.action.terminal.runActiveFile.noFile", + "noUnattachedTerminals", + "sendSequence", + "workbench.action.terminal.newWithCwd.cwd", + "workbench.action.terminal.renameWithArg.name", + "workbench.action.terminal.renameWithArg.noName", + "workbench.action.terminal.join.insufficientTerminals", + "workbench.action.terminal.join.onlySplits", + "stickyScroll", + { + "key": "miStickyScroll", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "emptyTerminalNameInfo", + "workbench.action.terminal.newWithProfile.profileName", + "newWithProfile.location", + "newWithProfile.location.view", + "newWithProfile.location.editor", + "workbench.action.terminal.newWorkspacePlaceholder", + "workbench.action.terminal.overriddenCwdDescription", + "workbench.action.terminal.newWorkspacePlaceholder", + "workbench.action.terminal.rename.prompt", + "workbench.action.terminal.newInActiveWorkspace", + "workbench.action.terminal.createTerminalEditor", + "workbench.action.terminal.createTerminalEditor", + "workbench.action.terminal.createTerminalEditorSide", + "workbench.action.terminal.focusPreviousPane", + "workbench.action.terminal.focusNextPane", + "workbench.action.terminal.runRecentCommand", + "workbench.action.terminal.copyLastCommand", + "workbench.action.terminal.copyLastCommandOutput", + "workbench.action.terminal.copyLastCommandAndOutput", + "workbench.action.terminal.goToRecentDirectory", + "goToRecentDirectory.metadata", + "workbench.action.terminal.resizePaneLeft", + "workbench.action.terminal.resizePaneRight", + "workbench.action.terminal.resizePaneUp", + "workbench.action.terminal.resizePaneDown", + "workbench.action.terminal.focus.tabsView", + "workbench.action.terminal.focusNext", + "workbench.action.terminal.focusPrevious", "workbench.action.terminal.runSelectedText", "workbench.action.terminal.runActiveFile", - "workbench.action.terminal.runActiveFile.noFile", "workbench.action.terminal.scrollDown", "workbench.action.terminal.scrollDownPage", "workbench.action.terminal.scrollToBottom", @@ -14856,23 +16343,14 @@ "workbench.action.terminal.clearSelection", "workbench.action.terminal.detachSession", "workbench.action.terminal.attachToSession", - "noUnattachedTerminals", "quickAccessTerminal", - "workbench.action.terminal.scrollToPreviousCommand", - "workbench.action.terminal.scrollToNextCommand", "workbench.action.terminal.selectToPreviousCommand", "workbench.action.terminal.selectToNextCommand", "workbench.action.terminal.selectToPreviousLine", "workbench.action.terminal.selectToNextLine", - "sendSequence", - "workbench.action.terminal.newWithCwd.cwd", - "workbench.action.terminal.renameWithArg.name", - "workbench.action.terminal.renameWithArg.noName", "workbench.action.terminal.relaunch", "workbench.action.terminal.joinInstance", "workbench.action.terminal.join", - "workbench.action.terminal.join.insufficientTerminals", - "workbench.action.terminal.join.onlySplits", "workbench.action.terminal.splitInActiveWorkspace", "workbench.action.terminal.selectAll", "workbench.action.terminal.new", @@ -14883,27 +16361,73 @@ "workbench.action.terminal.selectDefaultShell", "workbench.action.terminal.openSettings", "workbench.action.terminal.setFixedDimensions", - "workbench.action.terminal.sizeToContentWidth", "workbench.action.terminal.clearPreviousSessionHistory", - "workbench.action.terminal.selectPrevSuggestion", - "workbench.action.terminal.selectPrevPageSuggestion", - "workbench.action.terminal.selectNextSuggestion", - "workbench.action.terminal.selectNextPageSuggestion", - "workbench.action.terminal.acceptSelectedSuggestion", - "workbench.action.terminal.hideSuggestWidget", + "workbench.action.terminal.toggleStickyScroll", "workbench.action.terminal.copySelection", "workbench.action.terminal.copyAndClearSelection", "workbench.action.terminal.copySelectionAsHtml", "workbench.action.terminal.paste", "workbench.action.terminal.pasteSelection", "workbench.action.terminal.switchTerminal", - "emptyTerminalNameInfo", - "workbench.action.terminal.newWithProfile", - "workbench.action.terminal.newWithProfile.profileName", - "workbench.action.terminal.newWorkspacePlaceholder", - "workbench.action.terminal.overriddenCwdDescription", - "workbench.action.terminal.newWorkspacePlaceholder", - "workbench.action.terminal.rename.prompt" + "workbench.action.terminal.newWithProfile" + ], + "vs/workbench/contrib/terminal/browser/terminalMenus": [ + { + "key": "miNewTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miSplitTerminal", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miRunActiveFile", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "miRunSelectedText", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "workbench.action.terminal.copySelection.short", + "workbench.action.terminal.copySelectionAsHtml", + "workbench.action.terminal.paste.short", + "workbench.action.terminal.clear", + "workbench.action.terminal.selectAll", + "workbench.action.terminal.copySelection.short", + "workbench.action.terminal.copySelectionAsHtml", + "workbench.action.terminal.paste.short", + "workbench.action.terminal.clear", + "workbench.action.terminal.selectAll", + "workbench.action.terminal.newWithProfile.short", + "workbench.action.terminal.openSettings", + "workbench.action.tasks.runTask", + "workbench.action.tasks.configureTaskRunner", + "workbench.action.terminal.clearLong", + "workbench.action.terminal.runActiveFile", + "workbench.action.terminal.runSelectedText", + "workbench.action.terminal.renameInstance", + "workbench.action.terminal.changeIcon", + "workbench.action.terminal.changeColor", + "workbench.action.terminal.joinInstance", + "defaultTerminalProfile", + "defaultTerminalProfile", + "defaultTerminalProfile", + "splitTerminal", + "launchProfile", + "workbench.action.terminal.selectDefaultProfile", + "workbench.action.terminal.switchTerminal" + ], + "vs/workbench/contrib/terminal/browser/terminalWslRecommendationContribution": [ + "useWslExtension.title", + "install" ], "vs/workbench/contrib/terminal/browser/terminalQuickAccess": [ "workbench.action.terminal.newplus", @@ -14922,6 +16446,37 @@ "localTerminalVirtualWorkspace", "localTerminalRemote" ], + "vs/workbench/contrib/terminal/common/terminalStrings": [ + "terminal", + "terminal.new", + "doNotShowAgain", + "currentSessionCategory", + "previousSessionCategory", + "task", + "local", + "killTerminal.short", + "splitTerminal.short", + "terminalCategory", + "workbench.action.terminal.focus", + "workbench.action.terminal.focusAndHideAccessibleBuffer", + "killTerminal", + "moveToEditor", + "moveIntoNewWindow", + "workbench.action.terminal.moveToTerminalPanel", + "workbench.action.terminal.changeIcon", + "workbench.action.terminal.changeColor", + "splitTerminal", + "unsplitTerminal", + "workbench.action.terminal.rename", + "workbench.action.terminal.sizeToContentWidthInstance", + "workbench.action.terminal.focusHover", + "workbench.action.terminal.sendSequence", + "workbench.action.terminal.newWithCwd", + "workbench.action.terminal.renameWithArg", + "stickyScroll", + "workbench.action.terminal.scrollToPreviousCommand", + "workbench.action.terminal.scrollToNextCommand" + ], "vs/workbench/contrib/terminal/common/terminalConfiguration": [ "cwd", "cwdFolder", @@ -14967,6 +16522,9 @@ "terminal.integrated.altClickMovesCursor", "terminal.integrated.copyOnSelection", "terminal.integrated.enableMultiLinePasteWarning", + "terminal.integrated.enableMultiLinePasteWarning.auto", + "terminal.integrated.enableMultiLinePasteWarning.always", + "terminal.integrated.enableMultiLinePasteWarning.never", "terminal.integrated.drawBoldTextInBrightColors", "terminal.integrated.fontFamily", "terminal.integrated.fontSize", @@ -15002,6 +16560,9 @@ "terminal.integrated.rightClickBehavior.selectWord", "terminal.integrated.rightClickBehavior.nothing", "terminal.integrated.rightClickBehavior", + "terminal.integrated.middleClickBehavior.default", + "terminal.integrated.middleClickBehavior.paste", + "terminal.integrated.middleClickBehavior", "terminal.integrated.cwd", "terminal.integrated.confirmOnExit", "terminal.integrated.confirmOnExit.never", @@ -15013,6 +16574,7 @@ "terminal.integrated.confirmOnKill.panel", "terminal.integrated.confirmOnKill.always", "terminal.integrated.enableBell", + "terminal.integrated.enableVisualBell", "terminal.integrated.commandsToSkipShell", "openDefaultSettingsJson", "openDefaultSettingsJson.capitalized", @@ -15037,6 +16599,7 @@ "enableFileLinks.off", "enableFileLinks.on", "enableFileLinks.notRemote", + "terminal.integrated.allowedLinkSchemes", "terminal.integrated.unicodeVersion.six", "terminal.integrated.unicodeVersion.eleven", "terminal.integrated.unicodeVersion", @@ -15057,6 +16620,7 @@ "hideOnStartup.whenEmpty", "hideOnStartup.always", "terminal.integrated.customGlyphs", + "terminal.integrated.rescaleOverlappingGlyphs", "terminal.integrated.autoReplies", "terminal.integrated.autoReplies.reply", "terminal.integrated.shellIntegration.enabled", @@ -15067,6 +16631,7 @@ "terminal.integrated.shellIntegration.decorationsEnabled.never", "terminal.integrated.shellIntegration.history", "terminal.integrated.shellIntegration.suggestEnabled", + "suggestEnabled.deprecated", "terminal.integrated.smoothScrolling", "terminal.integrated.ignoreBracketedPasteMode", "terminal.integrated.enableImages", @@ -15074,127 +16639,35 @@ "terminal.integrated.focusAfterRun.terminal", "terminal.integrated.focusAfterRun.accessible-buffer", "terminal.integrated.focusAfterRun.none", - "terminal.integrated.accessibleViewPreserveCursorPosition" + "terminal.integrated.accessibleViewPreserveCursorPosition", + "terminal.integrated.accessibleViewFocusOnCommandExecution", + "terminal.integrated.stickyScroll.enabled", + "terminal.integrated.stickyScroll.maxLineCount", + "terminal.integrated.mouseWheelZoom.mac", + "terminal.integrated.mouseWheelZoom" ], - "vs/workbench/contrib/terminal/browser/terminalMenus": [ - { - "key": "miNewTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "miSplitTerminal", - "comment": [ - "&& denotes a mnemonic" - ] - }, - { - "key": "miRunActiveFile", - "comment": [ - "&& denotes a mnemonic" - ] - }, + "vs/workbench/contrib/terminal/browser/terminalTabbedView": [ + "moveTabsRight", + "moveTabsLeft", + "hideTabs" + ], + "vs/workbench/contrib/terminal/browser/terminalTooltip": [ + "shellIntegration.enabled", + "launchFailed.exitCodeOnlyShellIntegration", + "shellIntegration.activationFailed", { - "key": "miRunSelectedText", + "key": "shellProcessTooltip.processId", "comment": [ - "&& denotes a mnemonic" - ] - }, - "workbench.action.terminal.copySelection.short", - "workbench.action.terminal.copySelectionAsHtml", - "workbench.action.terminal.paste.short", - "workbench.action.terminal.clear", - "workbench.action.terminal.selectAll", - "workbench.action.terminal.copySelection.short", - "workbench.action.terminal.copySelectionAsHtml", - "workbench.action.terminal.paste.short", - "workbench.action.terminal.clear", - "workbench.action.terminal.selectAll", - "workbench.action.terminal.newWithProfile.short", - "workbench.action.terminal.selectDefaultProfile", - "workbench.action.terminal.openSettings", - "workbench.action.tasks.runTask", - "workbench.action.tasks.configureTaskRunner", - "workbench.action.terminal.switchTerminal", - "workbench.action.terminal.clearLong", - "workbench.action.terminal.runActiveFile", - "workbench.action.terminal.runSelectedText", - "workbench.action.terminal.renameInstance", - "workbench.action.terminal.changeIcon", - "workbench.action.terminal.changeColor", - "workbench.action.terminal.sizeToContentWidthInstance", - "workbench.action.terminal.joinInstance", - "defaultTerminalProfile", - "defaultTerminalProfile", - "defaultTerminalProfile", - "splitTerminal" - ], - "vs/workbench/contrib/terminal/browser/terminalIcons": [ - "terminalViewIcon", - "renameTerminalIcon", - "killTerminalIcon", - "newTerminalIcon", - "configureTerminalProfileIcon", - "terminalDecorationMark", - "terminalDecorationIncomplete", - "terminalDecorationError", - "terminalDecorationSuccess", - "terminalCommandHistoryRemove", - "terminalCommandHistoryOutput", - "terminalCommandHistoryFuzzySearch" - ], - "vs/workbench/contrib/terminal/common/terminalStrings": [ - "terminal", - "terminal.new", - "doNotShowAgain", - "currentSessionCategory", - "previousSessionCategory", - "task", - "local", - "terminalCategory", - "workbench.action.terminal.focus", - "workbench.action.terminal.focusAndHideAccessibleBuffer", - "killTerminal", - "killTerminal.short", - "moveToEditor", - "workbench.action.terminal.moveToTerminalPanel", - "workbench.action.terminal.changeIcon", - "workbench.action.terminal.changeColor", - "splitTerminal", - "splitTerminal.short", - "unsplitTerminal", - "workbench.action.terminal.rename", - "workbench.action.terminal.sizeToContentWidthInstance", - "workbench.action.terminal.focusHover", - "workbench.action.terminal.sendSequence", - "workbench.action.terminal.newWithCwd", - "workbench.action.terminal.renameWithArg" - ], - "vs/platform/terminal/common/terminalLogService": [ - "terminalLoggerName" - ], - "vs/workbench/contrib/terminal/browser/terminalTabbedView": [ - "moveTabsRight", - "moveTabsLeft", - "hideTabs" - ], - "vs/workbench/contrib/terminal/browser/terminalTooltip": [ - "shellIntegration.enabled", - "launchFailed.exitCodeOnlyShellIntegration", - "shellIntegration.activationFailed", - { - "key": "shellProcessTooltip.processId", - "comment": [ - "The first arg is \"PID\" which shouldn't be translated" + "The first arg is \"PID\" which shouldn't be translated" ] }, "shellProcessTooltip.commandLine" ], "vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp": [ - "focusAccessibleBuffer", - "focusAccessibleBufferNoKb", + "focusAccessibleTerminalView", + "focusAccessibleTerminalViewNoKb", "preserveCursor", + "focusViewOnExecution", "commandPromptMigration", "shellIntegration", "goToNextCommand", @@ -15216,6 +16689,8 @@ "focusAfterRun" ], "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ + "scheme", + "allow", "terminalLinkHandler.followLinkAlt.mac", "terminalLinkHandler.followLinkAlt", "terminalLinkHandler.followLinkCmd", @@ -15235,10 +16710,23 @@ "terminal.integrated.localFolderLinks", "terminal.integrated.searchLinks" ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ - "quickFix.command", - "quickFix.opener", - "codeAction.widget.id.quickfix" + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp": [ + "inlineChat.overview", + "inlineChat.access", + "inlineChat.input", + "inlineChat.inputNoKb", + "inlineChat.inspectResponseMessage", + "inlineChat.inspectResponseNoKb", + "inlineChat.focusResponse", + "inlineChat.focusResponseNoKb", + "inlineChat.focusInput", + "inlineChat.focusInputNoKb", + "inlineChat.runCommand", + "inlineChat.runCommandNoKb", + "inlineChat.insertCommand", + "inlineChat.insertCommandNoKb", + "inlineChat.toolbar", + "chat.signals" ], "vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions": [ "terminal.freePort", @@ -15252,52 +16740,34 @@ "vscode.extension.contributes.terminalQuickFixes.commandExitResult", "vscode.extension.contributes.terminalQuickFixes.kind" ], - "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ - "ratedLabel", - "sponsor", - "remote extension title", - "syncingore.label", - "activation", - "startup", - "pre-release-label", - "sponsor", - "publisher verified tooltip", - "updateRequired", - "activation", - "startup", - "uncaught error", - "uncaught errors", - "message", - "messages", - "dependencies", - "Show prerelease version", - "has prerelease", - "recommendationHasBeenIgnored", - "extensionIconStarForeground", - "extensionIconVerifiedForeground", - "extensionPreReleaseForeground", - "extensionIcon.sponsorForeground" - ], - "vs/workbench/contrib/extensions/browser/extensionsViewer": [ - "error", - "Unknown Extension", - "extensions" - ], - "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ - "exeBasedRecommendation" - ], - "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ - "workspaceRecommendation" - ], - "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ - "fileBasedRecommendation", - "languageName" + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions": [ + "startChat", + "closeChat", + "focusTerminalResponse", + "focusTerminalInput", + "discard", + "discardDescription", + "runCommand", + "run", + "runFirstCommand", + "runFirst", + "insertCommand", + "insert", + "insertFirstCommand", + "insertFirst", + "viewInChat", + "makeChatRequest", + "cancelChat", + "reportIssue" ], - "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ - "exeBasedRecommendation" + "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ + "quickFix.command", + "quickFix.opener", + "codeAction.widget.id.quickfix" ], - "vs/workbench/contrib/extensions/browser/webRecommendations": [ - "reason" + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry": [ + "terminalStickyScroll.background", + "terminalStickyScrollHover.background" ], "vs/workbench/contrib/tasks/common/jsonSchemaCommon": [ "JsonSchema.options", @@ -15366,6 +16836,10 @@ "vs/workbench/contrib/remote/browser/explorerViewItems": [ "switchRemote.label" ], + "vs/base/browser/ui/findinput/replaceInput": [ + "defaultLabel", + "label.preserveCaseToggle" + ], "vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions": [ "snippets" ], @@ -15396,7 +16870,6 @@ "vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent": [ "getting-started-setup-icon", "getting-started-beginner-icon", - "getting-started-intermediate-icon", "gettingStarted.newFile.title", "gettingStarted.newFile.description", "gettingStarted.openMac.title", @@ -15419,21 +16892,24 @@ "gettingStarted.topLevelOpenTunnel.description", "gettingStarted.setup.title", "gettingStarted.setup.description", - "gettingStarted.settingsSync.title", - "gettingStarted.settingsSync.description.interpolated", - "enableSync", "gettingStarted.pickColor.title", "gettingStarted.pickColor.description.interpolated", "titleID", - "gettingStarted.commandPalette.title", - "gettingStarted.commandPalette.description.interpolated", - "commandPalette", "gettingStarted.extensions.title", "gettingStarted.extensionsWeb.description.interpolated", - "browsePopular", + "browsePopularWeb", "gettingStarted.findLanguageExts.title", "gettingStarted.findLanguageExts.description.interpolated", "browseLangExts", + "gettingStarted.settings.title", + "gettingStarted.settings.description.interpolated", + "tweakSettings", + "gettingStarted.settingsSync.title", + "gettingStarted.settingsSync.description.interpolated", + "enableSync", + "gettingStarted.commandPalette.title", + "gettingStarted.commandPalette.description.interpolated", + "commandPalette", "gettingStarted.setup.OpenFolder.title", "gettingStarted.setup.OpenFolder.description.interpolated", "pickFolder", @@ -15443,26 +16919,29 @@ "gettingStarted.quickOpen.title", "gettingStarted.quickOpen.description.interpolated", "quickOpen", + "gettingStarted.videoTutorial.title", + "gettingStarted.videoTutorial.description.interpolated", + "watch", "gettingStarted.setupWeb.title", "gettingStarted.setupWeb.description", - "gettingStarted.settingsSync.title", - "gettingStarted.settingsSync.description.interpolated", - "enableSync", "gettingStarted.pickColor.title", "gettingStarted.pickColor.description.interpolated", "titleID", - "gettingStarted.commandPalette.title", - "gettingStarted.commandPalette.description.interpolated", - "commandPalette", "gettingStarted.menuBar.title", "gettingStarted.menuBar.description.interpolated", "toggleMenuBar", "gettingStarted.extensions.title", "gettingStarted.extensionsWeb.description.interpolated", - "browsePopular", + "browsePopularWeb", "gettingStarted.findLanguageExts.title", "gettingStarted.findLanguageExts.description.interpolated", "browseLangExts", + "gettingStarted.settingsSync.title", + "gettingStarted.settingsSync.description.interpolated", + "enableSync", + "gettingStarted.commandPalette.title", + "gettingStarted.commandPalette.description.interpolated", + "commandPalette", "gettingStarted.setup.OpenFolder.title", "gettingStarted.setup.OpenFolderWeb.description.interpolated", "openFolder", @@ -15472,33 +16951,12 @@ "quickOpen", "gettingStarted.beginner.title", "gettingStarted.beginner.description", - "gettingStarted.playground.title", - "gettingStarted.playground.description.interpolated", - "openEditorPlayground", + "gettingStarted.extensions.title", + "gettingStarted.extensions.description.interpolated", + "browsePopular", "gettingStarted.terminal.title", "gettingStarted.terminal.description.interpolated", "showTerminal", - "gettingStarted.extensions.title", - "gettingStarted.extensions.description.interpolated", - "browseRecommended", - "gettingStarted.settings.title", - "gettingStarted.settings.description.interpolated", - "tweakSettings", - "gettingStarted.profiles.title", - "gettingStarted.profiles.description.interpolated", - "tryProfiles", - "gettingStarted.workspaceTrust.title", - "gettingStarted.workspaceTrust.description.interpolated", - "workspaceTrust", - "enableTrust", - "gettingStarted.videoTutorial.title", - "gettingStarted.videoTutorial.description.interpolated", - "watch", - "gettingStarted.intermediate.title", - "gettingStarted.intermediate.description", - "gettingStarted.splitview.title", - "gettingStarted.splitview.description.interpolated", - "splitEditor", "gettingStarted.debug.title", "gettingStarted.debug.description.interpolated", "runProject", @@ -15525,22 +16983,14 @@ "gettingStarted.shortcuts.title", "gettingStarted.shortcuts.description.interpolated", "keyboardShortcuts", + "gettingStarted.workspaceTrust.title", + "gettingStarted.workspaceTrust.description.interpolated", + "workspaceTrust", + "enableTrust", "gettingStarted.notebook.title", "gettingStarted.notebookProfile.title", "gettingStarted.notebookProfile.description" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ - "gettingStarted.featuredTitle" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ - "welcomePage.background", - "welcomePage.tileBackground", - "welcomePage.tileHoverBackground", - "welcomePage.tileBorder", - "welcomePage.progress.background", - "welcomePage.progress.foreground", - "walkthrough.stepTitle.foreground" - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint": [ "title", "walkthroughs", @@ -15580,6 +17030,15 @@ "walkthroughs.steps.oneOn.command", "walkthroughs.steps.when" ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ + "welcomePage.background", + "welcomePage.tileBackground", + "welcomePage.tileHoverBackground", + "welcomePage.tileBorder", + "welcomePage.progress.background", + "welcomePage.progress.foreground", + "walkthrough.stepTitle.foreground" + ], "vs/workbench/contrib/welcomeWalkthrough/common/walkThroughUtils": [ "walkThrough.embeddedEditorBackground" ], @@ -15703,15 +17162,6 @@ "downloaded sync activity title", "troubleshoot" ], - "vs/workbench/browser/parts/notifications/notificationsList": [ - "notificationAccessibleViewHint", - "notificationAccessibleViewHintNoKb", - "notificationAriaLabelHint", - "notificationAriaLabel", - "notificationWithSourceAriaLabelHint", - "notificationWithSourceAriaLabel", - "notificationsList" - ], "vs/workbench/browser/parts/notifications/notificationsActions": [ "clearIcon", "clearAllIcon", @@ -15723,12 +17173,23 @@ "clearNotification", "clearNotifications", "toggleDoNotDisturbMode", + "toggleDoNotDisturbModeBySource", + "configureDoNotDisturbMode", "hideNotificationsCenter", "expandNotification", "collapseNotification", "configureNotification", "copyNotification" ], + "vs/workbench/browser/parts/notifications/notificationsList": [ + "notificationAccessibleViewHint", + "notificationAccessibleViewHintNoKb", + "notificationAriaLabelHint", + "notificationAriaLabel", + "notificationWithSourceAriaLabelHint", + "notificationWithSourceAriaLabel", + "notificationsList" + ], "vs/workbench/services/textfile/common/textFileSaveParticipant": [ "saveParticipants" ], @@ -15783,7 +17244,6 @@ }, "menubar.customTitlebarAccessibilityNotification", "goToSetting", - "focusMenu", { "key": "checkForUpdates", "comment": [ @@ -15810,7 +17270,8 @@ "comment": [ "&& denotes a mnemonic" ] - } + }, + "focusMenu" ], "vs/workbench/browser/parts/titlebar/commandCenterControl": [ "label.dfl", @@ -15820,6 +17281,10 @@ "title2", "title3" ], + "vs/workbench/browser/parts/editor/editorTabsControl": [ + "ariaLabelEditorActions", + "draggedEditorGroup" + ], "vs/workbench/browser/parts/globalCompositeBar": [ "accountsViewBarIcon", "hideAccounts", @@ -15832,7 +17297,27 @@ "signOut", "noAccounts", "manage", - "manage profile" + "manage profile", + "hideAccounts", + "accounts", + "manage" + ], + "vs/workbench/browser/parts/titlebar/titlebarActions": [ + "toggle.commandCenter", + "toggle.commandCenterDescription", + "toggle.layout", + "toggle.layoutDescription", + "toggle.hideCustomTitleBar", + "toggle.hideCustomTitleBarInFullScreen", + "toggle.customTitleBar", + "toggle.editorActions", + "accounts", + "accounts", + "manage", + "manage", + "showCustomTitleBar", + "hideCustomTitleBar", + "hideCustomTitleBarInFullScreen" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopy": [ "staleSaveError", @@ -15861,14 +17346,30 @@ "vs/platform/terminal/common/terminalProfiles": [ "terminalAutomaticProfile" ], - "vs/workbench/contrib/webview/browser/webviewElement": [ - "fatalErrorMessage" - ], "vs/platform/quickinput/browser/quickPickPin": [ "terminal.commands.pinned", "pinCommand", "pinnedCommand" ], + "vs/workbench/contrib/webview/browser/webviewElement": [ + "fatalErrorMessage" + ], + "vs/editor/browser/widget/multiDiffEditor/colors": [ + "multiDiffEditor.headerBackground", + "multiDiffEditor.background", + "multiDiffEditor.border" + ], + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChat": [ + "chatFocusedContextKey", + "chatVisibleContextKey", + "chatRequestActiveContextKey", + "chatInputHasTextContextKey", + "chatAgentRegisteredContextKey", + "chatResponseContainsCodeBlockContextKey", + "chatResponseContainsMultipleCodeBlocksContextKey", + "chatResponseSupportsIssueReportingContextKey", + "interactiveSessionResponseVote" + ], "vs/workbench/api/common/extHostDiagnostics": [ { "key": "limitHit", @@ -15877,13 +17378,14 @@ ] } ], + "vs/workbench/api/common/extHostNotebook": [ + "err.readonly", + "fileModifiedError" + ], "vs/workbench/api/common/extHostLanguageFeatures": [ "defaultPasteLabel", "defaultDropLabel" ], - "vs/workbench/api/common/extHostProgress": [ - "extensionSource" - ], "vs/workbench/api/common/extHostStatusBar": [ "extensionLabel", "status.extensionMessage" @@ -15891,74 +17393,11 @@ "vs/workbench/api/common/extHostTreeViews": [ "treeView.duplicateElement" ], - "vs/workbench/api/common/extHostNotebook": [ - "err.readonly", - "fileModifiedError" - ], - "vs/workbench/api/common/extHostChat": [ - "emptyResponse", - "errorResponse" - ], - "vs/workbench/api/common/extHostChatAgents2": [ - "errorResponse" - ], "vs/base/browser/ui/findinput/findInputToggles": [ "caseDescription", "wordsDescription", "regexDescription" ], - "vs/editor/browser/widget/diffEditor/accessibleDiffViewer": [ - "accessibleDiffViewerInsertIcon", - "accessibleDiffViewerRemoveIcon", - "accessibleDiffViewerCloseIcon", - "label.close", - "ariaLabel", - "no_lines_changed", - "one_line_changed", - "more_lines_changed", - { - "key": "header", - "comment": [ - "This is the ARIA label for a git diff header.", - "A git diff header looks like this: @@ -154,12 +159,39 @@.", - "That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.", - "Variables 0 and 1 refer to the diff index out of total number of diffs.", - "Variables 2 and 4 will be numbers (a line number).", - "Variables 3 and 5 will be \"no lines changed\", \"1 line changed\" or \"X lines changed\", localized separately." - ] - }, - "blankLine", - { - "key": "unchangedLine", - "comment": [ - "The placeholders are contents of the line and should not be translated." - ] - }, - "equalLine", - "insertLine", - "deleteLine" - ], - "vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature": [ - "foldUnchanged", - "diff.hiddenLines.top", - "showAll", - "diff.bottom", - "hiddenLines", - "diff.hiddenLines.expandAll" - ], - "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ - "codeMovedToWithChanges", - "codeMovedFromWithChanges", - "codeMovedTo", - "codeMovedFrom" - ], - "vs/editor/browser/widget/diffEditor/diffEditorEditors": [ - "diff-aria-navigation-tip" - ], - "vs/editor/browser/widget/diffEditor/colors": [ - "diffEditor.move.border", - "diffEditor.moveActive.border" - ], "vs/editor/browser/controller/textAreaHandler": [ "editor", "accessibilityModeOff", @@ -16054,31 +17493,74 @@ "suggestMoreInfoIcon", "readMore" ], - "vs/workbench/contrib/comments/common/commentModel": [ - "noComments" + "vs/workbench/contrib/chat/browser/chatFollowups": [ + "followUpAriaLabel" ], - "vs/workbench/contrib/comments/browser/commentsViewActions": [ - "focusCommentsList", - "commentsClearFilterText", - "focusCommentsFilter", - "toggle unresolved", - "comments", - "unresolved", - "toggle resolved", - "comments", - "resolved" + "vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer": [ + "accessibleDiffViewerInsertIcon", + "accessibleDiffViewerRemoveIcon", + "accessibleDiffViewerCloseIcon", + "label.close", + "ariaLabel", + "no_lines_changed", + "one_line_changed", + "more_lines_changed", + { + "key": "header", + "comment": [ + "This is the ARIA label for a git diff header.", + "A git diff header looks like this: @@ -154,12 +159,39 @@.", + "That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.", + "Variables 0 and 1 refer to the diff index out of total number of diffs.", + "Variables 2 and 4 will be numbers (a line number).", + "Variables 3 and 5 will be \"no lines changed\", \"1 line changed\" or \"X lines changed\", localized separately." + ] + }, + "blankLine", + { + "key": "unchangedLine", + "comment": [ + "The placeholders are contents of the line and should not be translated." + ] + }, + "equalLine", + "insertLine", + "deleteLine" ], - "vs/workbench/contrib/comments/browser/commentColors": [ - "resolvedCommentIcon", - "unresolvedCommentIcon", - "resolvedCommentBorder", - "unresolvedCommentBorder", - "commentThreadRangeBackground", - "commentThreadActiveRangeBackground" + "vs/editor/browser/widget/diffEditor/features/revertButtonsFeature": [ + "revertSelectedChanges", + "revertChange" + ], + "vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature": [ + "codeMovedToWithChanges", + "codeMovedFromWithChanges", + "codeMovedTo", + "codeMovedFrom" + ], + "vs/editor/browser/widget/diffEditor/components/diffEditorEditors": [ + "diff-aria-navigation-tip" + ], + "vs/workbench/browser/parts/editor/editorPlaceholder": [ + "trustRequiredEditor", + "requiresFolderTrustText", + "requiresWorkspaceTrustText", + "manageTrust", + "errorEditor", + "unavailableResourceErrorEditorText", + "unknownErrorEditorTextWithError", + "unknownErrorEditorTextWithoutError", + "retry" + ], + "vs/workbench/browser/parts/paneCompositeBar": [ + "resetLocation", + "resetLocation" + ], + "vs/workbench/browser/parts/compositePart": [ + "ariaCompositeToolbarLabel", + "viewsAndMoreActions", + "titleTooltip" ], "vs/workbench/browser/parts/editor/editorPanes": [ - "editorUnsupportedInAuxWindow", - "openFolder", "editorOpenErrorDialog", { "key": "ok", @@ -16088,6 +17570,7 @@ } ], "vs/workbench/browser/parts/editor/editorGroupWatermark": [ + "editorLineHighlight", "watermark.showCommands", "watermark.quickAccess", "watermark.openFile", @@ -16111,34 +17594,10 @@ }, "watermark.showSettings" ], - "vs/workbench/browser/parts/editor/editorPlaceholder": [ - "trustRequiredEditor", - "requiresFolderTrustText", - "requiresWorkspaceTrustText", - "manageTrust", - "errorEditor", - "unavailableResourceErrorEditorText", - "unknownErrorEditorTextWithError", - "unknownErrorEditorTextWithoutError", - "retry" - ], - "vs/workbench/browser/parts/compositePart": [ - "ariaCompositeToolbarLabel", - "viewsAndMoreActions", - "titleTooltip" - ], - "vs/workbench/browser/parts/paneCompositeBar": [ - "resetLocation", - "resetLocation" - ], - "vs/platform/quickinput/browser/quickInput": [ - "quickInput.back", - "inputModeEntry", - "quickInput.steps", - "quickInputBox.ariaLabel", - "inputModeEntryDescription" + "vs/platform/quickinput/browser/quickInputUtils": [ + "executeCommand" ], - "vs/platform/quickinput/browser/quickInputList": [ + "vs/platform/quickinput/browser/quickInputTree": [ "quickInput" ], "vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators": [ @@ -16224,34 +17683,37 @@ "objectKeyHeader", "objectValueHeader" ], - "vs/workbench/contrib/chat/browser/codeBlockPart": [ - "chat.codeBlockHelp", - "chat.codeBlock.toolbarVerbose", - "chat.codeBlock.toolbar", - "chat.codeBlockLabel" + "vs/workbench/contrib/chat/browser/chatAgentHover": [ + "marketplaceLabel" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ + "inlineChat.accessibilityHelp", + "inlineChat.accessibilityHelpNoKb", + "aria-label", + "original", + "modified" ], "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer": [ "cellExecutionOrderCountLabel" ], - "vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll": [ - "toggleStickyScroll", - { - "key": "mitoggleStickyScroll", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "notebookStickyScroll", - { - "key": "miNotebookStickyScroll", - "comment": [ - "&& denotes a mnemonic" - ] - } + "vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider": [ + "notebookTreeAriaLabelHelp", + "notebookTreeAriaLabelHelpNoKb", + "notebookTreeAriaLabel" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager": [ "join.fileWorkingCopyManager" ], + "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ + "empty" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource": [ + "notebook.indexedChildrenLimitReached" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree": [ + "debugConsole", + "notebookVariableAriaLabel" + ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget": [ "label.find", "placeholder.find", @@ -16270,29 +17732,8 @@ "notebook.find.filter.findInCodeInput", "notebook.find.filter.findInCodeOutput" ], - "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ - "empty" - ], - "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ - "exited slash command mode" - ], - "vs/platform/actions/browser/buttonbar": [ - "labelWithKeybinding" - ], - "vs/workbench/contrib/debug/common/debugger": [ - "cannot.find.da", - "launch.config.comment1", - "launch.config.comment2", - "launch.config.comment3", - "debugType", - "debugTypeNotRecognised", - "node2NotSupported", - "debugRequest", - "debugWindowsConfiguration", - "debugOSXConfiguration", - "debugLinuxConfiguration" - ], "vs/workbench/contrib/terminal/browser/xterm/decorationAddon": [ + "workbench.action.terminal.toggleVisibility", "terminal.rerunCommand", "rerun", "yes", @@ -16303,10 +17744,8 @@ "terminal.copyOutputAsHtml", "workbench.action.terminal.runRecentCommand", "workbench.action.terminal.goToRecentDirectory", - "terminal.configureCommandDecorations", "terminal.learnShellIntegration", "toggleVisibility", - "toggleVisibility", "gutter", "overviewRuler" ], @@ -16351,7 +17790,23 @@ "app.launch.json.compound.folder", "app.launch.json.compounds.configurations", "app.launch.json.compound.stopAll", - "compoundPrelaunchTask" + "compoundPrelaunchTask", + "debugger name", + "debugger type", + "debuggers" + ], + "vs/workbench/contrib/debug/common/debugger": [ + "cannot.find.da", + "launch.config.comment1", + "launch.config.comment2", + "launch.config.comment3", + "debugType", + "debugTypeNotRecognised", + "node2NotSupported", + "debugRequest", + "debugWindowsConfiguration", + "debugOSXConfiguration", + "debugLinuxConfiguration" ], "vs/workbench/contrib/debug/browser/rawDebugSession": [ "noDebugAdapterStart", @@ -16365,14 +17820,19 @@ "noDebugAdapter", "moreInfo" ], - "vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel": [ - "setInputHandled", - "undoMarkAsHandled" + "vs/workbench/contrib/comments/browser/commentThreadWidget": [ + "commentLabel", + "commentLabelWithKeybinding", + "commentLabelWithKeybindingNoKeybinding" ], "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ "conflictingLine", "conflictingLines" ], + "vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel": [ + "setInputHandled", + "undoMarkAsHandled" + ], "vs/workbench/contrib/mergeEditor/browser/view/conflictActions": [ "accept", "acceptTooltip", @@ -16396,40 +17856,6 @@ "resetToBase", "resetToBaseTooltip" ], - "vs/workbench/contrib/comments/browser/commentGlyphWidget": [ - "editorGutterCommentRangeForeground", - "editorOverviewRuler.commentForeground", - "editorOverviewRuler.commentUnresolvedForeground", - "editorGutterCommentGlyphForeground", - "editorGutterCommentUnresolvedGlyphForeground" - ], - "vs/workbench/contrib/customEditor/common/extensionPoint": [ - "contributes.customEditors", - "contributes.viewType", - "contributes.displayName", - "contributes.selector", - "contributes.selector.filenamePattern", - "contributes.priority", - "contributes.priority.default", - "contributes.priority.option" - ], - "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ - "useWslExtension.title", - "install" - ], - "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ - "terminal.integrated.selectProfileToCreate", - "terminal.integrated.chooseDefaultProfile", - "enterTerminalProfileName", - "terminalProfileAlreadyExists", - "terminalProfiles", - "ICreateContributedTerminalProfileOptions", - "terminalProfiles.detected", - "unsafePathWarning", - "yes", - "cancel", - "createQuickLaunchProfile" - ], "vs/workbench/contrib/terminal/browser/terminalInstance": [ "terminal.integrated.a11yPromptLabel", "terminal.integrated.useAccessibleBuffer", @@ -16437,15 +17863,6 @@ "bellStatus", "keybindingHandling", "configureTerminalSettings", - "preview", - "confirmMoveTrashMessageFilesAndDirectories", - { - "key": "multiLinePasteButton", - "comment": [ - "&& denotes a mnemonic" - ] - }, - "doNotAskAgain", "disconnectStatus", "workspaceNotTrustedCreateTerminal", "workspaceNotTrustedCreateTerminalCwd", @@ -16460,7 +17877,6 @@ "setTerminalDimensionsColumn", "setTerminalDimensionsRow", "terminalStaleTextBoxAriaLabel", - "changeIcon", "changeColor", "launchFailed.exitCodeAndCommandLine", "launchFailed.exitCodeOnly", @@ -16468,6 +17884,33 @@ "terminated.exitCodeOnly", "launchFailed.errorMessage" ], + "vs/workbench/contrib/customEditor/common/extensionPoint": [ + "contributes.customEditors", + "contributes.viewType", + "contributes.displayName", + "contributes.selector", + "contributes.selector.filenamePattern", + "contributes.priority", + "contributes.priority.default", + "contributes.priority.option", + "customEditors view type", + "customEditors priority", + "customEditors filenamePattern", + "customEditors" + ], + "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ + "terminal.integrated.selectProfileToCreate", + "terminal.integrated.chooseDefaultProfile", + "enterTerminalProfileName", + "terminalProfileAlreadyExists", + "terminalProfiles", + "ICreateContributedTerminalProfileOptions", + "terminalProfiles.detected", + "unsafePathWarning", + "yes", + "cancel", + "createQuickLaunchProfile" + ], "vs/workbench/contrib/terminal/browser/terminalTabsList": [ "terminalInputAriaLabel", "terminal.tabs", @@ -16489,6 +17932,31 @@ }, "label" ], + "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ + "searchWorkspace", + "openFile", + "focusFolder", + "openFolder", + "followLink" + ], + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget": [ + "default.placeholder", + "welcome.1" + ], + "vs/workbench/contrib/terminal/browser/xterm/decorationStyles": [ + "terminalPromptContextMenu", + "terminalPromptCommandFailed.duration", + "terminalPromptCommandFailedWithExitCode.duration", + "terminalPromptCommandSuccess.duration", + "terminalPromptCommandFailed", + "terminalPromptCommandFailedWithExitCode", + "terminalPromptCommandSuccess" + ], + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay": [ + "stickyScrollHoverTitle", + "labelWithKeybinding", + "labelWithKeybinding" + ], "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget": [ "label.find", "placeholder.find", @@ -16501,25 +17969,32 @@ "ariaSearchNoResultWithLineNumNoCurrentMatch", "simpleFindWidget.sashBorder" ], - "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ - "searchWorkspace", - "openFile", - "focusFolder", - "openFolder", - "followLink" + "vs/workbench/contrib/terminalContrib/find/browser/textInputContextMenu": [ + "undo", + "redo", + "cut", + "copy", + "paste", + "selectAll" ], - "vs/workbench/contrib/terminal/browser/xterm/decorationStyles": [ - "terminalPromptContextMenu", - "terminalPromptCommandFailed", - "terminalPromptCommandFailedWithExitCode", - "terminalPromptCommandSuccess" + "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ + "suggest", + "label.full", + "label.detail", + "label.desc", + "ariaCurrenttSuggestionReadDetails" ], - "vs/workbench/contrib/welcomeGettingStarted/common/media/theme_picker": [ - "dark", - "light", - "HighContrast", - "HighContrastLight", - "seeMore" + "vs/workbench/contrib/markdown/browser/markdownSettingRenderer": [ + "viewInSettings", + "viewInSettingsDetailed", + "restorePreviousValue", + "trueMessage", + "falseMessage", + "stringValue", + "numberValue", + "changeSettingTitle", + "copySettingId", + "copySettingId" ], "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ "default", @@ -16546,15 +18021,24 @@ "Theirs", "Yours" ], + "vs/platform/languagePacks/common/localizedStrings": [ + "open", + "close", + "find" + ], "vs/workbench/browser/parts/notifications/notificationsViewer": [ "executeCommand", "notificationActions", + "turnOnNotifications", + "turnOffNotifications", "notificationSource" ], - "vs/platform/languagePacks/common/localizedStrings": [ - "open", - "close", - "find" + "vs/workbench/contrib/welcomeGettingStarted/common/media/theme_picker": [ + "dark", + "light", + "HighContrast", + "HighContrastLight", + "seeMore" ], "vs/base/browser/ui/menu/menubar": [ "mAppMenu", @@ -16573,35 +18057,21 @@ "toggle", "toggleBadge" ], - "vs/editor/browser/widget/diffEditor/decorations": [ - "diffInsertIcon", - "diffRemoveIcon", - "revertChangeHoverMessage" - ], "vs/editor/common/viewLayout/viewLineRenderer": [ "showMore", "overflow.chars" ], - "vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin": [ - "diff.clipboard.copyDeletedLinesContent.label", - "diff.clipboard.copyDeletedLinesContent.single.label", - "diff.clipboard.copyChangedLinesContent.label", - "diff.clipboard.copyChangedLinesContent.single.label", - "diff.clipboard.copyDeletedLineContent.label", - "diff.clipboard.copyChangedLineContent.label", - "diff.inline.revertChange.label" - ], "vs/platform/actionWidget/browser/actionList": [ { "key": "label-preview", "comment": [ - "placeholders are keybindings, e.g \"F2 to apply, Shift+F2 to preview\"" + "placeholders are keybindings, e.g \"F2 to Apply, Shift+F2 to Preview\"" ] }, { "key": "label", "comment": [ - "placeholder is a keybinding, e.g \"F2 to apply\"" + "placeholder is a keybinding, e.g \"F2 to Apply\"" ] }, { @@ -16622,8 +18092,17 @@ "referenceCount", "treeAriaLabel" ], - "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ - "ariaLabelTabActions" + "vs/workbench/browser/parts/compositeBar": [ + "activityBarAriaLabel" + ], + "vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin": [ + "diff.clipboard.copyDeletedLinesContent.label", + "diff.clipboard.copyDeletedLinesContent.single.label", + "diff.clipboard.copyChangedLinesContent.label", + "diff.clipboard.copyChangedLinesContent.single.label", + "diff.clipboard.copyDeletedLineContent.label", + "diff.clipboard.copyChangedLineContent.label", + "diff.inline.revertChange.label" ], "vs/workbench/browser/parts/editor/breadcrumbsControl": [ "separatorIcon", @@ -16631,7 +18110,6 @@ "breadcrumbsVisible", "breadcrumbsActive", "empty", - "cmd.toggle", { "key": "miBreadcrumbs", "comment": [ @@ -16645,14 +18123,15 @@ "&& denotes a mnemonic" ] }, + "cmd.toggle", "cmd.focusAndSelect", "cmd.focus" ], - "vs/workbench/browser/parts/compositeBar": [ - "activityBarAriaLabel" + "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ + "ariaLabelTabActions" ], - "vs/platform/quickinput/browser/quickInputUtils": [ - "executeCommand" + "vs/platform/actions/browser/buttonbar": [ + "labelWithKeybinding" ], "vs/workbench/contrib/notebook/browser/diff/diffElementOutputs": [ "mimeTypePicker", @@ -16665,9 +18144,9 @@ ], "vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions": [ "notebook.lineNumbers", - "notebook.toggleLineNumbers", "notebook.showLineNumbers", - "notebook.cell.toggleLineNumbers.title" + "notebook.cell.toggleLineNumbers.title", + "notebook.toggleLineNumbers" ], "vs/workbench/contrib/notebook/browser/view/cellParts/codeCell": [ "cellExpandInputButtonLabelWithDoubleClick", @@ -16676,30 +18155,28 @@ "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar": [ "notebook.moreRunActionsLabel" ], + "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ + "hiddenCellsLabel", + "hiddenCellsLabelPlural" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ "cellOutputsCollapsedMsg", "cellExpandOutputButtonLabelWithDoubleClick", "cellExpandOutputButtonLabel" ], - "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ - "hiddenCellsLabel", - "hiddenCellsLabelPlural" - ], "vs/workbench/contrib/notebook/browser/view/cellParts/markupCell": [ "cellExpandInputButtonLabelWithDoubleClick", "cellExpandInputButtonLabel" ], - "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ - "suggest", - "label.full", - "label.detail", - "label.desc", - "ariaCurrenttSuggestionReadDetails" + "vs/workbench/contrib/comments/browser/commentThreadHeader": [ + "collapseIcon", + "label.collapse", + "startThread" ], - "vs/workbench/contrib/comments/browser/commentThreadWidget": [ - "commentLabel", - "commentLabelWithKeybinding", - "commentLabelWithKeybindingNoKeybinding" + "vs/workbench/contrib/comments/browser/commentThreadBody": [ + "commentThreadAria.withRange", + "commentThreadAria.document", + "commentThreadAria" ], "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ "killportfailure", @@ -16714,9 +18191,22 @@ "selectRecentDirectoryMac", "selectRecentDirectory" ], - "vs/workbench/browser/parts/editor/editorTabsControl": [ - "ariaLabelEditorActions", - "draggedEditorGroup" + "vs/workbench/contrib/terminal/common/terminalClipboard": [ + "preview", + "confirmMoveTrashMessageFilesAndDirectories", + { + "key": "multiLinePasteButton", + "comment": [ + "&& denotes a mnemonic" + ] + }, + { + "key": "multiLinePasteButton.oneLine", + "comment": [ + "&& denotes a mnemonic" + ] + }, + "doNotAskAgain" ], "vs/workbench/browser/parts/editor/breadcrumbs": [ "title", @@ -16774,22 +18264,21 @@ "promptChooseMimeType.placeHolder", "unavailableRenderInfo" ], + "vs/workbench/contrib/comments/browser/commentNode": [ + "commentToggleReaction", + "commentToggleReactionError", + "commentToggleReactionDefaultError", + "commentDeleteReactionError", + "commentDeleteReactionDefaultError", + "commentAddReactionError", + "commentAddReactionDefaultError" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellExecutionIcon": [ "notebook.cell.status.success", - "notebook.cell.status.failed", + "notebook.cell.status.failure", "notebook.cell.status.pending", "notebook.cell.status.executing" ], - "vs/workbench/contrib/comments/browser/commentThreadBody": [ - "commentThreadAria.withRange", - "commentThreadAria.document", - "commentThreadAria" - ], - "vs/workbench/contrib/comments/browser/commentThreadHeader": [ - "collapseIcon", - "label.collapse", - "startThread" - ], "vs/workbench/contrib/terminal/browser/environmentVariableInfo": [ "extensionEnvironmentContributionInfoStale", "relaunchTerminalLabel", @@ -16797,15 +18286,6 @@ "showEnvironmentContributions", "ScopedEnvironmentContributionInfo" ], - "vs/workbench/contrib/comments/browser/commentNode": [ - "commentToggleReaction", - "commentToggleReactionError", - "commentToggleReactionDefaultError", - "commentDeleteReactionError", - "commentDeleteReactionDefaultError", - "commentAddReactionError", - "commentAddReactionDefaultError" - ], "vs/workbench/contrib/comments/browser/reactionsAction": [ "pickReactions", "comment.toggleableReaction", @@ -16831,6 +18311,22 @@ "The emoji is also a button so that the current user can also toggle their own emoji reaction.", "The first arg is localized message \"Toggle reaction\" or empty if the user doesn't have permission to toggle the reaction, the second is number of users who have reacted with that reaction, and the third is the name of the reaction." ] + }, + { + "key": "comment.reactionLessThanTen", + "comment": [ + "This is a tooltip for an emoji that is a \"reaction\" to a comment where the count of the reactions is less than or equal to 10.", + "The emoji is also a button so that the current user can also toggle their own emoji reaction.", + "The first arg is localized message \"Toggle reaction\" or empty if the user doesn't have permission to toggle the reaction, the second iis a list of the reactors, and the third is the name of the reaction." + ] + }, + { + "key": "comment.reactionMoreThanTen", + "comment": [ + "This is a tooltip for an emoji that is a \"reaction\" to a comment where the count of the reactions is less than or equal to 10.", + "The emoji is also a button so that the current user can also toggle their own emoji reaction.", + "The first arg is localized message \"Toggle reaction\" or empty if the user doesn't have permission to toggle the reaction, the second iis a list of the reactors, and the third is the name of the reaction." + ] } ] }, @@ -16838,9 +18334,6 @@ "vs/platform/terminal/node/ptyHostMain": [ "Pty Host" ], - "vs/code/node/cliProcessMain": [ - "CLI" - ], "vs/code/electron-main/main": [ "Main", "Another instance of {0} is already running as administrator.", @@ -16852,8 +18345,8 @@ "{0}\n\nPlease make sure the following directories are writeable:\n\n{1}", "&&Close" ], - "vs/code/node/sharedProcess/sharedProcessMain": [ - "Shared" + "vs/code/node/cliProcessMain": [ + "CLI" ], "vs/code/electron-sandbox/processExplorer/processExplorerMain": [ "Process Name", @@ -16866,19 +18359,14 @@ "Copy All", "Debug" ], - "vs/workbench/electron-sandbox/desktop.main": [ - "Saving UI state" + "vs/code/node/sharedProcess/sharedProcessMain": [ + "Shared" ], "vs/workbench/electron-sandbox/desktop.contribution": [ - "New Window Tab", - "Show Previous Window Tab", - "Show Next Window Tab", - "Move Window Tab to New Window", - "Merge All Windows", - "Toggle Window Tabs Bar", "E&&xit", "Controls the timeout in seconds before giving up resolving the shell environment when the application is not already launched from a terminal. See our [documentation](https://go.microsoft.com/fwlink/?linkid=2149667) for more information.", "Window", + "Controls whether a confirmation dialog shows asking to save or discard an opened untitled workspace in the window when switching to another workspace. Disabling the confirmation dialog will always discard the untitled workspace.", "Open a new empty window.", "Focus the last active running instance.", "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).", @@ -16889,7 +18377,8 @@ "Never reopen a window. Unless a folder or workspace is opened (e.g. from the command line), an empty window will appear.", "Controls how windows are being reopened after starting for the first time. This setting has no effect when the application is already running.", "Controls whether a window should restore to full screen mode if it was exited in full screen mode.", - "Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.", + "Adjust the default zoom level for all windows. Each increment above `0` (e.g. `1`) or below (e.g. `-1`) represents zooming `20%` larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity. See {0} for configuring if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window.", + "Controls if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window. See {0} for configuring a default zoom level for all windows.", "Open new windows in the center of the screen.", "Open new windows with same dimension as last active one.", "Open new windows with same dimension as last active one with an offset position.", @@ -16899,6 +18388,10 @@ "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.", "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.", "Adjust the appearance of the window title bar to be native by the OS or custom. On Linux and Windows, this setting also affects the application and context menu appearances. Changes require a full restart to apply.", + "Automatically changes custom title bar visibility.", + "Hide custom titlebar in full screen. When not in full screen, automatically change custom title bar visibility.", + "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`.", + "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in none full screen mode with `never` when `#window.titleBarStyle#` is set to `native`.", "Adjust the appearance of dialog windows.", "Enables macOS Sierra window tabs. Note that changes require a full restart to apply and that native tabs will disable a custom title bar style if configured.", "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen.", @@ -16921,7 +18414,16 @@ "Disables the Chromium sandbox. This is useful when running VS Code as elevated on Linux and running under Applocker on Windows.", "Ensures that an in-memory store will be used for secret storage instead of using the OS's credential store. This is often used when running VS Code extension tests or when you're experiencing difficulties with the credential store.", "Forces the renderer to be accessible. ONLY change this if you are using a screen reader on Linux. On other platforms the renderer will automatically be accessible. This flag is automatically set if you have editor.accessibilitySupport: on.", - "Configures the backend used to store secrets on Linux. This argument is ignored on Windows & macOS." + "Configures the backend used to store secrets on Linux. This argument is ignored on Windows & macOS.", + "New Window Tab", + "Show Previous Window Tab", + "Show Next Window Tab", + "Move Window Tab to New Window", + "Merge All Windows", + "Toggle Window Tabs Bar" + ], + "vs/workbench/electron-sandbox/desktop.main": [ + "Saving UI state" ], "vs/workbench/services/textfile/electron-sandbox/nativeTextFileService": [ "Saving text files" @@ -16931,6 +18433,7 @@ "Save your workspace if you plan to open it again.", "&&Save", "Do&&n't Save", + "Always discard untitled workspaces without asking", "Unable to save workspace '{0}'", "The workspace is already opened in another window. Please close that window first and then try again.", "Opening a multi-root workspace." @@ -16955,14 +18458,14 @@ "Local", "Remote" ], + "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ + "Backup working copies" + ], "vs/workbench/services/integrity/electron-sandbox/integrityService": [ "Your {0} installation appears to be corrupt. Please reinstall.", "More Information", "Don't Show Again" ], - "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService": [ - "Backup working copies" - ], "vs/workbench/services/extensions/electron-sandbox/nativeExtensionService": [ "Extension host cannot start: version mismatch.", "Relaunch VS Code", @@ -16978,39 +18481,51 @@ "Extension '{0}' is required to open the remote window.\nDo you want to install the extension?", "Install and Reload", "`{0}` not found on marketplace", - "Restart Extension Host", - "Restarting extension host on explicit request." + "Restarting extension host on explicit request.", + "Restart Extension Host" ], - "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ - "Reveal in File Explorer", - "Reveal in Finder", - "Open Containing Folder", - "Share", - "File" + "vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService": [ + "Try saving or reverting the editors with unsaved changes first and then try again." ], "vs/workbench/contrib/localization/electron-sandbox/localization.contribution": [ "Would you like to change {0}'s display language to {1} and restart?", "Change Language and Restart", "Don't Show Again" ], - "vs/workbench/contrib/issue/electron-sandbox/issue.contribution": [ - "Report Performance Issue...", - "Open Process Explorer", - "Open &&Process Explorer", - "Stop Tracing", - "Tracing requires to launch with a '--trace' argument", - "&&Relaunch and Enable Tracing", - "Creating trace file...", - "This can take up to one minute to complete." + "vs/workbench/contrib/files/electron-sandbox/fileActions.contribution": [ + "Share", + "Reveal in File Explorer", + "Reveal in Finder", + "Open Containing Folder", + "File" ], "vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution": [ "Running Extensions" ], + "vs/workbench/contrib/issue/electron-sandbox/issue.contribution": [ + "Type the name of an extension to report on.", + "Open Issue Reporter", + "Open &&Process Explorer", + "Tracing requires to launch with a '--trace' argument", + "&&Relaunch and Enable Tracing", + "Creating trace file...", + "This can take up to one minute to complete.", + "Report Performance Issue...", + "Open Process Explorer", + "Stop Tracing" + ], "vs/workbench/contrib/remote/electron-sandbox/remote.contribution": [ "Whether the platform has the WSL feature installed", "Remote", "When enabled extensions are downloaded locally and installed on remote." ], + "vs/workbench/services/themes/electron-sandbox/themes.contribution": [ + "Native widget colors match the system colors.", + "Use light native widget colors for light color themes and dark for dark color themes.", + "Use light native widget colors.", + "Use dark native widget colors.", + "Set the color mode for native UI elements such as native dialogs, menus and title bar. Even if your OS is configured in light color mode, you can select a dark system color theme for the window. You can also configure to automatically adjust based on the {0} setting.\n\nNote: This setting is ignored when {1} is enabled." + ], "vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution": [ "Local backups folder does not exist", "Successfully downloaded Settings Sync activity.", @@ -17020,14 +18535,7 @@ "vs/workbench/contrib/performance/electron-sandbox/performance.contribution": [ "When enabled slow renderers are automatically profiled" ], - "vs/workbench/contrib/tasks/electron-sandbox/taskService": [ - "There is a task running. Do you want to terminate it?", - "&&Terminate Task", - "The launched task doesn't exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.", - "&&Exit Anyways" - ], "vs/workbench/contrib/externalTerminal/electron-sandbox/externalTerminal.contribution": [ - "Open New External Terminal", "External Terminal", "Use VS Code's integrated terminal.", "Use the configured external terminal.", @@ -17039,10 +18547,19 @@ "When opening a repository from the Source Control Repositories view in a terminal, determines what kind of terminal will be launched", "Customizes which terminal to run on Windows.", "Customizes which terminal application to run on macOS.", - "Customizes which terminal to run on Linux." + "Customizes which terminal to run on Linux.", + "Open New External Terminal" + ], + "vs/workbench/contrib/tasks/electron-sandbox/taskService": [ + "There is a task running. Do you want to terminate it?", + "&&Terminate Task", + "The launched task doesn't exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.", + "&&Exit Anyways" + ], + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contribution": [ + "Multi Diff Editor" ], "vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution": [ - "Remote Tunnels", "Turn on Remote Tunnel Access...", "Turn off Remote Tunnel Access...", "Show Remote Tunnel Service Log", @@ -17082,7 +18599,8 @@ "Change Tunnel Name", "The name under which the remote tunnel access is registered. If not set, the host name is used.", "The name must only consist of letters, numbers, underscore and dash. It must not start with a dash.", - "Prevent the computer from sleeping when remote tunnel access is turned on." + "Prevent this computer from sleeping when remote tunnel access is turned on.", + "Remote Tunnels" ], "vs/base/common/platform": [ "_" @@ -17111,6 +18629,7 @@ "Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '${publisher}.${name}'. Use '--force' argument to update to latest version. To install a specific version provide '@${version}'. For example: 'vscode.csharp@1.2.3'.", "Installs the pre-release version of the extension, when using --install-extension", "Uninstalls an extension.", + "Update the installed extensions.", "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.", "Print version.", "Print verbose output (implies --wait).", @@ -17135,6 +18654,14 @@ "Unknown version", "Unknown commit" ], + "vs/platform/log/common/log": [ + "Trace", + "Debug", + "Info", + "Warning", + "Error", + "Off" + ], "vs/platform/terminal/node/ptyService": [ "History restored" ], @@ -17189,6 +18716,9 @@ "Assumes that all characters are of the same width. This is a fast algorithm that works correctly for monospace fonts and certain scripts (like Latin characters) where glyphs are of equal width.", "Delegates wrapping points computation to the browser. This is a slow algorithm, that might cause freezes for large files, but it works correctly in all cases.", "Controls the algorithm that computes wrapping points. Note that when in accessibility mode, advanced will be used for the best experience.", + "Disable the code action menu.", + "Show the code action menu when the cursor is on lines with code.", + "Show the code action menu when the cursor is on lines with code or on empty lines.", "Enables the Code Action lightbulb in the editor.", "Shows the nested current scopes during the scroll at the top of the editor.", "Defines the maximum number of sticky lines to show.", @@ -17214,6 +18744,9 @@ "Scale of content drawn in the minimap: 1, 2 or 3.", "Render the actual characters on a line as opposed to color blocks.", "Limit the width of the minimap to render at most a certain number of columns.", + "Controls whether named regions are shown as section headers in the minimap.", + "Controls whether MARK: comments are shown as section headers in the minimap.", + "Controls the font size of section headers in the minimap.", "Controls the amount of space between the top edge of the editor and the first line.", "Controls the amount of space between the bottom edge of the editor and the last line.", "Enables a pop-up that shows parameter documentation and type information as you type.", @@ -17255,8 +18788,17 @@ "Controls whether to automatically show inline suggestions in the editor.", "Show the inline suggestion toolbar whenever an inline suggestion is shown.", "Show the inline suggestion toolbar when hovering over an inline suggestion.", + "Never show the inline suggestion toolbar.", "Controls when to show the inline suggestion toolbar.", "Controls how inline suggestions interact with the suggest widget. If enabled, the suggest widget is not shown automatically when inline suggestions are available.", + "Controls the font family of the inline suggestions.", + "Controls whether to show inline edits in the editor.", + "Show the inline edit toolbar whenever an inline suggestion is shown.", + "Show the inline edit toolbar when hovering over an inline suggestion.", + "Never show the inline edit toolbar.", + "Controls when to show the inline edit toolbar.", + "Controls the font family of the inline edit.", + "Controls whether to color the background of inline edits.", "Controls whether bracket pair colorization is enabled or not. Use {0} to override the bracket highlight colors.", "Controls whether each bracket type has its own independent color pool.", "Enables bracket pair guides.", @@ -17323,12 +18865,14 @@ "When enabled IntelliSense shows `issues`-suggestions.", "Whether leading and trailing whitespace should always be selected.", "Whether subwords (like 'foo' in 'fooBar' or 'foo_bar') should be selected.", + "Locales to be used for word segmentation when doing word related navigations or operations. Specify the BCP 47 language tag of the word you wish to recognize (e.g., ja, zh-CN, zh-Hant-TW, etc.).", + "Locales to be used for word segmentation when doing word related navigations or operations. Specify the BCP 47 language tag of the word you wish to recognize (e.g., ja, zh-CN, zh-Hant-TW, etc.).", "No indentation. Wrapped lines begin at column 1.", "Wrapped lines get the same indentation as the parent.", "Wrapped lines get +1 indentation toward the parent.", "Wrapped lines get +2 indentation toward the parent.", "Controls the indentation of wrapped lines.", - "Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor).", + "Controls whether you can drag and drop a file into a text editor by holding down the `Shift` key (instead of opening the file in an editor).", "Controls if a widget is shown when dropping files into the editor. This widget lets you control how the file is dropped.", "Show the drop selector widget after a file is dropped into the editor.", "Never show the drop selector widget. Instead the default drop provider is always used.", @@ -17386,7 +18930,7 @@ "Controls the minimal number of visible leading lines (minimum 0) and trailing lines (minimum 1) surrounding the cursor. Known as 'scrollOff' or 'scrollOffset' in some other editors.", "`cursorSurroundingLines` is enforced only when triggered via the keyboard or API.", "`cursorSurroundingLines` is enforced always.", - "Controls when `#cursorSurroundingLines#` should be enforced.", + "Controls when `#editor.cursorSurroundingLines#` should be enforced.", "Controls the width of the cursor when `#editor.cursorStyle#` is set to `line`.", "Controls whether the editor should allow moving selections via drag and drop.", "Use a new rendering method with svgs.", @@ -17412,6 +18956,7 @@ "Controls whether the editor should detect links and make them clickable.", "Highlight matching brackets.", "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.", + "Zoom the font of the editor when using mouse wheel and holding `Cmd`.", "Zoom the font of the editor when using mouse wheel and holding `Ctrl`.", "Merge multiple cursors when they are overlapping.", "Maps to `Control` on Windows and Linux and to `Command` on macOS.", @@ -17421,8 +18966,10 @@ "Each cursor pastes the full text.", "Controls pasting when the line count of the pasted text matches the cursor count.", "Controls the max number of cursors that can be in an active editor at once.", - "Controls whether the editor should highlight semantic symbol occurrences.", - "Experimental: Controls whether the editor should highlight word occurrences accross multiple open editors.", + "Does not highlight occurrences.", + "Highlights occurrences only in the current file.", + "Experimental: Highlights occurrences across all valid open files.", + "Controls whether occurrences should be highlighted across open files.", "Controls whether a border should be drawn around the overview ruler.", "Focus the tree when opening peek", "Focus the editor when opening peek", @@ -17474,7 +19021,7 @@ "Unusual line terminators are ignored.", "Unusual line terminators prompt to be removed.", "Remove unusual line terminators that might cause problems.", - "Inserting and deleting whitespace follows tab stops.", + "Spaces and tabs are inserted and deleted in alignment with tab stops.", "Use the default line break rule.", "Word breaks should not be used for Chinese/Japanese/Korean (CJK) text. Non-CJK text behavior is the same as for normal.", "Controls the word break rules used for Chinese/Japanese/Korean (CJK) text.", @@ -17496,47 +19043,23 @@ "{0} ({1} errors in total)", "An unknown error occurred. Please consult the log for more details." ], - "vs/platform/extensionManagement/common/extensionManagement": [ - "Extensions", - "Preferences" - ], - "vs/platform/extensionManagement/common/extensionManagementCLI": [ - "Extension '{0}' not found.", - "Make sure you use the full extension ID, including the publisher, e.g.: {0}", - "Extensions installed on {0}:", - "Installing extensions on {0}...", - "Installing extensions...", - "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", - "Extension '{0}' is already installed.", - "Error while installing extensions: {0}", - "Failed Installing Extensions: {0}", - "Extension '{0}' was successfully installed.", - "Cancelled installing extension '{0}'.", - "Extension '{0}' is already installed.", - "Updating the extension '{0}' to the version {1}", - "Installing builtin extension '{0}' v{1}...", - "Installing builtin extension '{0}'...", - "Installing extension '{0}' v{1}...", - "Installing extension '{0}'...", - "Extension '{0}' v{1} was successfully installed.", - "Cancelled installing extension '{0}'.", - "A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", - "Extension '{0}' is a Built-in extension and cannot be uninstalled", - "Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.", - "Uninstalling {0}...", - "Extension '{0}' was successfully uninstalled from {1}!", - "Extension '{0}' was successfully uninstalled!", - "Extension '{0}' is not installed on {1}.", - "Extension '{0}' is not installed." + "vs/code/electron-main/app": [ + "An external application wants to open '{0}' in {1}. Do you want to open this workspace file?", + "An external application wants to open '{0}' in {1}. Do you want to open this folder?", + "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", + "&&Yes", + "&&No", + "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'", + "Allow opening local paths without asking", + "Allow opening remote paths without asking" ], - "vs/platform/extensionManagement/common/extensionsScannerService": [ - "Cannot read file {0}: {1}.", - "Failed to parse {0}: [{1}, {2}] {3}.", - "Invalid manifest file {0}: Not an JSON object.", - "Failed to parse {0}: {1}.", - "Invalid format {0}: JSON object expected.", - "Failed to parse {0}: {1}.", - "Invalid format {0}: JSON object expected." + "vs/platform/environment/node/argvHelper": [ + "Option '{0}' is defined more than once. Using value '{1}'.", + "Option '{0}' requires a non empty value. Ignoring the option.", + "Option '{0}' is deprecated: {1}", + "Warning: '{0}' is not in the list of known options for subcommand '{1}'", + "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", + "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`." ], "vs/platform/files/common/files": [ "Unknown Error", @@ -17546,17 +19069,6 @@ "{0}GB", "{0}TB" ], - "vs/platform/extensionManagement/node/extensionManagementService": [ - "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", - "Marketplace is not enabled", - "Only Marketplace Extensions can be reinstalled", - "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", - "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", - "Unknown error while renaming {0} to {1}", - "Cannot read the extension from {0}", - "Please restart VS Code before reinstalling {0}.", - "Please restart VS Code before reinstalling {0}." - ], "vs/platform/files/common/fileService": [ "Unable to resolve filesystem provider with relative file path '{0}'", "ENOPRO: No file system provider found for resource '{0}'", @@ -17595,9 +19107,6 @@ "File to move/copy does not exist", "File at target already exists and thus will not be moved/copied to unless overwrite is specified" ], - "vs/platform/languagePacks/common/languagePacks": [ - " (Current)" - ], "vs/platform/request/common/request": [ "Network Requests", "HTTP", @@ -17613,6 +19122,91 @@ "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS, a reload of the window is required after turning this off.)", "Controls whether experimental loading of CA certificates from the OS should be enabled. This uses a more general approach than the default implemenation." ], + "vs/platform/update/common/update.config.contribution": [ + "Update", + "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service.", + "Disable updates.", + "Disable automatic background update checks. Updates will be available if you manually check for updates.", + "Check for updates only on startup. Disable automatic background update checks.", + "Enable automatic update checks. Code will check for updates automatically and periodically.", + "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service.", + "This setting is deprecated, please use '{0}' instead.", + "Enable Background Updates on Windows", + "Enable to download and install new VS Code versions in the background on Windows.", + "Show Release Notes after an update. The Release Notes are fetched from a Microsoft online service." + ], + "vs/platform/dialogs/common/dialogs": [ + "&&Yes", + "Cancel", + "Cancel", + "Cancel", + "&&OK", + "&&OK", + "Cancel", + "...1 additional file not shown", + "...{0} additional files not shown" + ], + "vs/platform/extensionManagement/common/extensionManagement": [ + "Extensions", + "Preferences" + ], + "vs/platform/extensionManagement/common/extensionManagementCLI": [ + "Extension '{0}' not found.", + "Make sure you use the full extension ID, including the publisher, e.g.: {0}", + "Extensions installed on {0}:", + "Installing extensions on {0}...", + "Installing extensions...", + "Error while installing extensions: {0}", + "Failed Installing Extensions: {0}", + "Fetching latest versions for {0} extensions", + "No extension to update", + "Updating extensions: {0}", + "Error while updating extension {0}: {1}", + "Extension '{0}' v{1} was successfully updated.", + "Extension '{0}' v{1} is already installed. Use '--force' option to update to latest version or provide '@' to install a specific version, for example: '{2}@1.2.3'.", + "Extension '{0}' is already installed.", + "Extension '{0}' is already installed.", + "Updating the extension '{0}' to the version {1}", + "Installing builtin extension '{0}' v{1}...", + "Installing builtin extension '{0}'...", + "Installing extension '{0}' v{1}...", + "Installing extension '{0}'...", + "Error while installing extension {0}: {1}", + "Extension '{0}' v{1} was successfully installed.", + "Extension '{0}' was successfully installed.", + "Cancelled installing extension '{0}'.", + "A newer version of extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", + "Extension '{0}' is a Built-in extension and cannot be uninstalled", + "Extension '{0}' is marked as a Built-in extension by user. Please use '--force' option to uninstall it.", + "Uninstalling {0}...", + "Extension '{0}' was successfully uninstalled from {1}!", + "Extension '{0}' was successfully uninstalled!", + "Extension '{0}' is not installed on {1}.", + "Extension '{0}' is not installed." + ], + "vs/platform/extensionManagement/common/extensionsScannerService": [ + "Cannot read file {0}: {1}.", + "Failed to parse {0}: [{1}, {2}] {3}.", + "Invalid manifest file {0}: Not an JSON object.", + "Failed to parse {0}: {1}.", + "Invalid format {0}: JSON object expected.", + "Failed to parse {0}: {1}.", + "Invalid format {0}: JSON object expected." + ], + "vs/platform/extensionManagement/node/extensionManagementService": [ + "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", + "Marketplace is not enabled", + "Only Marketplace Extensions can be reinstalled", + "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", + "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", + "Unknown error while renaming {0} to {1}", + "Cannot read the extension from {0}", + "Please restart VS Code before reinstalling {0}.", + "Please restart VS Code before reinstalling {0}." + ], + "vs/platform/languagePacks/common/languagePacks": [ + " (Current)" + ], "vs/platform/telemetry/common/telemetryService": [ "Controls {0} telemetry, first-party extension telemetry, and participating third-party extension telemetry. Some third party extensions might not respect this setting. Consult the specific extension's documentation to be sure. Telemetry helps us better understand how {0} is performing, where improvements need to be made, and how features are being used.", "Read more about the [data we collect]({0}).", @@ -17633,49 +19227,6 @@ "Enable diagnostic data to be collected. This helps us to better understand how {0} is performing and where improvements need to be made. [Read more]({1}) about what we collect and our privacy statement.", "If this setting is false, no telemetry will be sent regardless of the new setting's value. Deprecated in favor of the {0} setting." ], - "vs/platform/userDataProfile/common/userDataProfile": [ - "Default" - ], - "vs/code/electron-main/app": [ - "&&Yes", - "&&No", - "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", - "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'", - "Allow opening local paths without asking", - "Allow opening remote paths without asking" - ], - "vs/platform/environment/node/argvHelper": [ - "Option '{0}' is defined more than once. Using value '{1}'.", - "Option '{0}' requires a non empty value. Ignoring the option.", - "Option '{0}' is deprecated: {1}", - "Warning: '{0}' is not in the list of known options for subcommand '{1}'", - "Warning: '{0}' is not in the list of known options, but still passed to Electron/Chromium.", - "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`." - ], - "vs/platform/dialogs/common/dialogs": [ - "&&Yes", - "Cancel", - "Cancel", - "Cancel", - "&&OK", - "&&OK", - "Cancel", - "...1 additional file not shown", - "...{0} additional files not shown" - ], - "vs/platform/update/common/update.config.contribution": [ - "Update", - "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service.", - "Disable updates.", - "Disable automatic background update checks. Updates will be available if you manually check for updates.", - "Check for updates only on startup. Disable automatic background update checks.", - "Enable automatic update checks. Code will check for updates automatically and periodically.", - "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service.", - "This setting is deprecated, please use '{0}' instead.", - "Enable Background Updates on Windows", - "Enable to download and install new VS Code versions in the background on Windows.", - "Show Release Notes after an update. The Release Notes are fetched from a Microsoft online service." - ], "vs/code/electron-sandbox/issue/issueReporterPage": [ "Include my system information", "Include my currently running processes", @@ -17699,6 +19250,7 @@ "The title is too long.", "Please enter details.", "A description is required.", + "Please provide a longer description.", "show", "Extension does not have additional data to include.", "show", @@ -17707,6 +19259,9 @@ "show", "show" ], + "vs/platform/userDataProfile/common/userDataProfile": [ + "Default" + ], "vs/code/electron-sandbox/issue/issueReporterService": [ "hide", "show", @@ -17796,8 +19351,18 @@ "Use contiguous matching when searching.", "Controls the type of matching used when searching lists and trees in the workbench.", "Controls how tree folders are expanded when clicking the folder names. Note that some trees and lists might choose to ignore this setting if it is not applicable.", + "Controls whether sticky scrolling is enabled in trees.", + "Controls the number of sticky elements displayed in the tree when `#workbench.tree.enableStickyScroll#` is enabled.", "Controls how type navigation works in lists and trees in the workbench. When set to `trigger`, type navigation begins once the `list.triggerTypeNavigation` command is run." ], + "vs/platform/markers/common/markers": [ + "Error", + "Warning", + "Info" + ], + "vs/platform/contextkey/browser/contextKeyService": [ + "A command that returns information about context keys" + ], "vs/platform/contextkey/common/contextkey": [ "Empty context key expression", "Did you forget to write an expression? You can also put 'false' or 'true' to always evaluate to false or true, respectively.", @@ -17811,22 +19376,6 @@ "Unexpected token. Hint: {0}", "Unexpected token." ], - "vs/platform/contextkey/browser/contextKeyService": [ - "A command that returns information about context keys" - ], - "vs/platform/markers/common/markers": [ - "Error", - "Warning", - "Info" - ], - "vs/workbench/browser/actions/textInputActions": [ - "Undo", - "Redo", - "Cut", - "Copy", - "Paste", - "Select All" - ], "vs/workbench/browser/workbench.contribution": [ "The default size.", "Increases the size, so it can be grabbed more easily with the mouse.", @@ -17835,11 +19384,23 @@ "The active editor is displayed as a single large tab in the editor title area.", "The editor title area is not displayed.", "Controls whether opened editors should show as individual tabs, one single large tab or if the title area should not be shown.", - "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", - "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", - "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Show editor actions in the window title bar when {0} is set to {1}. Otherwise, editor actions are shown in the editor tab bar.", + "Show editor actions in the window title bar. If {0} is set to {1}, editor actions are hidden.", + "Editor actions are not shown.", + "Controls where the editor actions are shown.", + "Controls whether tabs should be wrapped over multiple lines when exceeding available space or whether a scrollbar should appear instead. This value is ignored when {0} is not set to '{1}'.", + "Controls whether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behavior for that duration. This value is ignored when {0} is not set to {1}.", + "Controls whether a top border is drawn on tabs for editors that have unsaved changes. This value is ignored when {0} is not set to {1}.", "Controls whether editor file decorations should use badges.", "Controls whether editor file decorations should use colors.", + "Controls whether the custom workbench editor labels should be applied.", + "Controls the rendering of the editor label. Each __Item__ is a pattern that matches a file path. Both relative and absolute file paths are supported. In case multiple patterns match, the longest matching path will be picked. Each __Value__ is the template for the rendered editor when the __Item__ matches. Variables are substituted based on the context:", + "`${dirname}`: name of the folder in which the file is located (e.g. `root/folder/file.txt -> folder`).", + "`${dirname(N)}`: name of the nth parent folder in which the file is located (e.g. `N=1: root/folder/file.txt -> root`). Folders can be picked from the start of the path by using negative numbers (e.g. `N=-1: root/folder/file.txt -> root`). If the __Item__ is an absolute pattern path, the first folder (`N=-1`) refers to the first folder in the absoulte path, otherwise it corresponds to the workspace folder.", + "`${filename}`: name of the file without the file extension (e.g. `root/folder/file.txt -> file`).", + "`${extname}`: the file extension (e.g. `root/folder/file.txt -> txt`).", + "Example: `\"**/static/**/*.html\": \"${filename} - ${dirname} (${extname})\"` will render a file `root/static/folder/file.html` as `file - folder (html)`.", + "The template which should be rendered when the pattern mtches. May include the variables ${dirname}, ${filename} and ${extname}.", "Show the name of the file. When tabs are enabled and two files have the same name in one group the distinguishing sections of each file's path are added. When tabs are disabled, the path relative to the workspace folder is shown if the editor is active.", "Show the name of the file followed by its directory name.", "Show the name of the file followed by its path relative to the workspace folder.", @@ -17855,19 +19416,21 @@ "When enabled, shows a Status bar Quick Fix when the editor language doesn't match detected content language.", "Show in untitled text editors", "Show in notebook editors", - "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls the position of the editor's tabs action buttons (close, unpin). This value is ignored when {0} is not set to {1}.", + "Controls the visibility of the tab close action button.", + "Controls the visibility of the tab unpin action button.", "Always keep tabs large enough to show the full editor label.", "Allow tabs to get smaller when the available space is not enough to show all tabs at once.", "Make all tabs the same size, while allowing them to get smaller when the available space is not enough to show all tabs at once.", - "Controls the size of editor tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", - "Controls the minimum width of tabs when `#workbench.editor.tabSizing#` size is set to `fixed`.", - "Controls the maximum width of tabs when `#workbench.editor.tabSizing#` size is set to `fixed`.", - "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls the size of editor tabs. This value is ignored when {0} is not set to {1}.", + "Controls the minimum width of tabs when {0} size is set to {1}.", + "Controls the maximum width of tabs when {0} size is set to {1}.", + "Controls the height of editor tabs. Also applies to the title control bar when {0} is not set to {1}.", "A pinned tab inherits the look of non pinned tabs.", "A pinned tab will show in a compact form with only icon or first letter of the editor name.", "A pinned tab shrinks to a compact fixed size showing parts of the editor name.", - "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", - "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls the size of pinned editor tabs. Pinned tabs are sorted to the beginning of all opened tabs and typically do not close until unpinned. This value is ignored when {0} is not set to {1}.", + "When enabled, displays pinned tabs in a separate row above all other tabs. This value is ignored when {0} is not set to {1}.", "Always prevent closing the pinned editor when using mouse middle click or keyboard.", "Prevent closing the pinned editor when using the keyboard.", "Prevent closing the pinned editor when using mouse middle click.", @@ -17878,13 +19441,14 @@ "Splits the active editor group to equal parts.", "Controls the size of editor groups when splitting them.", "Controls if editor groups can be split from drag and drop operations by dropping an editor or file on the edges of the editor area.", + "Controls if editors can be dragged out of the window to open them in a new window. Press and hold the `Alt` key while dragging to toggle this dynamically.", "Controls whether editors are closed in most recently used order or from left to right.", "Controls whether opened editors should show with an icon or not. This requires a file icon theme to be enabled as well.", "Controls whether opened editors show as preview editors. Preview editors do not stay open, are reused until explicitly set to be kept open (via double-click or editing), and show file names in italics.", - "Controls whether editors opened from Quick Open show as preview editors. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). When enabled, hold Ctrl before selection to open an editor as a non-preview. This value is ignored when `#workbench.editor.enablePreview#` is disabled.", - "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). This value is ignored when `#workbench.editor.enablePreview#` is disabled.", + "Controls whether editors opened from Quick Open show as preview editors. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). When enabled, hold Ctrl before selection to open an editor as a non-preview. This value is ignored when {0} is not set to {1}.", + "Controls whether editors remain in preview when a code navigation is started from them. Preview editors do not stay open, and are reused until explicitly set to be kept open (via double-click or editing). This value is ignored when {0} is not set to {1}.", "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that editors with unsaved changes will never close to preserve your data.", - "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.", + "Controls where editors open. Select {0} or {1} to open editors to the left or right of the currently active one. Select {2} or {3} to open editors independently from the currently active one.", "Controls the default direction of editors that are opened side by side (for example, from the Explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.", "Controls the behavior of empty editor groups when the last tab in the group is closed. When enabled, empty groups will automatically close. When disabled, empty groups will remain part of the grid.", "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, such as when forcing an editor to open in a specific group or to the side of the currently active group.", @@ -17900,7 +19464,7 @@ "Editors are positioned from left to right.", "Controls if the centered layout should automatically resize to maximum width when more than one group is open. Once only one group is open it will resize back to the original centered width.", "Controls whether the centered layout tries to maintain constant width when the window is resized.", - "Controls how the editor group is resized when double clicking on a tab. This value is ignored when `#workbench.editor.showTabs#` is not set to `multiple`.", + "Controls how the editor group is resized when double clicking on a tab. This value is ignored when {0} is not set to {1}.", "All other editor groups are hidden and the current editor group is maximized to take up the entire editor area.", "The editor group takes as much space as possible by making all other editor groups as small as possible.", "No editor group is resized when double clicking on a tab.", @@ -17932,13 +19496,14 @@ "Never maximize the panel when opening it. The panel will open un-maximized.", "Open the panel to the state that it was in, before it was closed.", "Controls the visibility of the status bar at the bottom of the workbench.", - "Controls the location of the activity bar. It can either show to the `side` or `top` (requires {0} set to {1}) of the primary side bar or `hidden`.", - "Show the activity bar to the side of the primary side bar.", - "Show the activity bar on top of the primary side bar.", - "Hide the activity bar.", - "Controls the behavior of clicking an activity bar icon in the workbench.", - "Hide the side bar if the clicked item is already visible.", - "Focus side bar if the clicked item is already visible.", + "Controls the location of the Activity Bar relative to the Primary and Secondary Side Bars.", + "Show the Activity Bar on the side of the Primary Side Bar and on top of the Secondary Side Bar.", + "Show the Activity Bar on top of the Primary and Secondary Side Bars.", + "Show the Activity Bar at the bottom of the Primary and Secondary Side Bars.", + "Hide the Activity Bar in the Primary and Secondary Side Bars.", + "Controls the behavior of clicking an Activity Bar icon in the workbench. This value is ignored when {0} is not set to {1}.", + "Hide the Primary Side Bar if the clicked item is already visible.", + "Focus the Primary Side Bar if the clicked item is already visible.", "Controls the visibility of view header actions. View header actions may either be always visible, or only visible when that view is focused or hovered over.", "Controls font aliasing method in the workbench.", "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text.", @@ -17954,7 +19519,7 @@ "Do not render with reduced motion", "Render with reduced motion based on OS configuration.", "Controls whether the layout control in the title bar is shown.", - "Controls whether the layout control is shown in the custom title bar. This setting only has an effect when {0} is set to {1}.", + "Controls whether the layout control is shown in the custom title bar. This setting only has an effect when {0} is not set to {1}.", "Shows a single button with a dropdown of layout options.", "Shows several buttons for toggling the visibility of the panels and side bar.", "Shows both the dropdown and toggle buttons.", @@ -17977,11 +19542,13 @@ "`${remoteName}`: e.g. SSH", "`${dirty}`: an indicator for when the active editor has unsaved changes.", "`${focusedView}`: the name of the view that is currently focused.", + "`${activeRepositoryName}`: the name of the active repository (e.g. vscode).", + "`${activeRepositoryBranchName}`: the name of the active branch in the active repository (e.g. main).", "`${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.", "Window", "Separator used by {0}.", "Show command launcher together with the window title.", - "Show command launcher together with the window title. This setting only has an effect when {0} is set to {1}.", + "Show command launcher together with the window title. This setting only has an effect when {0} is not set to {1}.", "Menu is displayed at the top of the window and only hidden in full screen mode.", "Menu is always visible at the top of the window even in full screen mode.", "Menu is hidden but can be displayed at the top of the window by executing the `Focus Application Menu` command.", @@ -18010,11 +19577,12 @@ "Never explicitly ask for confirmation unless data loss is imminent.", "Never explicitly ask for confirmation.", "Controls whether to show a confirmation dialog before closing the browser tab or window. Note that even if enabled, browsers may still decide to close a tab or window without confirmation and that this setting is only a hint that may not work in all cases.", - "Controls whether to show a confirmation dialog before closing the window or quitting the application.", + "Controls whether to show a confirmation dialog before closing a window or quitting the application.", + "Controls whether the problems are visible throughout the editor and workbench.", "Zen Mode", "Controls whether turning on Zen Mode also puts the workbench into full screen mode.", "Controls whether turning on Zen Mode also centers the layout.", - "Controls whether turning on Zen Mode should show multiple editor tabs, a single editor tab or hide the editor title area completely.", + "Controls whether turning on Zen Mode should show multiple editor tabs, a single editor tab, or hide the editor title area completely.", "Each editor is displayed as a tab in the editor title area.", "The active editor is displayed as a single large tab in the editor title area.", "The editor title area is not displayed.", @@ -18024,33 +19592,17 @@ "Controls whether a window should restore to Zen Mode if it was exited in Zen Mode.", "Controls whether notifications do not disturb mode should be enabled while in Zen Mode. If true, only error notifications will pop out." ], - "vs/workbench/browser/actions/helpActions": [ - "Keyboard Shortcuts Reference", - "&&Keyboard Shortcuts Reference", - "Video Tutorials", - "&&Video Tutorials", - "Tips and Tricks", - "Tips and Tri&&cks", - "Documentation", - "&&Documentation", - "Signup for the VS Code Newsletter", - "Join Us on YouTube", - "&&Join Us on YouTube", - "Search Feature Requests", - "&&Search Feature Requests", - "View License", - "View &&License", - "Privacy Statement", - "Privac&&y Statement" + "vs/workbench/browser/actions/textInputActions": [ + "Undo", + "Redo", + "Cut", + "Copy", + "Paste", + "Select All" ], "vs/workbench/browser/actions/developerActions": [ - "Inspect Context Keys", - "Toggle Screencast Mode", - "Log Storage Database Contents", "The storage database contents have been logged to the developer tools.", "Open developer tools from the menu and select the Console tab.", - "Log Working Copies", - "Remove Large Storage Database Entries...", "Scope: {0}, Target: {1}", "Global", "Profile", @@ -18063,9 +19615,6 @@ "Do you want to remove the selected storage entries from the database?", "{0}\n\nThis action is irreversible and may result in data loss!", "&&Remove", - "Start Tracking Disposables", - "Snapshot Tracked Disposables", - "Stop Tracking Disposables", "Screencast Mode", "Controls the vertical offset of the screencast mode overlay from the bottom as a percentage of the workbench height.", "Controls the font size (in pixels) of the screencast mode keyboard.", @@ -18077,7 +19626,34 @@ "Show single editor cursor move commands.", "Controls how long (in milliseconds) the keyboard overlay is shown in screencast mode.", "Controls the color in hex (#RGB, #RGBA, #RRGGBB or #RRGGBBAA) of the mouse indicator in screencast mode.", - "Controls the size (in pixels) of the mouse indicator in screencast mode." + "Controls the size (in pixels) of the mouse indicator in screencast mode.", + "Inspect Context Keys", + "Toggle Screencast Mode", + "Log Storage Database Contents", + "Log Working Copies", + "Remove Large Storage Database Entries...", + "Start Tracking Disposables", + "Snapshot Tracked Disposables", + "Stop Tracking Disposables" + ], + "vs/workbench/browser/actions/helpActions": [ + "&&Keyboard Shortcuts Reference", + "&&Video Tutorials", + "Tips and Tri&&cks", + "&&Documentation", + "&&Join Us on YouTube", + "&&Search Feature Requests", + "View &&License", + "Privac&&y Statement", + "Keyboard Shortcuts Reference", + "Video Tutorials", + "Tips and Tricks", + "Documentation", + "Signup for the VS Code Newsletter", + "Join Us on YouTube", + "Search Feature Requests", + "View License", + "Privacy Statement" ], "vs/workbench/browser/actions/layoutActions": [ "Represents the menu bar", @@ -18096,16 +19672,10 @@ "Represents full screen", "Represents centered layout mode", "Represents zen mode", - "Close Primary Side Bar", - "Toggle Activity Bar Visibility", - "Toggle Centered Layout", "&&Centered Layout", - "Move Primary Side Bar Right", - "Move Primary Side Bar Left", "Toggle Primary Side Bar Position", "Move Primary Side Bar Right", "Move Primary Side Bar Left", - "Toggle Primary Side Bar Position", "Icon represents workbench layout configuration.", "Configure Layout", "Move Primary Side Bar Right", @@ -18116,35 +19686,25 @@ "Move Secondary Side Bar Right", "&&Move Primary Side Bar Right", "&&Move Primary Side Bar Left", - "Toggle Editor Area Visibility", "Show &&Editor Area", "&&Appearance", - "Toggle Primary Side Bar Visibility", "Primary Side Bar", "&&Primary Side Bar", "Hide Primary Side Bar", "Hide Primary Side Bar", "Toggle Primary Side Bar", "Toggle Primary Side Bar", - "Toggle Status Bar Visibility", "S&&tatus Bar", - "Hide Editor Tabs", - "Show Multiple Editor Tabs", - "Show Single Editor Tab", "Tab Bar", - "Separate Pinned Editor Tabs", - "Toggle Zen Mode", + "Tab Bar", + "Editor Actions Position", "Zen Mode", - "Toggle Menu Bar", "Menu &&Bar", "Menu Bar", - "Reset View Locations", - "Move View", "Side Bar / {0}", "Panel / {0}", "Secondary Side Bar / {0}", "Select a View to Move", - "Move Focused View", "There is no view currently focused.", "The currently focused view is not movable.", "Select a Destination for the View", @@ -18155,14 +19715,7 @@ "Side Bar", "Panel", "Secondary Side Bar", - "Reset Focused View Location", "There is no view currently focused.", - "Increase Current View Size", - "Increase Editor Width", - "Increase Editor Height", - "Decrease Current View Size", - "Decrease Editor Width", - "Decrease Editor Height", "Select to Hide", "Select to Show", "Active", @@ -18181,14 +19734,56 @@ "Full Screen", "Zen Mode", "Centered Layout", - "Customize Layout...", "Visibility", "Primary Side Bar Position", "Panel Alignment", "Modes", "Customize Layout", "Close", - "Restore Defaults" + "Restore Defaults", + "Close Primary Side Bar", + "Toggle Centered Layout", + "Move Primary Side Bar Right", + "Move Primary Side Bar Left", + "Toggle Primary Side Bar Position", + "Toggle Editor Area Visibility", + "Toggle Primary Side Bar Visibility", + "Toggle Status Bar Visibility", + "Hide Editor Tabs", + "Hide Tab Bar", + "Hide Editor Tabs in Zen Mode", + "Hide Tab Bar in Zen Mode", + "Show Multiple Editor Tabs", + "Show Tab Bar with multiple tabs", + "Show Multiple Editor Tabs in Zen Mode", + "Show Tab Bar in Zen Mode", + "Show Single Editor Tab", + "Show Tab Bar with one Tab", + "Show Single Editor Tab in Zen Mode", + "Show Tab Bar in Zen Mode with one Tab", + "Move Editor Actions to Title Bar", + "Move Editor Actions from the tab bar to the title bar", + "Move Editor Actions to Tab Bar", + "Move Editor Actions from the title bar to the tab bar", + "Hide Editor Actions", + "Hide Editor Actions in the tab and title bar", + "Show Editor Actions", + "Make Editor Actions visible.", + "Separate Pinned Editor Tabs", + "Toggle whether pinned editor tabs are shown on a separate row above unpinned tabs.", + "Toggle Zen Mode", + "Toggle Menu Bar", + "Reset View Locations", + "Move View", + "Move Focused View", + "Reset Focused View Location", + "Increase Current View Size", + "Increase Editor Width", + "Increase Editor Height", + "Decrease Current View Size", + "Decrease Editor Width", + "Decrease Editor Height", + "Customize Layout..." ], "vs/workbench/browser/actions/navigationActions": [ "Navigate to the View on the Left", @@ -18198,6 +19793,11 @@ "Focus Next Part", "Focus Previous Part" ], + "vs/workbench/browser/actions/listCommands": [ + "&&Toggle Tree Sticky Scroll", + "Toggles Sticky Scroll widget at the top of tree structures such as the File Explorer and Debug variables View.", + "Toggle Tree Sticky Scroll" + ], "vs/workbench/browser/actions/windowActions": [ "Remove from Recently Opened", "Folder With Unsaved Files", @@ -18215,21 +19815,37 @@ "Folders with unsaved files cannot be removed until all unsaved files have been saved or reverted.", "{0}, workspace with unsaved changes", "{0}, folder with unsaved changes", - "Open Recent...", "&&More...", + "&&Full Screen", + "&&About", + "New &&Window", + "Confirm Before Close", + "Open &&Recent", + "Open Recent...", "Quick Open Recent...", "Toggle Full Screen", - "&&Full Screen", "Reload Window", "About", - "&&About", "New Window", - "New &&Window", - "Remove keyboard focus from focused element", - "Confirm Before Close", - "Open &&Recent" + "Remove keyboard focus from focused element" + ], + "vs/workbench/browser/actions/workspaceCommands": [ + "&&Add", + "Add Folder to Workspace", + "Select workspace folder", + "Add Folder to Workspace..." ], "vs/workbench/browser/actions/workspaceActions": [ + "&&Open File...", + "Open &&Folder...", + "Open &&Folder...", + "&&Open...", + "Open Wor&&kspace from File...", + "A&&dd Folder to Workspace...", + "Save Workspace As...", + "Duplicate Workspace", + "Close &&Folder", + "Close &&Workspace", "Workspaces", "Open File...", "Open Folder...", @@ -18240,27 +19856,11 @@ "Open Workspace Configuration File", "Remove Folder from Workspace...", "Save Workspace As...", - "Duplicate As Workspace in New Window", - "&&Open File...", - "Open &&Folder...", - "Open &&Folder...", - "&&Open...", - "Open Wor&&kspace from File...", - "A&&dd Folder to Workspace...", - "Save Workspace As...", - "Duplicate Workspace", - "Close &&Folder", - "Close &&Workspace" - ], - "vs/workbench/browser/actions/workspaceCommands": [ - "Add Folder to Workspace...", - "&&Add", - "Add Folder to Workspace", - "Select workspace folder" + "Duplicate As Workspace in New Window" ], "vs/workbench/browser/actions/quickAccessActions": [ - "Go to File...", "Quick Open", + "Go to File...", "Navigate Next in Quick Open", "Navigate Previous in Quick Open", "Select Next in Quick Open", @@ -18281,14 +19881,25 @@ "The debug callstack view context menu", "The debug variables view context menu", "The debug toolbar menu", + "The notebook variables view context menu", "The home indicator context menu (web only)", "'Copy as' submenu in the top level Edit menu", "The Source Control title menu", "The Source Control menu", + "The Source Control title menu", "The Source Control resource state context menu", "The Source Control resource folder context menu", "The Source Control resource group context menu", "The Source Control inline change menu", + "The Source Control input box menu", + "The Source Control incoming changes menu", + "The Source Control incoming changes context menu", + "The Source Control outgoing changes menu", + "The Source Control outgoing changes context menu", + "The Source Control all incoming changes context menu", + "The Source Control incoming changes history item context menu", + "The Source Control all outgoing changes context menu", + "The Source Control outgoing changes history item context menu", "The remote indicator menu in the status bar", "The terminal context menu", "The terminal tabs context menu", @@ -18302,14 +19913,17 @@ "The contributed comment title menu", "The contributed comment context menu, rendered as buttons below the comment editor", "The contributed comment context menu, rendered as a right click menu on the an individual comment in the comment thread's peek view.", + "The contributed comment thread context menu in the comments view", "The contributed notebook toolbar menu", "The contributed notebook kernel sources menu", "The contributed notebook cell title menu", "The contributed notebook cell execution menu", "The contributed interactive toolbar menu", "The contributed interactive cell title menu", + "The contributed issue reporter menu", "The contributed test item menu", "The menu for a gutter decoration for a test item", + "The menu for an item in the Test Results view or peek.", "A prominent button overlaying editor content where the message is displayed", "Context menu for the message in the results tree", "The extension context menu", @@ -18322,9 +19936,13 @@ "The webview context menu", "Share submenu shown in the top level File menu.", "The actions shown when hovering on an inline completion", + "The actions shown when hovering on an inline edit", "The prominent button in an editor, overlays its content", "The contributed editor line number context menu", "The result toolbar of the merge editor", + "The resource toolbar in the multi diff editor", + "The gutter toolbar in the diff editor", + "The gutter toolbar in the diff editor", "property `{0}` is mandatory and must be of type `string`", "property `{0}` can be omitted or must be of type `string`", "property `{0}` can be omitted or must be of type `string`", @@ -18378,7 +19996,12 @@ "Menu item references the same command as default and alt-command", "Menu item references a submenu for a menu which doesn't have submenu support.", "Menu item references a submenu `{0}` which is not defined in the 'submenus' section.", - "The `{0}` submenu was already contributed to the `{1}` menu." + "The `{0}` submenu was already contributed to the `{1}` menu.", + "ID", + "Title", + "Keyboard Shortcuts", + "Menu Contexts", + "Commands" ], "vs/workbench/api/common/configurationExtensionPoint": [ "A title for the current category of settings. This label will be rendered in the Settings editor as a subheading. If the title is the same as the extension display name, then the category will be grouped under the main extension heading.", @@ -18422,10 +20045,11 @@ "Workspace extensions", "The remote server where the workspace is located.", "A transient workspace will disappear when restarting or reloading.", - "Unknown workspace configuration property" - ], - "vs/workbench/browser/parts/editor/editorParts": [ - "Window {0}" + "Unknown workspace configuration property", + "ID", + "Description", + "Default", + "Settings" ], "vs/workbench/api/browser/viewsExtensionPoint": [ "Unique id used to identify the container in which views can be contributed using 'views' contribution point", @@ -18447,6 +20071,7 @@ "The view will not be shown in the view container, but will be discoverable through the views menu and other view entry points and can be un-hidden by the user.", "The view will show in the view container, but will be collapsed.", "The initial size of the view. The size will behave like the css 'flex' property, and will set the initial size when the view is first shown. In the side bar, this is the height of the view. This value is only respected when the same extension owns both the view and the view container.", + "When the accessibility help dialog is invoked in this view, this content will be presented to the user as a markdown string. Keybindings will be resolved when provided in the format of . If there is no keybinding, that will be indicated with a link to configure one.", "Identifier of the view. This should be unique across all views. It is recommended to include your extension id as part of the view id. Use this to register a data provider through `vscode.window.registerTreeDataProviderForView` API. Also to trigger activating your extension by registering `onView:${id}` event to `activationEvents`.", "The human-readable name of the view. Will be shown", "Condition which must be true to show this view", @@ -18476,7 +20101,15 @@ "property `{0}` can be omitted or must be of type `string`", "property `{0}` can be omitted or must be of type `string`", "property `{0}` can be omitted or must be of type `string`", - "property `{0}` can be omitted or must be one of {1}" + "property `{0}` can be omitted or must be one of {1}", + "ID", + "Title", + "Where", + "ID", + "Name", + "Where", + "View Containers", + "Views" ], "vs/workbench/browser/parts/editor/editor.contribution": [ "Text Editor", @@ -18489,22 +20122,34 @@ "Show All Opened Editors By Appearance", "Type the name of an editor to open it.", "Show All Opened Editors By Most Recently Used", + "Lock Group", "Unlock Group", "Close Group", "Split Up", "Split Down", "Split Left", "Split Right", + "New Window", "Lock Group", "Close", "Split Up", "Split Down", "Split Left", "Split Right", + "Move into New Window", + "Copy into New Window", "Tab Bar", "Multiple Tabs", "Single Tab", - "Hide", + "Hidden", + "Tab Bar", + "Multiple Tabs", + "Single Tab", + "Hidden", + "Editor Actions Position", + "Tab Bar", + "Title Bar", + "Hidden", "Close", "Close Others", "Close to the Right", @@ -18520,6 +20165,8 @@ "Split Right", "Split in Group", "Join in Group", + "Move into New Window", + "Copy into New Window", "Inline View", "Show Opened Editors", "Close All", @@ -18541,55 +20188,34 @@ "Close", "Unpin", "Close", + "Lock Group", "Unlock Group", "Icon for the previous change action in the diff editor.", - "Icon for the next change action in the diff editor.", - "Icon for the toggle whitespace action in the diff editor.", "Previous Change", + "Icon for the next change action in the diff editor.", "Next Change", + "Swap Left and Right Side", + "Icon for the toggle whitespace action in the diff editor.", "Show Leading/Trailing Whitespace Differences", - "Keep Editor", - "Pin Editor", - "Unpin Editor", - "Close Editor", - "Close Pinned Editor", - "Close All Editors in Group", - "Close Saved Editors in Group", - "Close Other Editors in Group", - "Close Editors to the Right in Group", - "Close Editor Group", - "Reopen Editor With...", "&&Reopen Closed Editor", - "&&Clear Recently Opened", + "&&Clear Recently Opened...", "Share", "Editor &&Layout", - "Split Up", "Split &&Up", - "Split Down", "Split &&Down", - "Split Left", "Split &&Left", - "Split Right", "Split &&Right", - "Split in Group", "Split in &&Group", - "Join in Group", "Join in &&Group", - "Single", + "&&Move Editor into New Window", + "&&Copy Editor into New Window", "&&Single", - "Two Columns", "&&Two Columns", - "Three Columns", "T&&hree Columns", - "Two Rows", "T&&wo Rows", - "Three Rows", "Three &&Rows", - "Grid (2x2)", "&&Grid (2x2)", - "Two Rows Right", "Two R&&ows Right", - "Two Columns Bottom", "Two &&Columns Bottom", "&&Last Edit Location", "&&First Side in Editor", @@ -18614,23 +20240,44 @@ "Group &&Right", "Group &&Above", "Group &&Below", - "Switch &&Group" + "Switch &&Group", + "Keep Editor", + "Pin Editor", + "Unpin Editor", + "Close Editor", + "Close Pinned Editor", + "Close All Editors in Group", + "Close Saved Editors in Group", + "Close Other Editors in Group", + "Close Editors to the Right in Group", + "Close Editor Group", + "Reopen Editor With...", + "Split Up", + "Split Down", + "Split Left", + "Split Right", + "Split in Group", + "Join in Group", + "Move Editor into New Window", + "Copy Editor into New Window", + "Single", + "Two Columns", + "Three Columns", + "Two Rows", + "Three Rows", + "Grid (2x2)", + "Two Rows Right", + "Two Columns Bottom" ], "vs/workbench/browser/parts/banner/bannerPart": [ "Focus Banner" ], + "vs/workbench/browser/parts/editor/editorParts": [ + "Window {0}" + ], "vs/workbench/browser/parts/statusbar/statusbarPart": [ "Hide Status Bar" ], - "vs/workbench/browser/parts/views/viewsService": [ - "Text Editor", - "Show {0}", - "Toggle {0}", - "Show {0}", - "Toggle {0}", - "Focus on {0} View", - "Reset Location" - ], "vs/platform/undoRedo/common/undoRedoService": [ "The following files have been closed and modified on disk: {0}.", "The following files have been modified in an incompatible way: {0}.", @@ -18655,19 +20302,15 @@ ], "vs/workbench/services/extensions/browser/extensionUrlHandler": [ "Allow '{0}' extension to open this URI?", - "Don't ask again for this extension.", + "Do not ask me again for this extension", "&&Open", - "Would you like to install '{0}' extension from '{1}' to open this URI?", - "'{0}' extension wants to open a URI:", - "&&Install and Open", - "Installing Extension '{0}'...", - "Extension '{0}' is disabled. Would you like to enable the extension and open the URL?", - "&&Enable and Open", + "This extension wants to open a URI:", + "Open URI", "Extension '{0}' is not loaded. Would you like to reload the window to load the extension and open the URL?", "&&Reload Window and Open", + "There are currently no authorized extension URIs.", "Manage Authorized Extension URIs...", - "Extensions", - "There are currently no authorized extension URIs." + "Extensions" ], "vs/workbench/services/keybinding/common/keybindingEditing": [ "Unable to write because the keybindings configuration file has unsaved changes. Please save it first and then try again.", @@ -18759,6 +20402,12 @@ "A icon to use as file icon, if no icon theme provides one for the language.", "Icon path when a light theme is used", "Icon path when a dark theme is used", + "ID", + "Name", + "File Extensions", + "Grammar", + "Snippets", + "Programming Languages", "Invalid `contributes.{0}`. Expected an array.", "Empty value for `contributes.{0}`", "property `{0}` is mandatory and must be of type `string`", @@ -18809,20 +20458,16 @@ "Workspace Folder", "Workspace" ], + "vs/workbench/services/extensionManagement/common/extensionFeaturesManagemetService": [ + "Access '{0}' Feature", + "'{0}' extension would like to access the '{1}' feature.", + "Allow", + "Don't Allow" + ], "vs/workbench/services/notification/common/notificationService": [ "Don't Show Again", "Don't Show Again" ], - "vs/workbench/services/userDataProfile/browser/userDataProfileManagement": [ - "The current profile has been updated. Please reload to switch back to the updated profile", - "The current profile has been removed. Please reload to switch back to default profile", - "The current profile has been removed. Please reload to switch back to default profile", - "Cannot rename the default profile", - "Cannot delete the default profile", - "Switching to a profile.", - "Switching a profile requires reloading VS Code.", - "&&Reload" - ], "vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService": [ "Error while importing profile: {0}", "{0}: Resolving profile content...", @@ -18904,12 +20549,22 @@ "Export Profile", "Profile name must be provided." ], - "vs/workbench/services/remote/common/remoteExplorerService": [ - "The ID of a Get Started walkthrough to open.", - "Contributes help information for Remote", - "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension", - "The url, or a command that returns the url, to your project's documentation page", - "The url, or a command that returns the url, to your project's feedback reporter", + "vs/workbench/services/userDataProfile/browser/userDataProfileManagement": [ + "The current profile has been updated. Please reload to switch back to the updated profile", + "The current profile has been removed. Please reload to switch back to default profile", + "The current profile has been removed. Please reload to switch back to default profile", + "Cannot rename the default profile", + "Cannot delete the default profile", + "Switching to a profile.", + "Switching a profile requires reloading VS Code.", + "&&Reload" + ], + "vs/workbench/services/remote/common/remoteExplorerService": [ + "The ID of a Get Started walkthrough to open.", + "Contributes help information for Remote", + "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension", + "The url, or a command that returns the url, to your project's documentation page", + "The url, or a command that returns the url, to your project's feedback reporter", "Use {0} instead", "The url, or a command that returns the url, to your project's issue reporter", "The url, or a command that returns the url, to your project's issues list" @@ -18925,6 +20580,15 @@ "Hide '{0}'", "Reset Location" ], + "vs/workbench/services/views/browser/viewsService": [ + "Text Editor", + "Show {0}", + "Toggle {0}", + "Show {0}", + "Toggle {0}", + "Focus on {0} View", + "Reset Location" + ], "vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService": [ "Settings sync cannot be turned on because there are no authentication providers available.", "No account available", @@ -18953,14 +20617,11 @@ "Sign in with {0}" ], "vs/workbench/services/authentication/browser/authenticationService": [ - "The id of the authentication provider.", - "The human readable name of the authentication provider.", - "Contributes authentication", - "No accounts requested yet...", "An authentication contribution must specify an id.", "An authentication contribution must specify a label.", - "This authentication id '{0}' has already been registered", - "Loading...", + "This authentication id '{0}' has already been registered" + ], + "vs/workbench/services/authentication/browser/authenticationExtensionsService": [ "Sign in requested", "The extension '{0}' wants to access the {1} account '{2}'.", "&&Allow", @@ -19004,22 +20665,29 @@ "**{0}** for your current keyboard layout (**{1}** for US standard).", "**{0}** for your current keyboard layout." ], - "vs/workbench/contrib/performance/browser/performance.contribution": [ - "Startup Performance", - "Print Service Cycles", - "Print Service Traces", - "Print Emitter Profiles" - ], "vs/workbench/contrib/preferences/browser/preferences.contribution": [ "Settings Editor 2", "Keybindings Editor", + "&&Settings", + "Open Folder Settings", + "&&Online Services Settings", + "&&Telemetry Settings", + "Focus settings file", + "Focus settings file", + "Focus settings list", + "Focus Setting Control", + "Keyboard Shortcuts", + "Keyboard Shortcuts", + "Clear Search Results", + "Clear Keyboard Shortcuts Search History", + "&&Preferences", "Open Settings (UI)", "Open User Settings (JSON)", "Open Application Settings (JSON)", "Preferences", "Settings", - "&&Settings", "Open Settings (UI)", + "Opens the JSON file containing the current user profile settings", "Open User Settings", "Open Default Settings (JSON)", "Open Workspace Settings", @@ -19027,35 +20695,29 @@ "Open Workspace Settings (JSON)", "Open Folder Settings", "Open Folder Settings (JSON)", - "Open Folder Settings", - "&&Online Services Settings", "Show untrusted workspace settings", - "&&Telemetry Settings", "Open Remote Settings ({0})", "Open Remote Settings (JSON) ({0})", "Focus Settings Search", "Clear Settings Search Results", - "Focus settings file", - "Focus settings file", - "Focus settings list", "Focus Settings Table of Contents", - "Focus Setting Control", "Show Setting Context Menu", "Move Focus Up One Level", "Preferences", "Open Keyboard Shortcuts", - "Keyboard Shortcuts", - "Keyboard Shortcuts", "Open Default Keyboard Shortcuts (JSON)", "Open Keyboard Shortcuts (JSON)", "Show System Keybindings", "Show Extension Keybindings", "Show User Keybindings", - "Clear Search Results", - "Clear Keyboard Shortcuts Search History", "Define Keybinding", - "Open Settings (JSON)", - "&&Preferences" + "Open Settings (JSON)" + ], + "vs/workbench/contrib/performance/browser/performance.contribution": [ + "Startup Performance", + "Print Service Cycles", + "Print Service Traces", + "Print Emitter Profiles" ], "vs/workbench/contrib/chat/browser/chat.contribution": [ "Chat", @@ -19064,9 +20726,11 @@ "Controls the font weight in chat codeblocks.", "Controls whether lines should wrap in chat codeblocks.", "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size.", + "Controls whether a checkbox is shown to allow the user to determine which implicit context is included with a chat participant's prompt.", "Chat", "Chat", - "Clear the session" + "Start a new chat", + "Choose a file in the workspace" ], "vs/workbench/contrib/notebook/browser/notebook.contribution": [ "Settings for code editors used in notebooks. This can be used to customize most editor.* settings.", @@ -19091,6 +20755,9 @@ "The insert actions don't appear anywhere.", "Control whether to render a global toolbar inside the notebook editor.", "Experimental. Control whether to render notebook Sticky Scroll headers in the notebook editor.", + "Control whether nested sticky lines appear to stack flat or indented.", + "Nested sticky lines appear flat.", + "Nested sticky lines appear indented.", "Control whether outputs action should be rendered in the output toolbar.", "Controls when the Markdown header folding arrow is shown.", "The folding controls are always visible.", @@ -19100,6 +20767,8 @@ "Control whether extra actions are shown in a dropdown next to the run button.", "Control whether the actions on the notebook toolbar should render label or not.", "Controls how many lines of text are displayed in a text output. If {0} is enabled, this setting is used to determine the scroll height of the output.", + "Control whether to disable filepath links in the output of notebook cells.", + "Control whether to render error output in a minimal style.", "Controls the font size in pixels of rendered markup in notebooks. When set to {0}, 120% of {1} is used.", "Controls whether code cells in the interactive window are collapsed by default.", "Line height of the output text within notebook cells.\n - When set to 0, editor line height is used.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values.", @@ -19108,6 +20777,7 @@ "Initially render notebook outputs in a scrollable region when longer than the limit.", "Controls whether the lines in output should wrap.", "Format a notebook on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down.", + "When enabled, insert a final new line into the end of code cells when saving a notebook.", "Run a series of Code Actions for a notebook on save. Code Actions must be specified, the file must not be saved after delay, and the editor must not be shutting down. Example: `\"notebook.source.organizeImports\": \"explicit\"`", "Triggers Code Actions only when explicitly saved.", "Never triggers Code Actions on save.", @@ -19121,40 +20791,28 @@ "Scroll to fully reveal the next cell.", "Scroll to reveal the first line of the next cell.", "Do not scroll.", - "Experimental. Keep the focused cell steady while surrounding cells change size.", - "Anchor the viewport to the focused cell depending on context unless {0} is set to {1}.", - "Always anchor the viewport to the focused cell.", - "The focused cell may shift around as cells resize." - ], - "vs/workbench/contrib/testing/browser/testing.contribution": [ - "Testing", - "T&&esting", - "No tests have been found in this workspace yet.", - "Install Additional Test Extensions...", - "Test Results", - "Test Results", - "Test Explorer" - ], - "vs/workbench/contrib/logs/common/logs.contribution": [ - "Set Default Log Level", - "{0} (Remote)", - "Show Window Log" + "Enable experimental floating chat widget in notebooks.", + "Enable experimental generate action to create code cell with inline chat enabled.", + "Enable the experimental notebook variables view within the debug panel.", + "Show available diagnostics for cell failures.", + "The limit of notebook output size in kilobytes (KB) where notebook files will no longer be backed up for hot reload. Use 0 for unlimited." ], "vs/workbench/contrib/interactive/browser/interactive.contribution": [ - "Interactive Window", "Open Interactive Window", + "Scroll to Top", + "Scroll to Bottom", + "The border color for the current interactive code cell when the editor has focus.", + "The border color for the current interactive code cell when the editor does not have focus.", + "Automatically scroll the interactive window to show the output of the last statement executed. If this value is false, the window will only scroll if the last cell was already the one scrolled to.", + "Prompt to save the interactive window when it is closed. Only new interactive windows will be affected by this setting change.", + "Interactive Window", "Open Interactive Window", "Execute Code", "Clear the interactive window input editor contents", "Previous value in history", "Next value in history", - "Scroll to Top", - "Scroll to Bottom", "Focus Input Editor", - "Focus History", - "The border color for the current interactive code cell when the editor has focus.", - "The border color for the current interactive code cell when the editor does not have focus.", - "Automatically scroll the interactive window to show the output of the last statement executed. If this value is false, the window will only scroll if the last cell was already the one scrolled to." + "Focus History" ], "vs/workbench/contrib/quickaccess/browser/quickAccess.contribution": [ "Type '{0}' to get help on the actions you can take from here.", @@ -19171,18 +20829,43 @@ "Command Palette...", "Command Palette..." ], + "vs/workbench/contrib/testing/browser/testing.contribution": [ + "T&&esting", + "No tests have been found in this workspace yet.", + "Install Additional Test Extensions...", + "Testing", + "Test Results", + "Test Results", + "Test Explorer", + "Test Coverage" + ], + "vs/workbench/contrib/files/browser/explorerViewlet": [ + "View icon of the explorer view.", + "View icon of the open editors view.", + "&&Explorer", + "Open Folder", + "add a folder", + "Open Recent", + "You have not yet added a folder to the workspace.\n{0}", + "You have not yet opened a folder.\n{0}\n{1}", + "Connected to remote.\n{0}", + "You have not yet opened a folder.\n{0}\nOpening a folder will close all currently open editors. To keep them open, {1} instead.", + "You have not yet opened a folder.\n{0}", + "Folders", + "Explorer", + "Explorer" + ], + "vs/workbench/contrib/logs/common/logs.contribution": [ + "{0} (Remote)", + "Set Default Log Level", + "Show Window Log" + ], "vs/workbench/contrib/files/browser/fileActions.contribution": [ "Copy Path", "Copy Relative Path", "Reveal in Explorer View", "Use your changes and overwrite file contents", "Discard your changes and revert to file contents", - "Copy Path of Active File", - "Copy Relative Path of Active File", - "Save All in Group", - "Save All Files", - "Revert File", - "Compare Active File with Saved", "Open to the Side", "Reopen Editor With...", "Revert File", @@ -19209,23 +20892,34 @@ "Re&&vert File", "&&Close Editor", "Go to &&File...", + "Copy Path of Active File", + "Copy Relative Path of Active File", + "Save All in Group", + "Save All Files", + "Revert File", + "Compare Active File with Saved", + "Opens a new diff editor to compare the active file with the version on disk.", "Create a new folder or directory" ], - "vs/workbench/contrib/files/browser/explorerViewlet": [ - "View icon of the explorer view.", - "View icon of the open editors view.", - "&&Explorer", - "Open Folder", - "add a folder", - "Open Recent", - "You have not yet added a folder to the workspace.\n{0}", - "You have not yet opened a folder.\n{0}\n{1}", - "Connected to remote.\n{0}", - "You have not yet opened a folder.\n{0}\nOpening a folder will close all currently open editors. To keep them open, {1} instead.", - "You have not yet opened a folder.\n{0}", - "Folders", - "Explorer", - "Explorer" + "vs/workbench/contrib/bulkEdit/browser/bulkEditService": [ + "Made no edits", + "Made {0} text edits in {1} files", + "Made {0} text edits in one file", + "Made {0} text edits in {1} files, also created or deleted {2} files", + "Workspace Edit", + "Workspace Edit", + "Made no edits", + "Are you sure you want to close the window?", + "&&Close Window", + "Are you sure you want to change the workspace?", + "Change &&Workspace", + "Are you sure you want to reload the window?", + "&&Reload Window", + "Are you sure you want to quit?", + "&&Quit", + "'{0}' is in progress.", + "File operation", + "Controls if files that were part of a refactoring are saved automatically" ], "vs/workbench/contrib/files/browser/files.contribution": [ "Text File Editor", @@ -19252,6 +20946,7 @@ "The default end of line character.", "Moves files/folders to the OS trash (recycle bin on Windows) when deleting. Disabling this will delete files/folders permanently.", "When enabled, will trim trailing whitespace when saving a file.", + "When enabled, trailing whitespace will be removed from multiline strings and regexes will be removed on save or when executing 'editor.action.trimTrailingWhitespace'. This can cause whitespace to not be trimmed from lines when there isn't up-to-date token information.", "When enabled, insert a final new line at the end of the file when saving it.", "When enabled, will trim all new lines after the final new line at the end of the file when saving it.", "An editor with changes is never automatically saved.", @@ -19260,6 +20955,8 @@ "An editor with changes is automatically saved when the window loses focus.", "Controls [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors that have unsaved changes.", "Controls the delay in milliseconds after which an editor with unsaved changes is saved automatically. Only applies when `#files.autoSave#` is set to `{0}`.", + "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that are inside the opened workspace. Only applies when `#files.autoSave#` is enabled.", + "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that have no errors reported in them at the time the auto save is triggered. Only applies when `#files.autoSave#` is enabled.", "Configure paths or [glob patterns](https://aka.ms/vscode-glob-patterns) to exclude from file watching. Paths can either be relative to the watched folder or absolute. Glob patterns are matched relative from the watched folder. When you experience the file watcher process consuming a lot of CPU, make sure to exclude large folders that are of less interest (such as build output folders).", "Configure extra paths to watch for changes inside the workspace. By default, all workspace folders will be watched recursively, except for folders that are symbolic links. You can explicitly add absolute or relative paths to support watching folders that are symbolic links. Relative paths will be resolved to an absolute path using the currently opened workspace.", "The default language identifier that is assigned to new files. If configured to `${activeEditorLanguage}`, will use the language identifier of the currently active text editor if any.", @@ -19295,6 +20992,7 @@ "Additional check on the siblings of a matching file. Use $(basename) as variable for the matching file name.", "Controls whether the Explorer should allow to move files and folders via drag and drop. This setting only effects drag and drop from inside the Explorer.", "Controls whether the Explorer should ask for confirmation to move files and folders via drag and drop.", + "Controls whether the Explorer should ask for confirmation when pasting native files and folders.", "Controls whether the Explorer should ask for confirmation when deleting a file via the trash.", "Controls whether the Explorer should support undoing file and folder operations.", "Controls whether the Explorer should ask for confirmation when undoing.", @@ -19331,26 +21029,6 @@ "Controls nesting of files in the Explorer. {0} must be set for this to take effect. Each __Item__ represents a parent pattern and may contain a single `*` character that matches any string. Each __Value__ represents a comma separated list of the child patterns that should be shown nested under a given parent. Child patterns may contain several special tokens:\n- `${capture}`: Matches the resolved value of the `*` from the parent pattern\n- `${basename}`: Matches the parent file's basename, the `file` in `file.ts`\n- `${extname}`: Matches the parent file's extension, the `ts` in `file.ts`\n- `${dirname}`: Matches the parent file's directory name, the `src` in `src/file.ts`\n- `*`: Matches any string, may only be used once per child pattern", "Each key pattern may contain a single `*` character which will match any string." ], - "vs/workbench/contrib/bulkEdit/browser/bulkEditService": [ - "Made no edits", - "Made {0} text edits in {1} files", - "Made {0} text edits in one file", - "Made {0} text edits in {1} files, also created or deleted {2} files", - "Workspace Edit", - "Workspace Edit", - "Made no edits", - "Are you sure you want to close the window?", - "&&Close Window", - "Are you sure you want to change the workspace?", - "Change &&Workspace", - "Are you sure you want to reload the window?", - "&&Reload Window", - "Are you sure you want to quit?", - "&&Quit", - "'{0}' is in progress.", - "File operation", - "Controls if files that were part of a refactoring are saved automatically" - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution": [ "Another refactoring is being previewed.", "Press 'Continue' to discard the previous refactoring and continue with the current refactoring.", @@ -19377,10 +21055,10 @@ "Go to File", "Type the name of a symbol to open.", "Go to Symbol in Workspace", - "Search for text in your workspace files (experimental).", - "Search for Text (Experimental)", + "Search for text in your workspace files.", + "Search for Text", "Search", - "Configure [glob patterns](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) for excluding files and folders in fulltext searches and quick open. Inherits all glob patterns from the `#files.exclude#` setting.", + "Configure [glob patterns](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options) for excluding files and folders in fulltext searches and file search in quick open. To exclude files from the recently opened list in quick open, patterns must be absolute (for example `**/node_modules/**`). Inherits all glob patterns from the `#files.exclude#` setting.", "The glob pattern to match file paths against. Set to true or false to enable or disable the pattern.", "Additional check on the siblings of a matching file. Use \\$(basename) as variable for the matching file name.", "Controls where new `Search: Find in Files` and `Find in Folder` operations occur: either in the search view, or in a search editor.", @@ -19422,6 +21100,9 @@ "Double-clicking opens the result in the active editor group.", "Double-clicking opens the result in the editor group to the side, creating one if it does not yet exist.", "Configure effect of double-clicking a result in a search editor.", + "Single-clicking does nothing.", + "Single-clicking opens a Peek Definition window.", + "Configure effect of single-clicking a result in a search editor.", "When enabled, new Search Editors will reuse the includes, excludes, and flags of the previously opened Search Editor.", "The default number of surrounding context lines to use when creating new Search Editors. If using `#search.searchEditor.reusePriorSearchConfiguration#`, this can be set to `null` (empty) to use the prior Search Editor's configuration.", "Results are sorted by folder and file names, in alphabetical order.", @@ -19436,14 +21117,72 @@ "Shows search results as a tree.", "Shows search results as a list.", "Controls the default search result view mode.", - "Show notebook editor rich content results for closed notebooks. Please refresh your search results after changing this setting.", "Controls whether the last typed input to Quick Search should be restored when opening it the next time.", + "Show notebook editor rich content results for closed notebooks. Please refresh your search results after changing this setting.", "Search", "Search" ], + "vs/workbench/contrib/search/browser/searchView": [ + "Search was canceled before any results could be found - ", + "Toggle Search Details", + "files to include", + "e.g. *.ts, src/**/include", + "files to exclude", + "e.g. *.ts, src/**/exclude", + "Replace All", + "&&Replace", + "Replaced {0} occurrence across {1} file with '{2}'.", + "Replaced {0} occurrence across {1} file.", + "Replaced {0} occurrence across {1} files with '{2}'.", + "Replaced {0} occurrence across {1} files.", + "Replaced {0} occurrences across {1} file with '{2}'.", + "Replaced {0} occurrences across {1} file.", + "Replaced {0} occurrences across {1} files with '{2}'.", + "Replaced {0} occurrences across {1} files.", + "Replace {0} occurrence across {1} file with '{2}'?", + "Replace {0} occurrence across {1} file?", + "Replace {0} occurrence across {1} files with '{2}'?", + "Replace {0} occurrence across {1} files?", + "Replace {0} occurrences across {1} file with '{2}'?", + "Replace {0} occurrences across {1} file?", + "Replace {0} occurrences across {1} files with '{2}'?", + "Replace {0} occurrences across {1} files?", + "Empty Search", + "Search path not found: {0}", + "No results found in open editors matching '{0}' excluding '{1}' - ", + "No results found in open editors matching '{0}' - ", + "No results found in open editors excluding '{0}' - ", + "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - ", + "No results found in '{0}' excluding '{1}' - ", + "No results found in '{0}' - ", + "No results found excluding '{0}' - ", + "No results found. Review your settings for configured exclusions and check your gitignore files - ", + "Search again", + "Search again in all files", + "Open Settings", + "Learn More", + "Search returned {0} results in {1} files", + "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.", + " - Search: {0}", + "exclude settings and ignore files are disabled", + "enable", + "Use Exclude Settings and Ignore Files", + "searching only in open files", + "disable", + "Search in entire workspace", + "Copy current search results to an editor", + "Open in editor", + "{0} result in {1} file", + "{0} result in {1} files", + "{0} results in {1} file", + "{0} results in {1} files", + "You have not opened or specified a folder. Only open files are currently searched - ", + "Open Folder" + ], "vs/workbench/contrib/searchEditor/browser/searchEditor.contribution": [ "Search Editor", "Search Editor", + "Open New Search Editor", "Search Editor", "Delete File Results", "New Search Editor", @@ -19460,8 +21199,7 @@ "Toggle Context Lines", "Increase Context Lines", "Decrease Context Lines", - "Select All Matches", - "Open New Search Editor" + "Select All Matches" ], "vs/workbench/contrib/sash/browser/sash.contribution": [ "Controls the feedback area size in pixels of the dragging area in between views/editors. Set it to a larger value if you feel it's hard to resize views using the mouse.", @@ -19469,7 +21207,6 @@ ], "vs/workbench/contrib/scm/browser/scm.contribution": [ "View icon of the Source Control view.", - "Source Control", "No source control providers registered.", "None of the registered source control providers work in Restricted Mode.", "Manage Workspace Trust", @@ -19503,7 +21240,7 @@ "Hide Source Control Provider count badges.", "Only show count badge for Source Control Provider when non-zero.", "Show Source Control Provider count badges.", - "Controls the count badges on Source Control Provider headers. These headers appear in the \"Source Control\", and \"Source Control Sync\" views when there is more than one provider or when the {0} setting is enabled, as well as in the \"Source Control Repositories\" view.", + "Controls the count badges on Source Control Provider headers. These headers appear in the Source Control view when there is more than one provider or when the {0} setting is enabled, and in the Source Control Repositories view.", "Show the repository changes as a tree.", "Show the repository changes as a list.", "Controls the default Source Control repository view mode.", @@ -19514,6 +21251,8 @@ "Controls whether the Source Control view should automatically reveal and select files when opening them.", "Controls the font for the input message. Use `default` for the workbench user interface font family, `editor` for the `#editor.fontFamily#`'s value, or a custom font family.", "Controls the font size for the input message in pixels.", + "Controls the maximum number of lines that the input will auto-grow to.", + "Controls the minimum number of lines that the input will auto-grow from.", "Controls whether repositories should always be visible in the Source Control view.", "Repositories in the Source Control Repositories view are sorted by discovery time. Repositories in the Source Control view are sorted in the order that they were selected.", "Repositories in the Source Control Repositories and Source Control views are sorted by repository name.", @@ -19521,76 +21260,28 @@ "Controls the sort order of the repositories in the source control repositories view.", "Controls how many repositories are visible in the Source Control Repositories section. Set to 0, to be able to manually resize the view.", "Controls whether an action button can be shown in the Source Control view.", - "Controls whether the Source Control Sync view is shown.", + "Controls whether an action button can be shown in the Source Control input.", + "Always show incoming changes in the Source Control view.", + "Never show incoming changes in the Source Control view.", + "Only show incoming changes in the Source Control view when any exist.", + "Controls whether incoming changes are shown in the Source Control view.", + "Always show outgoing changes in the Source Control view.", + "Never show outgoing changes in the Source Control view.", + "Only show outgoing changes in the Source Control view when any exist.", + "Controls whether outgoing changes are shown in the Source Control view.", + "Controls whether the All Changes entry is shown for incoming/outgoing changes in the Source Control view.", + "Controls whether to store editor working sets when switching between source control history item groups.", + "Use an empty working set when switching to a source control history item group that does not have a working set.", + "Use the current working set when switching to a source control history item group that does not have a working set.", + "Controls the default working set to use when switching to a source control history item group that does not have a working set.", "Source Control: Accept Input", "Source Control: View Next Commit", "Source Control: View Previous Commit", "Open in External Terminal", "Open in Integrated Terminal", "Source Control", - "Source Control Repositories", - "Source Control Sync" - ], - "vs/workbench/contrib/search/browser/searchView": [ - "Search was canceled before any results could be found - ", - "Toggle Search Details", - "files to include", - "e.g. *.ts, src/**/include", - "files to exclude", - "e.g. *.ts, src/**/exclude", - "Replace All", - "&&Replace", - "Replaced {0} occurrence across {1} file with '{2}'.", - "Replaced {0} occurrence across {1} file.", - "Replaced {0} occurrence across {1} files with '{2}'.", - "Replaced {0} occurrence across {1} files.", - "Replaced {0} occurrences across {1} file with '{2}'.", - "Replaced {0} occurrences across {1} file.", - "Replaced {0} occurrences across {1} files with '{2}'.", - "Replaced {0} occurrences across {1} files.", - "Replace {0} occurrence across {1} file with '{2}'?", - "Replace {0} occurrence across {1} file?", - "Replace {0} occurrence across {1} files with '{2}'?", - "Replace {0} occurrence across {1} files?", - "Replace {0} occurrences across {1} file with '{2}'?", - "Replace {0} occurrences across {1} file?", - "Replace {0} occurrences across {1} files with '{2}'?", - "Replace {0} occurrences across {1} files?", - "Empty Search", - "Search path not found: {0}", - "No results found in open editors matching '{0}' excluding '{1}' - ", - "No results found in open editors matching '{0}' - ", - "No results found in open editors excluding '{0}' - ", - "No results found in open editors. Review your settings for configured exclusions and check your gitignore files - ", - "No results found in '{0}' excluding '{1}' - ", - "No results found in '{0}' - ", - "No results found excluding '{0}' - ", - "No results found. Review your settings for configured exclusions and check your gitignore files - ", - "Search again", - "Search again in all files", - "Open Settings", - "Learn More", - "Search returned {0} results in {1} files", - "The result set only contains a subset of all matches. Be more specific in your search to narrow down the results.", - " - Search: {0}", - "exclude settings and ignore files are disabled", - "enable", - "Use Exclude Settings and Ignore Files", - "searching only in open files", - "disable", - "Search in entire workspace", - "Copy current search results to an editor", - "Open in editor", - "{0} result in {1} file", - "{0} result in {1} files", - "{0} results in {1} file", - "{0} results in {1} files", - "You have not opened or specified a folder. Only open files are currently searched - ", - "Open Folder" - ], - "vs/workbench/contrib/debug/browser/debugEditorContribution": [ - "Color for the debug inline value text.", - "Color for the debug inline value background." + "Source Control", + "Source Control Repositories" ], "vs/workbench/contrib/debug/browser/debug.contribution": [ "Debug", @@ -19603,9 +21294,10 @@ "Copy Call Stack", "View Binary Data", "Set Value", - "Copy Value", - "Copy as Expression", - "Add to Watch", + "Break on Value Read", + "Break on Value Change", + "Break on Value Access", + "View Binary Data", "Break on Value Read", "Break on Value Change", "Break on Value Access", @@ -19632,7 +21324,13 @@ "Disassembly", "Debug", "Allow setting breakpoints in any file.", + "Controls the action to perform when clicking the editor gutter with the middle mouse button.", + "Add Logpoint.", + "Add Conditional Breakpoint.", + "Add Triggered Breakpoint.", + "Don't perform any action.", "Automatically open the explorer view at the end of a debug session.", + "At the end of a debug session, all the read-only tabs associated with that session will be closed", "Show variable values inline in editor while debugging.", "Always show variable values inline in editor while debugging.", "Never show variable values inline in editor while debugging.", @@ -19684,6 +21382,7 @@ "Set Next Statement", "Inline Breakpoint", "Run or Debug...", + "Run", "Debug Console", "Debug Console", "Run and Debug", @@ -19697,7 +21396,12 @@ "Background color for the highlight of line at the top stack frame position.", "Background color for the highlight of line at focused stack frame position." ], + "vs/workbench/contrib/debug/browser/debugEditorContribution": [ + "Color for the debug inline value text.", + "Color for the debug inline value background." + ], "vs/workbench/contrib/debug/browser/breakpointEditorContribution": [ + "Click to add a breakpoint", "Logpoint", "Breakpoint", "This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.", @@ -19730,6 +21434,7 @@ "Add Breakpoint", "Add Conditional Breakpoint...", "Add Logpoint...", + "Add Triggered Breakpoint...", "Run to Line", "Icon color for breakpoints.", "Icon color for disabled breakpoints.", @@ -19737,85 +21442,54 @@ "Icon color for the current breakpoint stack frame.", "Icon color for all breakpoint stack frames." ], - "vs/workbench/contrib/debug/browser/debugViewlet": [ - "Open &&Configurations", - "Select a workspace folder to create a launch.json file in or add it to the workspace config file", - "Debug Console", - "Start Additional Session" - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ - "Merge Editor", - "Uses the legacy diffing algorithm.", - "Uses the advanced diffing algorithm." - ], - "vs/workbench/contrib/debug/browser/repl": [ - "Filter (e.g. text, !exclude)", - "Showing {0} of {1}", - "Debug Console", - "Please start a debug session to evaluate expressions", - "REPL Accept Input", - "REPL Focus Content to Filter", - "Debug: Console Copy All", - "Select Debug Console", - "Clear Console", - "Collapse All", - "Paste", - "Copy All", - "Copy" - ], "vs/workbench/contrib/markers/browser/markers.contribution": [ "View icon of the markers view.", "&&Problems", "View as Tree", "View as Table", - "Toggle Errors", - "Problems", "Show Errors", - "Toggle Warnings", "Problems", "Show Warnings", - "Toggle Infos", "Problems", "Show Infos", - "Toggle Active File", "Problems", "Show Active File Only", - "Toggle Excluded Files", "Problems", - "Hide Excluded Files", - "Copy", - "Copy Message", - "Copy Message", + "Show Excluded Files", + "Problems", "Focus problems view", "Focus problems filter", - "Show message in multiple lines", "Problems", - "Show message in single line", "Problems", "Clear filters text", "Problems", "Collapse All", "Problems", + "Problems are turned off. Click to open settings.", + "Problems Visibility", "Errors: {0}", "Warnings: {0}", "Infos: {0}", "No Problems", "10K+", - "Total {0} Problems" - ], - "vs/workbench/contrib/commands/common/commands.contribution": [ - "Run Commands", - "Run several commands", - "Commands to run", - "'runCommands' has received an argument with incorrect type. Please, review the argument passed to the command.", - "'runCommands' has not received commands to run. Did you forget to pass commands in the 'runCommands' argument?" + "Total {0} Problems", + "Copy", + "Copy Message", + "Copy Message", + "Show message in multiple lines", + "Show message in single line" ], - "vs/workbench/contrib/url/browser/url.contribution": [ - "Open URL", - "URL to open", - "When enabled, trusted domain prompts will appear when opening links in trusted workspaces." + "vs/workbench/contrib/debug/browser/debugViewlet": [ + "Open &&Configurations", + "Select a workspace folder to create a launch.json file in or add it to the workspace config file", + "Debug Console", + "Start Additional Session", + "Opens the file used to configure how your program is debugged" ], "vs/workbench/contrib/comments/browser/comments.contribution": [ + "Collapse All", + "Expand All", + "Reply", "Comments", "Controls when the comments panel should open.", "This setting is deprecated in favor of `comments.openView`.", @@ -19828,37 +21502,35 @@ "Controls the visibility of the comments bar and comment threads in editors that have commenting ranges and comments. Comments are still accessible via the Comments view and will cause commenting to be toggled on in the same way running the command \"Comments: Toggle Editor Commenting\" toggles comments.", "Controls whether the comments widget scrolls or expands.", "Controls whether the comment thread should collapse when the thread is resolved.", - "The editor contains commentable range(s). Some useful commands include:", - "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled ({0}).", - "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled with the command Toggle Tab Key Moves Focus, which is currently not triggerable via keybinding.", - "Some useful comment commands include:", - "- Dismiss Comment (Escape)", - "- Go to Next Commenting Range ({0})", - "- Go to Next Commenting Range, which is currently not triggerable via keybinding.", - "- Go to Previous Commenting Range ({0})", - "- Go to Previous Commenting Range, which is currently not triggerable via keybinding.", - "- Go to Next Comment Thread ({0})", - "- Go to Next Comment Thread, which is currently not triggerable via keybinding.", - "- Go to Previous Comment Thread ({0})", - "- Go to Previous Comment Thread, which is currently not triggerable via keybinding.", - "- Add Comment ({0})", - "- Add Comment on Current Selection, which is currently not triggerable via keybinding.", - "- Submit Comment ({0})", - "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding." + "{0} Unresolved Comments" ], - "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ - "webview editor" + "vs/workbench/contrib/commands/common/commands.contribution": [ + "Run several commands", + "Commands to run", + "'runCommands' has received an argument with incorrect type. Please, review the argument passed to the command.", + "'runCommands' has not received commands to run. Did you forget to pass commands in the 'runCommands' argument?", + "Run Commands" + ], + "vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution": [ + "Merge Editor", + "Uses the legacy diffing algorithm.", + "Uses the advanced diffing algorithm." + ], + "vs/workbench/contrib/url/browser/url.contribution": [ + "URL to open", + "When enabled, trusted domain prompts will appear when opening links in trusted workspaces.", + "Open URL" ], "vs/workbench/contrib/webview/browser/webview.contribution": [ "Cut", "Copy", "Paste" ], + "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution": [ + "webview editor" + ], "vs/workbench/contrib/extensions/browser/extensionsViewlet": [ - "Remote", "Installed", - "Install Local Extensions in '{0}'...", - "Install Remote Extensions Locally...", "Search Extensions in Marketplace", "1 extension found in the {0} section.", "1 extension found.", @@ -19868,10 +21540,13 @@ "Open User Settings", "{0} requires update", "{0} require update", - "{0} requires reload", - "{0} require reload", + "{0} requires restart", + "{0} require restart", "We have uninstalled '{0}' which was reported to be problematic.", "Reload Now", + "Remote", + "Install Local Extensions in '{0}'...", + "Install Remote Extensions Locally...", "Popular", "Recommended", "Enabled", @@ -19909,6 +21584,8 @@ "Select Output Channel", "Turn Auto Scrolling Off", "Turn Auto Scrolling On", + "Set Log Level...", + "Set As Default", "Extension Logs", "Select Log", "The id of the log file to open, for example `\"window\"`. Currently the best way to get this is to get the ID by checking the `workbench.action.output.show.` commands", @@ -19921,36 +21598,23 @@ "Output", "Clear Output", "Toggle Auto Scrolling", - "Open Log Output File", + "Open Output in Editor", + "Open Output in New Window", "Show Logs...", "Open Log File..." ], - "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ - "Open in Integrated Terminal", - "Open in External Terminal", - "Open in Windows Terminal" - ], - "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ - "A setting has changed that requires a restart to take effect.", - "A setting has changed that requires a reload to take effect.", - "Press the restart button to restart {0} and enable the setting.", - "Press the reload button to reload {0} and enable the setting.", - "&&Restart", - "&&Reload", - "Restarting extension host due to a workspace folder change." - ], "vs/workbench/contrib/extensions/browser/extensions.contribution": [ "Press Enter to manage extensions.", "Manage Extensions", "Extension", - "Extensions", "E&&xtensions", - "Extensions", "All Extensions", "Only Enabled Extensions", + "Only Selected Extensions", "None", "Download and install updates automatically for all extensions except for those updates are ignored.", "Download and install updates automatically only for enabled extensions except for those updates are ignored. Disabled extensions are not updated automatically.", + "Download and install updates automatically only for selected extensions.", "Extensions are not automatically updated.", "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service.", "When enabled, automatically checks extensions for updates. If an extension has an update, it is marked as outdated in the Extensions view. The updates are fetched from a Microsoft online service.", @@ -19971,12 +21635,15 @@ "Defines the untrusted workspace support setting for the extension.", "Defines the version of the extension for which the override should be applied. If not specified, the override will be applied independent of the extension version.", "When enabled, extensions which declare the `onStartupFinished` activation event will be activated after a timeout.", + "When enabled, extensions can be searched for via Quick Access and report issues from there.", "Extension '{0}' not found.", "Install the given extension", "Extension id or VSIX resource uri", "When enabled, VS Code installs only newly added extensions from the extension pack VSIX. This option is considered only while installing a VSIX.", "When enabled, VS Code installs the pre-release version of the extension if available.", "When enabled, VS Code do not sync this extension when Settings Sync is on.", + "Justification for installing the extension. This is a string or an object that can be used to pass any information to the installation handlers. i.e. `{reason: 'This extension wants to open a URI', action: 'Open URI'}` will show a message box with the reason and action upon install.", + "When enabled, the extension will be enabled if it is installed but disabled. If the extension is already enabled, this has no effect.", "Context for the installation. This is a JSON object that can be used to pass any information to the installation handlers. i.e. `{skipWalkthrough: true}` will skip opening the walkthrough upon install.", "Extension '{0}' not found.", "Uninstall the given extension", @@ -19990,55 +21657,33 @@ "Install or Search Extensions", "&&Extensions", "Extensions", - "Focus on Extensions View", - "Install Extensions", - "Keymaps", "Migrate Keyboard Shortcuts from...", - "Language Extensions", - "Check for Extension Updates", "All extensions are up to date.", "Auto Update Extensions", "All Extensions", - "Only Enabled Extensions", + "Enabled Extensions", + "Selected Extensions", "None", - "Update All Extensions", - "Disable Auto Update for All Extensions", - "Enable Auto Update for All Extensions", - "Enable All Extensions", - "Enable All Extensions for this Workspace", - "Disable All Installed Extensions", - "Disable All Installed Extensions for this Workspace", - "Install from VSIX...", "Install from VSIX", "&&Install", "Install Extension VSIX", "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", "Completed installing {0} extension from VSIX.", "Reload Now", - "Install Extension from Location...", "Install Extension from Location", "Install", "Location of the web extension", "Install Extension from Location", "Filter Extensions...", - "Show Featured Extensions", "Featured", - "Show Popular Extensions", "Most Popular", - "Show Recommended Extensions", "Recommended", - "Show Recently Published Extensions", "Recently Published", "Category", - "Show Built-in Extensions", "Built-in", - "Show Extension Updates", "Updates", - "Show Extensions Unsupported By Workspace", "Workspace Unsupported", - "Show Enabled Extensions", "Enabled", - "Show Disabled Extensions", "Disabled", "Sort By", "Install Count", @@ -20046,18 +21691,49 @@ "Name", "Published Date", "Updated Date", - "Clear Extensions Search Results", - "Refresh", "Install Workspace Recommended Extensions", - "Show Pre-Release Version", - "Show Release Version", - "Copy", + "Switch to Pre-Release Version", + "Switch to Release Version", "Name: {0}", "Id: {0}", "Description: {0}", "Version: {0}", "Publisher: {0}", "VS Marketplace Link: {0}", + "Extensions", + "Extensions", + "Extensions", + "Extensions", + "Extensions", + "Extensions", + "Focus on Extensions View", + "Install Extensions", + "Keymaps", + "Language Extensions", + "Check for Extension Updates", + "Update All Extensions", + "Disable Auto Update for All Extensions", + "Enable Auto Update for All Extensions", + "Enable All Extensions", + "Enable All Extensions for this Workspace", + "Disable All Installed Extensions", + "Disable All Installed Extensions for this Workspace", + "Install from VSIX...", + "Install Extension from Location...", + "Show Featured Extensions", + "Show Popular Extensions", + "Show Recommended Extensions", + "Show Recently Published Extensions", + "Show Built-in Extensions", + "Show Extension Updates", + "Show Extensions Unsupported By Workspace", + "Show Enabled Extensions", + "Show Disabled Extensions", + "Clear Extensions Search Results", + "Refresh", + "Show Pre-Release Version", + "Show Release Version", + "Copy", "Copy Extension ID", "Extension Settings", "Extension Keyboard Shortcuts", @@ -20068,14 +21744,23 @@ "Add to Workspace Recommendations", "Remove from Workspace Recommendations", "Add Extension to Workspace Recommendations", - "Extensions", "Add Extension to Workspace Folder Recommendations", - "Extensions", "Add Extension to Workspace Ignored Recommendations", - "Extensions", - "Add Extension to Workspace Folder Ignored Recommendations", - "Extensions", - "Extensions" + "Add Extension to Workspace Folder Ignored Recommendations" + ], + "vs/workbench/contrib/externalTerminal/browser/externalTerminal.contribution": [ + "Open in Integrated Terminal", + "Open in External Terminal", + "Open in Windows Terminal" + ], + "vs/workbench/contrib/relauncher/browser/relauncher.contribution": [ + "A setting has changed that requires a restart to take effect.", + "A setting has changed that requires a reload to take effect.", + "Press the restart button to restart {0} and enable the setting.", + "Press the reload button to reload {0} and enable the setting.", + "&&Restart", + "&&Reload", + "Restarting extension host due to a workspace folder change." ], "vs/workbench/contrib/tasks/browser/task.contribution": [ "Building...", @@ -20089,18 +21774,6 @@ "&&Terminate Task...", "&&Configure Tasks...", "Configure De&&fault Build Task...", - "Open Workspace Tasks", - "Show Task Log", - "Run Task", - "Rerun Last Task", - "Restart Running Task", - "Show Running Tasks", - "Terminate Task", - "Run Build Task", - "Run Test Task", - "Configure Default Build Task", - "Configure Default Test Task", - "Open User Tasks", "User Tasks", "Type the name of a task to run.", "Run Task", @@ -20123,24 +21796,36 @@ "Save all dirty editors before running a task.", "Always saves all editors before running.", "Never saves editors before running.", - "Prompts whether to save editors before running." + "Prompts whether to save editors before running.", + "Enable verbose logging for tasks.", + "Open Workspace Tasks", + "Show Task Log", + "Run Task", + "Rerun Last Task", + "Restart Running Task", + "Show Running Tasks", + "Terminate Task", + "Run Build Task", + "Run Test Task", + "Configure Default Build Task", + "Configure Default Test Task", + "Open User Tasks" ], "vs/workbench/contrib/remote/common/remote.contribution": [ "Workspace does not exist", "Please select another workspace to open.", "&&Open Workspace...", - "Connection: Trigger Reconnect", - "Connection: Pause socket writing", "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine.", "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.", "Remote", "Override the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote. By overriding an extension's default kind using this setting, you specify if that extension should be installed and enabled locally or remotely.", "Restores the ports you forwarded in a workspace.", "When enabled, new running processes are detected and ports that they listen on are automatically forwarded. Disabling this setting will not prevent all ports from being forwarded. Even when disabled, extensions will still be able to cause ports to be forwarded, and opening some URLs will still cause ports to forwarded.", - "Sets the source from which ports are automatically forwarded when {0} is true. On Windows and Mac remotes, the `process` and `hybrid` options have no effect and `output` will be used. Requires a reload to take effect.", + "Sets the source from which ports are automatically forwarded when {0} is true. On Windows and macOS remotes, the `process` and `hybrid` options have no effect and `output` will be used.", "Ports will be automatically forwarded when discovered by watching for processes that are started and include a port.", "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports forwarded based on output will not be \"un-forwarded\" until reload or until the port is closed by the user in the Ports view.", "Ports will be automatically forwarded when discovered by reading terminal and debug output. Not all processes that use ports will print to the integrated terminal or debug console, so some ports will be missed. Ports will be \"un-forwarded\" by watching for processes that listen on that port to be terminated.", + "The number of auto forwarded ports that will trigger the switch from `process` to `hybrid` when automatically forwarding ports and `remote.autoForwardPortsSource` is set to `process` by default. Set to `0` to disable the fallback. When `remote.autoForwardPortsFallback` hasn't been configured, but `remote.autoForwardPortsSource` has, `remote.autoForwardPortsFallback` will be treated as though it's set to `0`.", "Controls whether local URLs with a port will be forwarded when opened from the terminal and the debug console.", "A port, range of ports (ex. \"40000-55000\"), host and port (ex. \"db:1234\"), or regular expression (ex. \".+\\\\/server.js\"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression.", "Shows a notification when a port is automatically forwarded.", @@ -20170,7 +21855,28 @@ "When true, a modal dialog will show if the chosen local port isn't used for forwarding.", "The protocol to use when forwarding this port.", "Set default properties that are applied to all ports that don't get properties from the setting {0}. For example:\n\n```\n{\n \"onAutoForward\": \"ignore\"\n}\n```", - "Specifies the local host name that will be used for port forwarding." + "Specifies the local host name that will be used for port forwarding.", + "Connection: Trigger Reconnect", + "Connection: Pause socket writing" + ], + "vs/workbench/contrib/debug/browser/repl": [ + "Filter (e.g. text, !exclude, \\escape)", + "Showing {0} of {1}", + "Debug Console", + "Please start a debug session to evaluate expressions", + "REPL Accept Input", + "REPL Focus Content to Filter", + "Debug: Console Copy All", + "Select Debug Console", + "Collapse All", + "Paste", + "Copy All", + "Copy", + "Clear Console", + "Clears all program output from your debug REPL" + ], + "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ + "Toggle Keyboard Shortcuts Troubleshooting" ], "vs/workbench/contrib/snippets/browser/snippets.contribution": [ "Controls if surround-with-snippets or file template snippets show as Code Actions.", @@ -20184,9 +21890,6 @@ "User snippet configuration", "A list of language names to which this snippet applies, e.g. 'typescript,javascript'." ], - "vs/workbench/contrib/keybindings/browser/keybindings.contribution": [ - "Toggle Keyboard Shortcuts Troubleshooting" - ], "vs/workbench/contrib/folding/browser/folding.contribution": [ "All", "All active folding range providers", @@ -20208,30 +21911,6 @@ "Read Line With Inline Hints", "Stop Inlay Hints Reading" ], - "vs/workbench/contrib/update/browser/update.contribution": [ - "Show Release Notes", - "Show &&Release Notes", - "This version of {0} does not have release notes online", - "Check for Updates...", - "Download Update", - "Install Update", - "Restart to Update", - "Download {0}", - "Apply Update...", - "Apply Update", - "&&Update" - ], - "vs/workbench/contrib/surveys/browser/nps.contribution": [ - "Do you mind taking a quick feedback survey?", - "Take Survey", - "Remind Me Later", - "Don't Show Again" - ], - "vs/workbench/contrib/surveys/browser/ces.contribution": [ - "Got a moment to help the VS Code team? Please tell us about your experience with VS Code so far.", - "Give Feedback", - "Remind Me Later" - ], "vs/workbench/contrib/themes/browser/themes.contribution": [ "Icon for the 'Manage' action in the theme selection quick pick.", "Type to Search More. Select to Install. Up/Down Keys to Preview", @@ -20239,29 +21918,31 @@ "This will install extension '{0}' published by '{1}'. Do you want to continue?", "OK", "Installing Extension {0}...", - "Color Theme", + "Select Color Theme for System Dark Mode", + "Select Color Theme for System Light Mode", + "Select Color Theme for High Contrast Dark Mode", + "Select Color Theme for High Contrast Light Mode", + "Select Color Theme (detect system color mode disabled)", + "Detect system color mode enabled. Click to configure.", + "Detect system color mode disabled. Click to configure.", "Install Additional Color Themes...", "Browse Additional Color Themes...", - "Select Color Theme (Up/Down Keys to Preview)", "light themes", "dark themes", "high contrast themes", - "File Icon Theme", "Install Additional File Icon Themes...", "Select File Icon Theme (Up/Down Keys to Preview)", "file icon themes", "None", "Disable File Icons", - "Product Icon Theme", "Install Additional Product Icon Themes...", "Browse Additional Product Icon Themes...", "Select Product Icon Theme (Up/Down Keys to Preview)", "product icon themes", "Default", "Manage Extension", - "Generate Color Theme From Current Settings", - "Toggle between Light/Dark Themes", - "Browse Color Themes in Marketplace", + "Cannot toggle between light and dark themes when `{0}` is enabled in settings.", + "Open Settings", "Themes", "&&Theme", "Color Theme", @@ -20273,7 +21954,41 @@ "Visual Studio Code now ships with a new default theme '{0}'. If you prefer, you can switch back to the old theme or try one of the many other color themes available.", "Try New Theme", "Cancel", - "Visual Studio Code now ships with a new default theme '{0}'. Do you want to give it a try?" + "Visual Studio Code now ships with a new default theme '{0}'. Do you want to give it a try?", + "Color Theme", + "File Icon Theme", + "Product Icon Theme", + "Generate Color Theme From Current Settings", + "Toggle between Light/Dark Themes", + "Browse Color Themes in Marketplace" + ], + "vs/workbench/contrib/update/browser/update.contribution": [ + "Show &&Release Notes", + "This version of {0} does not have release notes online", + "Show &&Release Notes", + "Cannot open the current file as Release Notes", + "Apply Update", + "&&Update", + "Show Release Notes", + "Open Current File as Release Notes", + "Developer", + "Check for Updates...", + "Download Update", + "Install Update", + "Restart to Update", + "Download {0}", + "Apply Update..." + ], + "vs/workbench/contrib/surveys/browser/nps.contribution": [ + "Do you mind taking a quick feedback survey?", + "Take Survey", + "Remind Me Later", + "Don't Show Again" + ], + "vs/workbench/contrib/surveys/browser/ces.contribution": [ + "Got a moment to help the VS Code team? Please tell us about your experience with VS Code so far.", + "Give Feedback", + "Remind Me Later" ], "vs/workbench/contrib/surveys/browser/languageSurveys.contribution": [ "Help us improve our support for {0}", @@ -20283,12 +21998,8 @@ ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution": [ "Welcome", - "Welcome", - "Welcome", - "Go Back", "Mark Step Complete", "Mark Step Incomplete", - "Open Walkthrough...", "Select a walkthrough to open", "The platform of the current workspace, which in remote or serverless contexts may be different from the platform of the UI", "When enabled, an extension's walkthrough will open upon install of the extension.", @@ -20297,9 +22008,15 @@ "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global configuration, it will be ignored if set in a workspace or folder configuration.", "Open a new untitled text file (only applies when opening an empty window).", "Open the Welcome page when opening an empty workbench.", + "Open a new terminal in the editor area.", "Controls which editor is shown at startup, if none are restored from the previous session.", "Deprecated, use the global `workbench.reduceMotion`.", - "When enabled, reduce motion in welcome page." + "When enabled, reduce motion in welcome page.", + "Welcome", + "Opens a Walkthrough to help you get started in VS Code.", + "Welcome", + "Go Back", + "Open Walkthrough..." ], "vs/workbench/contrib/welcomeWalkthrough/browser/walkThrough.contribution": [ "Playground", @@ -20307,15 +22024,15 @@ ], "vs/workbench/contrib/welcomeViews/common/newFile.contribution": [ "Built-In", - "Create", - "New File...", "New File...", "Select File Type or Enter File Name...", "File", "Notebook", "Configure Keybinding", "Create New File ({0})", - "Text File" + "Text File", + "Create", + "New File..." ], "vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution": [ "Whether a call hierarchy provider is available", @@ -20323,13 +22040,13 @@ "Whether call hierarchy shows incoming or outgoing calls", "No results", "Failed to show call hierarchy", + "Icon for incoming calls in the call hierarchy view.", + "Icon for outgoing calls in the call hierarchy view.", + "Close", "Peek Call Hierarchy", "Show Incoming Calls", - "Icon for incoming calls in the call hierarchy view.", "Show Outgoing Calls", - "Icon for outgoing calls in the call hierarchy view.", - "Refocus Call Hierarchy", - "Close" + "Refocus Call Hierarchy" ], "vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution": [ "Whether a type hierarchy provider is available", @@ -20337,11 +22054,11 @@ "whether type hierarchy shows super types or subtypes", "No results", "Failed to show type hierarchy", + "Close", "Peek Type Hierarchy", "Show Supertypes", "Show Subtypes", - "Refocus Type Hierarchy", - "Close" + "Refocus Type Hierarchy" ], "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline": [ "Document Symbols" @@ -20353,9 +22070,9 @@ "Controls whether Outline items are collapsed or expanded.", "Collapse all items.", "Expand all items.", - "Show errors and warnings on Outline elements.", - "Use colors for errors and warnings on Outline elements.", - "Use badges for errors and warnings on Outline elements.", + "Show errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off.", + "Use colors for errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off.", + "Use badges for errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off.", "When enabled, Outline shows `file`-symbols.", "When enabled, Outline shows `module`-symbols.", "When enabled, Outline shows `namespace`-symbols.", @@ -20388,8 +22105,8 @@ "Accept Detected Language: {0}", "Language Detection", "Change to Detected Language: {0}", - "Detect Language from Content", - "Unable to detect editor language" + "Unable to detect editor language", + "Detect Language from Content" ], "vs/workbench/contrib/languageStatus/browser/languageStatus.contribution": [ "Editor Language Status", @@ -20401,6 +22118,19 @@ "{0} (Language Status)", "Reset Language Status Interaction Counter" ], + "vs/workbench/contrib/authentication/browser/authentication.contribution": [ + "The id of the authentication provider.", + "The human readable name of the authentication provider.", + "Contributes authentication", + "Label", + "ID", + "Authentication", + "No accounts requested yet...", + "An authentication contribution must specify an id.", + "An authentication contribution must specify a label.", + "This authentication id '{0}' has already been registered", + "Loading..." + ], "vs/workbench/contrib/userDataSync/browser/userDataSync.contribution": [ "Settings sync is suspended temporarily because the current device is making too many requests. Please reload {0} to resume.", "Settings sync is suspended temporarily because the current device is making too many requests. Please restart {0} to resume.", @@ -20423,19 +22153,12 @@ "Filter Timeline" ], "vs/workbench/contrib/editSessions/browser/editSessions.contribution": [ - "Continue Working On...", - "Open In Local Folder", - "Show Log", "Install additional development environment options", "Resuming working changes...", "Storing current working changes...", "Check for pending cloud changes", "Storing working changes...", - "Show Cloud Changes", "Storing your working changes...", - "Resume Latest Changes from Cloud", - "Resume Changes from Serialized Data", - "Store Working Changes in Cloud", "Storing working changes...", "Checking for pending cloud changes...", "There are no changes to resume from the cloud.", @@ -20475,7 +22198,14 @@ "Prompt the user to sign in to store working changes in the cloud with Continue Working On.", "Do not store working changes in the cloud with Continue Working On unless the user has already turned on Cloud Changes.", "Controls whether to prompt the user to store working changes in the cloud when using Continue Working On.", - "Controls whether to surface cloud changes which partially match the current session." + "Controls whether to surface cloud changes which partially match the current session.", + "Continue Working On...", + "Open In Local Folder", + "Show Log", + "Show Cloud Changes", + "Resume Latest Changes from Cloud", + "Resume Changes from Serialized Data", + "Store Working Changes in Cloud" ], "vs/workbench/contrib/workspaces/browser/workspaces.contribution": [ "This folder contains a workspace file '{0}'. Do you want to open it? [Learn more]({1}) about workspace files.", @@ -20483,8 +22213,8 @@ "This folder contains multiple workspace files. Do you want to open one? [Learn more]({0}) about workspace files.", "Select Workspace", "Select a workspace to open", - "Open Workspace", - "This workspace is already open." + "This workspace is already open.", + "Open Workspace" ], "vs/workbench/contrib/workspace/browser/workspace.contribution": [ "You are trying to open untrusted files in a workspace which is trusted.", @@ -20526,20 +22256,15 @@ "Restricted Mode is intended for safe code browsing. Trust this window to enable all features.", "Restricted Mode is intended for safe code browsing. Trust this folder to enable all features.", "Restricted Mode is intended for safe code browsing. Trust this workspace to enable all features.", - "This window is trusted.", "Restricted Mode: Some features are disabled because this window is not trusted.", "Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [window is not trusted]({1}).", - "This folder is trusted.", "Restricted Mode: Some features are disabled because this folder is not trusted.", "Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [folder is not trusted]({1}).", - "This workspace is trusted.", "Restricted Mode: Some features are disabled because this workspace is not trusted.", "Running in Restricted Mode\n\nSome [features are disabled]({0}) because this [workspace is not trusted]({1}).", "Workspace Trust", + "Restricted Mode", "Workspace Trust Editor", - "Workspaces", - "Configure Workspace Trust Settings", - "Manage Workspace Trust", "Controls whether or not Workspace Trust is enabled within VS Code.", "Controls when the startup prompt to trust a workspace is shown.", "Ask for trust every time an untrusted workspace is opened.", @@ -20553,168 +22278,66 @@ "Ask how to handle untrusted files for each workspace. Once untrusted files are introduced to a trusted workspace, you will not be prompted again.", "Always allow untrusted files to be introduced to a trusted workspace without prompting.", "Always open untrusted files in a separate window in restricted mode without prompting.", - "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window." + "Controls whether or not the empty window is trusted by default within VS Code. When used with `#{0}#`, you can enable the full functionality of VS Code without prompting in an empty window.", + "Workspaces", + "Configure Workspace Trust Settings", + "Manage Workspace Trust" + ], + "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ + "The extension 'Bracket pair Colorizer' got disabled because it was deprecated.", + "Uninstall Extension", + "Enable Native Bracket Pair Colorization", + "More Info" ], "vs/workbench/contrib/share/browser/share.contribution": [ - "Share...", "Generating link...", "Copied text to clipboard!", "Copied link to clipboard!", "Close", "Open Link", - "Controls whether to render the Share action next to the command center when {0} is {1}." - ], - "vs/workbench/contrib/audioCues/browser/audioCues.contribution": [ - "Enable audio cue when a screen reader is attached.", - "Enable audio cue.", - "Disable audio cue.", - "The volume of the audio cues in percent (0-100).", - "Whether or not position changes should be debounced", - "Plays a sound when the active line has a breakpoint.", - "Plays a sound when the active line has an inline suggestion.", - "Plays a sound when the active line has an error.", - "Plays a sound when the active line has a folded area that can be unfolded.", - "Plays a sound when the active line has a warning.", - "Plays a sound when the debugger stopped on a breakpoint.", - "Plays a sound when trying to read a line with inlay hints that has no inlay hints.", - "Plays a sound when a task is completed.", - "Plays a sound when a task fails (non-zero exit code).", - "Plays a sound when a terminal command fails (non-zero exit code).", - "Plays a sound when terminal Quick Fixes are available.", - "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change.", - "Plays a sound when the focus moves to a deleted line in Accessible Diff Viewer mode or to the next/previous change.", - "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change.", - "Plays a sound when a notebook cell execution is successfully completed.", - "Plays a sound when a notebook cell execution fails.", - "Plays a sound when a chat request is made.", - "Plays a sound on loop while the response is pending.", - "Plays a sound on loop while the response has been received.", - "Plays a sound when a feature is cleared (for example, the terminal, debug console, or output channel). When this is disabled, an aria alert will announce 'Cleared'.", - "Plays a sound when a file is saved. Also see {0}", - "Plays the audio cue when a user explicitly saves a file.", - "Plays the audio cue whenever a file is saved, including auto save.", - "Never plays the audio cue.", - "Plays a sound when a file or notebook is formatted. Also see {0}", - "Plays the audio cue when a user explicitly formats a file.", - "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell.", - "Never plays the audio cue." + "Controls whether to render the Share action next to the command center when {0} is {1}.", + "Share..." ], "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution": [ - "When enabled, available entitlements for the account will be show in the accounts menu." - ], - "vs/workbench/browser/workbench": [ - "Failed to load a required file. Please restart the application to try again. Details: {0}" - ], - "vs/workbench/contrib/deprecatedExtensionMigrator/browser/deprecatedExtensionMigrator.contribution": [ - "The extension 'Bracket pair Colorizer' got disabled because it was deprecated.", - "Uninstall Extension", - "Enable Native Bracket Pair Colorization", - "More Info" + "When enabled, available entitlements for the account will be show in the accounts menu.", + "When enabled, the chat panel welcome view will be shown." ], - "vs/workbench/electron-sandbox/window": [ - "Restart", - "Configure", - "Learn More", - "Writing login information to the keychain failed with error '{0}'.", - "Troubleshooting Guide", - "You are running an emulated version of {0}. For better performance download the native arm64 version of {0} build for your machine.", - "Download", - "Proxy Authentication Required", - "&&Log In", - "Username", - "Password", - "The proxy {0} requires a username and password.", - "Remember my credentials", - "Are you sure you want to quit?", - "Are you sure you want to exit?", - "Are you sure you want to close the window?", - "&&Quit", - "&&Exit", - "&&Close Window", - "Do not ask me again", - "Error: {0}", - "The following operations are still running: \n{0}", - "An unexpected error prevented the window to close", - "An unexpected error prevented the application to quit", - "An unexpected error prevented the window to reload", - "An unexpected error prevented to change the workspace", - "Closing the window is taking a bit longer...", - "Quitting the application is taking a bit longer...", - "Reloading the window is taking a bit longer...", - "Changing the workspace is taking a bit longer...", - "Close Anyway", - "Quit Anyway", - "Reload Anyway", - "Change Anyway", - "There is a dependency cycle in the AMD modules that needs to be resolved!", - "It is not recommended to run {0} as root user.", - "Files you store within the installation folder ('{0}') may be OVERWRITTEN or DELETED IRREVERSIBLY without warning at update time.", - "{0} on {1} will soon stop receiving updates. Consider upgrading your macOS version.", - "Learn More", - "Resolving shell environment...", - "Learn More" + "vs/workbench/electron-sandbox/actions/developerActions": [ + "Toggle Developer Tools", + "Configure Runtime Arguments", + "Reload With Extensions Disabled", + "Open User Data Folder" ], - "vs/platform/workspace/common/workspace": [ - "Code Workspace" - ], - "vs/workbench/services/configuration/browser/configurationService": [ - "Contribute defaults for configurations", - "Experiments", - "Configure settings to be applied for all profiles." - ], - "vs/workbench/services/log/electron-sandbox/logService": [ - "Window" - ], - "vs/platform/workspace/common/workspaceTrust": [ - "Trusted", - "Restricted Mode" - ], - "vs/workbench/services/userDataProfile/common/userDataProfile": [ - "Icon for Default Profile.", - "Profiles", - "Profile" - ], - "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ - "Open Developer Tools", - "Open in browser", - "Failed to connect to the remote extension host server (Error: {0})" - ], - "vs/workbench/electron-sandbox/actions/developerActions": [ - "Toggle Developer Tools", - "Configure Runtime Arguments", - "Reload With Extensions Disabled", - "Open User Data Folder" - ], - "vs/platform/configuration/common/configurationRegistry": [ - "Default Language Configuration Overrides", - "Configure settings to be overridden for the {0} language.", - "Configure editor settings to be overridden for a language.", - "This setting does not support per-language configuration.", - "Configure editor settings to be overridden for a language.", - "This setting does not support per-language configuration.", - "Cannot register an empty property", - "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", - "Cannot register '{0}'. This property is already registered.", - "Cannot register '{0}'. The associated policy {1} is already registered with {2}." + "vs/platform/configuration/common/configurationRegistry": [ + "Default Language Configuration Overrides", + "Configure settings to be overridden for the {0} language.", + "Configure editor settings to be overridden for a language.", + "This setting does not support per-language configuration.", + "Configure editor settings to be overridden for a language.", + "This setting does not support per-language configuration.", + "Cannot register an empty property", + "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", + "Cannot register '{0}'. This property is already registered.", + "Cannot register '{0}'. The associated policy {1} is already registered with {2}." ], "vs/workbench/electron-sandbox/actions/windowActions": [ - "Close Window", "Clos&&e Window", - "Zoom In", "&&Zoom In", - "Zoom Out", "&&Zoom Out", - "Reset Zoom", "&&Reset Zoom", "Close Window", "Close Window", - "Select a window to switch to", + "window group", "{0}, window with unsaved changes", "Current Window", + "Current Window", + "Select a window to switch to", + "Close Window", + "Zoom In", + "Zoom Out", + "Reset Zoom", "Switch Window...", - "Quick Switch Window...", - "Split Window (Experimental)", - "&&Split Window (Experimental)" + "Quick Switch Window..." ], "vs/platform/contextkey/common/contextkeys": [ "Whether the operating system is macOS", @@ -20728,11 +22351,11 @@ "Whether keyboard focus is inside an input box" ], "vs/workbench/electron-sandbox/actions/installActions": [ + "Shell command '{0}' successfully installed in PATH.", + "Shell command '{0}' successfully uninstalled from PATH.", "Shell Command", "Install '{0}' command in PATH", - "Shell command '{0}' successfully installed in PATH.", - "Uninstall '{0}' command from PATH", - "Shell command '{0}' successfully uninstalled from PATH." + "Uninstall '{0}' command from PATH" ], "vs/workbench/common/contextkeys": [ "The kind of workspace opened in the window, either 'empty' (no workspace), 'folder' (single folder) or 'workspace' (multi-root workspace)", @@ -20741,7 +22364,8 @@ "The name of the remote the window is connected to or an empty string if not connected to any remote", "The scheme of the current workspace is from a virtual file system or an empty string.", "The scheme of the current workspace is from a temporary file system.", - "Whether the window is in fullscreen mode", + "Whether the main window is in fullscreen mode", + "Whether an auxiliary window is focused", "The identifier of the embedder according to the product service, if one is defined", "Whether the active editor has unsaved changes", "Whether the active editor is not in preview mode", @@ -20749,6 +22373,7 @@ "Whether the active editor is the last one in its group", "Whether the active editor is pinned", "Whether the active editor is read-only", + "Whether the active compare editor can swap sides", "Whether the active editor can toggle between being read-only or writeable", "Whether the active editor can revert", "The identifier of the active editor", @@ -20762,13 +22387,15 @@ "Whether the active editor group is the last group", "Whether the active editor group is locked", "Whether there are multiple editor groups opened", + "Whether there are multiple editor groups opened in an editor part", + "Editor Part has a maximized group", + "Editor Part is in an auxiliary window", "Whether an editor is open", "Whether Zen mode is enabled", - "Whether centered layout is enabled", + "Whether centered layout is enabled for the main editor", "Whether editors split vertically", - "Whether the editor area is visible", + "Whether the editor area in the main window is visible", "Whether editor tabs are visible", - "Editor group is maximized", "Whether the sidebar is visible", "Whether the sidebar has keyboard focus", "The identifier of the active viewlet", @@ -20803,10 +22430,81 @@ "Application", "Workbench", "Security", + "Problems", "UNC host names must not contain backslashes.", "A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.", "If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc." ], + "vs/workbench/electron-sandbox/window": [ + "Restart", + "Configure", + "Learn More", + "Writing login information to the keychain failed with error '{0}'.", + "Troubleshooting Guide", + "You are running an emulated version of {0}. For better performance download the native arm64 version of {0} build for your machine.", + "Download", + "Proxy Authentication Required", + "&&Log In", + "Username", + "Password", + "The proxy {0} requires a username and password.", + "Remember my credentials", + "Error: {0}", + "The following operations are still running: \n{0}", + "An unexpected error prevented the window to close", + "An unexpected error prevented the application to quit", + "An unexpected error prevented the window to reload", + "An unexpected error prevented to change the workspace", + "Closing the window is taking a bit longer...", + "Quitting the application is taking a bit longer...", + "Reloading the window is taking a bit longer...", + "Changing the workspace is taking a bit longer...", + "Close Anyway", + "Quit Anyway", + "Reload Anyway", + "Change Anyway", + "There is a dependency cycle in the AMD modules that needs to be resolved!", + "It is not recommended to run {0} as root user.", + "Files you store within the installation folder ('{0}') may be OVERWRITTEN or DELETED IRREVERSIBLY without warning at update time.", + "{0} on {1} will soon stop receiving updates. Consider upgrading your macOS version.", + "Learn More", + "Resolving shell environment...", + "Learn More", + "Zoom Out", + "Zoom In", + "Reset", + "{0} ({1})", + "Settings", + "Window Zoom", + "Zoom Level: {0} ({1}%)" + ], + "vs/workbench/browser/workbench": [ + "Failed to load a required file. Please restart the application to try again. Details: {0}" + ], + "vs/workbench/services/configuration/browser/configurationService": [ + "Contribute defaults for configurations", + "Experiments", + "Configure settings to be applied for all profiles." + ], + "vs/platform/workspace/common/workspace": [ + "Code Workspace" + ], + "vs/workbench/services/remote/electron-sandbox/remoteAgentService": [ + "Open Developer Tools", + "Open in browser", + "Failed to connect to the remote extension host server (Error: {0})" + ], + "vs/workbench/services/log/electron-sandbox/logService": [ + "Window" + ], + "vs/workbench/services/files/electron-sandbox/diskFileSystemProvider": [ + "File Watcher" + ], + "vs/workbench/services/userDataProfile/common/userDataProfile": [ + "Icon for Default Profile.", + "Profile", + "Profiles" + ], "vs/workbench/browser/parts/dialogs/dialogHandler": [ "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", "&&Copy", @@ -20817,6 +22515,21 @@ "&&Copy", "OK" ], + "vs/workbench/services/textfile/browser/textFileService": [ + "File Created", + "File Replaced", + "Text File Model Decorations", + "Deleted, Read-only", + "Read-only", + "Deleted", + "File seems to be binary and cannot be opened as text", + "'{0}' already exists. Do you want to replace it?", + "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", + "&&Replace", + "'{0}' is marked as read-only. Do you want to save anyway?", + "Paths can be configured as read-only via settings.", + "&&Save Anyway" + ], "vs/workbench/services/dialogs/browser/abstractFileDialogService": [ "Your changes will be lost if you don't save them.", "Do you want to save the changes you made to {0}?", @@ -20834,20 +22547,8 @@ "All Files", "No Extension" ], - "vs/workbench/services/textfile/browser/textFileService": [ - "File Created", - "File Replaced", - "Text File Model Decorations", - "Deleted, Read-only", - "Read-only", - "Deleted", - "File seems to be binary and cannot be opened as text", - "'{0}' already exists. Do you want to replace it?", - "A file or folder with the name '{0}' already exists in the folder '{1}'. Replacing it will overwrite its current contents.", - "&&Replace", - "'{0}' is marked as read-only. Do you want to save anyway?", - "Paths can be configured as read-only via settings.", - "&&Save Anyway" + "vs/workbench/services/extensionManagement/common/extensionManagement": [ + "Extensions" ], "vs/workbench/common/theme": [ "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", @@ -20870,6 +22571,7 @@ "Border to the top of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", "Border to highlight tabs in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", + "Border between tabs to indicate that a tab can be inserted between two tabs. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", "Border on the top of modified active tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", "Border on the top of modified inactive tabs in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", "Border on the top of modified active tabs in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.", @@ -20900,314 +22602,108 @@ "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.", "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.", "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.", + "Background color of sticky scroll in the panel.", + "Border color of sticky scroll in the panel.", + "Shadow color of sticky scroll in the panel.", + "Output view background color.", + "Output view sticky scroll background color.", "Banner background color. The banner is shown under the title bar of the window.", "Banner foreground color. The banner is shown under the title bar of the window.", - "Banner icon color. The banner is shown under the title bar of the window.", - "Status bar foreground color when a workspace or folder is opened. The status bar is shown in the bottom of the window.", - "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.", - "Status bar background color when a workspace or folder is opened. The status bar is shown in the bottom of the window.", - "Status bar background color when no folder is opened. The status bar is shown in the bottom of the window.", - "Status bar border color separating to the sidebar and editor. The status bar is shown in the bottom of the window.", - "Status bar border color when focused on keyboard navigation. The status bar is shown in the bottom of the window.", - "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.", - "Status bar item background color when clicking. The status bar is shown in the bottom of the window.", - "Status bar item border color when focused on keyboard navigation. The status bar is shown in the bottom of the window.", - "Status bar item background color when hovering. The status bar is shown in the bottom of the window.", - "Status bar item foreground color when hovering. The status bar is shown in the bottom of the window.", - "Status bar item background color when hovering an item that contains two hovers. The status bar is shown in the bottom of the window.", - "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", - "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", - "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", - "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", - "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", - "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", - "Status bar error items foreground color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", - "Status bar error items background color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", - "Status bar warning items background color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", - "Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", - "Status bar warning items foreground color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", - "Status bar warning items background color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", - "Activity bar background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar item foreground color when it is active. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar item foreground color when it is inactive. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar focus border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Drag and drop feedback color for the activity bar items. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", - "Profile badge background color. The profile badge shows on top of the settings gear icon in the activity bar.", - "Profile badge foreground color. The profile badge shows on top of the settings gear icon in the activity bar.", - "Background color for the remote indicator on the status bar.", - "Foreground color for the remote indicator on the status bar.", - "Foreground color for the remote indicator on the status bar when hovering.", - "Background color for the remote indicator on the status bar when hovering.", - "Status bar item background color when the workbench is offline.", - "Status bar item foreground color when the workbench is offline.", - "Status bar item foreground hover color when the workbench is offline.", - "Status bar item background hover color when the workbench is offline.", - "Background color for the remote badge in the extensions view.", - "Foreground color for the remote badge in the extensions view.", - "Side bar background color. The side bar is the container for views like explorer and search.", - "Side bar foreground color. The side bar is the container for views like explorer and search.", - "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search.", - "Side bar title foreground color. The side bar is the container for views like explorer and search.", - "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", - "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", - "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", - "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", - "Title bar foreground when the window is active.", - "Title bar foreground when the window is inactive.", - "Title bar background when the window is active.", - "Title bar background when the window is inactive.", - "Title bar border color.", - "Foreground color of the selected menu item in the menubar.", - "Background color of the selected menu item in the menubar.", - "Border color of the selected menu item in the menubar.", - "Foreground color of the command center", - "Active foreground color of the command center", - "Foreground color of the command center when the window is inactive", - "Background color of the command center", - "Active background color of the command center", - "Border color of the command center", - "Active border color of the command center", - "Border color of the command center when the window is inactive", - "Notifications center border color. Notifications slide in from the bottom right of the window.", - "Notification toast border color. Notifications slide in from the bottom right of the window.", - "Notifications foreground color. Notifications slide in from the bottom right of the window.", - "Notifications background color. Notifications slide in from the bottom right of the window.", - "Notification links foreground color. Notifications slide in from the bottom right of the window.", - "Notifications center header foreground color. Notifications slide in from the bottom right of the window.", - "Notifications center header background color. Notifications slide in from the bottom right of the window.", - "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.", - "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.", - "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.", - "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.", - "The color used for the border of the window when it is active. Only supported in the macOS and Linux desktop client when using the custom title bar.", - "The color used for the border of the window when it is inactive. Only supported in the macOS and Linux desktop client when using the custom title bar." - ], - "vs/platform/theme/common/colorRegistry": [ - "Overall foreground color. This color is only used if not overridden by a component.", - "Overall foreground for disabled elements. This color is only used if not overridden by a component.", - "Overall foreground color for error messages. This color is only used if not overridden by a component.", - "Foreground color for description text providing additional information, for example for a label.", - "The default color for icons in the workbench.", - "Overall border color for focused elements. This color is only used if not overridden by a component.", - "An extra border around elements to separate them from others for greater contrast.", - "An extra border around active elements to separate them from others for greater contrast.", - "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor.", - "Color for text separators.", - "Foreground color for links in text.", - "Foreground color for links in text when clicked on and on mouse hover.", - "Foreground color for preformatted text segments.", - "Background color for preformatted text segments.", - "Background color for block quotes in text.", - "Border color for block quotes in text.", - "Background color for code blocks in text.", - "Shadow color of widgets such as find/replace inside the editor.", - "Border color of widgets such as find/replace inside the editor.", - "Input box background.", - "Input box foreground.", - "Input box border.", - "Border color of activated options in input fields.", - "Background color of activated options in input fields.", - "Background hover color of options in input fields.", - "Foreground color of activated options in input fields.", - "Input box foreground color for placeholder text.", - "Input validation background color for information severity.", - "Input validation foreground color for information severity.", - "Input validation border color for information severity.", - "Input validation background color for warning severity.", - "Input validation foreground color for warning severity.", - "Input validation border color for warning severity.", - "Input validation background color for error severity.", - "Input validation foreground color for error severity.", - "Input validation border color for error severity.", - "Dropdown background.", - "Dropdown list background.", - "Dropdown foreground.", - "Dropdown border.", - "Button foreground color.", - "Button separator color.", - "Button background color.", - "Button background color when hovering.", - "Button border color.", - "Secondary button foreground color.", - "Secondary button background color.", - "Secondary button background color when hovering.", - "Badge background color. Badges are small information labels, e.g. for search results count.", - "Badge foreground color. Badges are small information labels, e.g. for search results count.", - "Scrollbar shadow to indicate that the view is scrolled.", - "Scrollbar slider background color.", - "Scrollbar slider background color when hovering.", - "Scrollbar slider background color when clicked on.", - "Background color of the progress bar that can show for long running operations.", - "Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.", - "Foreground color of error squigglies in the editor.", - "If set, color of double underlines for errors in the editor.", - "Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.", - "Foreground color of warning squigglies in the editor.", - "If set, color of double underlines for warnings in the editor.", - "Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.", - "Foreground color of info squigglies in the editor.", - "If set, color of double underlines for infos in the editor.", - "Foreground color of hint squigglies in the editor.", - "If set, color of double underlines for hints in the editor.", - "Border color of active sashes.", - "Editor background color.", - "Editor default foreground color.", - "Sticky scroll background color for the editor", - "Sticky scroll on hover background color for the editor", - "Background color of editor widgets, such as find/replace.", - "Foreground color of editor widgets, such as find/replace.", - "Border color of editor widgets. The color is only used if the widget chooses to have a border and if the color is not overridden by a widget.", - "Border color of the resize bar of editor widgets. The color is only used if the widget chooses to have a resize border and if the color is not overridden by a widget.", - "Quick picker background color. The quick picker widget is the container for pickers like the command palette.", - "Quick picker foreground color. The quick picker widget is the container for pickers like the command palette.", - "Quick picker title background color. The quick picker widget is the container for pickers like the command palette.", - "Quick picker color for grouping labels.", - "Quick picker color for grouping borders.", - "Keybinding label background color. The keybinding label is used to represent a keyboard shortcut.", - "Keybinding label foreground color. The keybinding label is used to represent a keyboard shortcut.", - "Keybinding label border color. The keybinding label is used to represent a keyboard shortcut.", - "Keybinding label border bottom color. The keybinding label is used to represent a keyboard shortcut.", - "Color of the editor selection.", - "Color of the selected text for high contrast.", - "Color of the selection in an inactive editor. The color must not be opaque so as not to hide underlying decorations.", - "Color for regions with the same content as the selection. The color must not be opaque so as not to hide underlying decorations.", - "Border color for regions with the same content as the selection.", - "Color of the current search match.", - "Color of the other search matches. The color must not be opaque so as not to hide underlying decorations.", - "Color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.", - "Border color of the current search match.", - "Border color of the other search matches.", - "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.", - "Color of the Search Editor query matches.", - "Border color of the Search Editor query matches.", - "Color of the text in the search viewlet's completion message.", - "Highlight below the word for which a hover is shown. The color must not be opaque so as not to hide underlying decorations.", - "Background color of the editor hover.", - "Foreground color of the editor hover.", - "Border color of the editor hover.", - "Background color of the editor hover status bar.", - "Color of active links.", - "Foreground color of inline hints", - "Background color of inline hints", - "Foreground color of inline hints for types", - "Background color of inline hints for types", - "Foreground color of inline hints for parameters", - "Background color of inline hints for parameters", - "The color used for the lightbulb actions icon.", - "The color used for the lightbulb auto fix actions icon.", - "Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.", - "Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.", - "Background color for lines that got inserted. The color must not be opaque so as not to hide underlying decorations.", - "Background color for lines that got removed. The color must not be opaque so as not to hide underlying decorations.", - "Background color for the margin where lines got inserted.", - "Background color for the margin where lines got removed.", - "Diff overview ruler foreground for inserted content.", - "Diff overview ruler foreground for removed content.", - "Outline color for the text that got inserted.", - "Outline color for text that got removed.", - "Border color between the two text editors.", - "Color of the diff editor's diagonal fill. The diagonal fill is used in side-by-side diff views.", - "The background color of unchanged blocks in the diff editor.", - "The foreground color of unchanged blocks in the diff editor.", - "The background color of unchanged code in the diff editor.", - "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree outline color for the focused item when the list/tree is active and selected. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree icon foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", - "List/Tree background when hovering over items using the mouse.", - "List/Tree foreground when hovering over items using the mouse.", - "List/Tree drag and drop background when moving items around using the mouse.", - "List/Tree foreground color of the match highlights when searching inside the list/tree.", - "List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.", - "List/Tree foreground color for invalid items, for example an unresolved root in explorer.", - "Foreground color of list items containing errors.", - "Foreground color of list items containing warnings.", - "Background color of the type filter widget in lists and trees.", - "Outline color of the type filter widget in lists and trees.", - "Outline color of the type filter widget in lists and trees, when there are no matches.", - "Shadow color of the type filter widget in lists and trees.", - "Background color of the filtered match.", - "Border color of the filtered match.", - "Tree stroke color for the indentation guides.", - "Tree stroke color for the indentation guides that are not active.", - "Table border color between columns.", - "Background color for odd table rows.", - "List/Tree foreground color for items that are deemphasized. ", - "Background color of checkbox widget.", - "Background color of checkbox widget when the element it's in is selected.", - "Foreground color of checkbox widget.", - "Border color of checkbox widget.", - "Border color of checkbox widget when the element it's in is selected.", - "Please use quickInputList.focusBackground instead", - "Quick picker foreground color for the focused item.", - "Quick picker icon foreground color for the focused item.", - "Quick picker background color for the focused item.", - "Border color of menus.", - "Foreground color of menu items.", - "Background color of menu items.", - "Foreground color of the selected menu item in menus.", - "Background color of the selected menu item in menus.", - "Border color of the selected menu item in menus.", - "Color of a separator menu item in menus.", - "Toolbar background when hovering over actions using the mouse", - "Toolbar outline when hovering over actions using the mouse", - "Toolbar background when holding the mouse over actions", - "Highlight background color of a snippet tabstop.", - "Highlight border color of a snippet tabstop.", - "Highlight background color of the final tabstop of a snippet.", - "Highlight border color of the final tabstop of a snippet.", - "Color of focused breadcrumb items.", - "Background color of breadcrumb items.", - "Color of focused breadcrumb items.", - "Color of selected breadcrumb items.", - "Background color of breadcrumb item picker.", - "Current header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Current content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Incoming header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Incoming content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Common ancestor header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Common ancestor content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", - "Border color on headers and the splitter in inline merge-conflicts.", - "Current overview ruler foreground for inline merge-conflicts.", - "Incoming overview ruler foreground for inline merge-conflicts.", - "Common ancestor overview ruler foreground for inline merge-conflicts.", - "Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.", - "Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.", - "Minimap marker color for find matches.", - "Minimap marker color for repeating editor selections.", - "Minimap marker color for the editor selection.", - "Minimap marker color for infos.", - "Minimap marker color for warnings.", - "Minimap marker color for errors.", - "Minimap background color.", - "Opacity of foreground elements rendered in the minimap. For example, \"#000000c0\" will render the elements with 75% opacity.", - "Minimap slider background color.", - "Minimap slider background color when hovering.", - "Minimap slider background color when clicked on.", - "The color used for the problems error icon.", - "The color used for the problems warning icon.", - "The color used for the problems info icon.", - "The foreground color used in charts.", - "The color used for horizontal lines in charts.", - "The red color used in chart visualizations.", - "The blue color used in chart visualizations.", - "The yellow color used in chart visualizations.", - "The orange color used in chart visualizations.", - "The green color used in chart visualizations.", - "The purple color used in chart visualizations." + "Banner icon color. The banner is shown under the title bar of the window.", + "Status bar foreground color when a workspace or folder is opened. The status bar is shown in the bottom of the window.", + "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.", + "Status bar background color when a workspace or folder is opened. The status bar is shown in the bottom of the window.", + "Status bar background color when no folder is opened. The status bar is shown in the bottom of the window.", + "Status bar border color separating to the sidebar and editor. The status bar is shown in the bottom of the window.", + "Status bar border color when focused on keyboard navigation. The status bar is shown in the bottom of the window.", + "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.", + "Status bar item background color when clicking. The status bar is shown in the bottom of the window.", + "Status bar item border color when focused on keyboard navigation. The status bar is shown in the bottom of the window.", + "Status bar item background color when hovering. The status bar is shown in the bottom of the window.", + "Status bar item foreground color when hovering. The status bar is shown in the bottom of the window.", + "Status bar item background color when hovering an item that contains two hovers. The status bar is shown in the bottom of the window.", + "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", + "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", + "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", + "Status bar prominent items background color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.", + "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", + "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", + "Status bar error items foreground color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", + "Status bar error items background color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.", + "Status bar warning items background color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", + "Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", + "Status bar warning items foreground color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", + "Status bar warning items background color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.", + "Activity bar background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar item foreground color when it is active. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar item foreground color when it is inactive. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar border color separating to the side bar. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar focus border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Drag and drop feedback color for the activity bar items. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.", + "Active foreground color of the item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.", + "Focus border color for the active item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.", + "Background color for the active item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.", + "Inactive foreground color of the item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.", + "Drag and drop feedback color for the items in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.", + "Background color of the activity bar when set to top / bottom.", + "Profile badge background color. The profile badge shows on top of the settings gear icon in the activity bar.", + "Profile badge foreground color. The profile badge shows on top of the settings gear icon in the activity bar.", + "Background color for the remote indicator on the status bar.", + "Foreground color for the remote indicator on the status bar.", + "Foreground color for the remote indicator on the status bar when hovering.", + "Background color for the remote indicator on the status bar when hovering.", + "Status bar item background color when the workbench is offline.", + "Status bar item foreground color when the workbench is offline.", + "Status bar item foreground hover color when the workbench is offline.", + "Status bar item background hover color when the workbench is offline.", + "Background color for the remote badge in the extensions view.", + "Foreground color for the remote badge in the extensions view.", + "Side bar background color. The side bar is the container for views like explorer and search.", + "Side bar foreground color. The side bar is the container for views like explorer and search.", + "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search.", + "Side bar title background color. The side bar is the container for views like explorer and search.", + "Side bar title foreground color. The side bar is the container for views like explorer and search.", + "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", + "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", + "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", + "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.", + "Border color between the activity bar at the top/bottom and the views.", + "Background color of sticky scroll in the side bar.", + "Border color of sticky scroll in the side bar.", + "Shadow color of sticky scroll in the side bar.", + "Title bar foreground when the window is active.", + "Title bar foreground when the window is inactive.", + "Title bar background when the window is active.", + "Title bar background when the window is inactive.", + "Title bar border color.", + "Foreground color of the selected menu item in the menubar.", + "Background color of the selected menu item in the menubar.", + "Border color of the selected menu item in the menubar.", + "Foreground color of the command center", + "Active foreground color of the command center", + "Foreground color of the command center when the window is inactive", + "Background color of the command center", + "Active background color of the command center", + "Border color of the command center", + "Active border color of the command center", + "Border color of the command center when the window is inactive", + "Notifications center border color. Notifications slide in from the bottom right of the window.", + "Notification toast border color. Notifications slide in from the bottom right of the window.", + "Notifications foreground color. Notifications slide in from the bottom right of the window.", + "Notifications background color. Notifications slide in from the bottom right of the window.", + "Notification links foreground color. Notifications slide in from the bottom right of the window.", + "Notifications center header foreground color. Notifications slide in from the bottom right of the window.", + "Notifications center header background color. Notifications slide in from the bottom right of the window.", + "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.", + "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.", + "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.", + "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.", + "The color used for the border of the window when it is active. Only supported in the macOS and Linux desktop client when using the custom title bar.", + "The color used for the border of the window when it is inactive. Only supported in the macOS and Linux desktop client when using the custom title bar." ], "vs/base/common/actions": [ "(empty)" @@ -21244,25 +22740,30 @@ "Would you like to install and synchronize extensions across your devices?", "&&Install", "Install (Do &¬ sync)", - "Enabling this extension requires a trusted workspace.", "Trust Workspace & Install", "Install", "Learn More", + "Enabling this extension requires a trusted workspace.", "{0} for the Web", "'{0}' has limited functionality in {1}.", "&&Install Anyway", "&&Show Extensions", "Contains extensions which are not supported.", - "'{0}' contains extensions which are not supported in {1}." + "'{0}' contains extensions which are not supported in {1}.", + "Cannot activate, becase {0} not found" ], "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService": [ "Can't install release version of '{0}' extension because it has no release version.", "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2})." ], "vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupTracker": [ - "The following editors with unsaved changes could not be saved to the back up location.", + "The following editors with unsaved changes could not be saved to the backup location.", "The following editors with unsaved changes could not be saved or reverted.", "Try saving or reverting the editors with unsaved changes first and then try again.", + "&&OK", + "Close Anyway", + "Quit Anyway", + "Reload Anyway", "Backing up editors with unsaved changes is taking a bit longer...", "Click 'Cancel' to stop waiting and to save or revert editors with unsaved changes.", "Saving editors with unsaved changes is taking a bit longer...", @@ -21293,21 +22794,45 @@ "The reasons for blocking the operation:\n- {0}", "The remote extension host terminated unexpectedly. Restarting...", "Remote Extension host terminated unexpectedly 3 times within the last 5 minutes.", - "Restart Remote Extension Host" + "Restart Remote Extension Host", + "Activation Events" ], "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner": [ "Extensions have been modified on disk. Please reload the window.", "Reload Window" ], + "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ + "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.", + "Extension host did not start in 10 seconds, that might be a problem.", + "Reload Window", + "Terminating extension debug session" + ], + "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ + "Search language packs in the Marketplace to change the display language to {0}.", + "Search Marketplace", + "Install language pack to change the display language to {0}.", + "Install and Restart" + ], "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService": [ + "Changes that you made may not be saved. Please check press 'Cancel' and try again.", "Unable to open a new window.", "The browser interrupted the opening of a new window. Press 'Retry' to try again.", "To avoid this problem in the future, please ensure to allow popups for this website.", "&&Retry" ], - "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ - "Open Logs Folder", - "Open Extension Logs Folder" + "vs/workbench/contrib/localization/common/localization.contribution": [ + "Contributes localizations to the editor", + "Id of the language into which the display strings are translated.", + "Name of the language in English.", + "Name of the language in contributed language.", + "List of translations associated to the language.", + "Id of VS Code or Extension for which this translation is contributed to. Id of VS Code is always `vscode` and of extension should be in format `publisherId.extensionName`.", + "Id should be `vscode` or in format `publisherId.extensionName` for translating VS code or an extension respectively.", + "A relative path to a file containing translations for the language.", + "Language ID", + "Language Name", + "Language Name (Localized)", + "Langauage Packs" ], "vs/editor/common/editorContextKeys": [ "Whether the editor text has focus (cursor is blinking)", @@ -21316,9 +22841,17 @@ "Whether the editor is read-only", "Whether the context is a diff editor", "Whether the context is an embedded diff editor", + "Whether the context is a multi diff editor", + "Whether all files in multi diff editor are collapsed", + "Whether the diff editor has changes", "Whether a moved code block is selected for comparison", "Whether the accessible diff viewer is visible", "Whether the diff editor render side by side inline breakpoint is reached", + "Whether inline mode is active", + "Whether modified is writable in the diff editor", + "Whether modified is writable in the diff editor", + "The uri of the original document", + "The uri of the modified document", "Whether `editor.columnSelection` is enabled", "Whether the editor has text selected", "Whether the editor has multiple selections", @@ -21350,54 +22883,31 @@ "Whether the editor has multiple document formatting providers", "Whether the editor has multiple document selection formatting providers" ], - "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost": [ - "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.", - "Extension host did not start in 10 seconds, that might be a problem.", - "Reload Window", - "Terminating extension debug session" - ], "vs/workbench/common/editor": [ "Text Editor", "Built-in", "Open Anyway", "Configure Limit" ], - "vs/workbench/contrib/localization/electron-sandbox/minimalTranslations": [ - "Search language packs in the Marketplace to change the display language to {0}.", - "Search Marketplace", - "Install language pack to change the display language to {0}.", - "Install and Restart" - ], - "vs/workbench/contrib/localization/common/localization.contribution": [ - "Contributes localizations to the editor", - "Id of the language into which the display strings are translated.", - "Name of the language in English.", - "Name of the language in contributed language.", - "List of translations associated to the language.", - "Id of VS Code or Extension for which this translation is contributed to. Id of VS Code is always `vscode` and of extension should be in format `publisherId.extensionName`.", - "Id should be `vscode` or in format `publisherId.extensionName` for translating VS code or an extension respectively.", - "A relative path to a file containing translations for the language." - ], "vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard": [ "Paste Selection Clipboard" ], "vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate": [ - "Start Text Mate Syntax Grammar Logging" + "Start TextMate Syntax Grammar Logging" ], - "vs/workbench/contrib/issue/common/issue.contribution": [ - "Report Issue...", - "Report &&Issue" + "vs/workbench/contrib/logs/electron-sandbox/logsActions": [ + "Open Logs Folder", + "Open Extension Logs Folder" + ], + "vs/workbench/browser/editor": [ + "{0}, preview", + "{0}, pinned" ], "vs/workbench/contrib/extensions/electron-sandbox/runtimeExtensionsEditor": [ "Start Extension Host Profile", "Stop Extension Host Profile", "Save Extension Host Profile", - "Save Extension Host Profile", - "Save" - ], - "vs/workbench/browser/editor": [ - "{0}, preview", - "{0}, pinned" + "Save Extension Host Profile" ], "vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction": [ "Start Debugging Extension Host", @@ -21411,6 +22921,7 @@ "Cleanup Extensions Folder" ], "vs/workbench/contrib/extensions/common/runtimeExtensionsInput": [ + "Icon of the runtime extensions editor label.", "Running Extensions" ], "vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService": [ @@ -21427,6 +22938,10 @@ "The extension '{0}' took a very long time to complete its last operation and it has prevented other extensions from running.", "Show Extensions" ], + "vs/workbench/contrib/issue/common/issue.contribution": [ + "Report &&Issue", + "Report Issue..." + ], "vs/workbench/contrib/terminal/common/terminal": [ "Contributes terminal functionality.", "Defines additional terminal profiles that the user can create.", @@ -21436,6 +22951,11 @@ "Icon path when a light theme is used", "Icon path when a dark theme is used" ], + "vs/workbench/contrib/issue/browser/issueQuickAccess": [ + "Extension Marketplace", + "Extensions", + "Open Extension Page" + ], "vs/workbench/services/dialogs/browser/simpleFileDialog": [ "Open Local File...", "Save Local File...", @@ -21486,6 +23006,44 @@ "variable", "{0} ({1})" ], + "vs/workbench/services/themes/common/themeConfiguration": [ + "Specifies the color theme used in the workbench when {0} is not enabled.", + "Theme is unknown or not installed.", + "Specifies the color theme when system color mode is dark and {0} is enabled.", + "Theme is unknown or not installed.", + "Specifies the color theme when system color mode is light and {0} is enabled.", + "Theme is unknown or not installed.", + "Specifies the color theme when in high contrast dark mode and {0} is enabled.", + "Theme is unknown or not installed.", + "Specifies the color theme when in high contrast light mode and {0} is enabled.", + "Theme is unknown or not installed.", + "If enabled, will automatically select a color theme based on the system color mode. If the system color mode is dark, {0} is used, else {1}.", + "Overrides colors from the currently selected color theme.", + "Specifies the file icon theme used in the workbench or 'null' to not show any file icons.", + "None", + "No file icons", + "File icon theme is unknown or not installed.", + "Specifies the product icon theme used.", + "Default", + "Default", + "Product icon theme is unknown or not installed.", + "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme. The high contrast theme to use is specified by {0} and {1}.", + "Sets the colors and styles for comments", + "Sets the colors and styles for strings literals.", + "Sets the colors and styles for keywords.", + "Sets the colors and styles for number literals.", + "Sets the colors and styles for type declarations and references.", + "Sets the colors and styles for functions declarations and references.", + "Sets the colors and styles for variables declarations and references.", + "Sets colors and styles using textmate theming rules (advanced).", + "Whether semantic highlighting should be enabled for this theme.", + "Use `enabled` in `editor.semanticTokenColorCustomizations` setting instead.", + "Use `enabled` in {0} setting instead.", + "Overrides editor syntax colors and font style from the currently selected color theme.", + "Whether semantic highlighting is enabled or disabled for this theme", + "Semantic token styling rules for this theme.", + "Overrides editor semantic token color and styles from the currently selected color theme." + ], "vs/workbench/services/userDataSync/common/userDataSync": [ "Settings", "Keyboard Shortcuts", @@ -21496,20 +23054,8 @@ "Profiles", "Workspace State", "View icon of the Settings Sync view.", - "Download Settings Sync Activity", - "Settings Sync" - ], - "vs/workbench/contrib/tasks/common/tasks": [ - "Whether a task is currently running.", - "Tasks", - "Error: the task identifier '{0}' is missing the required property '{1}'. The task identifier will be ignored." - ], - "vs/workbench/contrib/tasks/common/taskService": [ - "Whether CustomExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "Whether ShellExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "Whether the task commands have been registered yet", - "Whether ProcessExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", - "True when in the web with no remote authority." + "Settings Sync", + "Download Settings Sync Activity" ], "vs/workbench/contrib/performance/electron-sandbox/startupProfiler": [ "Successfully created profiles.", @@ -21520,12 +23066,6 @@ "A final restart is required to continue to use '{0}'. Again, thank you for your contribution.", "&&Restart" ], - "vs/workbench/common/views": [ - "Views", - "Default view icon.", - "A view with id '{0}' is already registered", - "No tree view with id '{0}' registered." - ], "vs/workbench/contrib/terminal/common/terminalContextKey": [ "Whether the terminal is focused.", "Whether any terminal is focused, including detached terminals used in other UI.", @@ -21544,45 +23084,18 @@ "Whether the terminal run command picker is currently open.", "Whether shell integration is enabled in the active terminal" ], - "vs/platform/audioCues/browser/audioCueService": [ - "Error on Line", - "Warning on Line", - "Folded Area on Line", - "Breakpoint on Line", - "Inline Suggestion on Line", - "Terminal Quick Fix", - "Debugger Stopped on Breakpoint", - "No Inlay Hints on Line", - "Task Completed", - "Task Failed", - "Terminal Command Failed", - "Terminal Bell", - "Notebook Cell Completed", - "Notebook Cell Failed", - "Diff Line Inserted", - "Diff Line Deleted", - "Diff Line Modified", - "Chat Request Sent", - "Chat Response Received", - "Chat Response Pending", - "Clear", - "Save", - "Format" - ], - "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ - "Open Webview Developer Tools", - "Using standard dev tools to debug iframe based webview" - ], - "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ - "Reveal in File Explorer", - "Reveal in Finder", - "Open Containing Folder" + "vs/workbench/contrib/tasks/common/taskService": [ + "Whether CustomExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "Whether ShellExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "Whether the task commands have been registered yet", + "Whether ProcessExecution tasks are supported. Consider using in the when clause of a 'taskDefinition' contribution.", + "True when in the web with no remote authority." ], - "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ - "Merge Editor (Dev)", - "Open Merge Editor State from JSON", - "Enter JSON", - "Open Selection In Temporary Merge Editor" + "vs/workbench/common/views": [ + "Views", + "Default view icon.", + "A view with id '{0}' is already registered", + "No tree view with id '{0}' registered." ], "vs/workbench/contrib/tasks/browser/terminalTaskSystem": [ "A unknown error has occurred while executing a task. See task output log for details.", @@ -21601,9 +23114,15 @@ "Terminal will be reused by tasks, press any key to close it." ], "vs/workbench/contrib/tasks/browser/abstractTaskService": [ - "Configure Task", "Tasks", "Select the build task (there is no default build task defined)", + "Task Event kind: {0}", + "Startup kind not window reload, setting connected and removing persistent tasks", + "Setting tasks connected configured value {0}, tasks were already reconnected {1}", + "Reconnecting to running tasks...", + "Reconnected to running tasks.", + "No persistent tasks to reconnect.", + "Reconnecting to {0} tasks...", "Filters the tasks shown in the quickpick", "The task's label or a term to filter by", "The contributed task type", @@ -21611,7 +23130,16 @@ "There are task errors. See the output for details.", "Show output", "The folder {0} is ignored since it uses task version 0.1.0", - "Warning: {0} tasks are unavailable in the current environment.\n", + "Warning: {0} tasks are unavailable in the current environment.", + "Returning cached tasks {0}", + "Fetching tasks from task storage.", + "Reading tasks from task storage, {0}, {1}, {2}", + "Fetching a task from task storage failed: {0}.", + "Resolved task {0}", + "Unable to resolve task {0} ", + "Removing persistent task {0}", + "Setting persistent task {0}", + "Saving persistent tasks: {0}", "No test task defined. Mark a task with 'isTestCommand' in the tasks.json file.", "No test task defined. Mark a task with as a 'test' group in the tasks.json file.", "No build task defined. Mark a task with 'isBuildCommand' in the tasks.json file.", @@ -21625,29 +23153,28 @@ "Select for which kind of errors and warnings to scan the task output", "The current task configuration has errors. Please fix the errors first before customizing a task.", "\t// See https://go.microsoft.com/fwlink/?LinkId=733558 \n\t// for the documentation about the tasks.json format", - "There are many build tasks defined in the tasks.json. Executing the first one.\n", + "There are many build tasks defined in the tasks.json. Executing the first one.", "Save all editors?", "Do you want to save all editors before running the task?", "&&Save", - "Don't save", + "Do&&n't Save", "The task '{0}' is already active.", "Terminate Task", "Restart Task", "There is already a task running. Terminate it first before executing another task.", "Failed to terminate and restart task {0}", "The task provider for \"{0}\" tasks unexpectedly provided a task of type \"{1}\".\n", - "Warning: {0} tasks are unavailable in the current environment.\n", - "Error: The {0} task detection didn't contribute a task for the following configuration:\n{1}\nThe task will be ignored.\n", + "Warning: {0} tasks are unavailable in the current environment.", + "Error: The {0} task detection didn't contribute a task for the following configuration:\n{1}\nThe task will be ignored.", "Error: the provided task configuration has validation errors and can't not be used. Please correct the errors first.", - "Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n", + "Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.", "workspace file", "Only tasks version 2.0.0 permitted in workspace configuration files.", "user settings", "Only tasks version 2.0.0 permitted in user settings.", - "Workspace folder was undefined", "Error: the provided task configuration has validation errors and can't not be used. Please correct the errors first.", - "Ignoring task configurations for workspace folder {0}. Multi folder workspace task support requires that all folders use task version 2.0.0\n", - "Error: The content of the tasks.json file has syntax errors. Please correct them before executing a task.\n", + "Ignoring task configurations for workspace folder {0}. Multi folder workspace task support requires that all folders use task version 2.0.0", + "Error: The content of the tasks.json file has syntax errors. Please correct them before executing a task.", "Terminate Task", "An error has occurred while running a task. See task log for details.", "Configure Task", @@ -21688,24 +23215,119 @@ "The deprecated tasks version 0.1.0 has been removed. Your tasks have been upgraded to version 2.0.0. Open the diff to review the upgrade.", "The deprecated tasks version 0.1.0 has been removed. Your tasks have been upgraded to version 2.0.0. Open the diffs to review the upgrade.", "Open diff", - "Open diffs" + "Open diffs", + "Configure Task" + ], + "vs/platform/accessibilitySignal/browser/accessibilitySignalService": [ + "Error at Position", + "Error", + "Warning at Position", + "Warning", + "Error on Line", + "Error on Line", + "Warning on Line", + "Warning on Line", + "Folded Area on Line", + "Folded", + "Breakpoint on Line", + "Breakpoint", + "Inline Suggestion on Line", + "Terminal Quick Fix", + "Quick Fix", + "Debugger Stopped on Breakpoint", + "Breakpoint", + "No Inlay Hints on Line", + "No Inlay Hints", + "Task Completed", + "Task Completed", + "Task Failed", + "Task Failed", + "Terminal Command Failed", + "Command Failed", + "Terminal Bell", + "Terminal Bell", + "Notebook Cell Completed", + "Notebook Cell Completed", + "Notebook Cell Failed", + "Notebook Cell Failed", + "Diff Line Inserted", + "Diff Line Deleted", + "Diff Line Modified", + "Chat Request Sent", + "Chat Request Sent", + "Chat Response Received", + "Progress", + "Progress", + "Clear", + "Clear", + "Save", + "Save", + "Format", + "Format", + "Voice Recording Started", + "Voice Recording Stopped" + ], + "vs/workbench/contrib/tasks/common/tasks": [ + "Whether a task is currently running.", + "Error: the task identifier '{0}' is missing the required property '{1}'. The task identifier will be ignored.", + "Tasks" + ], + "vs/workbench/contrib/webview/electron-sandbox/webviewCommands": [ + "Opens Developer Tools for active webviews", + "Using standard dev tools to debug iframe based webview", + "Open Webview Developer Tools" + ], + "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands": [ + "Enter JSON", + "Merge Editor (Dev)", + "Open Merge Editor State from JSON", + "Open Selection In Temporary Merge Editor" + ], + "vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands": [ + "Reveal in File Explorer", + "Reveal in Finder", + "Open Containing Folder" + ], + "vs/workbench/contrib/multiDiffEditor/browser/actions": [ + "Open File", + "Collapse All Diffs", + "Expand All Diffs" + ], + "vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver": [ + "View Changes" + ], + "vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions": [ + "Hold for Speech" + ], + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput": [ + "Multi Diff Editor", + " ({0} files)" ], "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions": [ "True when getting ready for receiving voice input from the microphone for voice chat.", "True when voice recording from microphone is in progress for voice chat.", "True when voice recording from microphone is in progress for quick chat.", "True when voice recording from microphone is in progress for inline chat.", + "True when voice recording from microphone is in progress for terminal chat.", "True when voice recording from microphone is in progress in the chat view.", "True when voice recording from microphone is in progress in the chat editor.", "I'm listening", - "Voice Chat in Chat View", + "Microphone support requires this extension.", + "Keyword activation is disabled.", + "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the chat view.", + "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the quick chat.", + "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor if possible.", + "Keyword activation is enabled and listening for 'Hey Code' to start a voice chat session in the active editor or view depending on keyboard focus.", + "Controls whether the keyword phrase 'Hey Code' is recognized to start a voice chat session. Enabling this will start recording from the microphone but the audio is processed locally and never sent to a server.", + "Voice Keyword Activation", + "Listening to 'Hey Code'...", + "Waiting for voice chat to end...", + "Voice Chat in View", + "Hold to Voice Chat in View", "Inline Voice Chat", "Quick Voice Chat", - "Use Microphone", - "Stop Listening", - "Stop Listening", - "Stop Listening", - "Stop Listening", + "Start Voice Chat", + "Start Voice Chat", "Stop Listening", "Stop Listening and Submit" ], @@ -21731,6 +23353,10 @@ "Extension Host (Worker)", "Extension Host" ], + "vs/workbench/api/common/extHostLanguageModels": [ + "To allow access to the language models provided by {0}. Justification:\n\n{1}", + "To allow access to the language models provided by {0}" + ], "vs/platform/terminal/node/terminalProcess": [ "Starting directory (cwd) \"{0}\" is not a directory", "Starting directory (cwd) \"{0}\" does not exist", @@ -21740,70 +23366,6 @@ "vs/workbench/api/node/extHostDebugService": [ "Debug Process" ], - "vs/platform/extensions/common/extensionValidator": [ - "property publisher must be of type `string`.", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` is mandatory and must be of type `object`", - "property `{0}` is mandatory and must be of type `string`", - "property `{0}` can be omitted or must be of type `string[]`", - "property `{0}` can be omitted or must be of type `string[]`", - "property `{0}` should be omitted if the extension doesn't have a `{1}` or `{2}` property.", - "property `{0}` can be defined only if property `main` is also defined.", - "property `{0}` can be omitted or must be of type `string`", - "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", - "property `{0}` can be omitted or must be of type `string`", - "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", - "Extension version is not semver compatible.", - "Could not parse `engines.vscode` value {0}. Please use, for example: ^1.22.0, ^1.22.x, etc.", - "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions before 1.0.0, please define at a minimum the major and minor desired version. E.g. ^0.10.0, 0.10.x, 0.11.0, etc.", - "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions after 1.0.0, please define at a minimum the major desired version. E.g. ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", - "Extension is not compatible with Code {0}. Extension requires: {1}." - ], - "vs/platform/extensionManagement/common/extensionNls": [ - "Couldn't find message for key {0}." - ], - "vs/base/common/jsonErrorMessages": [ - "Invalid symbol", - "Invalid number format", - "Property name expected", - "Value expected", - "Colon expected", - "Comma expected", - "Closing brace expected", - "Closing bracket expected", - "End of file expected" - ], - "vs/base/node/zip": [ - "Error extracting {0}. Invalid file.", - "Incomplete. Found {0} of {1} entries", - "{0} not found inside zip." - ], - "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ - "Marketplace is not enabled", - "Can't install '{0}' extension since it was reported to be problematic.", - "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", - "The '{0}' extension is not available in {1} for {2}.", - "Can't install release version of '{0}' extension because it has no release version.", - "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", - "Cannot uninstall '{0}' extension. '{1}' extension depends on this.", - "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.", - "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.", - "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.", - "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.", - "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this." - ], - "vs/platform/extensionManagement/node/extensionManagementUtil": [ - "VSIX invalid: package.json is not a JSON file." - ], - "vs/platform/files/common/io": [ - "File is too large to open" - ], - "vs/platform/shell/node/shellEnv": [ - "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration and restart.", - "Unable to resolve your shell environment: {0}", - "Unexpected exit code from spawned shell (code {0}, signal {1})" - ], "vs/platform/dialogs/electron-main/dialogMainService": [ "Open", "Open Folder", @@ -21811,9 +23373,10 @@ "Open Workspace from File", "&&Open" ], - "vs/platform/files/electron-main/diskFileSystemProviderServer": [ - "Failed to move '{0}' to the recycle bin", - "Failed to move '{0}' to the trash" + "vs/platform/shell/node/shellEnv": [ + "Unable to resolve your shell environment in a reasonable time. Please review your shell configuration and restart.", + "Unable to resolve your shell environment: {0}", + "Unexpected exit code from spawned shell (code {0}, signal {1})" ], "vs/platform/externalTerminal/node/externalTerminalService": [ "VS Code Console", @@ -21823,6 +23386,10 @@ "'{0}' failed with exit code {1}", "can't find terminal application '{0}'" ], + "vs/platform/files/electron-main/diskFileSystemProviderServer": [ + "Failed to move '{0}' to the recycle bin ({1})", + "Failed to move '{0}' to the trash ({1})" + ], "vs/platform/issue/electron-main/issueMainService": [ "Local", "Issue Reporter", @@ -21849,6 +23416,10 @@ "Unable to find shell script in '{0}'" ], "vs/platform/workspaces/electron-main/workspacesHistoryMainService": [ + "&&Clear", + "&&Cancel", + "Do you want to clear all recently opened files and workspaces?", + "This action is irreversible!", "New Window", "Opens a new window", "Recent Folders & Workspaces", @@ -21874,9 +23445,84 @@ "The path '{0}' uses a host that is not allowed. Unless you trust the host, you should press 'Cancel'", "Permanently allow host '{0}'" ], + "vs/platform/files/common/io": [ + "File is too large to open" + ], + "vs/platform/extensions/common/extensionValidator": [ + "property publisher must be of type `string`.", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` is mandatory and must be of type `object`", + "property `{0}` is mandatory and must be of type `string`", + "property `{0}` can be omitted or must be of type `string[]`", + "property `{0}` can be omitted or must be of type `string[]`", + "property `{0}` should be omitted if the extension doesn't have a `{1}` or `{2}` property.", + "property `{0}` can be defined only if property `main` is also defined.", + "property `{0}` can be omitted or must be of type `string`", + "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", + "property `{0}` can be omitted or must be of type `string`", + "Expected `browser` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", + "Extension version is not semver compatible.", + "Could not parse `engines.vscode` value {0}. Please use, for example: ^1.22.0, ^1.22.x, etc.", + "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions before 1.0.0, please define at a minimum the major and minor desired version. E.g. ^0.10.0, 0.10.x, 0.11.0, etc.", + "Version specified in `engines.vscode` ({0}) is not specific enough. For vscode versions after 1.0.0, please define at a minimum the major desired version. E.g. ^1.10.0, 1.10.x, 1.x.x, 2.x.x, etc.", + "Extension is not compatible with Code {0}. Extension requires: {1}." + ], + "vs/base/common/jsonErrorMessages": [ + "Invalid symbol", + "Invalid number format", + "Property name expected", + "Value expected", + "Colon expected", + "Comma expected", + "Closing brace expected", + "Closing bracket expected", + "End of file expected" + ], + "vs/platform/extensionManagement/common/extensionNls": [ + "Couldn't find message for key {0}." + ], + "vs/base/node/zip": [ + "Error extracting {0}. Invalid file.", + "Incomplete. Found {0} of {1} entries", + "{0} not found inside zip." + ], + "vs/platform/extensionManagement/common/abstractExtensionManagementService": [ + "Marketplace is not enabled", + "Can't install '{0}' extension since it was reported to be problematic.", + "Can't install '{0}' extension since it was deprecated and the replacement extension '{1}' can't be found.", + "The '{0}' extension is not available in {1} for {2}.", + "Can't install release version of '{0}' extension because it has no release version.", + "Can't install '{0}' extension because it is not compatible with the current version of {1} (version {2}).", + "Cannot uninstall '{0}' extension. '{1}' extension depends on this.", + "Cannot uninstall '{0}' extension. '{1}' and '{2}' extensions depend on this.", + "Cannot uninstall '{0}' extension. '{1}', '{2}' and other extension depend on this.", + "Cannot uninstall '{0}' extension . It includes uninstalling '{1}' extension and '{2}' extension depends on this.", + "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}' and '{3}' extensions depend on this.", + "Cannot uninstall '{0}' extension. It includes uninstalling '{1}' extension and '{2}', '{3}' and other extensions depend on this." + ], + "vs/platform/extensionManagement/node/extensionManagementUtil": [ + "VSIX invalid: package.json is not a JSON file." + ], "vs/base/browser/ui/button/button": [ "More Actions..." ], + "vs/platform/theme/common/iconRegistry": [ + "The id of the font to use. If not set, the font that is defined first is used.", + "The font character associated with the icon definition.", + "Icon for the close action in widgets.", + "Icon for goto previous editor location.", + "Icon for goto next editor location." + ], + "vs/base/browser/ui/tree/abstractTree": [ + "Filter", + "Fuzzy Match", + "Type to filter", + "Type to search", + "Type to search", + "Close", + "No elements found." + ], "vs/base/common/date": [ "in {0}", "now", @@ -21931,7 +23577,16 @@ "{0} year", "{0} yr", "{0} years", - "{0} yrs" + "{0} yrs", + "{0} milliseconds", + "{0}ms", + "{0} seconds", + "{0}s", + "{0} minutes", + "{0} mins", + "{0} hours", + "{0} hrs", + "{0} days" ], "vs/platform/userDataSync/common/keybindingsSync": [ "Unable to sync keybindings because the content in the file is not valid. Please open the file and correct it.", @@ -21953,22 +23608,6 @@ "Cannot sync because current session is expired", "Cannot sync because syncing is turned off on this machine from another machine." ], - "vs/base/browser/ui/tree/abstractTree": [ - "Filter", - "Fuzzy Match", - "Type to filter", - "Type to search", - "Type to search", - "Close", - "No elements found." - ], - "vs/platform/theme/common/iconRegistry": [ - "The id of the font to use. If not set, the font that is defined first is used.", - "The font character associated with the icon definition.", - "Icon for the close action in widgets.", - "Icon for goto previous editor location.", - "Icon for goto next editor location." - ], "vs/editor/common/core/editorColorRegistry": [ "Background color for the highlight of line at the cursor position.", "Background color for the border around the line at the cursor position.", @@ -21978,6 +23617,10 @@ "Background color of the border around highlighted symbols.", "Color of the editor cursor.", "The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.", + "Color of the primary editor cursor when multiple cursors are present.", + "The background color of the primary editor cursor when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.", + "Color of secondary editor cursors when multiple cursors are present.", + "The background color of secondary editor cursors when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.", "Color of whitespace characters in the editor.", "Color of editor line numbers.", "Color of the editor indentation guides.", @@ -22035,33 +23678,8 @@ "Background color of active bracket pair guides (4). Requires enabling bracket pair guides.", "Background color of active bracket pair guides (5). Requires enabling bracket pair guides.", "Background color of active bracket pair guides (6). Requires enabling bracket pair guides.", - "Border color used to highlight unicode characters.", - "Background color used to highlight unicode characters." - ], - "vs/editor/browser/coreCommands": [ - "Stick to the end even when going to longer lines", - "Stick to the end even when going to longer lines", - "Removed secondary cursors" - ], - "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ - "Toggle Collapse Unchanged Regions", - "Toggle Show Moved Code Blocks", - "Toggle Use Inline View When Space Is Limited", - "Use Inline View When Space Is Limited", - "Show Moved Code Blocks", - "Diff Editor", - "Switch Side", - "Exit Compare Move", - "Collapse All Unchanged Regions", - "Show All Unchanged Regions", - "Accessible Diff Viewer", - "Go to Next Difference", - "Open Accessible Diff Viewer", - "Go to Previous Difference" - ], - "vs/editor/browser/widget/codeEditorWidget": [ - "The number of cursors has been limited to {0}. Consider using [find and replace](https://code.visualstudio.com/docs/editor/codebasics#_find-and-replace) for larger changes or increase the editor multi cursor limit setting.", - "Increase Multi Cursor Limit" + "Border color used to highlight unicode characters.", + "Background color used to highlight unicode characters." ], "vs/platform/contextkey/common/scanner": [ "Did you mean {0}?", @@ -22070,6 +23688,18 @@ "Did you forget to open or close the quote?", "Did you forget to escape the '/' (slash) character? Put two backslashes before it to escape, e.g., '\\\\/'." ], + "vs/editor/browser/widget/diffEditor/diffEditor.contribution": [ + "Use Inline View When Space Is Limited", + "Show Moved Code Blocks", + "Revert Block", + "Revert Selection", + "Open Accessible Diff Viewer" + ], + "vs/editor/browser/coreCommands": [ + "Stick to the end even when going to longer lines", + "Stick to the end even when going to longer lines", + "Removed secondary cursors" + ], "vs/editor/contrib/anchorSelect/browser/anchorSelect": [ "Selection Anchor", "Anchor set at {0}:{1}", @@ -22083,7 +23713,8 @@ "Go to Bracket", "Select to Bracket", "Remove Brackets", - "Go to &&Bracket" + "Go to &&Bracket", + "Select the text inside and including the brackets or curly braces" ], "vs/editor/contrib/caretOperations/browser/caretOperations": [ "Move Selected Text Left", @@ -22101,16 +23732,20 @@ "Copy", "Copy", "Copy", - "Copy As", - "Copy As", - "Share", - "Share", - "Share", "&&Paste", "Paste", "Paste", "Paste", - "Copy With Syntax Highlighting" + "Copy With Syntax Highlighting", + "Copy As", + "Copy As", + "Share", + "Share", + "Share" + ], + "vs/editor/browser/widget/codeEditor/codeEditorWidget": [ + "The number of cursors has been limited to {0}. Consider using [find and replace](https://code.visualstudio.com/docs/editor/codebasics#_find-and-replace) for larger changes or increase the editor multi cursor limit setting.", + "Increase Multi Cursor Limit" ], "vs/editor/contrib/codeAction/browser/codeActionContributions": [ "Enable/disable showing group headers in the Code Action menu.", @@ -22120,12 +23755,6 @@ "Show CodeLens Commands For Current Line", "Select a command" ], - "vs/editor/contrib/colorPicker/browser/standaloneColorPickerActions": [ - "Show or Focus Standalone Color Picker", - "&&Show or Focus Standalone Color Picker", - "Hide the Color Picker", - "Insert Color with Standalone Color Picker" - ], "vs/editor/contrib/comment/browser/comment": [ "Toggle Line Comment", "&&Toggle Line Comment", @@ -22134,6 +23763,19 @@ "Toggle Block Comment", "Toggle &&Block Comment" ], + "vs/editor/contrib/colorPicker/browser/standaloneColorPickerActions": [ + "&&Show or Focus Standalone Color Picker", + "Hide the Color Picker", + "Insert Color with Standalone Color Picker", + "Show or Focus Standalone Color Picker", + "Show or focus a standalone color picker which uses the default color provider. It displays hex/rgb/hsl colors.", + "Hide the standalone color picker.", + "Insert hex/rgb/hsl colors with the focused standalone color picker." + ], + "vs/editor/contrib/cursorUndo/browser/cursorUndo": [ + "Cursor Undo", + "Cursor Redo" + ], "vs/editor/contrib/contextmenu/browser/contextmenu": [ "Minimap", "Render Characters", @@ -22146,17 +23788,37 @@ "Always", "Show Editor Context Menu" ], - "vs/editor/contrib/cursorUndo/browser/cursorUndo": [ - "Cursor Undo", - "Cursor Redo" - ], "vs/editor/contrib/dropOrPasteInto/browser/copyPasteContribution": [ + "The kind of the paste edit to try applying. If not provided or there are multiple edits for this kind, the editor will show a picker.", "Paste As...", - "The id of the paste edit to try applying. If not provided, the editor will show a picker." + "Paste as Text" + ], + "vs/editor/contrib/find/browser/findController": [ + "The file is too large to perform a replace all operation.", + "Find", + "&&Find", + "Find With Arguments", + "Find With Selection", + "Find Next", + "Find Previous", + "Go to Match...", + "No matches. Try searching for something else.", + "Type a number to go to a specific match (between 1 and {0})", + "Please type a number between 1 and {0}", + "Please type a number between 1 and {0}", + "Find Next Selection", + "Find Previous Selection", + "Replace", + "&&Replace" ], "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorContribution": [ "Configures the default drop provider to use for content of a given mime type." ], + "vs/editor/contrib/fontZoom/browser/fontZoom": [ + "Increase Editor Font Size", + "Decrease Editor Font Size", + "Reset Editor Font Size" + ], "vs/editor/contrib/folding/browser/folding": [ "Unfold", "Unfold Recursively", @@ -22177,80 +23839,53 @@ "Remove Manual Folding Ranges", "Fold Level {0}" ], - "vs/editor/contrib/find/browser/findController": [ - "The file is too large to perform a replace all operation.", - "Find", - "&&Find", - "Overrides \"Use Regular Expression\" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False", - "Overrides \"Match Whole Word\" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False", - "Overrides \"Math Case\" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False", - "Overrides \"Preserve Case\" flag.\nThe flag will not be saved for the future.\n0: Do Nothing\n1: True\n2: False", - "Find With Arguments", - "Find With Selection", - "Find Next", - "Find Previous", - "Go to Match...", - "No matches. Try searching for something else.", - "Type a number to go to a specific match (between 1 and {0})", - "Please type a number between 1 and {0}", - "Please type a number between 1 and {0}", - "Find Next Selection", - "Find Previous Selection", - "Replace", - "&&Replace" - ], - "vs/editor/contrib/fontZoom/browser/fontZoom": [ - "Editor Font Zoom In", - "Editor Font Zoom Out", - "Editor Font Zoom Reset" - ], "vs/editor/contrib/format/browser/formatActions": [ "Format Document", "Format Selection" ], + "vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition": [ + "Click to show {0} definitions." + ], "vs/editor/contrib/gotoSymbol/browser/goToCommands": [ "Peek", "Definitions", "No definition found for '{0}'", "No definition found", - "Go to Definition", "Go to &&Definition", - "Open Definition to the Side", - "Peek Definition", "Declarations", "No declaration found for '{0}'", "No declaration found", - "Go to Declaration", "Go to &&Declaration", "No declaration found for '{0}'", "No declaration found", - "Peek Declaration", "Type Definitions", "No type definition found for '{0}'", "No type definition found", - "Go to Type Definition", "Go to &&Type Definition", - "Peek Type Definition", "Implementations", "No implementation found for '{0}'", "No implementation found", - "Go to Implementations", "Go to &&Implementations", - "Peek Implementations", "No references found for '{0}'", "No references found", - "Go to References", "Go to &&References", "References", - "Peek References", "References", - "Go to Any Symbol", "Locations", "No results for '{0}'", - "References" - ], - "vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition": [ - "Click to show {0} definitions." + "References", + "Go to Definition", + "Open Definition to the Side", + "Peek Definition", + "Go to Declaration", + "Peek Declaration", + "Go to Type Definition", + "Peek Type Definition", + "Go to Implementations", + "Peek Implementations", + "Go to References", + "Peek References", + "Go to Any Symbol" ], "vs/editor/contrib/gotoError/browser/gotoError": [ "Go to Next Problem (Error, Warning, Info)", @@ -22262,18 +23897,6 @@ "Go to Previous Problem in Files (Error, Warning, Info)", "Previous &&Problem" ], - "vs/editor/contrib/hover/browser/hover": [ - "Show or Focus Hover", - "Show Definition Preview Hover", - "Scroll Up Hover", - "Scroll Down Hover", - "Scroll Left Hover", - "Scroll Right Hover", - "Page Up Hover", - "Page Down Hover", - "Go To Top Hover", - "Go To Bottom Hover" - ], "vs/editor/contrib/indentation/browser/indentation": [ "Convert Indentation to Spaces", "Convert Indentation to Tabs", @@ -22286,7 +23909,15 @@ "Change Tab Display Size", "Detect Indentation from Content", "Reindent Lines", - "Reindent Selected Lines" + "Reindent Selected Lines", + "Convert the tab indentation to spaces.", + "Convert the spaces indentation to tabs.", + "Use indentation with tabs.", + "Use indentation with spaces.", + "Change the space size equivalent of the tab.", + "Detect the indentation from content.", + "Reindent the lines of the editor.", + "Reindent the selected lines of the editor." ], "vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace": [ "Replace with Previous Value", @@ -22324,6 +23955,7 @@ "Transform to Title Case", "Transform to Snake Case", "Transform to Camel Case", + "Transform to Pascal Case", "Transform to Kebab Case" ], "vs/editor/contrib/linkedEditing/browser/linkedEditing": [ @@ -22379,7 +24011,9 @@ "Rename failed to apply edits", "Rename failed to compute edits", "Rename Symbol", - "Enable/disable the ability to preview changes before renaming" + "Enable/disable the ability to preview changes before renaming", + "Focus Next Rename Suggestion", + "Focus Previous Rename Suggestion" ], "vs/editor/contrib/smartSelect/browser/smartSelect": [ "Expand Selection", @@ -22393,9 +24027,6 @@ "Whether there is a previous tab stop when in snippet mode", "Go to next placeholder..." ], - "vs/editor/contrib/tokenization/browser/tokenization": [ - "Developer: Force Retokenize" - ], "vs/editor/contrib/suggest/browser/suggestController": [ "Accepting '{0}' made {1} additional edits", "Trigger Suggest", @@ -22408,16 +24039,21 @@ "show more", "Reset Suggest Widget Size" ], + "vs/editor/contrib/tokenization/browser/tokenization": [ + "Developer: Force Retokenize" + ], "vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode": [ - "Toggle Tab Key Moves Focus", "Pressing Tab will now move focus to the next focusable element", - "Pressing Tab will now insert the tab character" + "Pressing Tab will now insert the tab character", + "Toggle Tab Key Moves Focus", + "Determines whether the tab key moves focus around the workbench or inserts the tab character in the current editor. This is also called tab trapping, tab navigation, or tab focus mode." ], "vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter": [ "Icon shown with a warning message in the extensions editor.", "This document contains many non-basic ASCII unicode characters", "This document contains many ambiguous unicode characters", "This document contains many invisible unicode characters", + "Configure Unicode Highlight Options", "The character {0} could be confused with the ASCII character {1}, which is more common in source code.", "The character {0} could be confused with the character {1}, which is more common in source code.", "The character {0} is invisible.", @@ -22436,8 +24072,7 @@ "Show Exclude Options", "Exclude {0} (invisible character) from being highlighted", "Exclude {0} from being highlighted", - "Allow unicode characters that are more common in the language \"{0}\".", - "Configure Unicode Highlight Options" + "Allow unicode characters that are more common in the language \"{0}\"." ], "vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators": [ "Unusual Line Terminators", @@ -22446,14 +24081,14 @@ "&&Remove Unusual Line Terminators", "Ignore" ], + "vs/editor/contrib/wordOperations/browser/wordOperations": [ + "Delete Word" + ], "vs/editor/contrib/wordHighlighter/browser/wordHighlighter": [ "Go to Next Symbol Highlight", "Go to Previous Symbol Highlight", "Trigger Symbol Highlight" ], - "vs/editor/contrib/wordOperations/browser/wordOperations": [ - "Delete Word" - ], "vs/editor/contrib/readOnlyMessage/browser/contribution": [ "Cannot edit in read-only input", "Cannot edit in read-only editor" @@ -22478,12 +24113,12 @@ "Pressing Tab in the current editor will insert the tab character. Toggle this behavior {0}.", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.", "Show Accessibility Help", - "`audioCues.save` is disabled, so an alert will occur when a file is saved.", - "`audioCues.save` is enabled, so will play whenever a file is saved.", - "`audioCues.save` is enabled, so will play when a file is saved via user gesture.", - "`audioCues.format` is disabled, so an alert will occur when a file is formatted.", - "`audioCues.format` is enabled, so will play whenever a file is formatted.", - "`audioCues.format` is enabled, so will play when a file is formatted via user gesture.", + "Run the command: List Signal Sounds for an overview of all sounds and their current status.", + "Run the command: List Signal Announcements for an overview of announcements and their current status.", + "Toggle quick chat ({0}) to open or close a chat session.", + "Toggle quick chat is not currently triggerable by a keybinding.", + "Start inline chat ({0}) to create an in editor chat session.", + "The command: Start inline chat is not currentlyt riggerable by a keybinding.", "Developer: Inspect Tokens", "Go to Line/Column...", "Show all Quick Access Providers", @@ -22496,64 +24131,6 @@ "Toggle High Contrast Theme", "Made {0} edits in {1} files" ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ - "Icon to toggle the auxiliary bar off in its right position.", - "Icon to toggle the auxiliary bar on in its right position.", - "Icon to toggle the auxiliary bar in its left position.", - "Icon to toggle the auxiliary bar on in its left position.", - "Toggle Secondary Side Bar Visibility", - "Secondary Side Bar", - "Secondary Si&&de Bar", - "Focus into Secondary Side Bar", - "Toggle Secondary Side Bar", - "Toggle Secondary Side Bar", - "Hide Secondary Side Bar" - ], - "vs/workbench/browser/parts/panel/panelActions": [ - "Icon to maximize a panel.", - "Icon to restore a panel.", - "Icon to close a panel.", - "Icon to toggle the panel off when it is on.", - "Icon to toggle the panel on when it is off.", - "Toggle Panel Visibility", - "Panel", - "&&Panel", - "Focus into Panel", - "Focus into Panel", - "Move Panel Left", - "Left", - "Move Panel Right", - "Right", - "Move Panel To Bottom", - "Bottom", - "Set Panel Alignment to Left", - "Left", - "Set Panel Alignment to Right", - "Right", - "Set Panel Alignment to Center", - "Center", - "Set Panel Alignment to Justify", - "Justify", - "Panel Position", - "Align Panel", - "Previous Panel View", - "Next Panel View", - "Toggle Maximized Panel", - "Maximize Panel Size", - "Restore Panel Size", - "Maximizing the panel is only supported when it is center aligned.", - "Close Panel", - "Close Secondary Side Bar", - "Toggle Panel", - "Hide Panel", - "Move Panel Views To Secondary Side Bar", - "Move Panel Views To Secondary Side Bar", - "Move Secondary Side Bar Views To Panel", - "Move Secondary Side Bar Views To Panel" - ], - "vs/workbench/browser/quickaccess": [ - "Whether keyboard focus is inside the quick open control" - ], "vs/workbench/api/common/jsonValidationExtensionPoint": [ "Contributes json schema configuration.", "The file pattern (or an array of patterns) to match, for example \"package.json\" or \"*.launch\". Exclusion patterns start with '!'", @@ -22563,22 +24140,10 @@ "'configuration.jsonValidation.url' must be a URL or relative path", "Expected `contributes.{0}.url` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", "'configuration.jsonValidation.url' is an invalid relative URL: {0}", - "'configuration.jsonValidation.url' must be an absolute URL or start with './' to reference schemas located in the extension." - ], - "vs/workbench/services/themes/common/iconExtensionPoint": [ - "Contributes extension defined themable icons", - "The identifier of the themable icon", - "Identifiers can only contain letters, digits and minuses and need to consist of at least two segments in the form `component-iconname`.", - "The description of the themable icon", - "The path of the icon font that defines the icon.", - "The character for the icon in the icon font.", - "The default of the icon. Either a reference to an extisting ThemeIcon or an icon in an icon font.", - "'configuration.icons' must be an object with the icon names as properties.", - "'configuration.icons' keys represent the icon id and can only contain letter, digits and minuses. They need to consist of at least two segments in the form `component-iconname`.", - "'configuration.icons.description' must be defined and can not be empty", - "Expected `contributes.icons.default.fontPath` to have file extension 'woff', woff2' or 'ttf', is '{0}'.", - "Expected `contributes.icons.default.fontPath` ({0}) to be included inside extension's folder ({0}).", - "'configuration.icons.default' must be either a reference to the id of an other theme icon (string) or a icon definition (object) with properties `fontPath` and `fontCharacter`." + "'configuration.jsonValidation.url' must be an absolute URL or start with './' to reference schemas located in the extension.", + "File Match", + "Schema", + "JSON Validation" ], "vs/workbench/services/themes/common/colorExtensionPoint": [ "Contributes extension defined themable colors", @@ -22596,7 +24161,28 @@ "'configuration.colors.description' must be defined and can not be empty", "'configuration.colors.defaults' must be defined and must contain 'light' and 'dark'", "If defined, 'configuration.colors.defaults.highContrast' must be a string.", - "If defined, 'configuration.colors.defaults.highContrastLight' must be a string." + "If defined, 'configuration.colors.defaults.highContrastLight' must be a string.", + "ID", + "Description", + "Dark Default", + "Light Default", + "High Contrast Default", + "Colors" + ], + "vs/workbench/services/themes/common/iconExtensionPoint": [ + "Contributes extension defined themable icons", + "The identifier of the themable icon", + "Identifiers can only contain letters, digits and minuses and need to consist of at least two segments in the form `component-iconname`.", + "The description of the themable icon", + "The path of the icon font that defines the icon.", + "The character for the icon in the icon font.", + "The default of the icon. Either a reference to an extisting ThemeIcon or an icon in an icon font.", + "'configuration.icons' must be an object with the icon names as properties.", + "'configuration.icons' keys represent the icon id and can only contain letter, digits and minuses. They need to consist of at least two segments in the form `component-iconname`.", + "'configuration.icons.description' must be defined and can not be empty", + "Expected `contributes.icons.default.fontPath` to have file extension 'woff', woff2' or 'ttf', is '{0}'.", + "Expected `contributes.icons.default.fontPath` ({0}) to be included inside extension's folder ({0}).", + "'configuration.icons.default' must be either a reference to the id of an other theme icon (string) or a icon definition (object) with properties `fontPath` and `fontCharacter`." ], "vs/workbench/services/themes/common/tokenClassificationExtensionPoint": [ "Contributes semantic token types.", @@ -22624,7 +24210,7 @@ "'configuration.semanticTokenScopes.scopes' values must be an array of strings", "configuration.semanticTokenScopes.scopes': Problems parsing selector {0}." ], - "vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint": [ + "vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint": [ "Errors parsing {0}: {1}", "{0}: Invalid format, JSON object expected.", "The opening bracket character or string sequence.", @@ -22703,6 +24289,9 @@ "Contributes items to the status bar.", "Invalid status bar item contribution." ], + "vs/workbench/api/browser/mainThreadLanguageModels": [ + "Language Models" + ], "vs/workbench/api/browser/mainThreadCLICommands": [ "Cannot install the '{0}' extension because it is declared to not run in this setup." ], @@ -22733,7 +24322,7 @@ "&&OK", "Show &&Preview", "Skip Changes", - "Don't ask again", + "Do not ask me again", "Running 'File Create' participants...", "Running 'File Rename' participants...", "Running 'File Copy' participants...", @@ -22742,7 +24331,6 @@ "Reset choice for 'File operation needs preview'" ], "vs/workbench/api/browser/mainThreadMessageService": [ - "{0} (Extension)", "Extension", "Manage Extension", "Cancel", @@ -22777,24 +24365,79 @@ "vs/workbench/api/browser/mainThreadTask": [ "{0}: {1}" ], - "vs/workbench/api/browser/mainThreadTunnelService": [ - "The extension {0} has forwarded port {1}. You'll need to run as superuser to use port {2} locally.", - "Use Port {0} as Sudo..." + "vs/workbench/api/browser/mainThreadTunnelService": [ + "The extension {0} has forwarded port {1}. You'll need to run as superuser to use port {2} locally.", + "Use Port {0} as Sudo..." + ], + "vs/workbench/api/browser/mainThreadAuthentication": [ + "Successfully signed out.", + "The extension '{0}' wants you to sign in again using {1}.", + "The extension '{0}' wants to sign in using {1}.", + "&&Allow", + "Learn more" + ], + "vs/workbench/browser/parts/titlebar/windowTitle": [ + "[Administrator]", + "[Superuser]", + "[Extension Development Host]" + ], + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions": [ + "Icon to toggle the auxiliary bar off in its right position.", + "Icon to toggle the auxiliary bar on in its right position.", + "Icon to toggle the auxiliary bar in its left position.", + "Icon to toggle the auxiliary bar on in its left position.", + "Secondary Side Bar", + "Secondary Si&&de Bar", + "Toggle Secondary Side Bar", + "Toggle Secondary Side Bar", + "Toggle Secondary Side Bar Visibility", + "Focus into Secondary Side Bar", + "Hide Secondary Side Bar" + ], + "vs/workbench/browser/parts/panel/panelActions": [ + "Icon to maximize a panel.", + "Icon to restore a panel.", + "Icon to close a panel.", + "Icon to toggle the panel off when it is on.", + "Icon to toggle the panel on when it is off.", + "Panel", + "&&Panel", + "Focus into Panel", + "Left", + "Right", + "Bottom", + "Left", + "Right", + "Center", + "Justify", + "Panel Position", + "Align Panel", + "Maximize Panel Size", + "Restore Panel Size", + "Maximizing the panel is only supported when it is center aligned.", + "Toggle Panel", + "Toggle Panel Visibility", + "Focus into Panel", + "Move Panel Left", + "Move Panel Right", + "Move Panel To Bottom", + "Set Panel Alignment to Left", + "Set Panel Alignment to Right", + "Set Panel Alignment to Center", + "Set Panel Alignment to Justify", + "Previous Panel View", + "Next Panel View", + "Toggle Maximized Panel", + "Hide Panel", + "Hide Secondary Side Bar", + "Hide Panel", + "Move Panel Views To Secondary Side Bar", + "Move Panel Views To Secondary Side Bar", + "Move Secondary Side Bar Views To Panel", + "Move Secondary Side Bar Views To Panel" ], - "vs/workbench/api/browser/mainThreadAuthentication": [ - "This account has not been used by any extensions.", - "Cancel", - "Last used this account {0}", - "Has not used this account", - "Manage Trusted Extensions", - "Choose which extensions can access this account", - "The account '{0}' has been used by: \n\n{1}\n\n Sign out from these extensions?", - "Sign out of '{0}'?", - "&&Sign Out", - "Successfully signed out.", - "The extension '{0}' wants you to sign in again using {1}.", - "The extension '{0}' wants to sign in using {1}.", - "&&Allow" + "vs/workbench/browser/quickaccess": [ + "Whether keyboard focus is inside the quick open control" ], "vs/workbench/services/extensions/common/extensionsRegistry": [ "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine.", @@ -22839,6 +24482,7 @@ "An activation event emitted when a specific terminal profile is launched.", "An activation event emitted when a command matches the selector associated with this ID", "An activation event emitted when a specified walkthrough is opened.", + "An activation event emitted when the issue reporter is opened.", "An activation event emitted on VS Code startup. To ensure a great end user experience, please use this activation event in your extension only when no other activation events combination works in your use-case.", "Array of badges to display in the sidebar of the Marketplace's extension page.", "Badge image URL.", @@ -22877,11 +24521,6 @@ "The pricing information for the extension. Can be Free (default) or Trial. For more details visit: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#extension-pricing-label", "API proposals that the respective extensions can freely use." ], - "vs/workbench/browser/parts/titlebar/windowTitle": [ - "[Administrator]", - "[Superuser]", - "[Extension Development Host]" - ], "vs/workbench/browser/parts/views/treeView": [ "There is no data provider registered that can provide view data.", "Whether the the tree view with id {0} enables collapse all.", @@ -22891,6 +24530,14 @@ "Whether collapse all is toggled for the tree view with id {0}.", "Error running command {1}: {0}. This is likely caused by the extension that contributes {1}." ], + "vs/workbench/browser/parts/views/viewPaneContainer": [ + "Views", + "Move View Up", + "Move View Left", + "Move View Down", + "Move View Right", + "Move Views" + ], "vs/workbench/contrib/debug/common/debug": [ "Debug type of the active debug session. For example 'python'.", "Debug type of the selected launch configuration. For example 'python'.", @@ -22914,10 +24561,13 @@ "Represents the item type of the focused element in the WATCH view. For example: 'expression', 'variable'", "Indicates whether the item in the view has an associated memory refrence.", "Represents the item type of the focused element in the BREAKPOINTS view. For example: 'breakpoint', 'exceptionBreakppint', 'functionBreakpoint', 'dataBreakpoint'", + "Whether the breakpoint item is a data breakpoint on a byte range.", + "Whether the breakpoint has multiple modes it can switch to.", "True when the focused breakpoint supports conditions.", "True when the focused sessions supports the LOADED SCRIPTS view", "Represents the item type of the focused element in the LOADED SCRIPTS view.", "True when the focused session is 'attach'.", + "True when the focused session is run without debugging.", "True when the focused session supports 'stepBack' requests.", "True when the focused session supports 'restartFrame' requests.", "True when the focused stack frame suppots 'restartFrame'.", @@ -22928,6 +24578,7 @@ "True when there is at least one debug extension installed and enabled.", "Represents the context the debug adapter sets on the focused variable in the VARIABLES view.", "True when the focused session supports 'setVariable' request.", + "True when the focused session supports 'getBreakpointInfo' request on an address.", "True when the focused session supports 'setExpression' request.", "True when the focused session supports to break when value changes.", "True when the focused breakpoint supports to break when value is accessed.", @@ -22936,6 +24587,12 @@ "True when the focused session supports the suspend debuggee capability.", "True when the focused variable has an 'evalauteName' field set.", "True when the focused variable is read-only.", + "Value of the variable, present for debug visualization clauses.", + "Type of the variable, present for debug visualization clauses.", + "Any interfaces or contracts that the variable satisfies, present for debug visualization clauses.", + "Name of the variable, present for debug visualization clauses.", + "Language of the variable source, present for debug visualization clauses.", + "Extension ID of the variable source, present for debug visualization clauses.", "True when the exception widget is visible.", "True when there is more than 1 debug console.", "True when there is more than 1 active debug session.", @@ -22946,14 +24603,6 @@ "Configured debug type '{0}' is installed but not supported in this environment.", "Controls when the internal Debug Console should open." ], - "vs/workbench/browser/parts/views/viewPaneContainer": [ - "Views", - "Move View Up", - "Move View Left", - "Move View Down", - "Move View Right", - "Move Views" - ], "vs/workbench/contrib/files/common/files": [ "True when the EXPLORER viewlet is visible.", "True when the FOLDERS view (the file tree within the explorer view container) is visible.", @@ -22973,27 +24622,30 @@ "vs/workbench/contrib/remote/browser/remoteExplorer": [ "No forwarded ports. Forward a port to access your running services locally.\n[Forward a Port]({0})", "No forwarded ports. Forward a port to access your locally running services over the internet.\n[Forward a Port]({0})", - "Ports", "1 forwarded port", "{0} forwarded ports", "No Ports Forwarded", "Forwarded Ports: {0}", "Forwarded Ports", - "Your application running on port {0} is available. ", + "Over 20 ports have been automatically forwarded. The `process` based automatic port forwarding has been switched to `hybrid` in settings. Some ports may no longer be detected.", + "Undo", + "Show Setting", + "Your application{0} running on port {1} is available. ", "[See all forwarded ports]({0})", "You'll need to run as superuser to use port {0} locally. ", "Make Public", - "Use Port {0} as Sudo..." + "Use Port {0} as Sudo...", + "Ports" ], "vs/workbench/common/editor/sideBySideEditorInput": [ "{0} - {1}" ], - "vs/workbench/browser/parts/editor/sideBySideEditor": [ - "Side by Side Editor" - ], "vs/workbench/common/editor/diffEditorInput": [ "{0} ↔ {1}" ], + "vs/workbench/browser/parts/editor/sideBySideEditor": [ + "Side by Side Editor" + ], "vs/workbench/browser/parts/editor/textDiffEditor": [ "Text Diff Editor", "At least one file is not displayed in the text compare editor because it is very large ({0}).", @@ -23038,7 +24690,6 @@ "Current Problem", "Current Problem", "Search Marketplace Extensions for '{0}'...", - "Change Language Mode", "No text editor active at this time", "({0}) - Configured Language", "({0})", @@ -23049,11 +24700,9 @@ "Select Language Mode", "Current Association", "Select Language Mode to Associate with '{0}'", - "Change End of Line Sequence", "No text editor active at this time", "The active code editor is read-only.", "Select End of Line Sequence", - "Change File Encoding", "No text editor active at this time", "No text editor active at this time", "No file active at this time", @@ -23062,7 +24711,18 @@ "Select Action", "Guessed from content", "Select File Encoding to Reopen File", - "Select File Encoding to Save with" + "Select File Encoding to Save with", + "Change Language Mode", + "Change End of Line Sequence", + "Change File Encoding" + ], + "vs/workbench/browser/parts/editor/diffEditorCommands": [ + "Compare", + "Compare", + "Go to Next Change", + "Go to Previous Change", + "Toggle Inline View", + "Swap Left and Right Editor Side" ], "vs/workbench/browser/parts/editor/editorCommands": [ "Move the active editor by tabs or groups", @@ -23071,10 +24731,6 @@ "Copy the active editor by groups", "Active editor copy argument", "Argument Properties:\n\t* 'to': String value providing where to copy.\n\t* 'value': Number value providing how many positions or an absolute position to copy.", - "Go to Next Change", - "Go to Previous Change", - "Toggle Inline View", - "Compare", "Split Editor in Group", "Join Editor in Group", "Toggle Split Editor in Group", @@ -23086,14 +24742,42 @@ "Lock Editor Group", "Unlock Editor Group" ], + "vs/editor/browser/editorExtensions": [ + "&&Undo", + "Undo", + "&&Redo", + "Redo", + "&&Select All", + "Select All" + ], "vs/workbench/browser/parts/editor/editorActions": [ + "Split Editor Up", + "Split Editor Down", + "Close Editor", + "Unpin Editor", + "Close", + "Go Forward", + "&&Forward", + "Go Back", + "&&Back", + "Do you want to clear all recently opened files and workspaces?", + "This action is irreversible!", + "&&Clear", + "Do you want to clear the history of recently opened editors?", + "This action is irreversible!", + "&&Clear", + "Split Editor into Left Group", + "&&Move Editor into New Window", + "&&Copy Editor into New Window", + "&&Move Editor Group into New Window", + "&&Copy Editor Group into New Window", + "&&Restore Editors into Main Window", + "&&New Empty Editor Window", "Split Editor", "Split Editor Orthogonal", "Split Editor Left", "Split Editor Right", "Split Editor Up", - "Split Editor Up", - "Split Editor Down", "Split Editor Down", "Join Editor Group with Next Group", "Join All Editor Groups", @@ -23107,9 +24791,6 @@ "Focus Right Editor Group", "Focus Editor Group Above", "Focus Editor Group Below", - "Close Editor", - "Unpin Editor", - "Close", "Revert and Close Editor", "Close Editors to the Left in Group", "Close All Editors", @@ -23125,6 +24806,7 @@ "Duplicate Editor Group Up", "Duplicate Editor Group Down", "Expand Editor Group", + "Expand Editor Group and Hide Side Bars", "Reset Editor Group Sizes", "Toggle Editor Group Sizes", "Maximize Editor Group and Hide Side Bars", @@ -23136,11 +24818,7 @@ "Open First Editor in Group", "Open Last Editor in Group", "Go Forward", - "Go Forward", - "&&Forward", - "Go Back", "Go Back", - "&&Back", "Go Previous", "Go Forward in Edit Locations", "Go Back in Edit Locations", @@ -23151,10 +24829,7 @@ "Go Previous in Navigation Locations", "Go to Last Navigation Location", "Reopen Closed Editor", - "Clear Recently Opened", - "Do you want to clear all recently opened files and workspaces?", - "This action is irreversible!", - "&&Clear", + "Clear Recently Opened...", "Show Editors in Active Group By Most Recently Used", "Show All Editors By Appearance", "Show All Editors By Most Recently Used", @@ -23168,9 +24843,6 @@ "Open Next Recently Used Editor In Group", "Open Previous Recently Used Editor In Group", "Clear Editor History", - "Do you want to clear the history of recently opened editors?", - "This action is irreversible!", - "&&Clear", "Move Editor Left", "Move Editor Right", "Move Editor into Previous Group", @@ -23186,7 +24858,6 @@ "Split Editor into Group Above", "Split Editor into Group Below", "Split Editor into Left Group", - "Split Editor into Left Group", "Split Editor into Right Group", "Split Editor into First Group", "Split Editor into Last Group", @@ -23204,16 +24875,22 @@ "New Editor Group Below", "Toggle Editor Type", "Reopen Editor With Text Editor", - "Move Active Editor into a New Window (Experimental)", - "&&Move Active Editor into a New Window (Experimental)" + "Move Editor into New Window", + "Copy Editor into New Window", + "Move Editor Group into New Window", + "Copy Editor Group into New Window", + "Restore Editors into Main Window", + "New Empty Editor Window" ], - "vs/editor/browser/editorExtensions": [ - "&&Undo", - "Undo", - "&&Redo", - "Redo", - "&&Select All", - "Select All" + "vs/workbench/browser/parts/editor/editorConfiguration": [ + "Interactive Window", + "Markdown Preview", + "Simple Browser", + "Live Preview", + "If an editor matching one of the listed types is opened as the first in an editor group and more than one group is open, the group is automatically locked. Locked groups will only be used for opening editors when explicitly chosen by a user gesture (for example drag and drop), but not by default. Consequently, the active editor in a locked group is less likely to be replaced accidentally with a different editor.", + "The default editor for files detected as binary. If undefined, the user will be presented with a picker.", + "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior.", + "Controls the minimum size of a file in MB before asking for confirmation when opening in the editor. Note that this setting may not apply to all editor types and environments." ], "vs/workbench/browser/parts/editor/editorQuickAccess": [ "No matching editors", @@ -23222,13 +24899,11 @@ "{0}, unsaved changes", "Close Editor" ], - "vs/workbench/browser/parts/editor/editorConfiguration": [ - "Interactive Window", - "Markdown Preview", - "If an editor matching one of the listed types is opened as the first in an editor group and more than one group is open, the group is automatically locked. Locked groups will only be used for opening editors when explicitly chosen by a user gesture (for example drag and drop), but not by default. Consequently, the active editor in a locked group is less likely to be replaced accidentally with a different editor.", - "The default editor for files detected as binary. If undefined, the user will be presented with a picker.", - "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior.", - "Controls the minimum size of a file in MB before asking for confirmation when opening in the editor. Note that this setting may not apply to all editor types and environments." + "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ + "Activity Bar Position", + "Move Secondary Side Bar Left", + "Move Secondary Side Bar Right", + "Hide Secondary Side Bar" ], "vs/workbench/browser/parts/panel/panelPart": [ "Panel Position", @@ -23236,27 +24911,18 @@ "Hide Panel" ], "vs/workbench/browser/parts/sidebar/sidebarPart": [ - "Manage", - "Accounts" - ], - "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart": [ - "Move Secondary Side Bar Left", - "Move Secondary Side Bar Right", - "Hide Secondary Side Bar" - ], - "vs/workbench/browser/parts/statusbar/statusbarActions": [ - "Hide '{0}'", - "Focus Status Bar" + "Toggle Activity Bar Visibility" ], "vs/platform/actions/common/menuResetAction": [ "Reset All Menus" ], "vs/platform/actions/common/menuService": [ - "Hide '{0}'" + "Hide '{0}'", + "Configure Keybinding" ], - "vs/base/browser/ui/icons/iconSelectBox": [ - "Search icons", - "No results" + "vs/workbench/browser/parts/statusbar/statusbarActions": [ + "Hide '{0}'", + "Focus Status Bar" ], "vs/base/browser/ui/dialog/dialog": [ "OK", @@ -23266,12 +24932,14 @@ "In Progress", "Close Dialog" ], - "vs/workbench/services/preferences/common/preferencesEditorInput": [ - "Settings" - ], "vs/workbench/services/preferences/browser/keybindingsEditorInput": [ + "Icon of the keybindings editor label.", "Keyboard Shortcuts" ], + "vs/workbench/services/preferences/common/preferencesEditorInput": [ + "Icon of the settings editor label.", + "Settings" + ], "vs/workbench/services/preferences/common/preferencesModels": [ "Commonly Used", "Override key bindings by placing them into your key bindings file." @@ -23279,6 +24947,9 @@ "vs/workbench/services/textfile/common/textFileEditorModel": [ "File Encoding Changed" ], + "vs/workbench/services/editor/common/editorResolverService": [ + "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior." + ], "vs/base/common/keybindingLabels": [ "Ctrl", "Shift", @@ -23307,9 +24978,6 @@ "The key combination ({0}, {1}) is not a command.", "The key combination ({0}, {1}) is not a command." ], - "vs/workbench/services/editor/common/editorResolverService": [ - "Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `\"*.hex\": \"hexEditor.hexedit\"`). These have precedence over the default behavior." - ], "vs/workbench/services/themes/common/colorThemeData": [ "Problems parsing JSON theme file: {0}", "Invalid format for JSON theme file: Object expected.", @@ -23380,25 +25048,6 @@ "Whether semantic highlighting should be enabled for this theme.", "Colors for semantic tokens" ], - "vs/workbench/services/themes/common/themeExtensionPoints": [ - "Contributes textmate color themes.", - "Id of the color theme as used in the user settings.", - "Label of the color theme as shown in the UI.", - "Base theme defining the colors around the editor: 'vs' is the light color theme, 'vs-dark' is the dark color theme. 'hc-black' is the dark high contrast theme, 'hc-light' is the light high contrast theme.", - "Path of the tmTheme file. The path is relative to the extension folder and is typically './colorthemes/awesome-color-theme.json'.", - "Contributes file icon themes.", - "Id of the file icon theme as used in the user settings.", - "Label of the file icon theme as shown in the UI.", - "Path of the file icon theme definition file. The path is relative to the extension folder and is typically './fileicons/awesome-icon-theme.json'.", - "Contributes product icon themes.", - "Id of the product icon theme as used in the user settings.", - "Label of the product icon theme as shown in the UI.", - "Path of the product icon theme definition file. The path is relative to the extension folder and is typically './producticons/awesome-product-icon-theme.json'.", - "Extension point `{0}` must be an array.", - "Expected string in `contributes.{0}.path`. Provided value: {1}", - "Expected string in `contributes.{0}.id`. Provided value: {1}", - "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable." - ], "vs/workbench/services/themes/browser/productIconThemeData": [ "Problems processing product icons definitions in {0}:\n{1}", "Default", @@ -23413,44 +25062,6 @@ "Skipping icon definition '{0}'. Unknown font.", "Skipping icon definition '{0}'. Unknown fontCharacter." ], - "vs/workbench/services/themes/common/themeConfiguration": [ - "Specifies the color theme used in the workbench.", - "Theme is unknown or not installed.", - "Specifies the preferred color theme for dark OS appearance when {0} is enabled.", - "Theme is unknown or not installed.", - "Specifies the preferred color theme for light OS appearance when {0} is enabled.", - "Theme is unknown or not installed.", - "Specifies the preferred color theme used in high contrast dark mode when {0} is enabled.", - "Theme is unknown or not installed.", - "Specifies the preferred color theme used in high contrast light mode when {0} is enabled.", - "Theme is unknown or not installed.", - "If set, automatically switch to the preferred color theme based on the OS appearance. If the OS appearance is dark, the theme specified at {0} is used, for light {1}.", - "Overrides colors from the currently selected color theme.", - "Specifies the file icon theme used in the workbench or 'null' to not show any file icons.", - "None", - "No file icons", - "File icon theme is unknown or not installed.", - "Specifies the product icon theme used.", - "Default", - "Default", - "Product icon theme is unknown or not installed.", - "If enabled, will automatically change to high contrast theme if the OS is using a high contrast theme. The high contrast theme to use is specified by {0} and {1}.", - "Sets the colors and styles for comments", - "Sets the colors and styles for strings literals.", - "Sets the colors and styles for keywords.", - "Sets the colors and styles for number literals.", - "Sets the colors and styles for type declarations and references.", - "Sets the colors and styles for functions declarations and references.", - "Sets the colors and styles for variables declarations and references.", - "Sets colors and styles using textmate theming rules (advanced).", - "Whether semantic highlighting should be enabled for this theme.", - "Use `enabled` in `editor.semanticTokenColorCustomizations` setting instead.", - "Use `enabled` in {0} setting instead.", - "Overrides editor syntax colors and font style from the currently selected color theme.", - "Whether semantic highlighting is enabled or disabled for this theme", - "Semantic token styling rules for this theme.", - "Overrides editor semantic token color and styles from the currently selected color theme." - ], "vs/workbench/services/themes/common/productIconThemeSchema": [ "The ID of the font.", "The ID must only contain letters, numbers, underscore and minus.", @@ -23461,16 +25072,37 @@ "The style of the font. See https://developer.mozilla.org/en-US/docs/Web/CSS/font-style for valid values.", "Association of icon name to a font character." ], + "vs/workbench/services/themes/common/themeExtensionPoints": [ + "Contributes textmate color themes.", + "Id of the color theme as used in the user settings.", + "Label of the color theme as shown in the UI.", + "Base theme defining the colors around the editor: 'vs' is the light color theme, 'vs-dark' is the dark color theme. 'hc-black' is the dark high contrast theme, 'hc-light' is the light high contrast theme.", + "Path of the tmTheme file. The path is relative to the extension folder and is typically './colorthemes/awesome-color-theme.json'.", + "Contributes file icon themes.", + "Id of the file icon theme as used in the user settings.", + "Label of the file icon theme as shown in the UI.", + "Path of the file icon theme definition file. The path is relative to the extension folder and is typically './fileicons/awesome-icon-theme.json'.", + "Contributes product icon themes.", + "Id of the product icon theme as used in the user settings.", + "Label of the product icon theme as shown in the UI.", + "Path of the product icon theme definition file. The path is relative to the extension folder and is typically './producticons/awesome-product-icon-theme.json'.", + "Color Themes", + "File Icon Themes", + "Product Icon Themes", + "Themes", + "Extension point `{0}` must be an array.", + "Expected string in `contributes.{0}.path`. Provided value: {1}", + "Expected string in `contributes.{0}.id`. Provided value: {1}", + "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable." + ], "vs/workbench/services/extensionManagement/browser/extensionBisect": [ "I can't reproduce", "I can reproduce", "Extension Bisect is active and has disabled 1 extension. Check if you can still reproduce the problem and proceed by selecting from these options.", "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", - "Start Extension Bisect", "Extension Bisect", "Extension Bisect will use binary search to find an extension that causes a problem. During the process the window reloads repeatedly (~{0} times). Each time you must confirm if you are still seeing problems.", "&&Start Extension Bisect", - "Continue Extension Bisect", "Extension Bisect", "Extension Bisect is done but no extension has been identified. This might be a problem with {0}.", "Extension Bisect", @@ -23484,6 +25116,8 @@ "I can &&reproduce", "&&Stop Bisect", "&&Cancel Bisect", + "Start Extension Bisect", + "Continue Extension Bisect", "Stop Extension Bisect" ], "vs/workbench/services/userDataProfile/browser/settingsResource": [ @@ -23511,9 +25145,6 @@ "vs/workbench/services/userDataProfile/common/userDataProfileIcons": [ "Settings icon in the view bar." ], - "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ - "Saving '{0}'" - ], "vs/workbench/services/remote/common/tunnelModel": [ "Whether the Ports view is enabled.", "User Forwarded", @@ -23521,9 +25152,18 @@ "Local port {0} could not be used for forwarding to remote port {1}.\n\nThis usually happens when there is already another process using local port {0}.\n\nPort number {2} has been used instead.", "Statically Forwarded" ], - "vs/workbench/services/hover/browser/hoverWidget": [ + "vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant": [ + "Saving '{0}'" + ], + "vs/workbench/services/views/common/viewContainerModel": [ + "Show Views Log" + ], + "vs/editor/browser/services/hoverService/hoverWidget": [ "Hold {0} key to mouse over" ], + "vs/editor/browser/services/hoverService/updatableHoverWidget": [ + "Loading..." + ], "vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl": [ "Already Logging.", "Stop", @@ -23543,12 +25183,6 @@ "{0} existing commands have this keybinding", "chord to" ], - "vs/workbench/contrib/performance/browser/perfviewEditor": [ - "Startup Performance" - ], - "vs/workbench/contrib/speech/common/speechService": [ - "A speech provider is registered to the speech service." - ], "vs/editor/contrib/suggest/browser/suggest": [ "Whether any suggestion is focused", "Whether suggestion details are visible", @@ -23559,6 +25193,11 @@ "Whether the default behaviour is to insert or replace", "Whether the current suggestion supports to resolve further details" ], + "vs/workbench/contrib/preferences/browser/preferencesActions": [ + "({0})", + "Select Language", + "Configure Language Specific Settings..." + ], "vs/workbench/contrib/preferences/browser/keybindingsEditor": [ "Record Keys", "Sort by Precedence (Highest first)", @@ -23608,16 +25247,11 @@ "Icon for the button that suggests filters for the Settings UI.", "Icon for open settings commands." ], - "vs/workbench/contrib/preferences/browser/preferencesActions": [ - "Configure Language Specific Settings...", - "({0})", - "Select Language" - ], "vs/workbench/contrib/preferences/common/preferencesContribution": [ "Split Settings Editor", "Controls whether to enable the natural language search mode for settings. The natural language search is provided by a Microsoft online service.", - "Hide the Table of Contents while searching. The search results will not be grouped by category, and instead will be sorted by similarity to the query, with exact keyword matches coming first.", - "Filter the Table of Contents to just categories that have matching settings. Clicking a category will filter the results to that category. The search results will be grouped by category.", + "Hide the Table of Contents while searching.", + "Filter the Table of Contents to just categories that have matching settings. Clicking on a category will filter the results to that category.", "Controls the behavior of the Settings editor Table of Contents while searching. If this setting is being changed in the Settings editor, the setting will take effect after the search query is modified." ], "vs/workbench/contrib/preferences/browser/settingsEditor2": [ @@ -23634,79 +25268,43 @@ "Backup and Sync Settings", "Last synced: {0}" ], - "vs/workbench/contrib/chat/browser/actions/chatActions": [ - "Chat", - "Quick Chat", - "Accept Chat Input", - "Submit to Secondary Agent", - "Clear Input History", - "Focus Chat List", - "Focus Chat Input", - "Open Editor ({0})", - "Show History", - "Delete", - "Select a chat session to restore" - ], - "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ - "Copy", - "Insert at Cursor", - "Insert Into New File", - "Run in Terminal", - "Next Code Block", - "Previous Code Block" - ], - "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ - "Copy All", - "Copy" - ], - "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ - "Submit", - "Cancel" - ], - "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ - "Open in Chat View", - "Close Quick Chat", - "Quick Chat", - "Toggle the quick chat", - "The query to open the quick chat with", - "Whether the query is partial; it will wait for more user input", - "The query to open the quick chat with", - "Open Quick Chat ({0})" - ], - "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ - "Helpful", - "Unhelpful", - "Insert into Notebook", - "Remove Request and Response" - ], - "vs/workbench/contrib/chat/browser/chatContributionServiceImpl": [ - "Contributes an Interactive Session provider", - "Unique identifier for this Interactive Session provider.", - "Display name for this Interactive Session provider.", - "An icon for this Interactive Session provider.", - "A condition which must be true to enable this Interactive Session provider.", - "Chat" - ], - "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ - "Chat Session", - "Export Session", - "Import Session" - ], - "vs/workbench/contrib/chat/browser/chatEditorInput": [ - "Chat" - ], - "vs/workbench/contrib/chat/common/chatServiceImpl": [ - "Provider returned null response" - ], - "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ - "Open Session In Editor", - "Open Session In Editor", - "Open Session In Sidebar" + "vs/workbench/contrib/performance/browser/perfviewEditor": [ + "Startup Performance" ], - "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ - "Clear", - "Clear", - "Clear" + "vs/workbench/contrib/speech/common/speechService": [ + "A speech provider is registered to the speech service.", + "A speech-to-text session is in progress.", + "Danish (Denmark)", + "German (Germany)", + "English (Australia)", + "English (Canada)", + "English (United Kingdom)", + "English (Ireland)", + "English (India)", + "English (New Zealand)", + "English (United States)", + "Spanish (Spain)", + "Spanish (Mexico)", + "French (Canada)", + "French (France)", + "Hindi (India)", + "Italian (Italy)", + "Japanese (Japan)", + "Korean (South Korea)", + "Dutch (Netherlands)", + "Portuguese (Portugal)", + "Portuguese (Brazil)", + "Russian (Russia)", + "Swedish (Sweden)", + "Turkish (Türkiye)", + "Chinese (Simplified, China)", + "Chinese (Traditional, Hong Kong)", + "Chinese (Traditional, Taiwan)" + ], + "vs/workbench/contrib/speech/browser/speechService": [ + "Contributes a Speech Provider", + "Unique name for this Speech Provider.", + "A description of this Speech Provider, shown in the UI." ], "vs/workbench/contrib/accessibility/browser/accessibleView": [ "({0}) {1}", @@ -23722,20 +25320,26 @@ "Accessibility Help, {0}", "Accessibility Help", "Accessible View", - "Navigate to the toolbar (Shift+Tab)).", + "Navigate to the toolbar (Shift+Tab).", "In the accessible view, you can:\n", + " - Insert the code block at the cursor ({0}).\n", + " - Insert the code block at the cursor by configuring a keybinding for the Chat: Insert Code Block command.\n", + " - Insert the code block into a new file ({0}).\n", + " - Insert the code block into a new file by configuring a keybinding for the Chat: Insert into New File command.\n", + " - Run the code block in the terminal ({0}).\n", + " - Run the coe block in the terminal by configuring a keybinding for the Chat: Insert into Terminal command.\n", "Show the next ({0}) or previous ({1}) item.", "Show the next or previous item by configuring keybindings for the Show Next & Previous in Accessible View commands.", "\n\nDisable accessibility verbosity for this feature ({0}).", "\n\nAdd a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.s", - "Go to a symbol ({0})", + "Go to a symbol ({0}).", "To go to a symbol, configure a keybinding for the command Go To Symbol in Accessible View", "Inspect this in the accessible view with {0}", "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.", "Type to search symbols", "Go to Symbol Accessible View" ], - "vs/workbench/contrib/accessibility/browser/accessibilityContributions": [ + "vs/workbench/contrib/accessibility/browser/accessibleViewContributions": [ "{0} Source: {1}", "{0}", "Clear Notification", @@ -23743,6 +25347,8 @@ ], "vs/workbench/contrib/accessibility/browser/accessibleViewActions": [ "Show Next in Accessible View", + "Accessible View: Next Code Block", + "Accessible View: Previous Code Block", "Show Previous in Accessible View", "Go To Symbol in Accessible View", "Open Accessibility Help", @@ -23750,34 +25356,218 @@ "Disable Accessible View Hint", "Accept Inline Completion" ], + "vs/workbench/contrib/chat/browser/actions/chatClearActions": [ + "New Chat", + "New Chat" + ], + "vs/workbench/contrib/chat/browser/actions/chatActions": [ + "Delete", + "Switch to chat", + "Chat", + "Open Chat", + "Show Chats...", + "Open Editor", + "Clear Input History", + "Clear All Workspace Chats", + "Focus Chat List", + "Focus Chat Input" + ], + "vs/workbench/contrib/chat/browser/actions/chatCodeblockActions": [ + "Copy", + "Insert at Cursor", + "Insert into New File", + "Insert into Terminal", + "Next Code Block", + "Previous Code Block", + "Apply Edits" + ], + "vs/workbench/contrib/chat/browser/actions/chatCopyActions": [ + "Copy All", + "Copy" + ], + "vs/workbench/contrib/chat/browser/actions/chatExecuteActions": [ + "Send", + "Submit to Secondary Agent", + "Send to New Chat", + "Cancel" + ], "vs/workbench/contrib/chat/browser/actions/chatFileTreeActions": [ "Next File Tree", "Previous File Tree" ], + "vs/workbench/contrib/chat/browser/actions/chatImportExport": [ + "Chat Session", + "Export Chat...", + "Import Chat..." + ], + "vs/workbench/contrib/chat/browser/actions/chatMoveActions": [ + "Open Chat in Editor", + "Open Chat in New Window", + "Open Chat in Side Bar" + ], + "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions": [ + "Toggle the quick chat", + "The query to open the quick chat with", + "Whether the query is partial; it will wait for more user input", + "The query to open the quick chat with", + "Open in Chat View", + "Close Quick Chat", + "Launch Inline Chat", + "Quick Chat", + "Open Quick Chat" + ], + "vs/workbench/contrib/chat/browser/actions/chatTitleActions": [ + "Rerun...", + "Helpful", + "Unhelpful", + "Report Issue", + "Insert into Notebook", + "Remove Request and Response", + "Rerun Request", + "Rerun without Command Detection" + ], + "vs/workbench/contrib/chat/browser/chat": [ + "Generating" + ], + "vs/workbench/contrib/chat/browser/chatEditorInput": [ + "Icon of the chat editor label.", + "Chat" + ], "vs/workbench/contrib/chat/common/chatContextKeys": [ - "True when the provider has assigned an id to this response.", "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.", + "When the agent or command was automatically detected", + "True when the current chat response supports issue reporting.", "True when the chat response was filtered out by the server.", "True when the current request is still in progress.", "The chat item is a response.", "The chat item is a request", + "True when the chat text edits have been applied.", "True when the chat input has text.", + "True when the chat input has focus.", "True when focus is in the chat input, false otherwise.", "True when focus is in the chat widget, false otherwise.", - "True when some chat provider has been registered." + "True when chat is enabled because a default chat participant is registered." ], - "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ - "Pick a file" + "vs/workbench/contrib/chat/common/chatServiceImpl": [ + "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date.", + "Show Extension", + "Provider returned null response" + ], + "vs/workbench/contrib/chat/common/languageModelStats": [ + "Language Models", + "Language models usage statistics of this extension." + ], + "vs/workbench/contrib/chat/browser/chatParticipantContributions": [ + "Contributes a chat participant", + "A unique id for this chat participant.", + "User-facing display name for this chat participant. The user will use '@' with this name to invoke the participant.", + "A description of this chat participant, shown in the UI.", + "**Only** allowed for extensions that have the `defaultChatParticipant` proposal.", + "Whether invoking the command puts the chat into a persistent mode, where the command is automatically added to the chat input for the next message.", + "Commands available for this chat participant, which the user can invoke with a `/`.", + "A short name by which this command is referred to in the UI, e.g. `fix` or * `explain` for commands that fix an issue or explain code. The name should be unique among the commands provided by this participant.", + "A description of this command.", + "A condition which must be true to enable this command.", + "When the user clicks this command in `/help`, this text will be submitted to this participant.", + "Whether invoking the command puts the chat into a persistent mode, where the command is automatically added to the chat input for the next message.", + "**Only** allowed for extensions that have the `chatParticipantAdditions` proposal. The names of the variables that are invoked by default", + "Locations in which this chat participant is available.", + "Chat" ], "vs/workbench/contrib/chat/common/chatColors": [ "The border color of a chat request.", + "The background color of a chat request.", "The background color of a chat slash command.", "The foreground color of a chat slash command.", "The background color of a chat avatar.", "The foreground color of a chat avatar." ], - "vs/workbench/contrib/notebook/common/notebookEditorInput": [ - "Notebook '{0}' could not be saved." + "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib": [ + "Pick a file" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ + "Failed to start editor chat", + "Getting ready...", + "AI-generated code may be incorrect", + "No results, please refine your input and try again", + "Failed to apply changes.", + "Failed to discard changes.", + "Accept or discard changes to continue saving" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ + "Icon which spawns the inline chat from the editor toolbar.", + "Cursor Up", + "Cursor Down", + "Discard", + "Discard...", + "Discard", + "Discard to Clipboard", + "Discard to New File", + "Accept", + "Cancel", + "Close", + "Configure ", + "'{0}' and {1} follow ups ({2})", + "View in Chat", + "Start in Editor", + "Resume Last Dismissed Inline Chat", + "Inline Chat", + "Focus Input", + "Toggle Changes", + "Accept Changes", + "Move to Next Change", + "Move to Previous Change", + "(Developer) Write Exchange to Clipboard" + ], + "vs/workbench/contrib/inlineChat/common/inlineChat": [ + "Whether a provider for interactive editors exists", + "Whether the interactive editor input is visible", + "Whether the interactive editor input is focused", + "Whether the interactive widget's response is focused", + "Whether the interactive editor input is empty", + "Whether the cursor of the iteractive editor input is on the first line", + "Whether the cursor of the iteractive editor input is on the last line", + "Whether the cursor of the iteractive editor input is on the start of the input", + "Whether the cursor of the iteractive editor input is on the end of the input", + "Whether the cursor of the outer editor is above or below the interactive editor input", + "Whether interactive editor has an active request", + "Whether interactive editor has kept a session for quick restore", + "What type was the last response of the current interactive editor session", + "What type was the responses have been receieved", + "Whether interactive editor did change any code", + "Whether the user did changes ontop of the inline chat", + "The last kind of feedback that was provided", + "Whether the interactive editor supports issue reporting", + "Whether the document has changed concurrently", + "Whether the current change supports showing a diff", + "Whether the current change showing a diff", + "Background color of the interactive editor widget", + "Border color of the interactive editor widget", + "Shadow color of the interactive editor widget", + "Background highlighting of the current interactive region. Must be transparent.", + "Border color of the interactive editor input", + "Border color of the interactive editor input when focused", + "Foreground color of the interactive editor input placeholder", + "Background color of the interactive editor input", + "Background color of inserted text in the interactive editor input", + "Overview ruler marker color for inline chat inserted content.", + "Overview ruler marker color for inline chat inserted content.", + "Background color of removed text in the interactive editor input", + "Overview ruler marker color for inline chat removed content.", + "Configure if changes crafted with inline chat are applied directly to the document or are previewed first.", + "Changes are applied directly to the document, can be highlighted via inline diffs, and accepted/discarded by hunks. Ending a session will keep the changes.", + "Changes are previewed only and need to be accepted via the apply button. Ending a session will discard the changes.", + "Whether to finish an inline chat session when typing outside of changed regions.", + "Whether pending inline chat sessions prevent saving.", + "Whether holding the inline chat keybinding will automatically enable speech recognition.", + "Whether the inline chat also renders an accessible diff viewer for its changes.", + "The accessible diff viewer is based screen reader mode being enabled.", + "The accessible diff viewer is always enabled.", + "The accessible diff viewer is never enabled." + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl": [ + "Waiting for Inline Chat changes to be Accepted or Discarded...", + "Waiting for Inline Chat changes in {0} editors to be Accepted or Discarded..." ], "vs/workbench/contrib/notebook/browser/notebookEditor": [ "Cannot open resource with notebook editor type '{0}', please check if you have the right extension installed and enabled.", @@ -23785,8 +25575,13 @@ "Enable extension for '{0}'", "Install extension for '{0}'", "Open As Text", + "The notebook is not displayed in the notebook editor because it is very large ({0}).", + "The notebook is not displayed in the notebook editor because it is very large.", "Open in Text Editor" ], + "vs/workbench/contrib/notebook/common/notebookEditorInput": [ + "Notebook '{0}' could not be saved." + ], "vs/workbench/contrib/notebook/browser/services/notebookServiceImpl": [ "Install extension for '{0}'" ], @@ -23796,13 +25591,16 @@ "vs/workbench/contrib/notebook/browser/services/notebookExecutionServiceImpl": [ "Executing a notebook cell will run code from this workspace." ], + "vs/editor/common/languages/modesRegistry": [ + "Plain Text" + ], "vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl": [ "Disable other keymaps ({0}) to avoid conflicts between keybindings?", "Yes", "No" ], - "vs/editor/common/languages/modesRegistry": [ - "Plain Text" + "vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl": [ + "Clear Notebook Kernels MRU Cache" ], "vs/workbench/contrib/comments/browser/commentReply": [ "Reply...", @@ -23810,9 +25608,6 @@ "Reply...", "Reply..." ], - "vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl": [ - "Clear Notebook Kernels MRU Cache" - ], "vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl": [ "Notebook rendering" ], @@ -23834,34 +25629,14 @@ "The Insert Cell Above/Below commands will create new empty code cells", "The Change Cell to Code/Markdown commands are used to switch between cell types." ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables": [ + "Notebook Variables" + ], "vs/workbench/contrib/notebook/browser/controller/coreActions": [ - "Notebook", "Insert Cell", "Notebook Cell", - "Share" - ], - "vs/workbench/contrib/notebook/browser/controller/executeActions": [ - "Render All Markdown Cells", - "Run All", - "Run All", - "Execute Cell", - "Execute Cell", - "Execute Above Cells", - "Execute Cell and Below", - "Execute Cell and Focus Container", - "Execute Cell and Focus Container", - "Stop Cell Execution", - "Stop Cell Execution", - "Execute Notebook Cell and Select Below", - "Execute Notebook Cell and Insert Below", - "Stop Execution", - "Interrupt", - "Go to Running Cell", - "Go to Running Cell", - "Go To", - "Go to Most Recently Failed Cell", - "Go to Most Recently Failed Cell", - "Go To" + "Share", + "Notebook" ], "vs/workbench/contrib/notebook/browser/controller/insertCellActions": [ "Insert Code Cell Above", @@ -23889,20 +25664,62 @@ "Markdown", "Add Markdown Cell" ], + "vs/workbench/contrib/notebook/browser/controller/sectionActions": [ + "&&Run Cell", + "Run Cell", + "&&Run Cells In Section", + "Run Cells In Section", + "&&Fold Section", + "Fold Section", + "&&Expand Section", + "Expand Section", + "Run Cell", + "Run Cells In Section", + "Fold Section", + "Expand Section" + ], + "vs/workbench/contrib/notebook/browser/controller/executeActions": [ + "Render All Markdown Cells", + "Run All", + "Run All", + "Execute Cell", + "Execute Cell", + "Execute Above Cells", + "Execute Cell and Below", + "Execute Cell and Focus Container", + "Execute Cell and Focus Container", + "Stop Cell Execution", + "Stop Cell Execution", + "Execute Notebook Cell and Select Below", + "Execute Notebook Cell and Insert Below", + "Go To", + "Go to Running Cell", + "Go to Running Cell", + "Go to Running Cell", + "Go to Most Recently Failed Cell", + "Go to Most Recently Failed Cell", + "Go to Most Recently Failed Cell", + "Stop Execution", + "Interrupt" + ], "vs/workbench/contrib/notebook/browser/controller/layoutActions": [ + "Notebook Line Numbers", + "Settings file to save in", + "User Settings", + "Workspace Settings", + "&&Toggle Notebook Sticky Scroll", + "Toggle Notebook Sticky Scroll", + "&&Toggle Notebook Sticky Scroll", "Select between Notebook Layouts", "Customize Notebook Layout", "Customize Notebook Layout", "Customize Notebook...", "Toggle Notebook Line Numbers", - "Notebook Line Numbers", "Toggle Cell Toolbar Position", "Toggle Breadcrumbs", "Save Mimetype Display Order", - "Settings file to save in", - "User Settings", - "Workspace Settings", - "Reset Notebook Webview" + "Reset Notebook Webview", + "Toggle Notebook Sticky Scroll" ], "vs/workbench/contrib/notebook/browser/controller/editActions": [ "Edit Cell", @@ -23920,33 +25737,40 @@ "Auto Detect", "languages (identifier)", "Select Language Mode", + "Unable to detect cell language", + "No notebook editor active at this time", + "The active notebook editor is read-only.", + "convert file", + "change view", + "Select Action", "Accept Detected Language for Cell", - "Unable to detect cell language" + "Select Indentation" ], "vs/workbench/contrib/notebook/browser/controller/cellOutputActions": [ - "Copy Output" + "Copy Cell Output" ], "vs/workbench/contrib/notebook/browser/controller/foldingController": [ "Fold Cell", "Unfold Cell", "Fold Cell" ], + "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ + "Hide Find in Notebook", + "Find in Notebook" + ], "vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard": [ "Copy Cell", "Cut Cell", "Paste Cell", "Paste Cell Above", + "Select All", "Toggle Notebook Clipboard Troubleshooting" ], - "vs/workbench/contrib/notebook/browser/contrib/find/notebookFind": [ - "Hide Find in Notebook", - "Find in Notebook" - ], "vs/workbench/contrib/notebook/browser/contrib/format/formatting": [ - "Format Notebook", "Format Notebook", "Format Cell", - "Format Cells" + "Format Cells", + "Format Notebook" ], "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants": [ "Formatting", @@ -23965,7 +25789,19 @@ "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions": [ "Toggle Cell Toolbar Position" ], + "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ + "When enabled, notebook outline will show only markdown cells containing a header.", + "When enabled, notebook outline shows code cells.", + "When enabled, notebook outline shows code cell symbols. Relies on `notebook.outline.showCodeCells` being enabled.", + "When enabled, notebook breadcrumbs contain code cells.", + "When enabled, the Go to Symbol Quick Pick will display full code symbols from the notebook, as well as Markdown headers.", + "Filter Entries", + "Markdown Headers Only", + "Code Cells", + "Code Cell Symbols" + ], "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow": [ + "Keypresses that should be handled by the focused element in the cell output.", "Focus Next Cell Editor", "Focus Previous Cell Editor", "Focus First Cell", @@ -23979,11 +25815,6 @@ "Cell Cursor Page Down Select", "When enabled cursor can navigate to the next/previous cell when the current cursor in the cell editor is at the first/last line." ], - "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline": [ - "When enabled notebook outline shows code cells.", - "When enabled notebook breadcrumbs contain code cells.", - "When enabled the Go to Symbol Quick Pick will display full code symbols from the notebook, as well as Markdown headers." - ], "vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile": [ "Set Profile" ], @@ -23998,7 +25829,8 @@ "Pending", "Executing", "Use the links above to file an issue using the issue reporter.", - "**Last Execution** {0}\n\n**Execution Time** {1}\n\n**Overhead Time** {2}\n\n**Render Times**\n\n{3}" + "**Last Execution** {0}\n\n**Execution Time** {1}\n\n**Overhead Time** {2}\n\n**Render Times**\n\n{3}", + "Quick Actions {0}" ], "vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar": [ "Notebook Kernel Info", @@ -24008,9 +25840,18 @@ "Select Kernel", "Notebook Editor Selections", "Cell {0} ({1} selected)", - "Cell {0} of {1}" + "Cell {0} of {1}", + "Notebook Indentation", + "Select Indentation" + ], + "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ + "Toggle Layout Troubleshoot", + "Inspect Notebook Layout", + "Clear Notebook Editor Type Cache" ], "vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands": [ + "Toggle Outputs", + "No code actions available", "Move Cell Up", "Move Cell Down", "Copy Cell Up", @@ -24026,160 +25867,213 @@ "Collapse Cell Output", "Expand Cell Output", "Toggle Outputs", - "Toggle Outputs", "Collapse All Cell Inputs", "Expand All Cell Inputs", "Collapse All Cell Outputs", "Expand All Cell Outputs", - "Toggle Scroll Cell Output" + "Toggle Scroll Cell Output", + "Show Cell Failure Actions" ], "vs/workbench/contrib/notebook/browser/diff/notebookDiffActions": [ - "Open Text Diff Editor", "Revert Metadata", "Switch Output Rendering", "Revert Outputs", "Revert Input", - "Show Outputs Differences", - "Show Metadata Differences", "Show Previous Change", "Show Next Change", "Hide Metadata Differences", - "Hide Outputs Differences" + "Hide Outputs Differences", + "Open Text Diff Editor", + "Show Outputs Differences", + "Show Metadata Differences" ], - "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout": [ - "Toggle Layout Troubleshoot", - "Inspect Notebook Layout", - "Clear Notebook Editor Type Cache" + "vs/editor/contrib/peekView/browser/peekView": [ + "Whether the current code editor is embedded inside peek", + "Close", + "Background color of the peek view title area.", + "Color of the peek view title.", + "Color of the peek view title info.", + "Color of the peek view borders and arrow.", + "Background color of the peek view result list.", + "Foreground color for line nodes in the peek view result list.", + "Foreground color for file nodes in the peek view result list.", + "Background color of the selected entry in the peek view result list.", + "Foreground color of the selected entry in the peek view result list.", + "Background color of the peek view editor.", + "Background color of the gutter in the peek view editor.", + "Background color of sticky scroll in the peek view editor.", + "Match highlight color in the peek view result list.", + "Match highlight color in the peek view editor.", + "Match highlight border in the peek view editor." ], - "vs/workbench/contrib/inlineChat/browser/inlineChatController": [ - "AI-generated code may be incorrect", - "Getting ready...", - "Failed to start editor chat", - "Please consult the error log and try again later.", - "AI-generated code may be incorrect", - "Ask a question", - "{0} ({1}, {2} for history)", - "Thinking…", - "Review proposed changes in the diff editor.", - "No results, please refine your input and try again", - "No results, please refine your input and try again", - "Failed to apply changes.", - "Failed to discard changes." + "vs/workbench/contrib/interactive/browser/interactiveEditor": [ + "Type '{0}' code here and press {1} to run" ], - "vs/workbench/contrib/inlineChat/common/inlineChat": [ - "Whether a provider for interactive editors exists", - "Whether the interactive editor input is visible", - "Whether the interactive editor input is focused", - "Whether the interactive widget's response is focused", - "Whether the interactive editor input is empty", - "Whether the cursor of the iteractive editor input is on the first line", - "Whether the cursor of the iteractive editor input is on the last line", - "Whether the cursor of the iteractive editor input is on the start of the input", - "Whether the cursor of the iteractive editor input is on the end of the input", - "Whether the interactive editor message is cropped, not cropped or expanded", - "Whether the cursor of the outer editor is above or below the interactive editor input", - "Whether interactive editor has an active request", - "Whether interactive editor has kept a session for quick restore", - "What type was the last response of the current interactive editor session", - "What type was the responses have been receieved", - "Whether interactive editor did change any code", - "Whether the user did changes ontop of the inline chat", - "The last kind of feedback that was provided", - "Whether the document has changed concurrently", - "Background color of the interactive editor widget", - "Border color of the interactive editor widget", - "Shadow color of the interactive editor widget", - "Background highlighting of the current interactive region. Must be transparent.", - "Border color of the interactive editor input", - "Border color of the interactive editor input when focused", - "Foreground color of the interactive editor input placeholder", - "Background color of the interactive editor input", - "Background color of inserted text in the interactive editor input", - "Background color of removed text in the interactive editor input", - "Configure if changes crafted in the interactive editor are applied directly to the document or are previewed first.", - "Changes are applied directly to the document and are highlighted visually via inline or side-by-side diffs. Ending a session will keep the changes.", - "Changes are previewed only and need to be accepted via the apply button. Ending a session will discard the changes.", - "Changes are applied directly to the document but can be highlighted via inline diffs. Ending a session will keep the changes.", - "Enable/disable showing the diff when edits are generated. Works only with inlineChat.mode equal to live or livePreview.", - "Show/hide a gutter icon for spawning inline chat on empty lines." + "vs/workbench/contrib/notebook/browser/notebookIcons": [ + "Configure icon to select a kernel in notebook editors.", + "Icon to execute in notebook editors.", + "Icon to execute above cells in notebook editors.", + "Icon to execute below cells in notebook editors.", + "Icon to stop an execution in notebook editors.", + "Icon to delete a cell in notebook editors.", + "Icon to execute all cells in notebook editors.", + "Icon to edit a cell in notebook editors.", + "Icon to stop editing a cell in notebook editors.", + "Icon to move up a cell in notebook editors.", + "Icon to move down a cell in notebook editors.", + "Icon to clear cell outputs in notebook editors.", + "Icon to split a cell in notebook editors.", + "Icon to indicate a success state in notebook editors.", + "Icon to indicate an error state in notebook editors.", + "Icon to indicate a pending state in notebook editors.", + "Icon to indicate an executing state in notebook editors.", + "Icon to annotate a collapsed section in notebook editors.", + "Icon to annotate an expanded section in notebook editors.", + "Icon to open the notebook in a text editor.", + "Icon to revert in notebook editors.", + "Icon to render output in diff editor.", + "Icon for a mime type in notebook editors.", + "Icon to copy content to clipboard", + "Icon for the previous change action in the diff editor.", + "Icon for the next change action in the diff editor.", + "View icon of the variables view." ], - "vs/workbench/contrib/inlineChat/browser/inlineChatActions": [ - "Start Inline Chat", - "Resume Last Dismissed Inline Chat", - "Inline Chat", - "Make Request", - "Regenerate Response", - "Regenerate", - "Stop Request", - "Cursor Up", - "Cursor Down", - "Focus Input", - "Previous From History", - "Next From History", - "Discard...", - "Discard", - "Discard to Clipboard", - "Discard to New File", - "Helpful", - "Unhelpful", - "Show Diff", - "&&Show Diff", - "Show Diff", - "&&Show Diff", - "Accept Changes", - "Accept", - "Cancel", - "(Developer) Write Exchange to Clipboard", - "'{0}' and {1} follow ups ({2})", - "View in Chat", - "Show More", - "Show Less" + "vs/platform/quickinput/browser/helpQuickAccess": [ + "{0}, {1}" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations": [ - "Icon which spawns the inline chat from the gutter", - "Toggle Inline Chat Icon" + "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ + "No matching views", + "Side Bar", + "Panel", + "Secondary Side Bar", + "{0}: {1}", + "Terminal", + "Debug Console", + "Output", + "Open View", + "Quick Open View" ], "vs/workbench/contrib/files/browser/fileConstants": [ + "Remove Folder from Workspace", "Save As...", "Save", "Save without Formatting", "Save All", - "Remove Folder from Workspace", "New Untitled Text File" ], - "vs/workbench/contrib/testing/browser/testingProgressUiService": [ - "Running tests...", - "Running tests, {0}/{1} passed ({2}%)", - "Running tests, {0}/{1} tests passed ({2}%, {3} skipped)", - "{0}/{1} tests passed ({2}%)", - "{0}/{1} tests passed ({2}%, {3} skipped)" + "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ + "No matching commands", + "Configure Keybinding", + "Ask {0}: {1}", + "{0}: {1}", + "Do you want to clear the history of recently used commands?", + "This action is irreversible!", + "&&Clear", + "Show All Commands", + "Clear Command History" + ], + "vs/workbench/contrib/testing/browser/codeCoverageDecorations": [ + "Toggle Inline Coverage", + "{0} of {1} of branches in {2} were covered.", + "Branch {0} in {1} was not covered.", + "Branch {0} in {1} was executed.", + "Branch {0} in {1} was executed {2} time(s).", + "`{0}` was not executed.", + "`{0}` was executed {1} time(s).", + "`{0}` was executed.", + "Toggle Inline Coverage" + ], + "vs/workbench/contrib/testing/browser/testCoverageBars": [ + "{0}/{1} statements covered ({2})", + "{0}/{1} functions covered ({2})", + "{0}/{1} branches covered ({2})" ], - "vs/workbench/contrib/testing/browser/testingOutputPeek": [ - "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled.", - "Test Output", - "Expected result", - "Actual result", - "Test output is only available for new test runs.", - "The test run did not record any output.", - "Close", - "Unnamed Task", - "+ {0} more lines", - "+ 1 more line", - "Test Result Messages", - "Show Result Output", - "Show Result Output", - "Rerun Test Run", - "Debug Test Run", - "Show Result Output", - "Reveal in Test Explorer", + "vs/workbench/contrib/testing/browser/icons": [ + "View icon of the test view.", + "Icons for test results.", + "Icon of the \"run test\" action.", + "Icon of the \"rerun tests\" action.", + "Icon of the \"run all tests\" action.", + "Icon of the \"debug all tests\" action.", + "Icon of the \"debug test\" action.", + "Icon of the \"run test with coverage\" action.", + "Icon of the \"run all tests with coverage\" action.", + "Icon to cancel ongoing test runs.", + "Icon for the 'Filter' action in the testing view.", + "Icon shown beside hidden tests, when they've been shown.", + "Icon shown when the test explorer is disabled as a tree.", + "Icon shown when the test explorer is disabled as a list.", + "Icon shown to update test profiles.", + "Icon on the button to refresh tests.", + "Icon to turn continuous test runs on.", + "Icon to turn continuous test runs off.", + "Icon when continuous run is on for a test ite,.", + "Icon on the button to cancel refreshing tests.", + "Icon representing test coverage", + "Icon representing that an element was covered", + "Icon representing a uncovered block without a range", + "Icon shown for tests that have an error.", + "Icon shown for tests that failed.", + "Icon shown for tests that passed.", + "Icon shown for tests that are queued.", + "Icon shown for tests that are skipped.", + "Icon shown for tests that are in an unset state." + ], + "vs/workbench/contrib/testing/browser/testCoverageView": [ + "{0} declarations without coverage...", + "Loading Coverage Details...", + "{0} coverage: {0}%", + "Test Coverage Explorer", + "Sort by Location", + "Files are sorted alphabetically, declarations are sorted by position", + "Sort by Coverage", + "Files and declarations are sorted by total coverage", + "Sort by Name", + "Files and declarations are sorted alphabetically", + "Sort the Test Coverage view...", + "Change Sort Order" + ], + "vs/workbench/contrib/testing/browser/testingDecorations": [ + "Peek Test Output", + "Expected", + "Actual", + "Click for test options", + "Click to debug tests, right click for more options", + "Click to run tests with coverage, right click for more options", + "Click to run tests, right click for more options", "Run Test", "Debug Test", - "Go to Source", - "Go to Source", - "Go to Next Test Failure", - "Go to Previous Test Failure", - "Open in Editor", - "Toggle Test History in Peek" + "Run with Coverage", + "Execute Using Profile...", + "Peek Error", + "Reveal in Test Explorer", + "Run All Tests", + "Run All Tests with Coverage", + "Debug All Tests", + "{0} more tests...", + "Select a test to run" + ], + "vs/workbench/contrib/testing/browser/testingProgressUiService": [ + "Running tests...", + "Running tests, {0}/{1} passed ({2}%)", + "Running tests, {0}/{1} tests passed ({2}%, {3} skipped)", + "{0}/{1} tests passed ({2}%)", + "{0}/{1} tests passed ({2}%, {3} skipped)" + ], + "vs/workbench/contrib/testing/browser/testingExplorerView": [ + "{0} (Default)", + "Select Default Profile", + "Configure Test Profiles", + "No test results yet.", + "Tests are being watched for changes", + "{0} passed tests", + "{0} skipped tests", + "{0} failed tests", + "No tests were found in this file.", + "Show Workspace Tests", + "{0}, in {1}", + "{0}, outdated result", + "Test Explorer" ], "vs/workbench/contrib/testing/browser/testingViewPaneContainer": [ "Testing" @@ -24202,6 +26096,7 @@ "Controls the action to take when left-clicking on a test decoration in the gutter.", "Run the test.", "Debug the test.", + "Run the test with coverage.", "Open the context menu for more options.", "Controls whether test decorations are shown in the editor gutter.", "Control whether save all dirty editors before running a test.", @@ -24210,22 +26105,49 @@ "Open the test result view on any test failure", "Open the test explorer when tests start", "Controls when the testing view should open.", - "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed." + "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed.", + "Whether test coverage should be down in the File Explorer view.", + "Configures what percentage is displayed by default for test coverage.", + "A calculation of the combined statement, function, and branch coverage.", + "The statement coverage.", + "The minimum of statement, function, and branch coverage.", + "Configures the colors used for percentages in test coverage bars." ], - "vs/workbench/contrib/testing/browser/testingDecorations": [ - "Peek Test Output", - "Expected", - "Actual", - "Click for test options", - "Click to debug tests, right click for more options", - "Click to run tests, right click for more options", + "vs/workbench/contrib/testing/browser/testingOutputPeek": [ + "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled.", + "Test Output", + "Expected result", + "Actual result", + "The test case did not report any output.", + "The test run did not record any output.", + "Test output is only available for new test runs.", + "View Test Coverage", + "Close Test Coverage", + "Unnamed Task", + "+ {0} more lines", + "+ 1 more line", + "Test Result Messages", + "Show Result Output", + "Show Result Output", + "Rerun Test Run", + "Debug Test Run", + "Show Result Output", + "Reveal in Test Explorer", "Run Test", "Debug Test", - "Execute Using Profile...", - "Peek Error", - "Reveal in Test Explorer", - "Run All Tests", - "Debug All Tests" + "Go to Source", + "Go to Source", + "Close", + "Go to Next Test Failure", + "Shows the next failure message in your file", + "Go to Previous Test Failure", + "Shows the previous failure message in your file", + "Open in Editor", + "Toggle Test History in Peek", + "Shows or hides the history of test runs in the peek view" + ], + "vs/workbench/contrib/testing/common/testingContentProvider": [ + "The test run did not record any output." ], "vs/workbench/contrib/testing/common/testServiceImpl": [ "Running tests may execute code in your workspace.", @@ -24233,35 +26155,6 @@ "Running tests may execute code in your workspace.", "An error occurred attempting to run tests: {0}" ], - "vs/workbench/contrib/testing/common/testingContentProvider": [ - "The test run did not record any output." - ], - "vs/workbench/contrib/testing/browser/icons": [ - "View icon of the test view.", - "Icons for test results.", - "Icon of the \"run test\" action.", - "Icon of the \"rerun tests\" action.", - "Icon of the \"run all tests\" action.", - "Icon of the \"debug all tests\" action.", - "Icon of the \"debug test\" action.", - "Icon to cancel ongoing test runs.", - "Icon for the 'Filter' action in the testing view.", - "Icon shown beside hidden tests, when they've been shown.", - "Icon shown when the test explorer is disabled as a tree.", - "Icon shown when the test explorer is disabled as a list.", - "Icon shown to update test profiles.", - "Icon on the button to refresh tests.", - "Icon to turn continuous test runs on.", - "Icon to turn continuous test runs off.", - "Icon when continuous run is on for a test ite,.", - "Icon on the button to cancel refreshing tests.", - "Icon shown for tests that have an error.", - "Icon shown for tests that failed.", - "Icon shown for tests that passed.", - "Icon shown for tests that are queued.", - "Icon shown for tests that are skipped.", - "Icon shown for tests that are in an unset state." - ], "vs/workbench/contrib/testing/common/testingContextKeys": [ "Indicates whether any test controller has an attached refresh handler.", "Indicates whether any test controller is currently refreshing tests.", @@ -24274,44 +26167,54 @@ "Indicates whether continous test running is supported", "Indicates whether the parent of a test is continuously running, set in the menu context of test items", "Indicates whether any tests are present in the current editor", + "Indicates whether a test coverage report is open", "Type of the item in the output peek view. Either a \"test\", \"message\", \"task\", or \"result\".", "Controller ID of the current test item", "ID of the current test item, set when creating or opening menus on test items", "Boolean indicating whether the test item has a URI defined", "Boolean indicating whether the test item is hidden", "Value set in `testMessage.contextValue`, available in editor/content and testing/message/context", - "Value available in editor/content and testing/message/context when the result is outdated" + "Value available in editor/content and testing/message/context when the result is outdated", + "Value available testing/item/result indicating the state of the item." ], "vs/workbench/contrib/testing/browser/testingConfigurationUi": [ "Pick a test profile to use", "Update Test Configuration" ], "vs/workbench/contrib/testing/browser/testExplorerActions": [ + "Turn off Continuous Run", + "Select a profile to update", + "No test continuous run-enabled profiles were found", + "Select profiles to run when files change:", + "Discovering Tests", + "No tests found in this workspace. You may need to install a test provider extension", + "No debuggable tests found in this workspace. You may need to install a test provider extension", + "No tests with coverage runners found in this workspace. You may need to install a test provider extension", + "No tests found here", + "No tests found in the selected file or folder", + "No tests found in this file", + "No coverage information available on the last test run.", + "Run Tests", + "Debug Tests", + "Run Tests with Coverage", "Hide Test", "Unhide Test", "Unhide All Tests", "Debug Test", + "Run Test with Coverage", "Execute Using Profile...", "Run Test", "Select Default Profile", "Turn on Continuous Run", - "Turn off Continuous Run", "Start Continous Run Using...", "Configure Test Profiles", - "Select a profile to update", "Stop Continuous Run", - "No test continuous run-enabled profiles were found", - "Select profiles to run when files change:", "Start Continuous Run", "Get Selected Profiles", "Get Explorer Selection", - "Run Tests", - "Debug Tests", - "Discovering Tests", "Run All Tests", - "No tests found in this workspace. You may need to install a test provider extension", "Debug All Tests", - "No debuggable tests found in this workspace. You may need to install a test provider extension", + "Run All Tests with Coverage", "Cancel Test Run", "View as List", "View as Tree", @@ -24322,24 +26225,47 @@ "Collapse All Tests", "Clear All Results", "Go to Test", - "No tests found here", "Run Test at Cursor", "Debug Test at Cursor", - "No tests found in this file", + "Run Test at Cursor with Coverage", "Run Tests in Current File", "Debug Tests in Current File", + "Run Tests with Coverage in Current File", "Rerun Failed Tests", "Debug Failed Tests", "Rerun Last Run", "Debug Last Run", + "Rerun Last Run with Coverage", "Search for Test Extension", "Peek Output", "Toggle Inline Test Output", "Refresh Tests", - "Cancel Test Refresh" + "Cancel Test Refresh", + "Clear Coverage", + "Open Coverage" + ], + "vs/workbench/contrib/files/browser/views/emptyView": [ + "No Folder Opened" + ], + "vs/workbench/contrib/files/browser/views/explorerView": [ + "Explorer Section: {0}", + "New File...", + "New Folder...", + "Refresh Explorer", + "Forces a refresh of the Explorer.", + "Collapse Folders in Explorer", + "Folds all folders in the Explorer." + ], + "vs/workbench/contrib/files/browser/views/openEditorsView": [ + "{0} unsaved", + "Open Editors", + "Flip &&Layout", + "Open Editors", + "Toggle Vertical/Horizontal Editor Layout", + "Flip Layout", + "New Untitled Text File" ], "vs/workbench/contrib/logs/common/logsActions": [ - "Set Log Level...", "All", "Extension Logs", "Logs", @@ -24347,112 +26273,14 @@ " {0}: Select log level", "Select log level", "Set as Default Log Level", - "Trace", - "Debug", - "Info", - "Warning", - "Error", - "Off", "Default", - "Open Window Log File (Session)...", "Current", "Select Session", - "Select Log file" - ], - "vs/editor/contrib/peekView/browser/peekView": [ - "Whether the current code editor is embedded inside peek", - "Close", - "Background color of the peek view title area.", - "Color of the peek view title.", - "Color of the peek view title info.", - "Color of the peek view borders and arrow.", - "Background color of the peek view result list.", - "Foreground color for line nodes in the peek view result list.", - "Foreground color for file nodes in the peek view result list.", - "Background color of the selected entry in the peek view result list.", - "Foreground color of the selected entry in the peek view result list.", - "Background color of the peek view editor.", - "Background color of the gutter in the peek view editor.", - "Background color of sticky scroll in the peek view editor.", - "Match highlight color in the peek view result list.", - "Match highlight color in the peek view editor.", - "Match highlight border in the peek view editor." - ], - "vs/workbench/contrib/testing/browser/testingExplorerView": [ - "{0} (Default)", - "Select Default Profile", - "Configure Test Profiles", - "No test results yet.", - "Tests are being watched for changes", - "{0} passed tests", - "{0} skipped tests", - "{0} failed tests", - "No tests were found in this file.", - "Show Workspace Tests", - "{0}, in {1}", - "{0}, outdated result", - "Test Explorer" - ], - "vs/workbench/contrib/interactive/browser/interactiveEditor": [ - "Type '{0}' code here and press {1} to run" - ], - "vs/platform/quickinput/browser/helpQuickAccess": [ - "{0}, {1}" - ], - "vs/workbench/contrib/quickaccess/browser/viewQuickAccess": [ - "No matching views", - "Side Bar", - "Panel", - "Secondary Side Bar", - "{0}: {1}", - "Terminal", - "Debug Console", - "Output", - "Open View", - "Quick Open View" - ], - "vs/workbench/contrib/quickaccess/browser/commandsQuickAccess": [ - "No matching commands", - "Configure Keybinding", - "Ask {0}: {1}", - "{0}: {1}", - "Show All Commands", - "Clear Command History", - "Do you want to clear the history of recently used commands?", - "This action is irreversible!", - "&&Clear" - ], - "vs/workbench/contrib/notebook/browser/notebookIcons": [ - "Configure icon to select a kernel in notebook editors.", - "Icon to execute in notebook editors.", - "Icon to execute above cells in notebook editors.", - "Icon to execute below cells in notebook editors.", - "Icon to stop an execution in notebook editors.", - "Icon to delete a cell in notebook editors.", - "Icon to execute all cells in notebook editors.", - "Icon to edit a cell in notebook editors.", - "Icon to stop editing a cell in notebook editors.", - "Icon to move up a cell in notebook editors.", - "Icon to move down a cell in notebook editors.", - "Icon to clear cell outputs in notebook editors.", - "Icon to split a cell in notebook editors.", - "Icon to indicate a success state in notebook editors.", - "Icon to indicate an error state in notebook editors.", - "Icon to indicate a pending state in notebook editors.", - "Icon to indicate an executing state in notebook editors.", - "Icon to annotate a collapsed section in notebook editors.", - "Icon to annotate an expanded section in notebook editors.", - "Icon to open the notebook in a text editor.", - "Icon to revert in notebook editors.", - "Icon to render output in diff editor.", - "Icon for a mime type in notebook editors.", - "Icon to copy content to clipboard", - "Icon for the previous change action in the diff editor.", - "Icon for the next change action in the diff editor." + "Select Log file", + "Set Log Level...", + "Open Window Log File (Session)..." ], "vs/workbench/contrib/files/browser/fileActions": [ - "New File...", - "New Folder...", "Rename...", "Delete", "Copy", @@ -24468,8 +26296,8 @@ "You are deleting {0} with unsaved changes. Do you want to continue?", "Your changes will be lost if you don't save them.", "This action is irreversible!", - "You can restore these files using the Undo command", - "You can restore this file using the Undo command", + "You can restore these files using the Undo command.", + "You can restore this file using the Undo command.", "You can restore these files from the Recycle Bin.", "You can restore this file from the Recycle Bin.", "You can restore these files from the Trash.", @@ -24495,42 +26323,54 @@ "Are you sure you want to permanently delete '{0}'?", "A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", "&&Replace", - "Compare Active File With...", "Save All in Group", "Close Group", - "Focus on Files Explorer", - "Reveal Active File in Explorer View", - "Open Active File in New Window", "The active editor must contain an openable resource.", "A file or folder name must be provided.", "A file or folder name cannot start with a slash.", "A file or folder **{0}** already exists at this location. Please choose a different name.", "The name **{0}** is not valid as a file or folder name. Please choose a different name.", "Leading or trailing whitespace detected in file or folder name.", - "Compare New Untitled Text Files", - "Compare Active File with Clipboard", "Clipboard ↔ {0}", "Retry", "Create {0}", "Creating {0}", "Rename {0} to {1}", "Renaming {0} to {1}", + "Are you sure you want to paste the following {0} items?", + "Are you sure you want to paste '{0}'?", + "Do not ask me again", + "&&Paste", "File to paste is an ancestor of the destination folder", "Moving {0} files", "Moving {0}", "Move {0} files", "Move {0}", + "The file(s) to paste have been deleted or moved since you copied them. {0}", "Copying {0} files", "Copying {0}", "Paste {0} files", "Paste {0}", - "The file(s) to paste have been deleted or moved since you copied them. {0}", + "New File...", + "New Folder...", + "Compare Active File With...", + "Opens a picker to select a file to diff with the active editor.", + "Toggle Auto Save", + "Toggle the ability to save files automatically after typing", + "Focus on Files Explorer", + "Moves focus to the file explorer view container.", + "Reveal Active File in Explorer View", + "Reveals and selects the active file within the explorer view.", + "Open Active File in New Empty Workspace", + "Opens the active file in a new window with no folders open.", + "Compare New Untitled Text Files", + "Opens a new diff editor with two untitled files.", + "Compare Active File with Clipboard", + "Opens a new diff editor to compare the active file with the contents of the clipboard.", "Set Active Editor Read-only in Session", "Set Active Editor Writeable in Session", "Toggle Active Editor Read-only in Session", - "Reset Active Editor Read-only in Session", - "Toggle Auto Save", - "Toggle the ability to save files automatically after typing" + "Reset Active Editor Read-only in Session" ], "vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler": [ "Use the actions in the editor tool bar to either undo your changes or overwrite the content of the file with your changes.", @@ -24560,30 +26400,14 @@ "Failed to save '{0}': {1}", "Retry", "Discard", - "Failed to revert '{0}': {1}", - "Create File" - ], - "vs/workbench/contrib/files/browser/views/explorerView": [ - "Explorer Section: {0}", - "New File...", - "New Folder...", - "Refresh Explorer", - "Collapse Folders in Explorer" - ], - "vs/workbench/contrib/files/browser/views/openEditorsView": [ - "{0} unsaved", - "Open Editors", - "Toggle Vertical/Horizontal Editor Layout", - "Flip Layout", - "Flip &&Layout", - "New Untitled Text File", - "Open Editors" + "Failed to revert '{0}': {1}", + "Create File" ], "vs/workbench/contrib/files/browser/editors/binaryFileEditor": [ "Binary File Viewer" ], "vs/workbench/contrib/files/browser/workspaceWatcher": [ - "Unable to watch for file changes in this large workspace folder. Please follow the instructions link to resolve this issue.", + "Unable to watch for file changes. Please follow the instructions link to resolve this issue.", "Instructions", "File changes watcher stopped unexpectedly. A reload of the window may enable the watcher again unless the workspace cannot be watched for file changes.", "Reload" @@ -24596,11 +26420,11 @@ "Controls whether {0} and {1} will be automatically detected when a file is opened based on the file contents.", "Remove trailing auto inserted whitespace.", "Special handling for large files to disable certain memory intensive features.", - "Controls whether completions should be computed based on words in the document.", + "Turn off Word Based Suggestions.", "Only suggest words from the active document.", "Suggest words from all open documents of the same language.", "Suggest words from all open documents.", - "Controls from which documents word based completions are computed.", + "Controls whether completions should be computed based on words in the document and from which documents they are computed.", "Semantic highlighting enabled for all color themes.", "Semantic highlighting disabled for all color themes.", "Semantic highlighting is configured by the current color theme's `semanticHighlighting` setting.", @@ -24622,6 +26446,7 @@ "If the diff editor width is smaller than this value, the inline view is used.", "If enabled and the editor width is too small, the inline view is used.", "When enabled, the diff editor shows arrows in its glyph margin to revert changes.", + "When enabled, the diff editor shows a special gutter for revert and stage actions.", "When enabled, the diff editor ignores changes in leading or trailing whitespace.", "Controls whether the diff editor shows +/- indicators for added/removed changes.", "Controls whether the editor shows CodeLens.", @@ -24656,15 +26481,7 @@ "Discard", "Invoke a code action, like rename, to see a preview of its changes here.", "Cannot apply refactoring because '{0}' has changed in the meantime.", - "Cannot apply refactoring because {0} other files have changed in the meantime.", - "{0} (delete, refactor preview)", - "rename", - "create", - "{0} ({1}, refactor preview)", - "{0} (refactor preview)" - ], - "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ - "Other" + "Cannot apply refactoring because {0} other files have changed in the meantime." ], "vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess": [ "Open a text editor first to go to a line.", @@ -24675,15 +26492,16 @@ ], "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess": [ "No matching entries", - "Go to Symbol in Editor...", "Go to &&Symbol in Editor...", "Type the name of a symbol to go to.", "Go to Symbol in Editor", - "Go to Symbol in Editor by Category" + "Go to Symbol in Editor by Category", + "Go to Symbol in Editor..." ], "vs/workbench/contrib/search/browser/anythingQuickAccess": [ "No matching results", "recently opened", + "recently opened", "file and symbol results", "file results", "{0}, {1}", @@ -24695,6 +26513,7 @@ ], "vs/workbench/contrib/search/browser/searchIcons": [ "Icon to make search details visible.", + "Icon to view more context in the search view.", "Icon for toggle the context in the search editor.", "Icon to collapse the replace section in the search view.", "Icon to expand the replace section in the search view.", @@ -24710,7 +26529,14 @@ "Icon for stop in the search view.", "View icon of the search view.", "Icon for the action to open a new search editor.", - "Icon for the action to go to the file of the current search result." + "Icon for the action to go to the file of the current search result.", + "Icon to show AI results in search.", + "Icon to hide AI results in search." + ], + "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ + "No matching workspace symbols", + "Open to the Side", + "Open to the Bottom" ], "vs/workbench/contrib/search/browser/searchWidget": [ "Replace All (Submit Search to Enable)", @@ -24722,15 +26548,13 @@ "Replace: Type replace term and press Enter to preview", "Replace" ], - "vs/workbench/contrib/search/browser/symbolsQuickAccess": [ - "No matching workspace symbols", - "Open to the Side", - "Open to the Bottom" - ], "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess": [ "See More Files", "Open File", - "More" + "More", + "See in Search Panel", + "Enter a term to search for across your files.", + "No matching results" ], "vs/workbench/contrib/search/browser/searchActionsCopy": [ "Copy", @@ -24738,13 +26562,14 @@ "Copy All" ], "vs/workbench/contrib/search/browser/searchActionsFind": [ + "Expand Recursively", + "Find &&in Files", + "Open a workspace search", + "A set of options for the search", "Restrict Search to Folder", "Exclude Folder from Search", "Reveal in Explorer View", "Find in Files", - "Find &&in Files", - "Open a workspace search", - "A set of options for the search", "Find in Folder...", "Find in Workspace..." ], @@ -24773,6 +26598,11 @@ "Replace All", "Replace All" ], + "vs/workbench/contrib/search/browser/searchActionsSymbol": [ + "Go to Symbol in Workspace...", + "Go to Symbol in &&Workspace...", + "Go to Symbol in Workspace..." + ], "vs/workbench/contrib/search/browser/searchActionsTopBar": [ "Clear Search History", "Cancel Search", @@ -24783,17 +26613,50 @@ "View as Tree", "View as List" ], - "vs/workbench/contrib/search/browser/searchActionsSymbol": [ - "Go to Symbol in Workspace...", - "Go to Symbol in Workspace...", - "Go to Symbol in &&Workspace..." - ], "vs/workbench/contrib/search/browser/searchActionsTextQuickAccess": [ - "Quick Text Search (Experimental)" + "Quick Search" + ], + "vs/workbench/browser/parts/views/viewPane": [ + "Icon for an expanded view pane container.", + "Icon for a collapsed view pane container.", + "{0} actions", + "Use Alt+F1 for accessibility help {0}" + ], + "vs/workbench/browser/labels": [ + "{0} • Cell {1}", + "{0} • Cell {1}" ], "vs/workbench/contrib/search/browser/searchActionsBase": [ "Search" ], + "vs/workbench/contrib/search/browser/patternInputWidget": [ + "input", + "Search only in Open Editors", + "Use Exclude Settings and Ignore Files" + ], + "vs/workbench/contrib/search/browser/searchMessage": [ + "Unable to open command link from untrusted source: {0}", + "Unable to open unknown link: {0}" + ], + "vs/workbench/contrib/search/browser/searchResultsView": [ + "Other files", + "Other files", + "{0} files found", + "{0} file found", + "{0} matches found", + "{0} match found", + "From line {0}", + "{0} more lines", + "Search", + "{0} matches in folder root {1}, Search result", + "{0} matches outside of the workspace, Search result", + "{0} matches in file {1} of folder {2}, Search result", + "'{0}' at column {1} replace {2} with {3}", + "'{0}' at column {1} found {2}" + ], + "vs/workbench/services/search/common/queryBuilder": [ + "Workspace folder does not exist: {0}" + ], "vs/workbench/contrib/searchEditor/browser/searchEditor": [ "Toggle Search Details", "files to include", @@ -24805,10 +26668,9 @@ "Search", "Search editor text input box border." ], - "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ - "Search: {0}", - "Search: {0}", - "Search" + "vs/workbench/contrib/scm/browser/activity": [ + "Source Control", + "{0} pending changes" ], "vs/workbench/contrib/scm/browser/dirtydiffDecorator": [ "{0} - {1} of {2} changes", @@ -24830,20 +26692,54 @@ "Minimap gutter background color for lines that are deleted.", "Overview ruler marker color for modified content.", "Overview ruler marker color for added content.", - "Overview ruler marker color for deleted content." + "Overview ruler marker color for deleted content.", + "Added lines", + "Changed lines", + "Removed lines" ], - "vs/workbench/contrib/scm/browser/activity": [ - "Source Control", - "{0} pending changes" + "vs/workbench/contrib/searchEditor/browser/searchEditorInput": [ + "Icon of the search editor label.", + "Search: {0}", + "Search: {0}", + "Search" ], "vs/workbench/contrib/scm/browser/scmViewPaneContainer": [ "Source Control" ], + "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ + "Source Control Repositories" + ], + "vs/workbench/contrib/workspace/common/workspace": [ + "Whether the workspace trust feature is enabled.", + "Whether the current workspace has been trusted by the user." + ], "vs/workbench/contrib/scm/browser/scmViewPane": [ + "History item additions foreground color.", + "History item deletions foreground color.", + "History item statistics border color.", + "History item selected statistics border color.", + "{0} file changed", + "{0} files changed", + "{0} insertion{1}", + "{0} insertions{1}", + "{0} deletion{1}", + "{0} deletions{1}", "Source Control Management", "Source Control Input", "View & Sort", + "Incoming & Outgoing", "Repositories", + "Show Incoming Changes", + "Show Incoming Changes", + "Always", + "Auto", + "Never", + "Show Outgoing Changes", + "Show Outgoing Changes", + "Always", + "Auto", + "Never", + "Show Changes Summary", "View as List", "View as Tree", "Sort by Discovery Time", @@ -24854,92 +26750,39 @@ "Sort Changes by Status", "Collapse All Repositories", "Expand All Repositories", - "Close" - ], - "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane": [ - "Source Control Repositories" - ], - "vs/workbench/contrib/workspace/common/workspace": [ - "Whether the workspace trust feature is enabled.", - "Whether the current workspace has been trusted by the user." - ], - "vs/workbench/contrib/scm/browser/scmSyncViewPane": [ - "Source Control Sync", - "Incoming Changes", - "Outgoing Changes", - "Refresh", - "View as List", - "View as Tree" - ], - "vs/workbench/browser/parts/views/viewPane": [ - "Icon for an expanded view pane container.", - "Icon for a collapsed view pane container.", - "{0} actions" - ], - "vs/workbench/contrib/search/browser/patternInputWidget": [ - "input", - "Search only in Open Editors", - "Use Exclude Settings and Ignore Files" - ], - "vs/workbench/contrib/search/browser/searchMessage": [ - "Unable to open command link from untrusted source: {0}", - "Unable to open unknown link: {0}" - ], - "vs/workbench/contrib/search/browser/searchResultsView": [ - "Other files", - "Other files", - "{0} files found", - "{0} file found", - "{0} matches found", - "{0} match found", - "From line {0}", - "{0} more lines", - "Search", - "{0} matches in folder root {1}, Search result", - "{0} matches outside of the workspace, Search result", - "{0} matches in file {1} of folder {2}, Search result", - "'{0}' at column {1} replace {2} with {3}", - "'{0}' at column {1} found {2}" - ], - "vs/workbench/services/search/common/queryBuilder": [ - "Workspace folder does not exist: {0}" - ], - "vs/workbench/contrib/debug/browser/debugHover": [ - "Hold {0} key to switch to editor language hover", - "Debug Hover", - "{0}, value {1}, variables, debug" - ], - "vs/workbench/contrib/debug/browser/exceptionWidget": [ - "Exception widget border color.", - "Exception widget background color.", - "Exception has occurred: {0}", - "Exception has occurred.", - "Close" - ], - "vs/workbench/contrib/debug/common/debugModel": [ - "Invalid variable attributes", - "Please start a debug session to evaluate expressions", - "not available", - "Paused on {0}", - "Paused", - "Running", - "Unverified breakpoint. File is modified, please restart debug session." + "More Actions...", + "Cancel", + "Close", + "Incoming/Outgoing", + "Incoming and outgoing changes", + "Incoming", + "Incoming changes", + "Outgoing", + "Outgoing changes", + "Incoming changes from {0}", + "Outgoing changes to {0}", + "All Changes" ], "vs/workbench/contrib/debug/browser/breakpointsView": [ "Unverified Exception Breakpoint", "Expression condition: {0}", - "Expression: {0} | Hit Count: {1}", + "Condition: {0} | Hit Count: {1}", "Function breakpoints are not supported by this debug type", "Data breakpoints are not supported by this debug type", "Read", "Write", "Access", + "Condition: {0} | Hit Count: {1}", "Function to break on", "Type function breakpoint.", "Break when expression evaluates to true", "Type expression. Function breakpoint will break when expression evaluates to true", "Break when hit count is met", "Type hit count. Function breakpoint will break when hit count is met.", + "Break when expression evaluates to true", + "Type expression. Data breakpoint will break when expression evaluates to true", + "Break when hit count is met", + "Type hit count. Data breakpoint will break when hit count is met.", "Type exception breakpoint condition", "Break when expression evaluates to true", "Breakpoints", @@ -24951,7 +26794,7 @@ "Data Breakpoint", "Function breakpoints not supported by this debug type", "Function Breakpoint", - "Expression condition: {0}", + "Condition: {0}", "Hit Count: {0}", "Instruction breakpoints not supported by this debug type", "Instruction breakpoint at address {0}", @@ -24959,25 +26802,37 @@ "Hit Count: {0}", "Breakpoints of this type are not supported by the debugger", "Log Message: {0}", - "Expression condition: {0}", + "Condition: {0}", "Hit Count: {0}", + "Hit after breakpoint: {0}", "Breakpoint", - "Add Function Breakpoint", "&&Function Breakpoint...", - "Toggle Activate Breakpoints", + "Failed to set data breakpoint at {0}: {1}", + "Select the access type to monitor", + "Enter a memory range in which to break", + "Absolute range (0x1234 - 0x1300) or range of bytes after an address (0x1234 + 0xff)", + "Address should be a range of numbers the form \"[Start] - [End]\" or \"[Start] + [Bytes]\"", + "Number must be a decimal integer or hex value starting with \"0x\", got {0}", + "&&Data Breakpoint...", "Remove Breakpoint", - "Remove All Breakpoints", "Remove &&All Breakpoints", - "Enable All Breakpoints", "&&Enable All Breakpoints", - "Disable All Breakpoints", "Disable A&&ll Breakpoints", - "Reapply All Breakpoints", "Edit Condition...", "Edit Condition...", "Edit Hit Count...", - "Edit Function Breakpoint...", - "Edit Hit Count..." + "Edit Function Condition...", + "Edit Hit Count...", + "Edit Mode...", + "Select Breakpoint Mode", + "Add Function Breakpoint", + "Add Data Breakpoint at Address", + "Edit Address...", + "Toggle Activate Breakpoints", + "Remove All Breakpoints", + "Enable All Breakpoints", + "Disable All Breakpoints", + "Reapply All Breakpoints" ], "vs/workbench/contrib/debug/browser/callStackView": [ "Running", @@ -25012,54 +26867,20 @@ "Debug toolbar icon for continue.", "Debug toolbar icon for step back." ], - "vs/workbench/contrib/debug/browser/debugCommands": [ - "Debug", - "Restart", - "Step Over", - "Step Into", - "Step Into Target", - "Step Out", - "Pause", - "Disconnect", - "Disconnect and Suspend", - "Stop", - "Continue", - "Focus Session", - "Select and Start Debugging", - "Open '{0}'", - "Start Debugging", - "Start Without Debugging", - "Focus Next Debug Console", - "Focus Previous Debug Console", - "Open Loaded Script...", - "Navigate to Top of Call Stack", - "Navigate to Bottom of Call Stack", - "Navigate Up Call Stack", - "Navigate Down Call Stack", - "Select Debug Console", - "Select Debug Session", - "Choose the specific location", - "No executable code is associated at the current cursor position.", - "Jump to Cursor", - "No step targets available", - "Add Configuration...", - "Add Inline Breakpoint" - ], "vs/workbench/contrib/debug/browser/debugConsoleQuickAccess": [ "Start a New Debug Session" ], "vs/workbench/contrib/debug/browser/debugEditorActions": [ - "Debug: Toggle Breakpoint", "Toggle &&Breakpoint", "Debug: Add Conditional Breakpoint...", "&&Conditional Breakpoint...", "Debug: Add Logpoint...", "&&Logpoint...", + "Debug: Add Triggered Breakpoint...", + "&&Triggered Breakpoint...", "Debug: Edit Breakpoint", "&&Edit Breakpoint", - "Open Disassembly View", "&&DisassemblyView", - "Toggle Source Code in Disassembly View", "&&ToggleSource", "Debug: Show Hover", "Step targets are not available here", @@ -25067,6 +26888,10 @@ "Debug: Go to Next Breakpoint", "Debug: Go to Previous Breakpoint", "Close Exception Widget", + "Debug: Toggle Breakpoint", + "Open Disassembly View", + "Toggle Source Code in Disassembly View", + "Shows or hides source code in disassembly", "Run to Cursor", "Evaluate in Debug Console", "Add to Watch" @@ -25082,6 +26907,7 @@ "Icon for breakpoints.", "Icon for disabled breakpoints.", "Icon for unverified breakpoints.", + "Icon for breakpoints waiting on another breakpoint.", "Icon for function breakpoints.", "Icon for disabled function breakpoints.", "Icon for unverified function breakpoints.", @@ -25122,6 +26948,7 @@ "Icon for the Remove action in the watch view.", "Icon for the add action in the watch view.", "Icon for the add function breakpoint action in the watch view.", + "Icon for the add data breakpoint action in the breakpoints view.", "Icon for the Remove All action in the breakpoints view.", "Icon for the activate action in the breakpoints view.", "Icon for the debug evaluation input marker.", @@ -25161,6 +26988,42 @@ "Added breakpoint, line {0}, file {1}", "Removed breakpoint, line {0}, file {1}" ], + "vs/workbench/contrib/debug/browser/debugCommands": [ + "Open '{0}'", + "Choose the specific location", + "No executable code is associated at the current cursor position.", + "Jump to Cursor", + "No step targets available", + "Add Inline Breakpoint", + "Debug", + "Restart", + "Step Over", + "Step Into", + "Step Into Target", + "Step Out", + "Pause", + "Disconnect", + "Disconnect and Suspend", + "Stop", + "Continue", + "Focus Session", + "Select and Start Debugging", + "Start Debugging", + "Start Without Debugging", + "Focus Next Debug Console", + "Focus Previous Debug Console", + "Open Loaded Script...", + "Navigate to Top of Call Stack", + "Navigate to Bottom of Call Stack", + "Navigate Up Call Stack", + "Navigate Down Call Stack", + "Copy as Expression", + "Copy Value", + "Add to Watch", + "Select Debug Console", + "Select Debug Session", + "Add Configuration..." + ], "vs/workbench/contrib/debug/browser/debugStatus": [ "Debug", "Debug: {0}", @@ -25177,16 +27040,8 @@ "from disassembly", "Disassembly View", "Address", - "Bytes", - "Instruction" - ], - "vs/workbench/contrib/debug/browser/loadedScriptsView": [ - "Debug Session", - "Debug Loaded Scripts", - "Workspace folder {0}, loaded script, debug", - "Session {0}, loaded script, debug", - "Folder {0}, loaded script, debug", - "{0}, loaded script, debug" + "Bytes", + "Instruction" ], "vs/workbench/contrib/debug/browser/statusbarColorProvider": [ "Status bar background color when a program is being debugged. The status bar is shown in the bottom of the window", @@ -25196,15 +27051,33 @@ ], "vs/workbench/contrib/debug/browser/variablesView": [ "Type new variable value", + "Remove Visualizer", + "Type new variable value", + "Visualize Variable...", "Debug Variables", "Scope {0}", "{0}, value {1}", - "Inspecting binary data requires the Hex Editor extension. Would you like to install it now?", - "Cancel", - "Install", - "Installing the Hex Editor...", + "Inspecting binary data requires this extension.", + "Collapse All" + ], + "vs/workbench/contrib/debug/browser/loadedScriptsView": [ + "Debug Session", + "Debug Loaded Scripts", + "Workspace folder {0}, loaded script, debug", + "Session {0}, loaded script, debug", + "Folder {0}, loaded script, debug", + "{0}, loaded script, debug", "Collapse All" ], + "vs/workbench/contrib/debug/browser/welcomeView": [ + "[Open a file](command:{0}) which can be debugged or run.", + "Run and Debug", + "Show all automatic debug configurations", + "To customize Run and Debug [create a launch.json file](command:{0}).", + "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", + "All debug extensions are disabled. Enable a debug extension or install a new one from the Marketplace.", + "Run" + ], "vs/workbench/contrib/debug/browser/watchExpressionsView": [ "Type new value", "Type watch expression", @@ -25227,16 +27100,33 @@ "&&Stop Debugging" ], "vs/workbench/contrib/debug/common/disassemblyViewInput": [ + "Icon of the disassembly editor label.", "Disassembly" ], - "vs/workbench/contrib/debug/browser/welcomeView": [ - "[Open a file](command:{0}) which can be debugged or run.", - "Run and Debug", - "Show all automatic debug configurations", - "To customize Run and Debug [create a launch.json file](command:{0}).", - "To customize Run and Debug, [open a folder](command:{0}) and create a launch.json file.", - "All debug extensions are disabled. Enable a debug extension or install a new one from the Marketplace.", - "Run" + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariableCommands": [ + "Copy Value", + "Execute Notebook Variable Provider" + ], + "vs/workbench/contrib/debug/browser/debugHover": [ + "Hold {0} key to switch to editor language hover", + "Debug Hover", + "{0}, value {1}, variables, debug" + ], + "vs/workbench/contrib/debug/browser/exceptionWidget": [ + "Exception widget border color.", + "Exception widget background color.", + "Exception has occurred: {0}", + "Exception has occurred.", + "Close" + ], + "vs/workbench/contrib/debug/common/debugModel": [ + "Invalid variable attributes", + "Please start a debug session to evaluate expressions", + "not available", + "Paused on {0}", + "Paused", + "Running", + "Unverified breakpoint. File is modified, please restart debug session." ], "vs/workbench/contrib/debug/browser/breakpointWidget": [ "Message to log when breakpoint is hit. Expressions within {} are interpolated. '{0}' to accept, '{1}' to cancel.", @@ -25245,7 +27135,78 @@ "Expression", "Hit Count", "Log Message", - "Breakpoint Type" + "Wait for Breakpoint", + "Breakpoint Type", + "Mode", + "None", + "Loading...", + "Could not load source.", + "Select breakpoint", + "Ok" + ], + "vs/workbench/contrib/markers/browser/markersView": [ + "Showing {0} of {1}", + "Showing {0} problems", + "Showing {0} of {1} problems", + "Clear Filters" + ], + "vs/workbench/contrib/markers/browser/messages": [ + "Toggle Problems (Errors, Warnings, Infos)", + "Problems View", + "Controls whether Problems view should automatically reveal files when opening them.", + "Controls the default view mode of the Problems view.", + "When enabled shows the current problem in the status bar.", + "Controls the order in which problems are navigated.", + "Navigate problems ordered by severity", + "Navigate problems ordered by position", + "No problems have been detected in the workspace.", + "No problems have been detected in the current file.", + "No results found with provided filter criteria.", + "More Filters...", + "Show Errors", + "Show Warnings", + "Show Infos", + "Hide Excluded Files", + "Show Active File Only", + "Filter Problems", + "Show fixes", + "Filter Problems", + "Filter (e.g. text, **/*.ts, !**/node_modules/**)", + "errors", + "warnings", + "infos", + "1 Error", + "{0} Errors", + "1 Warning", + "{0} Warnings", + "1 Info", + "{0} Infos", + "1 Unknown", + "{0} Unknowns", + "[Ln {0}, Col {1}]", + "{0} problems in file {1} of folder {2}", + " This problem has references to {0} locations.", + "Error: {0} at line {1} and character {2}.{3} generated by {4}", + "Error: {0} at line {1} and character {2}.{3}", + "Warning: {0} at line {1} and character {2}.{3} generated by {4}", + "Warning: {0} at line {1} and character {2}.{3}", + "Info: {0} at line {1} and character {2}.{3} generated by {4}", + "Info: {0} at line {1} and character {2}.{3}", + "Problem: {0} at line {1} and character {2}.{3} generated by {4}", + "Problem: {0} at line {1} and character {2}.{3}", + "{0} at line {1} and character {2} in {3}", + "Show Errors and Warnings", + "Focus Problems (Errors, Warnings, Infos)", + "Problems" + ], + "vs/workbench/contrib/markers/browser/markersFileDecorations": [ + "Problems", + "1 problem in this file", + "{0} problems in this file", + "Show Errors & Warnings on files and folder. Overwritten by `#problems.visibility#` when it is off." + ], + "vs/workbench/browser/parts/views/viewFilter": [ + "More Filters..." ], "vs/platform/actions/browser/menuEntryActionViewItem": [ "{0} ({1})", @@ -25259,148 +27220,249 @@ "Add Configuration...", "Debug Session" ], + "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ + "{0} replies", + "1 reply", + "1 comment", + "Image: {0}", + "Image", + "Outdated", + "[Ln {0}]", + "[Ln {0}-{1}]", + "Last reply from {0}", + "Comments" + ], + "vs/workbench/contrib/comments/browser/commentsView": [ + "Filter (e.g. text, author)", + "Filter comments", + "Showing {0} of {1}", + "Inspect this in the accessible view ({0}).\n", + "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.\n", + "Outdated from {0} at line {1} column {2} in {3},{4} comment: {5}", + "{0} at line {1} column {2} in {3},{4} comment: {5}", + "Outdated from {0} in {1},{2} comment: {3}", + "{0} in {1},{2} comment: {3}", + "{0} {1}", + " {0} replies,", + "Comments for current workspace", + "Comments in {0}, full path {1}" + ], + "vs/workbench/contrib/comments/browser/commentsController": [ + "Line {0}", + "Lines {0} to {1}", + "Editor has commenting ranges, run the command Open Accessibility Help ({0}), for more information.", + "Editor has commenting ranges, run the command Open Accessibility Help, which is currently not triggerable via keybinding, for more information.", + "Editor has commenting ranges.", + "Select Comment Provider" + ], + "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ + "This setting is deprecated. Use the `signals` settings instead.", + "Accessibility", + "Enable sound when a screen reader is attached.", + "Enable sound.", + "Disable sound.", + "Enable announcement, will only play when in screen reader optimized mode.", + "Disable announcement.", + "Provide information about how to access the terminal accessibility help menu when the terminal is focused.", + "Provide information about how to navigate changes in the diff editor when it is focused.", + "Provide information about how to access the chat help menu when the chat input is focused.", + "Provide information about how to access the inline editor chat accessibility help menu and alert with hints that describe how to use the feature when the input is focused.", + "Provide information about how to access the inline completions hover and Accessible View.", + "Provide information about how to change a keybinding in the keybindings editor when a row is focused.", + "Provide information about how to focus the cell container or inner editor when a notebook cell is focused.", + "Provide information about how to open the hover in an Accessible View.", + "Provide information about how to open the notification in an Accessible View.", + "Provide information about relevant actions in an empty text editor.", + "Provide information about actions that can be taken in the comment widget or in a file which contains comments.", + "Indicate when a diff editor becomes the active editor.", + "Indicates when a file is saved. Also see {0}.", + "Indicates when a file is saved via user gesture.", + "Indicates whenever is a file is saved, including auto save.", + "Never alerts.", + "Indicates when a feature is cleared (for example, the terminal, Debug Console, or Output channel). Also see {0}.", + "Indicates when a file or notebook cell is formatted. Also see {0}.", + "Indicates when a file is formatted via user gesture.", + "Indicates whenever is a file is formatted, including auto save, on cell execution, and more.", + "Never alerts.", + "Indicates when the debugger breaks. Also see {0}.", + "Indicates when the active line has an error. Also see {0}.", + "Indicates when the active line has a warning. Also see {0}.", + "Indicates when the active line has a folded area that can be unfolded. Also see {0}.", + "Indicates when there is an available terminal quick fix. Also see {0}.", + "Indicates when the terminal bell is activated.", + "Indicates when a terminal command fails (non-zero exit code). Also see {0}.", + "Indicates when a task fails (non-zero exit code). Also see {0}.", + "Indicates when a task completes successfully (zero exit code). Also see {0}.", + "Indicates when a chat request is sent. Also see {0}.", + "Indicates when a chat response is pending. Also see {0}.", + "Indicates when there are no inlay hints. Also see {0}.", + "Indicates when on a line with a breakpoint. Also see {0}.", + "Indicates when a notebook cell completes successfully. Also see {0}.", + "Indicates when a notebook cell fails. Also see {0}.", + "Indicates when the debugger breaks. Also see {0}.", + "On keypress, close the Accessible View and focus the element from which it was invoked.", + "The volume of the sounds in percent (0-100).", + "Whether or not position changes should be debounced", + "Plays a signal when the active line has a breakpoint.", + "Plays a sound when the active line has a breakpoint.", + "Indicates when the active line has a breakpoint.", + "Indicates when the active line has an inline suggestion.", + "Plays a sound when the active line has an inline suggestion.", + "Indicates when the active line has an error.", + "Plays a sound when the active line has an error.", + "Indicates when the active line has an error.", + "Indicates when the active line has a folded area that can be unfolded.", + "Plays a sound when the active line has a folded area that can be unfolded.", + "Indicates when the active line has a folded area that can be unfolded.", + "Plays a signal when the active line has a warning.", + "Plays a sound when the active line has a warning.", + "Indicates when the active line has a warning.", + "Plays a signal when the active line has a warning.", + "Plays a sound when the active line has a warning.", + "Indicates when the active line has a warning.", + "Plays a signal when the active line has a warning.", + "Plays a sound when the active line has a warning.", + "Indicates when the active line has a warning.", + "Plays a signal when the debugger stopped on a breakpoint.", + "Plays a sound when the debugger stopped on a breakpoint.", + "Indicates when the debugger stopped on a breakpoint.", + "Plays a signal when trying to read a line with inlay hints that has no inlay hints.", + "Plays a sound when trying to read a line with inlay hints that has no inlay hints.", + "Indicates when trying to read a line with inlay hints that has no inlay hints.", + "Plays a signal when a task is completed.", + "Plays a sound when a task is completed.", + "Indicates when a task is completed.", + "Plays a signal when a task fails (non-zero exit code).", + "Plays a sound when a task fails (non-zero exit code).", + "Indicates when a task fails (non-zero exit code).", + "Plays a signal when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view.", + "Plays a sound when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view.", + "Indicates when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view.", + "Plays a signal when terminal Quick Fixes are available.", + "Plays a sound when terminal Quick Fixes are available.", + "Indicates when terminal Quick Fixes are available.", + "Plays a signal when the terminal bell is ringing.", + "Plays a sound when the terminal bell is ringing.", + "Indicates when the terminal bell is ringing.", + "Indicates when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change.", + "Indicates when the focus moves to an modified line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change.", + "Indicates when the focus moves to an deleted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to an deleted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a signal when a notebook cell execution is successfully completed.", + "Plays a sound when a notebook cell execution is successfully completed.", + "Indicates when a notebook cell execution is successfully completed.", + "Plays a signal when a notebook cell execution fails.", + "Plays a sound when a notebook cell execution fails.", + "Indicates when a notebook cell execution fails.", + "Plays a signal when a chat request is made.", + "Plays a sound when a chat request is made.", + "Indicates when a chat request is made.", + "Plays a signal on loop while progress is occurring.", + "Plays a sound on loop while progress is occurring.", + "Alerts on loop while progress is occurring.", + "Indicates when the response has been received.", + "Plays a sound on loop while the response has been received.", + "Indicates when the voice recording has started.", + "Plays a sound when the voice recording has started.", + "Indicates when the voice recording has stopped.", + "Plays a sound when the voice recording has stopped.", + "Plays a signal when a feature is cleared (for example, the terminal, Debug Console, or Output channel).", + "Plays a sound when a feature is cleared.", + "Indicates when a feature is cleared.", + "Plays a signal when a file is saved.", + "Plays a sound when a file is saved.", + "Plays the audio cue when a user explicitly saves a file.", + "Plays the audio cue whenever a file is saved, including auto save.", + "Never plays the audio cue.", + "Indicates when a file is saved.", + "Announces when a user explicitly saves a file.", + "Announces whenever a file is saved, including auto save.", + "Never plays the audio cue.", + "Plays a signal when a file or notebook is formatted.", + "Plays a sound when a file or notebook is formatted.", + "Plays the audio cue when a user explicitly formats a file.", + "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell.", + "Never plays the audio cue.", + "Indicates when a file or notebook is formatted.", + "Announceswhen a user explicitly formats a file.", + "Announces whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell.", + "Never announces.", + "Whether to dim unfocused editors and terminals, which makes it more clear where typed input will go to. This works with the majority of editors with the notable exceptions of those that utilize iframes like notebooks and extension webview editors.", + "The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.", + "Controls whether the Accessible View is hidden.", + "The duration in milliseconds that voice speech recognition remains active after you stop speaking. For example in a chat session, the transcribed text is submitted automatically after the timeout is met. Set to `0` to disable this feature.", + "The language that voice speech recognition should recognize. Select `auto` to use the configured display language if possible. Note that not all display languages maybe supported by speech recognition", + "Auto (Use Display Language)" + ], + "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ + "Go to Next Commenting Range", + "Go to Previous Commenting Range", + "Toggle Editor Commenting", + "The cursor must be within a commenting range to add a comment", + "Add Comment on Current Selection", + "Collapse All Comments", + "Expand All Comments", + "Expand Unresolved Comments" + ], "vs/workbench/contrib/mergeEditor/browser/commands/devCommands": [ - "Merge Editor (Dev)", - "Copy Merge Editor State as JSON", "Merge Editor", "No active merge editor", "Merge Editor", "Successfully copied merge editor state", - "Save Merge Editor State to Folder", "Merge Editor", "No active merge editor", "Select folder to save to", "Merge Editor", "Successfully saved merge editor state to folder", - "Load Merge Editor State from Folder", - "Select folder to save to" - ], - "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ - "Open Merge Editor", - "Mixed Layout", - "Column Layout", - "Show Non-Conflicting Changes", - "Show Base", - "Show Base Top", - "Show Base Center", - "Merge Editor", - "Open File", - "Go to Next Unhandled Conflict", - "Go to Previous Unhandled Conflict", - "Toggle Current Conflict from Left", - "Toggle Current Conflict from Right", - "Compare Input 1 With Base", - "Compare With Base", - "Compare Input 2 With Base", - "Compare With Base", - "Open Base File", - "Accept All Changes from Left", - "Accept All Changes from Right", - "Reset Result", - "Reset", - "Reset Choice for 'Close with Conflicts'", - "Complete Merge", - "Do you want to complete the merge of {0}?", - "The file contains unhandled conflicts.", - "&&Complete with Conflicts" - ], - "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ - "Merging: {0}" - ], - "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ - "Text Merge Editor" - ], - "vs/platform/history/browser/contextScopedHistoryWidget": [ - "Whether suggestion are visible" - ], - "vs/workbench/contrib/debug/browser/linkDetector": [ - "follow link using forwarded port", - "follow link", - "Cmd + click to {0}\n{1}", - "Ctrl + click to {0}\n{1}", - "Cmd + click to {0}", - "Ctrl + click to {0}" - ], - "vs/workbench/contrib/debug/browser/replViewer": [ - "Debug Console", - "Variable {0}, value {1}", - ", occurred {0} times", - "Debug console variable {0}, value {1}", - "Debug console group {0}" - ], - "vs/workbench/contrib/debug/common/replModel": [ - "Console was cleared" - ], - "vs/workbench/contrib/markers/browser/markersView": [ - "Showing {0} of {1}", - "Showing {0} problems", - "Showing {0} of {1} problems", - "Clear Filters" + "Select folder to save to", + "Merge Editor (Dev)", + "Copy Merge Editor State as JSON", + "Save Merge Editor State to Folder", + "Load Merge Editor State from Folder" ], - "vs/workbench/contrib/markers/browser/messages": [ - "Toggle Problems (Errors, Warnings, Infos)", - "Focus Problems (Errors, Warnings, Infos)", - "Problems View", - "Controls whether Problems view should automatically reveal files when opening them.", - "Controls the default view mode of the Problems view.", - "When enabled shows the current problem in the status bar.", - "Controls the order in which problems are navigated.", - "Navigate problems ordered by severity", - "Navigate problems ordered by position", - "No problems have been detected in the workspace.", - "No problems have been detected in the current file.", - "No results found with provided filter criteria.", - "More Filters...", - "Show Errors", - "Show Warnings", - "Show Infos", - "Hide Excluded Files", - "Show Active File Only", - "Filter Problems", - "Show fixes", - "Filter Problems", - "Filter (e.g. text, **/*.ts, !**/node_modules/**)", - "errors", - "warnings", - "infos", - "1 Error", - "{0} Errors", - "1 Warning", - "{0} Warnings", - "1 Info", - "{0} Infos", - "1 Unknown", - "{0} Unknowns", - "[Ln {0}, Col {1}]", - "{0} problems in file {1} of folder {2}", - " This problem has references to {0} locations.", - "Error: {0} at line {1} and character {2}.{3} generated by {4}", - "Error: {0} at line {1} and character {2}.{3}", - "Warning: {0} at line {1} and character {2}.{3} generated by {4}", - "Warning: {0} at line {1} and character {2}.{3}", - "Info: {0} at line {1} and character {2}.{3} generated by {4}", - "Info: {0} at line {1} and character {2}.{3}", - "Problem: {0} at line {1} and character {2}.{3} generated by {4}", - "Problem: {0} at line {1} and character {2}.{3}", - "{0} at line {1} and character {2} in {3}", - "Show Errors and Warnings", - "Problems" + "vs/workbench/contrib/mergeEditor/browser/mergeEditorInput": [ + "Merging: {0}" ], - "vs/workbench/browser/parts/views/viewFilter": [ - "More Filters..." + "vs/workbench/contrib/mergeEditor/browser/commands/commands": [ + "Compare With Base", + "Compare With Base", + "Reset", + "Do you want to complete the merge of {0}?", + "The file contains unhandled conflicts.", + "&&Complete with Conflicts", + "Open Merge Editor", + "Mixed Layout", + "Column Layout", + "Show Non-Conflicting Changes", + "Show Base", + "Show Base Top", + "Show Base Center", + "Merge Editor", + "Open File", + "Go to Next Unhandled Conflict", + "Go to Previous Unhandled Conflict", + "Toggle Current Conflict from Left", + "Toggle Current Conflict from Right", + "Compare Input 1 With Base", + "Compare Input 2 With Base", + "Open Base File", + "Accept All Changes from Left", + "Accept All Changes from Right", + "Reset Result", + "Reset Choice for 'Close with Conflicts'", + "Complete Merge" ], - "vs/workbench/contrib/markers/browser/markersFileDecorations": [ - "Problems", - "1 problem in this file", - "{0} problems in this file", - "Show Errors & Warnings on files and folder." + "vs/workbench/contrib/mergeEditor/browser/view/mergeEditor": [ + "Text Merge Editor" ], "vs/workbench/contrib/url/browser/trustedDomains": [ - "Manage Trusted Domains", "Trust {0}", "Trust {0} on all ports", "Trust {0} and all its subdomains", "Trust all domains (disables link protection)", + "Manage Trusted Domains", "Manage Trusted Domains" ], "vs/workbench/contrib/url/browser/trustedDomainsValidator": [ @@ -25409,27 +27471,6 @@ "&&Copy", "Configure &&Trusted Domains" ], - "vs/workbench/contrib/comments/common/commentContextKeys": [ - "Whether the position at the active cursor has a commenting range", - "Whether the active editor has a commenting range", - "Whether the open workspace has either comments or commenting ranges.", - "Set when the comment thread has no comments", - "Set when the comment has no input", - "The context value of the comment", - "The context value of the comment thread", - "The comment controller id associated with a comment thread", - "Set when the comment is focused" - ], - "vs/workbench/contrib/comments/browser/commentsEditorContribution": [ - "Go to Next Commenting Range", - "Go to Previous Commenting Range", - "Toggle Editor Commenting", - "The cursor must be within a commenting range to add a comment", - "Add Comment on Current Selection", - "Collapse All Comments", - "Expand All Comments", - "Expand Unresolved Comments" - ], "vs/workbench/contrib/webviewPanel/browser/webviewCommands": [ "Show find", "Stop find", @@ -25443,6 +27484,11 @@ "vs/workbench/contrib/customEditor/common/customEditor": [ "The viewType of the currently active custom editor." ], + "vs/workbench/contrib/customEditor/browser/customEditorInput": [ + "Unable to open the editor in this window, it contains modifications that can only be saved in the original window.", + "Open in Original Window", + "Unable to move '{0}': The editor contains changes that can only be saved in its current window." + ], "vs/workbench/contrib/externalUriOpener/common/configuration": [ "Configure the opener to use for external URIs (http, https).", "Map URI pattern to an opener id.\nExample patterns: \n{0}", @@ -25456,21 +27502,9 @@ "How would you like to open: {0}" ], "vs/workbench/contrib/extensions/common/extensionsInput": [ + "Icon of the extensions editor label.", "Extension: {0}" ], - "vs/workbench/contrib/extensions/browser/extensionsViews": [ - "Extensions", - "Unable to search the Marketplace when offline, please check your network connection.", - "Error while fetching extensions. {0}", - "No extensions found.", - "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", - "Open User Settings", - "There are no extensions to install.", - "Verified Publisher {0}", - "Publisher {0}", - "Deprecated", - "Rated {0} out of 5 stars by {1} users" - ], "vs/workbench/contrib/extensions/browser/extensionsIcons": [ "View icon of the extensions view.", "Icon for the 'Manage' action in the extensions view.", @@ -25497,14 +27531,33 @@ "Icon shown with a workspace trust message in the extension editor.", "Icon shown with a activation time message in the extension editor." ], + "vs/workbench/contrib/extensions/browser/extensionsViews": [ + "Extensions", + "Unable to search the Marketplace when offline, please check your network connection.", + "Error while fetching extensions. {0}", + "No extensions found.", + "Marketplace returned 'ECONNREFUSED'. Please check the 'http.proxy' setting.", + "Open User Settings", + "There are no extensions to install.", + "Verified Publisher {0}", + "Publisher {0}", + "Deprecated", + "Rated {0} out of 5 stars by {1} users" + ], "vs/platform/dnd/browser/dnd": [ "File is too large to open as untitled editor. Please upload it first into the file explorer and then try again." ], + "vs/platform/actions/browser/toolbar": [ + "Hide", + "Reset Menu" + ], "vs/workbench/contrib/extensions/browser/extensionsActions": [ "{0} for the Web", "The '{0}' extension is not available in {1}. Click 'More Information' to learn more.", "&&More Information", "Close", + "Install Pre-Release", + "Cancel", "{0} cannot verify the '{1}' extension. Are you sure you want to install it?", "Install Anyway", "Cancel", @@ -25524,6 +27577,7 @@ "Are you sure you want to install '{0}'?", "Installing extension {0} started. An editor is now open with more details on this extension", "Installing extension {0} is completed.", + "Install Workspace Extension", "Install Pre-Release", "Install Pre-Release Version", "Install", @@ -25545,17 +27599,22 @@ "Update", "Updating extension {0} to version {1} started.", "Updating extension {0} to version {1} completed.", - "Ignore Updates", - "Ignoring {0} updates", + "Enabled auto updates for", + "Disabled auto updates for", + "Auto Update All (From Publisher)", + "Ignoring updates published by {0}.", + "Enabled auto updates for", + "Disabled auto updates for", "Migrate", "Migrate to {0}", "Migrate", "Manage", "Manage", - "Switch to Pre-Release Version", - "Switch to Pre-Release version of this extension", + "Pre-Release", "Switch to Release Version", - "Switch to Release version of this extension", + "This will switch and enable updates to release versions", + "Switch to Pre-Release Version", + "This will switch to pre-release version and enable updates to latest version always", "Install Another Version...", "This extension has no other versions.", "pre-release", @@ -25571,17 +27630,14 @@ "Disable this extension", "Enable", "Disable", - "Reload", - "Reload Required", + "Reload Window", + "Restart Extensions", + "Restart to Update", + "Update {0}", "current", - "Set Color Theme", "Select Color Theme", - "Set File Icon Theme", "Select File Icon Theme", - "Set Product Icon Theme", "Select Product Icon Theme", - "Set Display Language", - "Clear Display Language", "Show Recommended Extension", "Install Recommended Extension", "Do not recommend this extension again", @@ -25608,116 +27664,67 @@ "Learn More", "{0} for the Web", "The '{0}' extension is not available in {1}.", - "Learn Why", - "This extension is disabled by the environment.", - "This extension is enabled because it is required in the current environment.", - "This extension has been disabled because it does not support virtual workspaces.", - "This extension has limited features because the current workspace is virtual.", - "This extension has been disabled because the current workspace is not trusted.", - "This extension has limited features because the current workspace is not trusted.", - "This extension is disabled in this workspace because it is defined to run in the Remote Extension Host. Please install the extension in '{0}' to enable.", - "Learn More", - "This extension is disabled in this workspace because it is defined to run in the Local Extension Host. Please install the extension locally to enable.", - "Learn More", - "This extension is disabled because it is defined to run only in {0} for the Desktop.", - "Learn More", - "This extension is disabled because it is not supported in {0} for the Web.", - "Learn More", - "Install the language pack extension on '{0}' to enable it there also.", - "Install the language pack extension locally to enable it there also.", - "This extension is enabled in the Remote Extension Host because it prefers to run there.", - "Learn More", - "This extension is enabled in the Local Extension Host because it prefers to run there.", - "Learn More", - "This extension is enabled in the Web Worker Extension Host because it prefers to run there.", - "Learn More", - "This extension has been disabled because it depends on an extension that is disabled.", - "This extension is enabled for this workspace by the user.", - "Extension is enabled on '{0}'", - "This extension is enabled globally.", - "This extension is disabled globally by the user.", - "This extension is disabled for this workspace by the user.", - "Reinstall Extension...", - "Select Extension to Reinstall", - "Please reload Visual Studio Code to complete reinstalling the extension {0}.", - "Reinstalling the extension {0} is completed.", - "Reload Now", - "Install Specific Version of Extension...", - "Select Extension", - "Select extensions to install", - "There are no extensions to install.", - "Installing Extensions...", - "Successfully installed extensions.", - "Install Local Extensions in '{0}'...", - "Install Local Extensions in '{0}'", - "Install Remote Extensions Locally...", - "Install Remote Extensions Locally", - "Button background color for extension actions.", - "Button foreground color for extension actions.", - "Button background hover color for extension actions.", - "Button separator color for extension actions", - "Button background color for extension actions that stand out (e.g. install button).", - "Button foreground color for extension actions that stand out (e.g. install button).", - "Button background hover color for extension actions that stand out (e.g. install button)." - ], - "vs/workbench/contrib/terminal/browser/terminal.contribution": [ - "Type the name of a terminal to open.", - "Show All Opened Terminals", - "&&Terminal", - "Terminal", - "Terminal" - ], - "vs/workbench/contrib/terminal/browser/terminalView": [ - "Use 'monospace'", - "The terminal only supports monospace fonts. Be sure to restart VS Code if this is a newly installed font.", - "Open Terminals.", - "Starting..." - ], - "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ - "Focus Accessible Buffer", - "Accessible Buffer Go to Next Command", - "Accessible Buffer Go to Previous Command" - ], - "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ - "Show Terminal Texture Atlas", - "Write Data to Terminal", - "Enter data to write directly to the terminal, bypassing the pty", - "Restart Pty Host" - ], - "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ - "Show Environment Contributions", - "Terminal Environment Changes", - "Extension: {0}", - "workspace" - ], - "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ - "Focus Find", - "Hide Find", - "Toggle Find Using Regex", - "Toggle Find Using Whole Word", - "Toggle Find Using Case Sensitive", - "Find Next", - "Find Previous", - "Search Workspace" - ], - "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ - "Open Detected Link...", - "Open Last URL Link", - "Open Last Local File Link" - ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ - "Show Terminal Quick Fixes" - ], - "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ - "Extensions", - "List of extensions which should be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", - "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.", - "List of extensions recommended by VS Code that should not be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", - "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." + "Learn Why", + "This extension is disabled by the environment.", + "This extension is enabled because it is required in the current environment.", + "This extension has been disabled because it does not support virtual workspaces.", + "This extension has limited features because the current workspace is virtual.", + "This extension has been disabled because the current workspace is not trusted.", + "This extension has limited features because the current workspace is not trusted.", + "This extension is disabled in this workspace because it is defined to run in the Remote Extension Host. Please install the extension in '{0}' to enable.", + "Learn More", + "This extension is disabled in this workspace because it is defined to run in the Local Extension Host. Please install the extension locally to enable.", + "Learn More", + "This extension is disabled because it is defined to run only in {0} for the Desktop.", + "Learn More", + "This extension is disabled because it is not supported in {0} for the Web.", + "Learn More", + "Manage Access", + "Install the language pack extension on '{0}' to enable it there also.", + "Install the language pack extension locally to enable it there also.", + "This extension is enabled in the Remote Extension Host because it prefers to run there.", + "Learn More", + "This extension is enabled in the Local Extension Host because it prefers to run there.", + "Learn More", + "This extension is enabled in the Web Worker Extension Host because it prefers to run there.", + "Learn More", + "This extension has been disabled because it depends on an extension that is disabled.", + "This extension is enabled for this workspace by the user.", + "Extension is enabled on '{0}'", + "This extension is enabled globally.", + "This extension is disabled globally by the user.", + "This extension is disabled for this workspace by the user.", + "Reinstall Extension...", + "Select Extension to Reinstall", + "Please reload Visual Studio Code to complete reinstalling the extension {0}.", + "Reinstalling the extension {0} is completed.", + "Reload Now", + "Install Specific Version of Extension...", + "Select Extension", + "Select extensions to install", + "There are no extensions to install.", + "Installing Extensions...", + "Successfully installed extensions.", + "Install Local Extensions in '{0}'...", + "Install Local Extensions in '{0}'", + "Install Remote Extensions Locally...", + "Install Remote Extensions Locally", + "Button background color for extension actions.", + "Button foreground color for extension actions.", + "Button background hover color for extension actions.", + "Button separator color for extension actions", + "Button background color for extension actions that stand out (e.g. install button).", + "Button foreground color for extension actions that stand out (e.g. install button).", + "Button background hover color for extension actions that stand out (e.g. install button).", + "Auto Update", + "Set Color Theme", + "Set File Icon Theme", + "Set Product Icon Theme", + "Set Display Language", + "Clear Display Language" ], "vs/workbench/contrib/extensions/browser/extensionEditor": [ "Extension Version", - "Pre-Release", "Extension name", "Preview", "Preview", @@ -25725,18 +27732,18 @@ "Publisher", "Install count", "Rating", + "Workspace Extension", + "Local Extension", "Details", "Extension details, rendered from the extension's 'README.md' file", - "Feature Contributions", - "Lists contributions to VS Code by this extension", + "Features", + "Lists features contributed by this extension", "Changelog", "Extension update history, rendered from the extension's 'CHANGELOG.md' file", "Dependencies", "Lists extensions this extension depends on", "Extension Pack", "Lists extensions those will be installed together with this extension", - "Runtime Status", - "Extension runtime status", "No README available.", "Readme", "Extension Pack ({0})", @@ -25744,9 +27751,10 @@ "Readme", "Categories", "Marketplace", + "Issues", "Repository", "License", - "Extension Resources", + "Resources", "More Info", "Published", "Last released", @@ -25754,86 +27762,17 @@ "Identifier", "No Changelog available.", "Changelog", - "No Contributions", - "No Contributions", "No Dependencies", - "Activation Event:", - "Startup", - "Activation Time:", - "Activated By:", - "Not yet activated.", - "Uncaught Errors ({0})", - "Messages ({0})", - "No status available.", - "Settings ({0})", - "ID", - "Description", - "Default", - "Debuggers ({0})", - "Name", - "Type", - "View Containers ({0})", - "ID", - "Title", - "Where", - "Views ({0})", - "ID", - "Name", - "Where", - "Localizations ({0})", - "Language ID", - "Language Name", - "Language Name (Localized)", - "Custom Editors ({0})", - "View Type", - "Priority", - "Filename Pattern", - "Code Actions ({0})", - "Title", - "Kind", - "Description", - "Languages", - "Authentication ({0})", - "Label", - "ID", - "Color Themes ({0})", - "File Icon Themes ({0})", - "Product Icon Themes ({0})", - "Colors ({0})", - "ID", - "Description", - "Dark Default", - "Light Default", - "High Contrast Default", - "JSON Validation ({0})", - "File Match", - "Schema", - "Commands ({0})", - "ID", - "Title", - "Keyboard Shortcuts", - "Menu Contexts", - "Languages ({0})", - "ID", - "Name", - "File Extensions", - "Grammar", - "Snippets", - "Activation Events ({0})", - "Notebooks ({0})", - "ID", - "Name", - "Notebook Renderers ({0})", - "Name", - "Mimetypes", "Find", "Find Next", "Find Previous" ], - "vs/workbench/contrib/extensions/common/extensionsUtils": [ - "Disable other keymaps ({0}) to avoid conflicts between keybindings?", - "Yes", - "No" + "vs/workbench/contrib/extensions/common/extensionsFileTemplate": [ + "Extensions", + "List of extensions which should be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", + "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.", + "List of extensions recommended by VS Code that should not be recommended for users of this workspace. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'.", + "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'." ], "vs/workbench/contrib/extensions/browser/extensionsActivationProgress": [ "Activating Extensions..." @@ -25845,6 +27784,11 @@ "Reload Window", "There are no missing dependencies to install." ], + "vs/workbench/contrib/extensions/common/extensionsUtils": [ + "Disable other keymaps ({0}) to avoid conflicts between keybindings?", + "Yes", + "No" + ], "vs/workbench/contrib/extensions/browser/extensionsQuickAccess": [ "Type an extension name to install or search.", "Press Enter to search for extension '{0}'.", @@ -25852,7 +27796,6 @@ "Press Enter to manage your extensions." ], "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService": [ - "Don't Show Again", "Do you want to ignore all extension recommendations?", "Yes, Ignore All", "No", @@ -25863,40 +27806,13 @@ "extensions from {0}", "Do you want to install the recommended {0} for {1}?", "You have {0} installed on your system. Do you want to install the recommended {1} for it?", + "Don't Show Again for this Repository", + "Don't Show Again for these Extensions", + "Don't Show Again for this Extension", "Install", "Install (Do not sync)", "Show Recommendations" ], - "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ - "Manifest is not found", - "Please reload Visual Studio Code to complete the uninstallation of this extension.", - "Please reload Visual Studio Code to enable the updated extension.", - "Please reload Visual Studio Code to enable this extension locally.", - "Please reload Visual Studio Code to enable this extension in {0}.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to disable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "Please reload Visual Studio Code to enable this extension.", - "This extension is reported to be problematic.", - "Can't install '{0}' extension because it is not compatible.", - "Uninstalling extension....", - "Unable to install extension '{0}' because the requested version '{1}' is not found.", - "Installing extension....", - "Installing '{0}' extension....", - "Disable All", - "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", - "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", - "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?" - ], - "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ - "Example" - ], - "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ - "You have deprecated extensions installed. We recommend to review them and migrate to alternatives.", - "Show Deprecated Extensions", - "Don't Show Again" - ], "vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor": [ "Activated by {0} on start-up", "Activated by {1} because a file matching {0} exists in your workspace", @@ -25908,12 +27824,137 @@ "Extension is activating...", "Extension has caused the extension host to freeze.", "{0} uncaught errors", + "{0} Requests: {1} (Overall)", + ", {0} (Session)", + "Last request was {0}.", "Runtime Extensions", "Copy id ({0})", "Disable (Workspace)", "Disable", "Show Running Extensions" ], + "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ + "Restarting extension host due to workspace trust change." + ], + "vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider": [ + "Example" + ], + "vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker": [ + "You have deprecated extensions installed. We recommend to review them and migrate to alternatives.", + "Show Deprecated Extensions", + "Don't Show Again" + ], + "vs/workbench/contrib/extensions/browser/extensionsWorkbenchService": [ + "Manifest is not found", + "Enable or Disable extensions", + "reload window", + "restart extensions", + "Please {0} to complete the uninstallation of this extension.", + "Please update {0} to enable the updated extension.", + "Please update {0} to enable the updated extension.", + "Please restart {0} to enable the updated extension.", + "Please {0} to enable the updated extension.", + "Please {0} to enable this extension locally.", + "Please {0} to enable this extension in {1}.", + "Please {0} to enable this extension.", + "Please {0} to enable this extension.", + "Please {0} to disable this extension.", + "Please {0} to enable this extension.", + "Please {0} to enable this extension.", + "This extension is reported to be problematic.", + "Unable to install extension '{0}' because the requested version '{1}' is not found.", + "Unable to install extension '{0}' because it is not found.", + "&&Install Extension", + "&&Install Extension and {0}", + "Open Extension", + "Install Extension", + "Would you like to install '{0}' extension from '{1}'?", + "Would you like to install the extension?", + "Sync this extension", + "Unable to install extension", + "Enable Extension", + "Would you like to enable '{0}' extension?", + "&&Enable Extension", + "&&Enable Extension and {0}", + "Can't install '{0}' extension because it is not compatible.", + "Uninstalling extension....", + "Installing '{0}' extension....", + "Installing extension....", + "Disable All", + "Cannot disable '{0}' extension alone. '{1}' extension depends on this. Do you want to disable all these extensions?", + "Cannot disable '{0}' extension alone. '{1}' and '{2}' extensions depend on this. Do you want to disable all these extensions?", + "Cannot disable '{0}' extension alone. '{1}', '{2}' and other extensions depend on this. Do you want to disable all these extensions?" + ], + "vs/workbench/contrib/terminal/browser/terminal.contribution": [ + "Type the name of a terminal to open.", + "Show All Opened Terminals", + "&&Terminal", + "Terminal", + "Terminal" + ], + "vs/workbench/contrib/terminal/browser/terminalView": [ + "Use 'monospace'", + "The terminal only supports monospace fonts. Be sure to restart VS Code if this is a newly installed font.", + "Open Terminals.", + "Starting..." + ], + "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution": [ + "Enter data to write directly to the terminal, bypassing the pty", + "Terminal Dev Mode", + "Show Terminal Texture Atlas", + "Write Data to Terminal", + "Restart Pty Host" + ], + "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution": [ + "Terminal Environment Changes", + "Extension: {0}", + "workspace", + "Show Environment Contributions" + ], + "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution": [ + "Focus Accessible Terminal View", + "Accessible Buffer Go to Next Command", + "Accessible Buffer Go to Previous Command", + "Scroll to Accessible View Bottom", + "Scroll to Accessible View Top" + ], + "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution": [ + "Open Detected Link...", + "Open Last URL Link", + "Opens the last detected URL/URI link in the terminal", + "Open Last Local File Link" + ], + "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution": [ + "Show Terminal Quick Fixes" + ], + "vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution": [ + "Increase Font Size", + "Decrease Font Size", + "Reset Font Size" + ], + "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution": [ + "Focus Find", + "Hide Find", + "Toggle Find Using Regex", + "Toggle Find Using Whole Word", + "Toggle Find Using Case Sensitive", + "Find Next", + "Find Previous", + "Search Workspace" + ], + "vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution": [ + "Select the Previous Suggestion", + "Select the Previous Page Suggestion", + "Select the Next Suggestion", + "Select the Next Page Suggestion", + "Accept Selected Suggestion", + "Hide Suggest Widget" + ], + "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ + "Manage Automatic Tasks", + "Allow Automatic Tasks", + "Disallow Automatic Tasks" + ], "vs/workbench/contrib/tasks/common/problemMatcher": [ "The problem pattern is missing a regular expression.", "The loop property is only supported on the last line matcher.", @@ -25984,10 +28025,15 @@ "ESLint stylish problems", "Go problems" ], - "vs/workbench/contrib/tasks/browser/runAutomaticTasks": [ - "Manage Automatic Tasks", - "Allow Automatic Tasks", - "Disallow Automatic Tasks" + "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ + "Task version 0.1.0 is deprecated. Please use 2.0.0", + "The config's version number", + "The runner has graduated. Use the official runner property", + "Defines whether the task is executed as a process and the output is shown in the output window or inside the terminal.", + "Windows specific command configuration", + "Mac specific command configuration", + "Linux specific command configuration", + "Specifies whether the command is a shell command or an external program. Defaults to false if omitted." ], "vs/workbench/contrib/tasks/common/jsonSchema_v2": [ "Specifies whether the command is a shell command or an external program. Defaults to false if omitted.", @@ -26062,29 +28108,16 @@ "The customize property is deprecated. See the 1.14 release notes on how to migrate to the new task customization approach", "The task's name property is deprecated. Use the label property instead.", "The property showOutput is deprecated. Use the reveal property inside the presentation property instead. See also the 1.14 release notes.", - "The property echoCommand is deprecated. Use the echo property inside the presentation property instead. See also the 1.14 release notes.", - "The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", - "The property isBuildCommand is deprecated. Use the group property instead. See also the 1.14 release notes.", - "The property isTestCommand is deprecated. Use the group property instead. See also the 1.14 release notes.", - "Defines whether the task is run as a process or as a command inside a shell.", - "The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", - "The property taskSelector is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", - "Windows specific command configuration", - "Mac specific command configuration", - "Linux specific command configuration" - ], - "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant": [ - "Restarting extension host due to workspace trust change." - ], - "vs/workbench/contrib/tasks/common/jsonSchema_v1": [ - "Task version 0.1.0 is deprecated. Please use 2.0.0", - "The config's version number", - "The runner has graduated. Use the official runner property", - "Defines whether the task is executed as a process and the output is shown in the output window or inside the terminal.", + "The property echoCommand is deprecated. Use the echo property inside the presentation property instead. See also the 1.14 release notes.", + "The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", + "The property isBuildCommand is deprecated. Use the group property instead. See also the 1.14 release notes.", + "The property isTestCommand is deprecated. Use the group property instead. See also the 1.14 release notes.", + "Defines whether the task is run as a process or as a command inside a shell.", + "The property suppressTaskName is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", + "The property taskSelector is deprecated. Inline the command with its arguments into the task instead. See also the 1.14 release notes.", "Windows specific command configuration", "Mac specific command configuration", - "Linux specific command configuration", - "Specifies whether the command is a shell command or an external program. Defaults to false if omitted." + "Linux specific command configuration" ], "vs/workbench/contrib/tasks/browser/tasksQuickAccess": [ "No matching tasks", @@ -26109,7 +28142,6 @@ "Select url to open", "Remote Help", "Remote Explorer", - "Remote Explorer", "Attempting to reconnect in {0} second...", "Attempting to reconnect in {0} seconds...", "Reconnect Now", @@ -26118,14 +28150,11 @@ "Disconnected. Attempting to reconnect...", "Cannot reconnect. Please reload the window.", "&&Reload Window", - "Help and feedback" + "Help and feedback", + "Remote Explorer" ], "vs/workbench/contrib/remote/browser/remoteIndicator": [ - "Remote", - "Show Remote Menu", - "Close Remote Connection", "Close Re&&mote Connection", - "Install Remote Development Extensions", "Opening Remote...", "Opening Remote...", "Reconnecting to {0}...", @@ -26143,22 +28172,39 @@ "Reload Window", "Close Remote Workspace", "Select an option to open a Remote Window", - "Installing extension... " + "Installing extension... ", + "When enabled, remote extensions recommendations will be shown in the Remote Indicator menu.", + "Remote", + "Show Remote Menu", + "Close Remote Connection", + "Install Remote Development Extensions" + ], + "vs/workbench/contrib/remote/browser/remoteConnectionHealth": [ + "You are about to connect to an OS version that is unsupported by {0}.", + "&&Allow", + "&&Learn More", + "Do not show again", + "Learn More", + "You are connected to an OS version that is unsupported by {0}." ], "vs/workbench/contrib/emmet/browser/actions/expandAbbreviation": [ "Emmet: Expand Abbreviation", "Emmet: E&&xpand Abbreviation" ], "vs/workbench/contrib/codeEditor/browser/accessibility/accessibility": [ - "Toggle Screen Reader Accessibility Mode" + "Toggle Screen Reader Accessibility Mode", + "Toggles an optimized mode for usage with screen readers, braille devices, and other assistive technologies." ], "vs/workbench/contrib/codeEditor/browser/diffEditorHelper": [ "Show Whitespace Differences", "The diff algorithm was stopped early (after {0} ms.)", "Remove Limit", + "Run the command Diff Editor: Switch Side ({0}) to toggle between the original and modified editors.", + "Run the command Diff Editor: Switch Side, which is currently not triggerable via keybinding, to toggle between the original and modified editors.", + "The setting, accessibility.verbosity.diffEditorActive, controls if a diff editor announcement is made when it becomes the active editor.", "You are in a diff editor.", "View the next ({0}) or previous ({1}) diff in diff review mode, which is optimized for screen readers.", - "To control which audio cues should be played, the following settings can be configured: {0}." + "To control which accessibility signals should be played, the following settings can be configured: {0}." ], "vs/workbench/contrib/codeEditor/browser/inspectKeybindings": [ "Inspect Key Mappings", @@ -26173,6 +28219,11 @@ "Developer: Inspect Editor Tokens and Scopes", "Loading..." ], + "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ + "Type the line number and optional column to go to (e.g. 42:5 for line 42 and column 5).", + "Go to Line/Column", + "Go to Line/Column..." + ], "vs/workbench/contrib/codeEditor/browser/saveParticipants": [ "Running '{0}' Formatter ([configure]({1})).", "Quick Fixes", @@ -26180,31 +28231,26 @@ "Applying code action '{0}'." ], "vs/workbench/contrib/codeEditor/browser/toggleColumnSelection": [ - "Toggle Column Selection Mode", - "Column &&Selection Mode" + "Column &&Selection Mode", + "Toggle Column Selection Mode" ], "vs/workbench/contrib/codeEditor/browser/toggleMinimap": [ - "Toggle Minimap", - "&&Minimap" + "&&Minimap", + "Toggle Minimap" ], - "vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess": [ - "Go to Line/Column...", - "Type the line number and optional column to go to (e.g. 42:5 for line 42 and column 5).", - "Go to Line/Column" + "vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter": [ + "Render &&Control Characters", + "Toggle Control Characters" ], "vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier": [ - "Toggle Multi-Cursor Modifier", "Switch to Alt+Click for Multi-Cursor", "Switch to Cmd+Click for Multi-Cursor", - "Switch to Ctrl+Click for Multi-Cursor" - ], - "vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter": [ - "Toggle Control Characters", - "Render &&Control Characters" + "Switch to Ctrl+Click for Multi-Cursor", + "Toggle Multi-Cursor Modifier" ], "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace": [ - "Toggle Render Whitespace", - "&&Render Whitespace" + "&&Render Whitespace", + "Toggle Render Whitespace" ], "vs/workbench/contrib/codeEditor/browser/toggleWordWrap": [ "Whether the editor is currently using word wrapping.", @@ -26221,6 +28267,37 @@ "Execute {0} to select a language, execute {1} to fill with template, or execute {2} to open a different editor and get started. Start typing to dismiss.", " Toggle {0} in settings to disable this hint." ], + "vs/workbench/contrib/codeEditor/browser/dictation/editorDictation": [ + "Stop Dictation ({0})", + "Stop Dictation", + "Voice", + "Start Dictation in Editor", + "Stop Dictation in Editor" + ], + "vs/platform/history/browser/contextScopedHistoryWidget": [ + "Whether suggestion are visible" + ], + "vs/workbench/contrib/debug/browser/linkDetector": [ + "follow link using forwarded port", + "follow link", + "Cmd + click to {0}\n{1}", + "Ctrl + click to {0}\n{1}", + "Cmd + click to {0}", + "Ctrl + click to {0}" + ], + "vs/workbench/contrib/debug/browser/replViewer": [ + "Debug Console", + "Variable {0}, value {1}", + ", occurred {0} times", + "Debug console variable {0}, value {1}", + "Debug console group {0}" + ], + "vs/workbench/contrib/debug/common/replModel": [ + "Console was cleared" + ], + "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ + "Surround with Snippet..." + ], "vs/workbench/contrib/snippets/browser/commands/configureSnippets": [ "(global)", "({0})", @@ -26229,8 +28306,6 @@ "Invalid file name", "'{0}' is not a valid file name", "'{0}' already exists", - "Configure User Snippets", - "User Snippets", "User &&Snippets", "global", "New Global Snippets file...", @@ -26239,23 +28314,23 @@ "Existing Snippets", "New Snippets", "New Snippets", - "Select Snippets File or Create Snippets" + "Select Snippets File or Create Snippets", + "Configure User Snippets", + "User Snippets" ], "vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets": [ - "Fill File with Snippet", - "Select a snippet" - ], - "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ - "Insert Snippet" - ], - "vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet": [ - "Surround With Snippet..." + "Select a snippet", + "Fill File with Snippet" ], "vs/workbench/contrib/snippets/browser/snippetCodeActionProvider": [ - "Surround With: {0}", + "More...", + "{0}", "Start with Snippet", "Start with: {0}" ], + "vs/workbench/contrib/snippets/browser/commands/insertSnippet": [ + "Insert Snippet" + ], "vs/workbench/contrib/snippets/browser/snippetsService": [ "Expected string in `contributes.{0}.path`. Provided value: {1}", "When omitting the language, the value of `contributes.{0}.path` must be a `.code-snippets`-file. Provided value: {1}", @@ -26277,6 +28352,7 @@ "None", "None", "Extension '{0}' is configured as formatter but it cannot format '{1}'-files", + "Extension '{0}' is configured as formatter but it can only format '{1}'-files as a whole, not selections or parts of it.", "There are multiple formatters for '{0}' files. One of them should be configured as default formatter.", "Extension '{0}' is configured as formatter but not available. Select a different default formatter to continue.", "Configure Default Formatter", @@ -26340,6 +28416,12 @@ "&&Insiders", "&&Stable (current)" ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ + "Built-In", + "Developer", + "Reset Welcome Page Walkthrough Progress", + "Reset the progress of all Walkthrough steps on the Welcome Page to make them appear as if they are being viewed for the first time, providing a fresh start to the getting started experience." + ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedInput": [ "Welcome" ], @@ -26347,6 +28429,10 @@ "Welcome Page", "Could not open markdown preview: {0}.\n\nPlease make sure the markdown extension is enabled." ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ + "Used to represent walkthrough steps which have not been completed", + "Used to represent walkthrough steps which have been completed" + ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted": [ "Overview of how to get up to speed with your editor.", "Open Walkthrough...", @@ -26370,6 +28456,9 @@ "More...", "Hide", "Hide", + "Videos", + "Watch Getting Started Tutorials", + "Learn VS Code's must-have features in short and practical videos", "All {0} steps complete!", "{0} of {1} steps complete", "Tip: Use keyboard shortcut ", @@ -26380,23 +28469,15 @@ "opt out", "{0} collects usage data. Read our {1} and learn how to {2}." ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService": [ - "Built-In", - "Developer", - "Reset Welcome Page Walkthrough Progress" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedIcons": [ - "Used to represent walkthrough steps which have not been completed", - "Used to represent walkthrough steps which have been completed" + "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ + "Editor Playground", + "Interactive Editor Playground", + "Opens an interactive playground for learning about the editor." ], "vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart": [ "unbound", "It looks like Git is not installed on your system." ], - "vs/workbench/contrib/welcomeWalkthrough/browser/editor/editorWalkThrough": [ - "Editor Playground", - "Interactive Editor Playground" - ], "vs/workbench/contrib/welcomeViews/common/viewsWelcomeContribution": [ "The viewsWelcome contribution in '{0}' requires 'enabledApiProposals: [\"contribViewsWelcome\"]' in order to use the 'group' proposed property." ], @@ -26444,14 +28525,28 @@ "Sort By: Name", "Sort By: Category" ], + "vs/workbench/contrib/authentication/browser/actions/signOutOfAccountAction": [ + "Sign out of account", + "The account '{0}' has been used by: \n\n{1}\n\n Sign out from these extensions?", + "Sign out of '{0}'?", + "&&Sign Out" + ], + "vs/workbench/contrib/authentication/browser/actions/manageTrustedExtensionsForAccountAction": [ + "Pick an account to manage trusted extensions for", + "This account has not been used by any extensions.", + "Cancel", + "Last used this account {0}", + "Has not used this account", + "This extension is trusted by Microsoft and\nalways has access to this account", + "Trusted by Microsoft", + "Manage Trusted Extensions", + "Choose which extensions can access this account", + "Manage Trusted Extensions For Account", + "Accounts" + ], "vs/workbench/contrib/userDataSync/browser/userDataSync": [ - "Turn Off", - "Configure...", - "Sync Now", "syncing", "synced {0}", - "Show Settings", - "Show Synced Data", "Unable to sync due to conflicts in {0}. Please resolve them to continue.", "Replace Remote", "Replace Local", @@ -26508,12 +28603,10 @@ "Default", "Insiders", "Stable", - "Backup and Sync Settings...", "Turning on Settings Sync...", "Cancel", "Sign in to Sync Settings", "Sign in to Sync Settings (1)", - "Show Conflicts ({0})", "Settings Sync is On", "Error while turning off Settings Sync. Please check [logs]({0}) for more details.", "Configure...", @@ -26521,40 +28614,23 @@ "Show Log", "Complete Merge", "Successfully downloaded Settings Sync activity.", - "Clear Data in Cloud..." - ], - "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ - "Profiles ({0})", - "Switch Profile...", - "Select Profile", - "Edit Profile...", - "Show Profile Contents", - "Export Profile...", - "Export Profile ({0})...", - "Import Profile...", - "Import from URL", - "Select File...", - "Profile Templates", - "Import from Profile Template...", - "Provide Profile Template URL", - "Error while creating profile: {0}", - "Select Profile Template File", - "Import Profile...", - "Save Current Profile As...", - "Create Profile...", - "Delete Profile...", - "Current", - "Delete Profile...", - "Select Profiles to Delete" + "Clear Data in Cloud...", + "Turn Off", + "Configure...", + "Sync Now", + "Show Settings", + "Show Synced Data", + "Backup and Sync Settings...", + "Show Conflicts ({0})" ], "vs/workbench/contrib/userDataProfile/browser/userDataProfileActions": [ - "Create a Temporary Profile", - "Rename...", "Rename {0}", "Profile with name {0} already exists.", "Current", "Rename Profile...", "Select Profile to Rename", + "Create a Temporary Profile", + "Rename...", "Manage...", "Cleanup Profiles", "Reset Workspace Profiles Associations" @@ -26564,7 +28640,12 @@ "Language modes that the code actions are enabled for.", "`CodeActionKind` of the contributed code action.", "Label for the code action used in the UI.", - "Description of what the code action does." + "Description of what the code action does.", + "Title", + "Kind", + "Description", + "Languages", + "Code Actions" ], "vs/workbench/contrib/codeActions/common/documentationExtensionPoint": [ "Contributed documentation.", @@ -26574,16 +28655,6 @@ "When clause.", "Command executed." ], - "vs/workbench/contrib/codeActions/browser/codeActionsContribution": [ - "Triggers Code Actions on explicit saves and auto saves triggered by window or focus changes.", - "Triggers Code Actions only when explicitly saved", - "Never triggers Code Actions on save", - "Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of \"explicit\".", - "Never triggers Code Actions on save. This value will be deprecated in favor of \"never\".", - "Controls whether auto fix action should be run on file save.", - "Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. Example: `\"source.organizeImports\": \"explicit\" `", - "Controls whether '{0}' actions should be run on file save." - ], "vs/workbench/contrib/timeline/browser/timelinePane": [ "Loading...", "Load more", @@ -26600,58 +28671,89 @@ "Icon for the refresh timeline action.", "Icon for the pin timeline action.", "Icon for the unpin timeline action.", + "Timeline", "Refresh", "Timeline", "Pin the Current Timeline", "Timeline", "Unpin the Current Timeline", - "Timeline", "Timeline" ], + "vs/workbench/contrib/codeActions/browser/codeActionsContribution": [ + "Triggers Code Actions on explicit saves and auto saves triggered by window or focus changes.", + "Triggers Code Actions only when explicitly saved", + "Never triggers Code Actions on save", + "Triggers Code Actions only when explicitly saved. This value will be deprecated in favor of \"explicit\".", + "Never triggers Code Actions on save. This value will be deprecated in favor of \"never\".", + "Controls whether auto fix action should be run on file save.", + "Run Code Actions for the editor on save. Code Actions must be specified and the editor must not be shutting down. Example: `\"source.organizeImports\": \"explicit\" `", + "Controls whether '{0}' actions should be run on file save." + ], "vs/workbench/contrib/localHistory/browser/localHistoryTimeline": [ "Local History" ], + "vs/workbench/contrib/userDataProfile/browser/userDataProfile": [ + "Profiles ({0})", + "Select Profile", + "Import from URL", + "Select File...", + "Profile Templates", + "Import from Profile Template...", + "Provide Profile Template URL", + "Error while creating profile: {0}", + "Select Profile Template File", + "Current", + "Delete Profile...", + "Select Profiles to Delete", + "Switch Profile...", + "Edit Profile...", + "Show Profile Contents", + "Export Profile...", + "Export Profile ({0})...", + "Import Profile...", + "Import Profile...", + "Save Current Profile As...", + "Create Profile...", + "Delete Profile..." + ], "vs/workbench/contrib/localHistory/browser/localHistoryCommands": [ - "Local History", - "Compare with File", - "Compare with Previous", - "Select for Compare", - "Compare with Selected", - "Show Contents", - "Restore Contents", "File Restored", "Do you want to restore the contents of '{0}'?", "Restoring will discard any unsaved changes.", "&&Restore", "Unable to restore '{0}'.", - "Find Entry to Restore", "Select the file to show local history for", "Select the local history entry to open", - "Local History: Find Entry to Restore...", - "Rename", "Rename Local History Entry", "Enter the new name of the local history entry", - "Delete", "Do you want to delete the local history entry of '{0}' from {1}?", "This action is irreversible!", "&&Delete", - "Delete All", "Do you want to delete all entries of all files in local history?", "This action is irreversible!", "&&Delete All", - "Create Entry", "Create Local History Entry", "Enter the new name of the local history entry for '{0}'", "{0} ({1} • {2})", "{0} ({1} • {2}) ↔ {3}", - "{0} ({1} • {2}) ↔ {3} ({4} • {5})" + "{0} ({1} • {2}) ↔ {3} ({4} • {5})", + "Local History", + "Compare with File", + "Compare with Previous", + "Select for Compare", + "Compare with Selected", + "Show Contents", + "Restore Contents", + "Find Entry to Restore", + "Local History: Find Entry to Restore...", + "Rename", + "Delete", + "Delete All", + "Create Entry" ], "vs/workbench/contrib/editSessions/common/editSessions": [ - "Cloud Changes", "View icon of the cloud changes view.", - "Cloud Changes" - ], - "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ + "Cloud Changes", "Cloud Changes" ], "vs/workbench/contrib/editSessions/browser/editSessionsStorageService": [ @@ -26666,6 +28768,9 @@ "Do you want to disable storing working changes in the cloud?", "Delete all stored data from the cloud." ], + "vs/workbench/contrib/editSessions/common/editSessionsLogService": [ + "Cloud Changes" + ], "vs/workbench/contrib/editSessions/browser/editSessionsViews": [ "You have no stored changes in the cloud to display.\n{0}", "Store Working Changes", @@ -26682,6 +28787,21 @@ "Cloud Changes", "Open File" ], + "vs/workbench/contrib/accessibilitySignals/browser/commands": [ + "List all accessibility sounds, noises, or audio cues and configure their settings", + "Configure Sound", + "Select a sound to play and configure", + "List all accessibility announcements, alerts, braille messages, and configure their settings", + "Configure Announcement", + "Select an announcement to configure", + "Screen reader is not active, announcements are disabled by default.", + "Help: List Signal Sounds", + "Help: List Signal Announcements" + ], + "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ + "Icon of the workspace trust editor label.", + "Workspace Trust" + ], "vs/workbench/contrib/workspace/browser/workspaceTrustEditor": [ "Icon for workspace trust ion the banner.", "Icon for the checkmark in the workspace trust editor.", @@ -26742,51 +28862,18 @@ "Debugging is disabled", "[{0} extensions]({1}) are disabled or have limited functionality", "Tasks are not allowed to run", - "Debugging is disabled", - "[{0} workspace settings]({1}) are not applied", - "Workspace settings requiring trust are not applied", - "[{0} extensions]({1}) are disabled or have limited functionality", - "Keyboard Shortcut: {0}", - "Trust", - "Trust the authors of all files in the current folder or its parent '{0}'.", - "Trust Parent", - "Don't Trust", - "This workspace is trusted via the bolded entries in the trusted folders below.", - "This folder is trusted via the bolded entries in the the trusted folders below.", - "This window is trusted by nature of the workspace that is opened." - ], - "vs/workbench/services/workspaces/browser/workspaceTrustEditorInput": [ - "Workspace Trust" - ], - "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration": [ - "Accessibility", - "Provide information about how to access the terminal accessibility help menu when the terminal is focused.", - "Provide information about how to navigate changes in the diff editor when it is focused.", - "Provide information about how to access the chat help menu when the chat input is focused.", - "Provide information about how to access the inline editor chat accessibility help menu and alert with hints that describe how to use the feature when the input is focused.", - "Provide information about how to access the inline completions hover and accessible view.", - "Provide information about how to change a keybinding in the keybindings editor when a row is focused.", - "Provide information about how to focus the cell container or inner editor when a notebook cell is focused.", - "Provide information about how to open the hover in an accessible view.", - "Provide information about how to open the notification in an accessible view.", - "Provide information about relevant actions in an empty text editor.", - "Provide information about actions that can be taken in the comment widget or in a file which contains comments.", - "When in screen reader mode, alerts when a file is saved. Note that this will be ignored when {0} is enabled.", - "Alerts when a file is saved via user gesture.", - "Alerts whenever is a file is saved, including auto save.", - "Never alerts.", - "When in screen reader mode, alerts when a file or notebook cell is formatted. Note that this will be ignored when {0} is enabled.", - "Alerts when a file is formatted via user gesture.", - "Alerts whenever is a file is formatted, including auto save, on cell execution, and more.", - "Never alerts.", - "Whether to dim unfocused editors and terminals, which makes it more clear where typed input will go to. This works with the majority of editors with the notable exceptions of those that utilize iframes like notebooks and extension webview editors.", - "The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.", - "Controls whether the accessible view is hidden." - ], - "vs/workbench/contrib/accessibility/browser/accessibleNotificationService": [ - "Cleared", - "Saved", - "Formatted" + "Debugging is disabled", + "[{0} workspace settings]({1}) are not applied", + "Workspace settings requiring trust are not applied", + "[{0} extensions]({1}) are disabled or have limited functionality", + "Keyboard Shortcut: {0}", + "Trust", + "Trust the authors of all files in the current folder or its parent '{0}'.", + "Trust Parent", + "Don't Trust", + "This workspace is trusted via the bolded entries in the trusted folders below.", + "This folder is trusted via the bolded entries in the the trusted folders below.", + "This window is trusted by nature of the workspace that is opened." ], "vs/workbench/contrib/accessibility/browser/accessibilityStatus": [ "Are you using a screen reader to operate VS Code?", @@ -26795,109 +28882,439 @@ "Screen Reader Optimized", "Screen Reader Mode" ], - "vs/workbench/contrib/audioCues/browser/commands": [ - "Help: List Audio Cues", - "Disabled", - "Enable/Disable Audio Cue", - "Select an audio cue to play" + "vs/workbench/contrib/comments/browser/commentsAccessibility": [ + "The editor contains commentable range(s). Some useful commands include:", + "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled ({0}).", + "This widget contains a text area, for composition of new comments, and actions, that can be tabbed to once tab moves focus mode has been enabled with the command Toggle Tab Key Moves Focus, which is currently not triggerable via keybinding.", + "Some useful comment commands include:", + "- Dismiss Comment (Escape)", + "- Go to Next Commenting Range ({0})", + "- Go to Next Commenting Range, which is currently not triggerable via keybinding.", + "- Go to Previous Commenting Range ({0})", + "- Go to Previous Commenting Range, which is currently not triggerable via keybinding.", + "- Go to Next Comment Thread ({0})", + "- Go to Next Comment Thread, which is currently not triggerable via keybinding.", + "- Go to Previous Comment Thread ({0})", + "- Go to Previous Comment Thread, which is currently not triggerable via keybinding.", + "- Add Comment ({0})", + "- Add Comment on Current Selection, which is currently not triggerable via keybinding.", + "- Submit Comment ({0})", + "- Submit Comment, accessible via tabbing, as it's currently not triggerable with a keybinding." + ], + "vs/workbench/contrib/accessibilitySignals/browser/openDiffEditorAnnouncement": [ + "Diff editor" + ], + "vs/workbench/contrib/accessibility/browser/audioCueConfiguration": [ + "Enable audio cue when a screen reader is attached.", + "Enable audio cue.", + "Disable audio cue.", + "This setting is deprecated. Use `signals` settings instead.", + "Whether or not position changes should be debounced", + "This setting is deprecated, instead use the `signals.debouncePositionChanges` setting.", + "Plays a sound when the active line has a breakpoint.", + "Plays a sound when the active line has an inline suggestion.", + "Plays a sound when the active line has an error.", + "Plays a sound when the active line has a folded area that can be unfolded.", + "Plays a sound when the active line has a warning.", + "Plays a sound when the debugger stopped on a breakpoint.", + "Plays a sound when trying to read a line with inlay hints that has no inlay hints.", + "Plays a sound when a task is completed.", + "Plays a sound when a task fails (non-zero exit code).", + "Plays a sound when a terminal command fails (non-zero exit code).", + "Plays a sound when terminal Quick Fixes are available.", + "Plays a sound when the terminal bell is ringing.", + "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to a deleted line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change.", + "Plays a sound when a notebook cell execution is successfully completed.", + "Plays a sound when a notebook cell execution fails.", + "Plays a sound when a chat request is made.", + "Plays a sound on loop while the response is pending.", + "Plays a sound on loop while the response has been received.", + "Plays a sound when a feature is cleared (for example, the terminal, Debug Console, or Output channel). When this is disabled, an ARIA alert will announce 'Cleared'.", + "Plays a sound when a file is saved. Also see {0}", + "Plays the audio cue when a user explicitly saves a file.", + "Plays the audio cue whenever a file is saved, including auto save.", + "Never plays the audio cue.", + "Plays a sound when a file or notebook is formatted. Also see {0}", + "Plays the audio cue when a user explicitly formats a file.", + "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell.", + "Never plays the audio cue." + ], + "vs/workbench/contrib/scrollLocking/browser/scrollLocking": [ + "Scrolling Locked", + "Lock Scrolling Enabled", + "Locked Scrolling", + "Synchronize Scrolling Editors", + "Locked Scrolling", + "Toggle Locked Scrolling Across Editors", + "Hold Locked Scrolling Across Editors" ], "vs/workbench/contrib/share/browser/shareService": [ "The number of available share providers", "Choose how to share {0}" ], + "vs/workbench/browser/window": [ + "Are you sure you want to quit?", + "Are you sure you want to exit?", + "Are you sure you want to close the window?", + "&&Quit", + "&&Exit", + "&&Close Window", + "Do not ask me again", + "An unexpected error occurred that requires a reload of this page.", + "The workbench was unexpectedly disposed while running.", + "&&Reload", + "The browser interrupted the opening of a new tab or window. Press 'Open' to open it anyway.", + "&&Open", + "&&Learn More", + "&&Try Again", + "We launched {0} on your computer.\n\nIf {1} did not launch, try again or install it below.", + "&&Install", + "We launched {0} on your computer.\n\nIf {1} did not launch, try again below.", + "All done. You can close this tab now." + ], "vs/workbench/browser/parts/notifications/notificationsCenter": [ "No new notifications", "Notifications", "Notification Center Actions", + "Enable Do Not Disturb Mode", + "Disable Do Not Disturb Mode", + "More…", "Notifications Center" ], - "vs/workbench/browser/parts/notifications/notificationsAlerts": [ - "Error: {0}", - "Warning: {0}", - "Info: {0}" + "vs/workbench/browser/parts/notifications/notificationsStatus": [ + "Notifications", + "Notifications", + "Do Not Disturb", + "Do Not Disturb Mode is Enabled", + "Hide Notifications", + "No Notifications", + "No New Notifications", + "1 New Notification", + "{0} New Notifications", + "No New Notifications ({0} in progress)", + "1 New Notification ({0} in progress)", + "{0} New Notifications ({1} in progress)", + "Status Message" + ], + "vs/workbench/browser/parts/notifications/notificationsAlerts": [ + "Error: {0}", + "Warning: {0}", + "Info: {0}" + ], + "vs/workbench/browser/parts/notifications/notificationsCommands": [ + "Select sources to enable notifications for", + "Notifications", + "Show Notifications", + "Hide Notifications", + "Clear All Notifications", + "Accept Notification Primary Action", + "Toggle Do Not Disturb Mode", + "Toggle Do Not Disturb Mode By Source...", + "Focus Notification Toast" + ], + "vs/workbench/browser/parts/notifications/notificationsToasts": [ + "{0}, notification", + "{0}, source: {1}, notification" + ], + "vs/workbench/services/configuration/common/configurationEditing": [ + "Error while writing to {0}. {1}", + "Open Tasks Configuration", + "Open Launch Configuration", + "Open Settings", + "Open Tasks Configuration", + "Open Launch Configuration", + "Save and Retry", + "Save and Retry", + "Open Settings", + "Unable to write {0} because it is configured in system policy.", + "Unable to write to {0} because {1} is not a registered configuration.", + "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", + "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", + "Unable to write to Folder Settings because {0} does not support the folder resource scope.", + "Unable to write to User Settings because {0} does not support for global scope.", + "Unable to write to Workspace Settings because {0} does not support for workspace scope in a multi folder workspace.", + "Unable to write to Folder Settings because no resource is provided.", + "Unable to write to Language Settings because {0} is not a resource language setting.", + "Unable to write to {0} because no workspace is opened. Please open a workspace first and try again.", + "Unable to write into the tasks configuration file. Please open it to correct errors/warnings in it and try again.", + "Unable to write into the launch configuration file. Please open it to correct errors/warnings in it and try again.", + "Unable to write into user settings. Please open the user settings to correct errors/warnings in it and try again.", + "Unable to write into remote user settings. Please open the remote user settings to correct errors/warnings in it and try again.", + "Unable to write into workspace settings. Please open the workspace settings to correct errors/warnings in the file and try again.", + "Unable to write into folder settings. Please open the '{0}' folder settings to correct errors/warnings in it and try again.", + "Unable to write into tasks configuration file because the file has unsaved changes. Please save it first and then try again.", + "Unable to write into launch configuration file because the file has unsaved changes. Please save it first and then try again.", + "Unable to write into user settings because the file has unsaved changes. Please save the user settings file first and then try again.", + "Unable to write into remote user settings because the file has unsaved changes. Please save the remote user settings file first and then try again.", + "Unable to write into workspace settings because the file has unsaved changes. Please save the workspace settings file first and then try again.", + "Unable to write into folder settings because the file has unsaved changes. Please save the '{0}' folder settings file first and then try again.", + "Unable to write into tasks configuration file because the content of the file is newer.", + "Unable to write into launch configuration file because the content of the file is newer.", + "Unable to write into user settings because the content of the file is newer.", + "Unable to write into remote user settings because the content of the file is newer.", + "Unable to write into workspace settings because the content of the file is newer.", + "Unable to write into folder settings because the content of the file is newer.", + "Unable to write to {0} because of an internal error.", + "User Settings", + "Remote User Settings", + "Workspace Settings", + "Folder Settings" + ], + "vs/workbench/services/textfile/common/textFileEditorModelManager": [ + "Failed to save '{0}': {1}" + ], + "vs/workbench/common/editor/textEditorModel": [ + "Language {0} was automatically detected and set as the language mode." + ], + "vs/platform/theme/common/colors/chartsColors": [ + "The foreground color used in charts.", + "The color used for horizontal lines in charts.", + "The red color used in chart visualizations.", + "The blue color used in chart visualizations.", + "The yellow color used in chart visualizations.", + "The orange color used in chart visualizations.", + "The green color used in chart visualizations.", + "The purple color used in chart visualizations." + ], + "vs/platform/theme/common/colors/inputColors": [ + "Input box background.", + "Input box foreground.", + "Input box border.", + "Border color of activated options in input fields.", + "Background color of activated options in input fields.", + "Background hover color of options in input fields.", + "Foreground color of activated options in input fields.", + "Input box foreground color for placeholder text.", + "Input validation background color for information severity.", + "Input validation foreground color for information severity.", + "Input validation border color for information severity.", + "Input validation background color for warning severity.", + "Input validation foreground color for warning severity.", + "Input validation border color for warning severity.", + "Input validation background color for error severity.", + "Input validation foreground color for error severity.", + "Input validation border color for error severity.", + "Dropdown background.", + "Dropdown list background.", + "Dropdown foreground.", + "Dropdown border.", + "Button foreground color.", + "Button separator color.", + "Button background color.", + "Button background color when hovering.", + "Button border color.", + "Secondary button foreground color.", + "Secondary button background color.", + "Secondary button background color when hovering.", + "Background color of checkbox widget.", + "Background color of checkbox widget when the element it's in is selected.", + "Foreground color of checkbox widget.", + "Border color of checkbox widget.", + "Border color of checkbox widget when the element it's in is selected.", + "Keybinding label background color. The keybinding label is used to represent a keyboard shortcut.", + "Keybinding label foreground color. The keybinding label is used to represent a keyboard shortcut.", + "Keybinding label border color. The keybinding label is used to represent a keyboard shortcut.", + "Keybinding label border bottom color. The keybinding label is used to represent a keyboard shortcut." + ], + "vs/platform/theme/common/colors/editorColors": [ + "Editor background color.", + "Editor default foreground color.", + "Background color of sticky scroll in the editor", + "Background color of sticky scroll on hover in the editor", + "Border color of sticky scroll in the editor", + " Shadow color of sticky scroll in the editor", + "Background color of editor widgets, such as find/replace.", + "Foreground color of editor widgets, such as find/replace.", + "Border color of editor widgets. The color is only used if the widget chooses to have a border and if the color is not overridden by a widget.", + "Border color of the resize bar of editor widgets. The color is only used if the widget chooses to have a resize border and if the color is not overridden by a widget.", + "Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.", + "Foreground color of error squigglies in the editor.", + "If set, color of double underlines for errors in the editor.", + "Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.", + "Foreground color of warning squigglies in the editor.", + "If set, color of double underlines for warnings in the editor.", + "Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.", + "Foreground color of info squigglies in the editor.", + "If set, color of double underlines for infos in the editor.", + "Foreground color of hint squigglies in the editor.", + "If set, color of double underlines for hints in the editor.", + "Color of active links.", + "Color of the editor selection.", + "Color of the selected text for high contrast.", + "Color of the selection in an inactive editor. The color must not be opaque so as not to hide underlying decorations.", + "Color for regions with the same content as the selection. The color must not be opaque so as not to hide underlying decorations.", + "Border color for regions with the same content as the selection.", + "Color of the current search match.", + "Color of the other search matches. The color must not be opaque so as not to hide underlying decorations.", + "Color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.", + "Border color of the current search match.", + "Border color of the other search matches.", + "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.", + "Highlight below the word for which a hover is shown. The color must not be opaque so as not to hide underlying decorations.", + "Background color of the editor hover.", + "Foreground color of the editor hover.", + "Border color of the editor hover.", + "Background color of the editor hover status bar.", + "Foreground color of inline hints", + "Background color of inline hints", + "Foreground color of inline hints for types", + "Background color of inline hints for types", + "Foreground color of inline hints for parameters", + "Background color of inline hints for parameters", + "The color used for the lightbulb actions icon.", + "The color used for the lightbulb auto fix actions icon.", + "The color used for the lightbulb AI icon.", + "Highlight background color of a snippet tabstop.", + "Highlight border color of a snippet tabstop.", + "Highlight background color of the final tabstop of a snippet.", + "Highlight border color of the final tabstop of a snippet.", + "Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.", + "Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.", + "Background color for lines that got inserted. The color must not be opaque so as not to hide underlying decorations.", + "Background color for lines that got removed. The color must not be opaque so as not to hide underlying decorations.", + "Background color for the margin where lines got inserted.", + "Background color for the margin where lines got removed.", + "Diff overview ruler foreground for inserted content.", + "Diff overview ruler foreground for removed content.", + "Outline color for the text that got inserted.", + "Outline color for text that got removed.", + "Border color between the two text editors.", + "Color of the diff editor's diagonal fill. The diagonal fill is used in side-by-side diff views.", + "The background color of unchanged blocks in the diff editor.", + "The foreground color of unchanged blocks in the diff editor.", + "The background color of unchanged code in the diff editor.", + "Shadow color of widgets such as find/replace inside the editor.", + "Border color of widgets such as find/replace inside the editor.", + "Toolbar background when hovering over actions using the mouse", + "Toolbar outline when hovering over actions using the mouse", + "Toolbar background when holding the mouse over actions", + "Color of focused breadcrumb items.", + "Background color of breadcrumb items.", + "Color of focused breadcrumb items.", + "Color of selected breadcrumb items.", + "Background color of breadcrumb item picker.", + "Current header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Current content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Incoming header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Incoming content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Common ancestor header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Common ancestor content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.", + "Border color on headers and the splitter in inline merge-conflicts.", + "Current overview ruler foreground for inline merge-conflicts.", + "Incoming overview ruler foreground for inline merge-conflicts.", + "Common ancestor overview ruler foreground for inline merge-conflicts.", + "Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.", + "Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.", + "The color used for the problems error icon.", + "The color used for the problems warning icon.", + "The color used for the problems info icon." + ], + "vs/platform/theme/common/colors/minimapColors": [ + "Minimap marker color for find matches.", + "Minimap marker color for repeating editor selections.", + "Minimap marker color for the editor selection.", + "Minimap marker color for infos.", + "Minimap marker color for warnings.", + "Minimap marker color for errors.", + "Minimap background color.", + "Opacity of foreground elements rendered in the minimap. For example, \"#000000c0\" will render the elements with 75% opacity.", + "Minimap slider background color.", + "Minimap slider background color when hovering.", + "Minimap slider background color when clicked on." ], - "vs/workbench/browser/parts/notifications/notificationsStatus": [ - "Notifications", - "Notifications", - "Do Not Disturb", - "Do Not Disturb Mode is Enabled", - "Hide Notifications", - "No Notifications", - "No New Notifications", - "1 New Notification", - "{0} New Notifications", - "No New Notifications ({0} in progress)", - "1 New Notification ({0} in progress)", - "{0} New Notifications ({1} in progress)", - "Status Message" + "vs/platform/theme/common/colors/listColors": [ + "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree outline color for the focused item when the list/tree is active and selected. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree icon foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.", + "List/Tree background when hovering over items using the mouse.", + "List/Tree foreground when hovering over items using the mouse.", + "List/Tree drag and drop background when moving items over other items when using the mouse.", + "List/Tree drag and drop border color when moving items between items when using the mouse.", + "List/Tree foreground color of the match highlights when searching inside the list/tree.", + "List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.", + "List/Tree foreground color for invalid items, for example an unresolved root in explorer.", + "Foreground color of list items containing errors.", + "Foreground color of list items containing warnings.", + "Background color of the type filter widget in lists and trees.", + "Outline color of the type filter widget in lists and trees.", + "Outline color of the type filter widget in lists and trees, when there are no matches.", + "Shadow color of the type filter widget in lists and trees.", + "Background color of the filtered match.", + "Border color of the filtered match.", + "List/Tree foreground color for items that are deemphasized.", + "Tree stroke color for the indentation guides.", + "Tree stroke color for the indentation guides that are not active.", + "Table border color between columns.", + "Background color for odd table rows." ], - "vs/workbench/browser/parts/notifications/notificationsToasts": [ - "{0}, notification", - "{0}, source: {1}, notification" + "vs/platform/theme/common/colors/miscColors": [ + "Border color of active sashes.", + "Badge background color. Badges are small information labels, e.g. for search results count.", + "Badge foreground color. Badges are small information labels, e.g. for search results count.", + "Scrollbar shadow to indicate that the view is scrolled.", + "Scrollbar slider background color.", + "Scrollbar slider background color when hovering.", + "Scrollbar slider background color when clicked on.", + "Background color of the progress bar that can show for long running operations." ], - "vs/workbench/browser/parts/notifications/notificationsCommands": [ - "Notifications", - "Show Notifications", - "Hide Notifications", - "Clear All Notifications", - "Accept Notification Primary Action", - "Toggle Do Not Disturb Mode", - "Focus Notification Toast" + "vs/platform/theme/common/colors/menuColors": [ + "Border color of menus.", + "Foreground color of menu items.", + "Background color of menu items.", + "Foreground color of the selected menu item in menus.", + "Background color of the selected menu item in menus.", + "Border color of the selected menu item in menus.", + "Color of a separator menu item in menus." ], - "vs/workbench/services/configuration/common/configurationEditing": [ - "Error while writing to {0}. {1}", - "Open Tasks Configuration", - "Open Launch Configuration", - "Open Settings", - "Open Tasks Configuration", - "Open Launch Configuration", - "Save and Retry", - "Save and Retry", - "Open Settings", - "Unable to write {0} because it is configured in system policy.", - "Unable to write to {0} because {1} is not a registered configuration.", - "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", - "Unable to write {0} to Workspace Settings. This setting can be written only into User settings.", - "Unable to write to Folder Settings because {0} does not support the folder resource scope.", - "Unable to write to User Settings because {0} does not support for global scope.", - "Unable to write to Workspace Settings because {0} does not support for workspace scope in a multi folder workspace.", - "Unable to write to Folder Settings because no resource is provided.", - "Unable to write to Language Settings because {0} is not a resource language setting.", - "Unable to write to {0} because no workspace is opened. Please open a workspace first and try again.", - "Unable to write into the tasks configuration file. Please open it to correct errors/warnings in it and try again.", - "Unable to write into the launch configuration file. Please open it to correct errors/warnings in it and try again.", - "Unable to write into user settings. Please open the user settings to correct errors/warnings in it and try again.", - "Unable to write into remote user settings. Please open the remote user settings to correct errors/warnings in it and try again.", - "Unable to write into workspace settings. Please open the workspace settings to correct errors/warnings in the file and try again.", - "Unable to write into folder settings. Please open the '{0}' folder settings to correct errors/warnings in it and try again.", - "Unable to write into tasks configuration file because the file has unsaved changes. Please save it first and then try again.", - "Unable to write into launch configuration file because the file has unsaved changes. Please save it first and then try again.", - "Unable to write into user settings because the file has unsaved changes. Please save the user settings file first and then try again.", - "Unable to write into remote user settings because the file has unsaved changes. Please save the remote user settings file first and then try again.", - "Unable to write into workspace settings because the file has unsaved changes. Please save the workspace settings file first and then try again.", - "Unable to write into folder settings because the file has unsaved changes. Please save the '{0}' folder settings file first and then try again.", - "Unable to write into tasks configuration file because the content of the file is newer.", - "Unable to write into launch configuration file because the content of the file is newer.", - "Unable to write into user settings because the content of the file is newer.", - "Unable to write into remote user settings because the content of the file is newer.", - "Unable to write into workspace settings because the content of the file is newer.", - "Unable to write into folder settings because the content of the file is newer.", - "Unable to write to {0} because of an internal error.", - "User Settings", - "Remote User Settings", - "Workspace Settings", - "Folder Settings" + "vs/platform/theme/common/colors/baseColors": [ + "Overall foreground color. This color is only used if not overridden by a component.", + "Overall foreground for disabled elements. This color is only used if not overridden by a component.", + "Overall foreground color for error messages. This color is only used if not overridden by a component.", + "Foreground color for description text providing additional information, for example for a label.", + "The default color for icons in the workbench.", + "Overall border color for focused elements. This color is only used if not overridden by a component.", + "An extra border around elements to separate them from others for greater contrast.", + "An extra border around active elements to separate them from others for greater contrast.", + "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor.", + "Foreground color for links in text.", + "Foreground color for links in text when clicked on and on mouse hover.", + "Color for text separators.", + "Foreground color for preformatted text segments.", + "Background color for preformatted text segments.", + "Background color for block quotes in text.", + "Border color for block quotes in text.", + "Background color for code blocks in text." ], - "vs/workbench/services/textfile/common/textFileEditorModelManager": [ - "Failed to save '{0}': {1}" + "vs/platform/theme/common/colors/quickpickColors": [ + "Quick picker background color. The quick picker widget is the container for pickers like the command palette.", + "Quick picker foreground color. The quick picker widget is the container for pickers like the command palette.", + "Quick picker title background color. The quick picker widget is the container for pickers like the command palette.", + "Quick picker color for grouping labels.", + "Quick picker color for grouping borders.", + "Please use quickInputList.focusBackground instead", + "Quick picker foreground color for the focused item.", + "Quick picker icon foreground color for the focused item.", + "Quick picker background color for the focused item." ], - "vs/workbench/common/editor/textEditorModel": [ - "Language {0} was automatically detected and set as the language mode." + "vs/platform/theme/common/colors/searchColors": [ + "Color of the text in the search viewlet's completion message.", + "Color of the Search Editor query matches.", + "Border color of the Search Editor query matches." ], "vs/workbench/browser/parts/titlebar/titlebarPart": [ - "Focus Title Bar", - "Command Center", - "Layout Controls" + "Title actions", + "Focus Title Bar" ], "vs/workbench/services/configurationResolver/common/variableResolver": [ "Variable {0} can not be resolved. Please open an editor.", @@ -26922,22 +29339,19 @@ "vs/workbench/services/extensions/common/extensionHostManager": [ "Measure Extension Host Latency" ], - "vs/workbench/services/extensions/common/extensionsUtil": [ - "Overwriting extension {0} with {1}.", - "Overwriting extension {0} with {1}.", - "Loading development extension at {0}" - ], "vs/workbench/contrib/localization/common/localizationsActions": [ "Select Display Language", "Installed", "Available", "More Info", - "Clear Display Language Preference", "Configure Display Language", - "Changes the locale of VS Code based on installed language packs. Common languages include French, Chinese, Spanish, Japanese, German, Korean, and more." + "Changes the locale of VS Code based on installed language packs. Common languages include French, Chinese, Spanish, Japanese, German, Korean, and more.", + "Clear Display Language Preference" ], - "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ - "Report Issue" + "vs/workbench/services/extensions/common/extensionsUtil": [ + "Overwriting extension {0} with {1}.", + "Overwriting extension {0} with {1}.", + "Loading development extension at {0}" ], "vs/workbench/contrib/extensions/electron-sandbox/extensionsSlowActions": [ "Performance Issue", @@ -26948,6 +29362,9 @@ "Did you attach the CPU-Profile?", "This is a reminder to make sure that you have not forgotten to attach '{0}' to an existing performance issue." ], + "vs/workbench/contrib/extensions/common/reportExtensionIssueAction": [ + "Report Issue" + ], "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote": [ "Create New Integrated Terminal (Local)" ], @@ -26957,21 +29374,49 @@ "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host.", "Pty Host is unresponsive" ], - "vs/workbench/contrib/localHistory/browser/localHistory": [ - "Icon for a local history entry in the timeline view.", - "Icon for restoring contents of a local history entry." - ], - "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ - "Task is running", - "Task succeeded", - "Task succeeded and waiting...", - "Task has errors", - "Task has errors and is waiting...", - "Task has warnings", - "Task has warnings and is waiting...", - "Task has infos", - "Task has infos and is waiting...", - "Beginning of detected errors for this run" + "vs/platform/theme/common/tokenClassificationRegistry": [ + "Colors and styles for the token.", + "Foreground color for the token.", + "Token background colors are currently not supported.", + "Sets the all font styles of the rule: 'italic', 'bold', 'underline' or 'strikethrough' or a combination. All styles that are not listed are unset. The empty string unsets all styles.", + "Font style must be 'italic', 'bold', 'underline' or 'strikethrough' or a combination. The empty string unsets all styles.", + "None (clear inherited style)", + "Sets or unsets the font style to bold. Note, the presence of 'fontStyle' overrides this setting.", + "Sets or unsets the font style to italic. Note, the presence of 'fontStyle' overrides this setting.", + "Sets or unsets the font style to underline. Note, the presence of 'fontStyle' overrides this setting.", + "Sets or unsets the font style to strikethrough. Note, the presence of 'fontStyle' overrides this setting.", + "Style for comments.", + "Style for strings.", + "Style for keywords.", + "Style for numbers.", + "Style for expressions.", + "Style for operators.", + "Style for namespaces.", + "Style for types.", + "Style for structs.", + "Style for classes.", + "Style for interfaces.", + "Style for enums.", + "Style for type parameters.", + "Style for functions", + "Style for member functions", + "Style for method (member functions)", + "Style for macros.", + "Style for variables.", + "Style for parameters.", + "Style for properties.", + "Style for enum members.", + "Style for events.", + "Style for decorators & annotations.", + "Style for labels. ", + "Style for all symbol declarations.", + "Style to use for references in documentation.", + "Style to use for symbols that are static.", + "Style to use for symbols that are abstract.", + "Style to use for symbols that are deprecated.", + "Style to use for write accesses.", + "Style to use for symbols that are async.", + "Style to use for symbols that are read-only." ], "vs/workbench/contrib/tasks/common/taskConfiguration": [ "Warning: options.cwd must be of type string. Ignoring value {0}\n", @@ -26991,6 +29436,18 @@ "Error: the task '{0}' doesn't define a command. The task will be ignored. Its definition is:\n{1}", "Task version 2.0.0 doesn't support global OS specific tasks. Convert them to a task with a OS specific command. Affected tasks are:\n{0}" ], + "vs/workbench/contrib/tasks/browser/taskTerminalStatus": [ + "Task is running", + "Task succeeded", + "Task succeeded and waiting...", + "Task has errors", + "Task has errors and is waiting...", + "Task has warnings", + "Task has warnings and is waiting...", + "Task has infos", + "Task has infos and is waiting...", + "Beginning of detected errors for this run" + ], "vs/workbench/contrib/tasks/common/taskTemplates": [ "Executes .NET Core build command", "Executes the build target", @@ -27016,6 +29473,13 @@ "No {0} tasks found. Go back ↩", "There is no task provider registered for tasks of type \"{0}\"." ], + "vs/workbench/contrib/localHistory/browser/localHistory": [ + "Icon for a local history entry in the timeline view.", + "Icon for restoring contents of a local history entry." + ], + "vs/workbench/contrib/multiDiffEditor/browser/icons.contribution": [ + "Icon of the multi diff editor label." + ], "vs/workbench/contrib/debug/common/abstractDebugAdapter": [ "Timeout after {0} ms for '{1}'" ], @@ -27082,7 +29546,7 @@ ], "vs/platform/terminal/common/terminalPlatformConfiguration": [ "An optional set of arguments to run the shell executable with.", - "Controls whether or not the profile name overrides the auto detected one.", + "Whether or not to replace the dynamic terminal title that detects what program is running with the static profile name.", "A codicon ID to associate with the terminal icon.", "A theme color ID to associate with the terminal icon.", "An object with environment variables that will be added to the terminal profile process. Set to `null` to delete environment variables from the base environment.", @@ -27120,9 +29584,44 @@ "Error: {0}", "Warning: {0}", "Info: {0}", - "for history", + " or {0} for history", + " ({0} for history)", "Cleared Input" ], + "vs/editor/browser/widget/diffEditor/registrations.contribution": [ + "The border color for text that got moved in the diff editor.", + "The active border color for text that got moved in the diff editor.", + "The color of the shadow around unchanged region widgets.", + "Line decoration for inserts in the diff editor.", + "Line decoration for removals in the diff editor." + ], + "vs/editor/browser/widget/diffEditor/commands": [ + "Toggle Collapse Unchanged Regions", + "Toggle Show Moved Code Blocks", + "Toggle Use Inline View When Space Is Limited", + "Diff Editor", + "Switch Side", + "Exit Compare Move", + "Collapse All Unchanged Regions", + "Show All Unchanged Regions", + "Revert", + "Accessible Diff Viewer", + "Go to Next Difference", + "Go to Previous Difference" + ], + "vs/editor/contrib/dropOrPasteInto/browser/copyPasteController": [ + "Whether the paste widget is showing", + "Show paste options...", + "No paste edits for '{0}' found", + "Running paste handlers. Click to cancel", + "Select Paste Action", + "Running paste handlers" + ], + "vs/editor/contrib/codeAction/browser/codeActionController": [ + "Context: {0} at line {1} and column {2}.", + "Hide Disabled", + "Show Disabled" + ], "vs/editor/contrib/codeAction/browser/codeActionCommands": [ "Kind of the code action to run.", "Controls when the returned actions are applied.", @@ -27153,12 +29652,8 @@ "Auto Fix...", "No auto fixes available" ], - "vs/editor/contrib/codeAction/browser/codeActionController": [ - "Context: {0} at line {1} and column {2}.", - "Hide Disabled", - "Show Disabled" - ], "vs/editor/contrib/codeAction/browser/lightBulbWidget": [ + "Run: {0}", "Show Code Actions. Preferred Quick Fix Available ({0})", "Show Code Actions ({0})", "Show Code Actions" @@ -27166,35 +29661,15 @@ "vs/base/browser/ui/actionbar/actionViewItems": [ "{0} ({1})" ], - "vs/editor/contrib/dropOrPasteInto/browser/copyPasteController": [ - "Whether the paste widget is showing", - "Show paste options...", - "Running paste handlers. Click to cancel", - "Select Paste Action", - "Running paste handlers" - ], "vs/editor/contrib/dropOrPasteInto/browser/defaultProviders": [ - "Built-in", "Insert Plain Text", "Insert Uris", "Insert Uri", "Insert Paths", "Insert Path", "Insert Relative Paths", - "Insert Relative Path" - ], - "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController": [ - "Whether the drop widget is showing", - "Show drop options...", - "Running drop handlers. Click to cancel" - ], - "vs/editor/contrib/folding/browser/foldingDecorations": [ - "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations.", - "Color of the folding control in the editor gutter.", - "Icon for expanded ranges in the editor glyph margin.", - "Icon for collapsed ranges in the editor glyph margin.", - "Icon for manually collapsed ranges in the editor glyph margin.", - "Icon for manually expanded ranges in the editor glyph margin." + "Insert Relative Path", + "Insert HTML" ], "vs/editor/contrib/find/browser/findWidget": [ "Icon for 'Find in Selection' in the editor find widget.", @@ -27225,6 +29700,21 @@ "{0} found for '{1}'", "Ctrl+Enter now inserts line break instead of replacing all. You can modify the keybinding for editor.action.replaceAll to override this behavior." ], + "vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController": [ + "Whether the drop widget is showing", + "Show drop options...", + "Running drop handlers. Click to cancel" + ], + "vs/editor/contrib/folding/browser/foldingDecorations": [ + "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations.", + "Color of the folding control in the editor gutter.", + "Icon for expanded ranges in the editor glyph margin.", + "Icon for collapsed ranges in the editor glyph margin.", + "Icon for manually collapsed ranges in the editor glyph margin.", + "Icon for manually expanded ranges in the editor glyph margin.", + "Click to expand the range.", + "Click to collapse the range." + ], "vs/editor/contrib/inlineCompletions/browser/commands": [ "Show Next Inline Suggestion", "Show Previous Inline Suggestion", @@ -27238,11 +29728,56 @@ "Hide Inline Suggestion", "Always Show Toolbar" ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController": [ + "Inspect this in the accessible view ({0})" + ], "vs/editor/contrib/inlineCompletions/browser/hoverParticipant": [ "Suggestion:" ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController": [ - "Inspect this in the accessible view ({0})" + "vs/editor/contrib/hover/browser/hoverActions": [ + "Show or Focus Hover", + "The hover will not automatically take focus.", + "The hover will take focus only if it is already visible.", + "The hover will automatically take focus when it appears.", + "Show Definition Preview Hover", + "Scroll Up Hover", + "Scroll Down Hover", + "Scroll Left Hover", + "Scroll Right Hover", + "Page Up Hover", + "Page Down Hover", + "Go To Top Hover", + "Go To Bottom Hover", + "Increase Hover Verbosity Level", + "Decrease Hover Verbosity Level", + "Show or focus the editor hover which shows documentation, references, and other content for a symbol at the current cursor position.", + "Show the definition preview hover in the editor.", + "Scroll up the editor hover.", + "Scroll down the editor hover.", + "Scroll left the editor hover.", + "Scroll right the editor hover.", + "Page up the editor hover.", + "Page down the editor hover.", + "Go to the top of the editor hover.", + "Go to the bottom of the editor hover." + ], + "vs/editor/contrib/hover/browser/markerHoverParticipant": [ + "View Problem", + "No quick fixes available", + "Checking for quick fixes...", + "No quick fixes available", + "Quick Fix..." + ], + "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ + "Icon for increaseing hover verbosity.", + "Icon for decreasing hover verbosity.", + "Loading...", + "Rendering paused for long line for performance reasons. This can be configured via `editor.stopRenderingLineAfter`.", + "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`.", + "Increase Verbosity ({0})", + "Increase Verbosity", + "Decrease Verbosity ({0})", + "Decrease Verbosity" ], "vs/editor/contrib/gotoSymbol/browser/peek/referencesController": [ "Whether reference peek is visible, like 'Peek References' or 'Peek Definition'", @@ -27283,25 +29818,6 @@ "Editor marker navigation widget info heading background.", "Editor marker navigation widget background." ], - "vs/editor/contrib/hover/browser/markdownHoverParticipant": [ - "Loading...", - "Rendering paused for long line for performance reasons. This can be configured via `editor.stopRenderingLineAfter`.", - "Tokenization is skipped for long lines for performance reasons. This can be configured via `editor.maxTokenizationLineLength`." - ], - "vs/editor/contrib/hover/browser/markerHoverParticipant": [ - "View Problem", - "No quick fixes available", - "Checking for quick fixes...", - "No quick fixes available", - "Quick Fix..." - ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget": [ - "Icon for show next parameter hint.", - "Icon for show previous parameter hint.", - "{0} ({1})", - "Previous", - "Next" - ], "vs/editor/contrib/inlayHints/browser/inlayHintsHover": [ "Double-click to insert", "cmd + click", @@ -27329,21 +29845,26 @@ "{0}, hint", "Foreground color of the active item in the parameter hint." ], - "vs/editor/contrib/rename/browser/renameInputField": [ + "vs/editor/contrib/rename/browser/renameWidget": [ "Whether the rename input widget is visible", + "Whether the rename input widget is focused", + "{0} to Rename, {1} to Preview", + "Received {0} rename suggestions", "Rename input. Type new name and press Enter to commit.", - "{0} to Rename, {1} to Preview" + "Generate new name suggestions", + "Cancel" ], "vs/editor/contrib/stickyScroll/browser/stickyScrollActions": [ - "Toggle Sticky Scroll", - "&&Toggle Sticky Scroll", + "&&Toggle Editor Sticky Scroll", "Sticky Scroll", "&&Sticky Scroll", - "Focus Sticky Scroll", "&&Focus Sticky Scroll", - "Select next sticky scroll line", - "Select previous sticky scroll line", - "Go to focused sticky scroll line", + "Toggle Editor Sticky Scroll", + "Toggle/enable the editor sticky scroll which shows the nested scopes at the top of the viewport", + "Focus on the editor sticky scroll", + "Select the next editor sticky scroll line", + "Select the previous sticky scroll line", + "Go to the focused sticky scroll line", "Select Editor" ], "vs/editor/contrib/suggest/browser/suggestWidget": [ @@ -27364,49 +29885,25 @@ "{0}, {1}", "{0}, docs: {1}" ], - "vs/platform/theme/common/tokenClassificationRegistry": [ - "Colors and styles for the token.", - "Foreground color for the token.", - "Token background colors are currently not supported.", - "Sets the all font styles of the rule: 'italic', 'bold', 'underline' or 'strikethrough' or a combination. All styles that are not listed are unset. The empty string unsets all styles.", - "Font style must be 'italic', 'bold', 'underline' or 'strikethrough' or a combination. The empty string unsets all styles.", - "None (clear inherited style)", - "Sets or unsets the font style to bold. Note, the presence of 'fontStyle' overrides this setting.", - "Sets or unsets the font style to italic. Note, the presence of 'fontStyle' overrides this setting.", - "Sets or unsets the font style to underline. Note, the presence of 'fontStyle' overrides this setting.", - "Sets or unsets the font style to strikethrough. Note, the presence of 'fontStyle' overrides this setting.", - "Style for comments.", - "Style for strings.", - "Style for keywords.", - "Style for numbers.", - "Style for expressions.", - "Style for operators.", - "Style for namespaces.", - "Style for types.", - "Style for structs.", - "Style for classes.", - "Style for interfaces.", - "Style for enums.", - "Style for type parameters.", - "Style for functions", - "Style for member functions", - "Style for method (member functions)", - "Style for macros.", - "Style for variables.", - "Style for parameters.", - "Style for properties.", - "Style for enum members.", - "Style for events.", - "Style for decorators & annotations.", - "Style for labels. ", - "Style for all symbol declarations.", - "Style to use for references in documentation.", - "Style to use for symbols that are static.", - "Style to use for symbols that are abstract.", - "Style to use for symbols that are deprecated.", - "Style to use for write accesses.", - "Style to use for symbols that are async.", - "Style to use for symbols that are read-only." + "vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature": [ + "Fold Unchanged Region", + "Click or drag to show more above", + "Show Unchanged Region", + "Click or drag to show more below", + "{0} hidden lines", + "Double click to unfold" + ], + "vs/workbench/contrib/chat/browser/chatInputPart": [ + "Chat Input, Type to ask questions or type / for topics, press enter to send out the request. Use {0} for Chat Accessibility Help.", + "Chat Input, Type code here and press Enter to run. Use the Chat Accessibility Help command for more information.", + "Chat Input", + "More...", + "Use", + "Send to @{0}" + ], + "vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables": [ + "All Files", + "Search for relevant files in the workspace and provide context from them" ], "vs/workbench/api/browser/mainThreadWebviews": [ "An error occurred while loading view: {0}" @@ -27424,42 +29921,9 @@ "Custom editor '{0}' could not be saved.", "Edit" ], - "vs/workbench/contrib/comments/browser/commentsView": [ - "Filter (e.g. text, author)", - "Filter comments", - "{0} Unresolved Comments", - "Showing {0} of {1}", - "Comments for current workspace", - "Comments in {0}, full path {1}", - "Comment from ${0} at line {1} column {2} in {3}, source: {4}", - "Comment from ${0} in {1}, source: {2}", - "Collapse All", - "Expand All" - ], - "vs/workbench/contrib/comments/browser/commentsTreeViewer": [ - "{0} comments", - "1 comment", - "Image: {0}", - "Image", - "[Ln {0}]", - "[Ln {0}-{1}]", - "Last reply from {0}", - "Comments" - ], "vs/workbench/contrib/testing/common/testResult": [ "Test run at {0}" ], - "vs/workbench/browser/parts/editor/editorDropTarget": [ - "Hold __{0}__ to drop into editor" - ], - "vs/workbench/browser/parts/editor/editorGroupView": [ - "Empty editor group actions", - "{0} (empty)", - "{0}: Group {1}", - "Group {0}", - "{0}: Editor Group {1}", - "Editor Group {0}" - ], "vs/base/browser/ui/tree/treeDefaults": [ "Collapse All" ], @@ -27504,13 +29968,11 @@ "Port number must be ≥ 0 and < {0}.", "May Require Sudo", "Port is already forwarded", - "Forward a Port", "Forward Port", "Port number or address (eg. 3000 or 10.10.10.10:2000).", "Unable to forward {0}:{1}. The host may not be available or that remote port may already be forwarded", "Unable to forward {0}:{1}. {2}", "No ports currently forwarded. Try running the {0} command", - "Stop Forwarding Port", "Choose a port to stop forwarding", "Open in Browser", "Preview in Editor", @@ -27530,7 +29992,9 @@ "Port Visibility", "Change Port Protocol", "The color of the icon for a port that has an associated running process.", - "Ports" + "Ports", + "Forward a Port", + "Stop Forwarding Port" ], "vs/workbench/contrib/remote/browser/remoteIcons": [ "Getting started icon in the remote explorer view.", @@ -27559,36 +30023,48 @@ "The file is not displayed in the text editor because it is either binary or uses an unsupported text encoding.", "Open Anyway" ], - "vs/workbench/browser/parts/paneCompositePart": [ - "Drag a view here to display.", - "More Actions...", - "Views" - ], "vs/workbench/browser/parts/activitybar/activitybarPart": [ "Menu", "Hide Menu", "Activity Bar Position", - "Move Activity Bar to Side", - "&&Side", - "Side", - "Move Activity Bar to Top", + "&&Default", + "Default", "&&Top", "Top", - "Hide Activity Bar", + "&&Bottom", + "Bottom", "&&Hidden", "Hidden", "Activity Bar Position", "Activity Bar Position", "Activity Bar Position", + "Move Activity Bar to Side", + "Move Activity Bar to Top", + "Move Activity Bar to Bottom", + "Hide Activity Bar", "Previous Primary Side Bar View", "Next Primary Side Bar View", "Focus Activity Bar" ], + "vs/workbench/browser/parts/paneCompositePart": [ + "Drag a view here to display.", + "More Actions...", + "Views" + ], "vs/workbench/browser/parts/sidebar/sidebarActions": [ "Focus into Primary Side Bar" ], - "vs/base/browser/ui/iconLabel/iconLabelHover": [ - "Loading..." + "vs/workbench/browser/parts/editor/editorGroupView": [ + "Empty editor group actions", + "{0} (empty)", + "{0}: Group {1}", + "Group {0}", + "{0}: Editor Group {1}", + "Editor Group {0}", + "Try saving or reverting the editor first and then try again." + ], + "vs/workbench/browser/parts/editor/editorDropTarget": [ + "Hold __{0}__ to drop into editor" ], "vs/workbench/services/preferences/browser/keybindingsEditorModel": [ "System", @@ -27605,6 +30081,7 @@ "The enum options should be strings, but there is a non-string option. Please file an issue with the extension author.", "Incorrect type. Expected \"string\".", "Setting has an invalid type, expected {0}. Fix in JSON.", + "Error parsing the following regex both with and without the u flag:", "Value must be {0} or fewer characters long.", "Value must be {0} or more characters long.", "Value must match regex `{0}`.", @@ -27629,11 +30106,26 @@ "Incorrect type. Expected an object.", "Property {0} is not allowed.\n" ], - "vs/editor/common/model/editStack": [ - "Typing" + "vs/editor/common/model/editStack": [ + "Typing" + ], + "vs/base/browser/ui/selectBox/selectBoxCustom": [ + "Select Box" + ], + "vs/base/browser/ui/icons/iconSelectBox": [ + "Search icons", + "No results" + ], + "vs/platform/quickinput/browser/quickInput": [ + "Back", + "Press 'Enter' to confirm your input or 'Escape' to cancel", + "{0}/{1}", + "Type to narrow down results.", + "{0} (Press 'Enter' to confirm or 'Escape' to cancel)" ], - "vs/base/browser/ui/selectBox/selectBoxCustom": [ - "Select Box" + "vs/base/browser/ui/hover/hoverWidget": [ + "Inspect this in the accessible view with {0}.", + "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding." ], "vs/platform/quickinput/browser/quickInputController": [ "Toggle all checkboxes", @@ -27644,10 +30136,6 @@ "Back ({0})", "Back" ], - "vs/base/browser/ui/hover/hoverWidget": [ - "Inspect this in the accessible view with {0}.", - "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding." - ], "vs/workbench/services/textMate/common/TMGrammars": [ "Contributes textmate tokenizers.", "Language identifier for which this syntax is contributed to.", @@ -27659,6 +30147,9 @@ "Defines which scope names contain balanced brackets.", "Defines which scope names do not contain balanced brackets." ], + "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ + "Unbound" + ], "vs/workbench/contrib/preferences/browser/preferencesWidgets": [ "User", "Remote", @@ -27671,8 +30162,26 @@ "User", "Workspace" ], - "vs/base/browser/ui/keybindingLabel/keybindingLabel": [ - "Unbound" + "vs/workbench/contrib/preferences/browser/preferencesRenderers": [ + "Edit", + "Replace in Settings", + "Copy to Settings", + "This setting cannot be applied because it is configured in the system policy.", + "This setting cannot be applied because it is not registered as language override setting.", + "This setting cannot be applied while a non-default profile is active. It will be applied when the default profile is active.", + "This setting cannot be applied because it is configured to be applied in all profiles using setting {0}. Value from the default profile will be used instead.", + "This setting cannot be applied in this window. It will be applied when you open a local window.", + "This setting cannot be applied in this workspace. It will be applied when you open the containing workspace folder directly.", + "This setting has an application scope and can be set only in the user settings file.", + "This setting can only be applied in user settings in local window or in remote settings in remote window.", + "This setting can only be applied in a trusted workspace.", + "Unknown Configuration Setting", + "Manage Workspace Trust", + "Manage Workspace Trust", + "Unsupported Property" + ], + "vs/base/browser/ui/toolbar/toolbar": [ + "More Actions..." ], "vs/workbench/contrib/preferences/common/settingsEditorColorRegistry": [ "The foreground color for a section header or active title.", @@ -27697,47 +30206,6 @@ "The background color of a settings row when hovered.", "The color of the row's top and bottom border when the row is focused." ], - "vs/workbench/contrib/preferences/browser/preferencesRenderers": [ - "Edit", - "Replace in Settings", - "Copy to Settings", - "This setting cannot be applied because it is configured in the system policy.", - "This setting cannot be applied because it is not registered as language override setting.", - "This setting cannot be applied while a non-default profile is active. It will be applied when the default profile is active.", - "This setting cannot be applied because it is configured to be applied in all profiles using setting {0}. Value from the default profile will be used instead.", - "This setting cannot be applied in this window. It will be applied when you open a local window.", - "This setting cannot be applied in this workspace. It will be applied when you open the containing workspace folder directly.", - "This setting has an application scope and can be set only in the user settings file.", - "This setting can only be applied in user settings in local window or in remote settings in remote window.", - "This setting can only be applied in a trusted workspace.", - "Unknown Configuration Setting", - "Manage Workspace Trust", - "Manage Workspace Trust", - "Unsupported Property" - ], - "vs/base/browser/ui/toolbar/toolbar": [ - "More Actions..." - ], - "vs/workbench/contrib/preferences/browser/settingsTree": [ - "Extensions", - "The setting has been configured in the current scope.", - "More Actions... ", - "Show matching extensions", - "Edit in settings.json", - "Edit settings for {0}", - "default", - "The setting has been configured in the current scope.", - "Show Extension", - "Reset Setting", - "Validation Error.", - "Validation Error.", - "Modified.", - "Settings", - "Copy Setting ID", - "Copy Setting as JSON", - "Sync This Setting", - "Apply Setting to all Profiles" - ], "vs/workbench/contrib/preferences/browser/settingsLayout": [ "Commonly Used", "Text Editor", @@ -27746,6 +30214,7 @@ "Font", "Formatting", "Diff Editor", + "Multi-File Diff Editor", "Minimap", "Suggestions", "Files", @@ -27759,6 +30228,7 @@ "Window", "New Window", "Features", + "Accessibility Signals", "Accessibility", "Explorer", "Search", @@ -27774,7 +30244,6 @@ "Remote", "Timeline", "Notebook", - "Audio Cues", "Merge Editor", "Chat", "Application", @@ -27808,15 +30277,36 @@ "Policy services", "Show settings for policy services" ], + "vs/workbench/contrib/preferences/browser/settingsTree": [ + "Extensions", + "The setting has been configured in the current scope.", + "More Actions... ", + "Show matching extensions", + "Edit in settings.json", + "Edit settings for {0}", + "default", + "The setting has been configured in the current scope.", + "Show Extension", + "Reset Setting", + "Validation Error.", + "Validation Error.", + "Modified.", + "Settings", + "Copy Setting ID", + "Copy Setting as JSON", + "Sync This Setting", + "Apply Setting to all Profiles" + ], "vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp": [ "The chat view is comprised of an input box and a request/response list. The input box is used to make requests and the list is used to display responses.", "In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the submit button to run a new request.", - "In the input box, inspect the last response in the accessible view via {0}", + "In the input box, inspect the last response in the accessible view {0}", "With the input box focused, inspect the last response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", + "In the input box, navigate to the suggested follow up question (Shift+Tab) and press Enter to run it.", "Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.", "To focus the chat request/response list, which can be navigated with up and down arrows, invoke the Focus Chat command ({0}).", "To focus the chat request/response list, which can be navigated with up and down arrows, invoke The Focus Chat List command, which is currently not triggerable by a keybinding.", - "To focus the input box for chat requests, invoke the Focus Chat Input command ({0})", + "To focus the input box for chat requests, invoke the Focus Chat Input command ({0}).", "To focus the input box for chat requests, invoke the Focus Chat Input command, which is currently not triggerable by a keybinding.", "To focus the next code block within a response, invoke the Chat: Next Code Block command ({0}).", "To focus the next code block within a response, invoke the Chat: Next Code Block command, which is currently not triggerable by a keybinding.", @@ -27827,35 +30317,45 @@ "Inline chat occurs within a code editor and takes into account the current selection. It is useful for making changes to the current editor. For example, fixing diagnostics, documenting or refactoring code. Keep in mind that AI generated code may be incorrect.", "It can be activated via code actions or directly using the command: Inline Chat: Start Inline Chat ({0}).", "In the input box, use {0} and {1} to navigate your request history. Edit input and use enter or the submit button to run a new request.", - "In the input box, inspect the response in the accessible view via {0}", + "In the input box, inspect the response in the accessible view {0}.", "With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", "Context menu actions may run a request prefixed with a /. Type / to discover such ready-made commands.", "If a fix action is invoked, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.", "Once in the diff editor, enter review mode with ({0}). Use up and down arrows to navigate lines with the proposed changes.", "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes.", "Use tab to reach conditional parts like commands, status, message responses and more.", - "Audio cues can be changed via settings with a prefix of audioCues.chat. By default, if a request takes more than 4 seconds, you will hear an audio cue indicating that progress is still occurring." + "Accessibility Signals can be changed via settings with a prefix of signals.chat. By default, if a request takes more than 4 seconds, you will hear a sound indicating that progress is still occurring." + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ + "Whether an inline suggestion is visible", + "Whether the inline suggestion starts with whitespace", + "Whether the inline suggestion starts with whitespace that is less than what would be inserted by tab", + "Whether suggestions should be suppressed for the current suggestion" + ], + "vs/workbench/contrib/chat/browser/codeBlockPart": [ + "Code block", + "Toolbar for code block which can be reached via tab", + "Code block toolbar", + "Code block {0}", + "{0} vulnerabilities", + "{0} vulnerability", + "Code block", + "Original", + "Modified", + "Toolbar for code block which can be reached via tab", + "Code block toolbar", + "Code Edits", + "Made {0} changes in [[{1}]]", + "Made 1 change in [[{0}]]", + "The original file has been modified.", + "Do you want to apply the changes anyway?" ], "vs/workbench/contrib/notebook/browser/controller/cellOperations": [ "Cannot join cells of different kinds", "Join Notebook Cells" ], - "vs/workbench/contrib/chat/browser/chatInputPart": [ - "Chat Input, Type to ask questions or type / for topics, press enter to send out the request. Use {0} for Chat Accessibility Help.", - "Chat Input, Type code here and press Enter to run. Use the Chat Accessibility Help command for more information.", - "Chat Input" - ], - "vs/workbench/contrib/chat/browser/chatListRenderer": [ - "used {0}", - "using {0}", - "Thinking", - "Used {0} references", - "Used {0} reference", - "{0}, expanded", - "{0}, collapsed", + "vs/workbench/contrib/chat/browser/chatAccessibilityProvider": [ "Chat", - "Command: {0}", - "Commands: {0}", "1 file tree", "{0} file trees", "{0} {1} {2}", @@ -27863,22 +30363,37 @@ "{0} 1 code block: {1} {2}", "{0} 1 code block: {1}", "{0} {1} code blocks: {2}", - "{0} {1} code blocks", - "File Tree" + "{0} {1} code blocks" ], - "vs/platform/actions/browser/toolbar": [ - "Hide", - "Reset Menu" + "vs/workbench/contrib/chat/browser/chatListRenderer": [ + "used {0}", + "using {0}", + "used {0}", + "using {0}", + "Used {0} references", + "Used {0} reference", + "{0}, expanded", + "{0}, collapsed", + "Button not available in restored chat", + "Making changes...", + "Made changes.", + "File Tree", + "Used References" ], - "vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys": [ - "Whether an inline suggestion is visible", - "Whether the inline suggestion starts with whitespace", - "Whether the inline suggestion starts with whitespace that is less than what would be inserted by tab", - "Whether suggestions should be suppressed for the current suggestion" + "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ + "Nothing changed.", + "$(info) Accept or Discard 1 change.", + "1 change", + "$(info) Accept or Discard {0} changes.", + "{0} changes", + "Review (accept or discard) all changes before continuing." + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget": [ + "Closed inline chat widget" ], "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView": [ - "Select Notebook Kernel", - "Notebook Kernel Args" + "Notebook Kernel Args", + "Select Notebook Kernel" ], "vs/workbench/contrib/notebook/browser/notebookExtensionPoint": [ "Contributes notebook document provider.", @@ -27907,12 +30422,21 @@ "Contributes notebook preloads.", "Type of the notebook.", "Path to file loaded in the webview.", - "Paths to additional resources that should be allowed in the webview." + "Paths to additional resources that should be allowed in the webview.", + "ID", + "Name", + "Name", + "Mimetypes", + "Notebooks", + "Notebook Renderers" + ], + "vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView": [ + "Empty markdown cell, double-click or press enter to edit.", + "No renderer found for '$0'", + "Could not render content for '$0'", + "Notebook webview content" ], "vs/workbench/contrib/notebook/browser/notebookEditorWidget": [ - "Notebook\nUse {0} for accessibility help", - "Notebook\nRun the Open Accessibility Help command for more information", - "Notebook", "The border color for notebook cells.", "The color of the notebook cell editor border.", "The error icon color of notebook cells in the cell status bar.", @@ -27938,12 +30462,6 @@ "Cell editor background color.", "Notebook background color." ], - "vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView": [ - "Empty markdown cell, double-click or press enter to edit.", - "No renderer found for '$0'", - "Could not render content for '$0'", - "Notebook webview content" - ], "vs/workbench/services/workingCopy/common/fileWorkingCopyManager": [ "File Created", "File Replaced", @@ -27976,34 +30494,86 @@ "Detecting Kernels", "Select Kernel" ], + "vs/workbench/contrib/comments/common/commentContextKeys": [ + "Whether the position at the active cursor has a commenting range", + "Whether the active editor has a commenting range", + "Whether the open workspace has either comments or commenting ranges.", + "Set when the comment thread has no comments", + "Set when the comment has no input", + "The context value of the comment", + "The context value of the comment thread", + "The comment controller id associated with a comment thread", + "Set when the comment is focused" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView": [ + "Notebook Variables" + ], + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext": [ + "Whether the cell chat editor is focused", + "Whether the cell chat editor has an active request", + "Whether the user did changes ontop of the notebook cell chat", + "Whether the focus of the notebook editor is above or below the cell chat" + ], + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController": [ + "Ask a question", + "AI-generated code may be incorrect", + "Ask a question", + "AI-generated code may be incorrect" + ], + "vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions": [ + "Indent Using Tabs", + "Indent Using Spaces", + "Change Tab Display Size", + "Convert Indentation to Spaces", + "Convert Indentation to Tabs", + "Select Tab Size for Current File", + "Convert Indentation" + ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget": [ "{0} found", "{0} found for '{1}'", "{0} found for '{1}'" ], + "vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions": [ + "Cursor Up", + "Cursor Down", + "Focus Chat Widget", + "Focus Next Cell Chat Widget", + "Accept", + "Accept Changes", + "Discard", + "Helpful", + "Unhelpful", + "Report Issue", + "Generate", + "Start Chat to Generate Code", + "Start Chat to Generate Code", + "Generate", + "Start Chat to Generate Code", + "Generate", + "Start Chat to Generate Code", + "Focus Chat", + "Focus Next Cell", + "Focus Previous Cell", + "Make Request", + "Stop Request", + "Close Chat", + "Accept Changes", + "Previous From History", + "Next From History", + "Generate" + ], "vs/editor/contrib/codeAction/browser/codeAction": [ "An unknown error occurred while applying the code action" ], - "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies": [ - "Nothing changed", - "Changed 1 line", - "Changed {0} lines" - ], - "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ - "Inline Chat Input", - "Original", - "Modified", - "Inline Chat Input, Use {0} for Inline Chat Accessibility Help.", - "Inline Chat Input, Run the Inline Chat Accessibility Help command for more information.", - "Using {0} to generate response...", - "Closed inline chat widget" - ], - "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ - "The terminal has no selection to copy", - "Yes", - "No", - "Don't Show Again", - "Terminal GPU acceleration appears to be slow on your computer. Would you like to switch to disable it which may improve performance? [Read more about terminal settings](https://code.visualstudio.com/docs/editor/integrated-terminal#_changing-how-the-terminal-is-rendered)." + "vs/platform/quickinput/browser/commandsQuickAccess": [ + "recently used", + "similar commands", + "commonly used", + "other commands", + "similar commands", + "{0}, {1}", + "Command '{0}' resulted in an error" ], "vs/workbench/contrib/testing/browser/theme": [ "Color for the 'failed' icon in the test explorer.", @@ -28014,11 +30584,56 @@ "Color for the 'Unset' icon in the test explorer.", "Color for the 'Skipped' icon in the test explorer.", "Color of the peek view borders and arrow.", + "Color of the peek view borders and arrow when peeking a logged message.", "Color of the peek view borders and arrow.", + "Color of the peek view borders and arrow when peeking a logged message.", + "Background color of text that was covered.", + "Border color of text that was covered.", + "Gutter color of regions where code was covered.", + "Background of the widget shown for an uncovered branch.", + "Background color of text that was not covered.", + "Border color of text that was not covered.", + "Gutter color of regions where code not covered.", + "Background for the badge indicating execution count", + "Foreground for the badge indicating execution count", "Text color of test error messages shown inline in the editor.", "Margin color beside error messages shown inline in the editor.", "Text color of test info messages shown inline in the editor.", - "Margin color beside info messages shown inline in the editor." + "Margin color beside info messages shown inline in the editor.", + "Retired color for the 'Errored' icon in the test explorer.", + "Retired color for the 'failed' icon in the test explorer.", + "Retired color for the 'passed' icon in the test explorer.", + "Retired color for the 'Queued' icon in the test explorer.", + "Retired color for the 'Unset' icon in the test explorer.", + "Retired color for the 'Skipped' icon in the test explorer." + ], + "vs/workbench/contrib/testing/common/constants": [ + "Errored", + "Failed", + "Passed", + "Queued", + "Running", + "Skipped", + "Not yet run", + "{0} ({1})", + "Debug", + "Run", + "Coverage" + ], + "vs/workbench/contrib/testing/browser/testingExplorerFilter": [ + "Show Only Failed Tests", + "Show Only Executed Tests", + "Show in Active File Only", + "Show Hidden Tests", + "More Filters...", + "Filter text for tests in the explorer", + "Filter (e.g. text, !exclude, @tag)", + "Fuzzy Match", + "Show Hidden Tests", + "Unhide All Tests" + ], + "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal": [ + "The terminal has no selection to copy" ], "vs/workbench/contrib/terminal/common/terminalColorRegistry": [ "The background color of the terminal, this allows coloring the terminal differently to the panel.", @@ -28043,39 +30658,27 @@ "Border on the side of the terminal tab in the panel. This defaults to tab.activeBorder.", "'{0}' ANSI color in the terminal." ], - "vs/workbench/contrib/testing/common/constants": [ - "Errored", - "Failed", - "Passed", - "Queued", - "Running", - "Skipped", - "Not yet run", - "{0} ({1})", - "Debug", - "Run", - "Coverage" - ], - "vs/workbench/contrib/testing/browser/testingExplorerFilter": [ - "Show Only Failed Tests", - "Show Only Executed Tests", - "Show in Active File Only", - "Show Hidden Tests", - "More Filters...", - "Filter text for tests in the explorer", - "Filter (e.g. text, !exclude, @tag)", - "Fuzzy Match", - "Show Hidden Tests", - "Unhide All Tests" + "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ + "Unable to resolve workspace folder ({0})", + "Symbolic Link", + "Unknown File Type", + "Explorer" ], - "vs/platform/quickinput/browser/commandsQuickAccess": [ - "recently used", - "similar commands", - "commonly used", - "other commands", - "similar commands", - "{0}, {1}", - "Command '{0}' resulted in an error" + "vs/workbench/contrib/files/browser/views/explorerViewer": [ + "Files Explorer", + "Type file name. Press Enter to confirm or Escape to cancel.", + "Are you sure you want to change the order of multiple root folders in your workspace?", + "Are you sure you want to move the following {0} files into '{1}'?", + "Are you sure you want to change the order of root folder '{0}' in your workspace?", + "Are you sure you want to move '{0}' into '{1}'?", + "Do not ask me again", + "&&Move", + "Copy {0}", + "Copying {0}", + "Move {0}", + "Moving {0}", + "{0} folders", + "{0} files" ], "vs/workbench/contrib/files/browser/fileImportExport": [ "Uploading", @@ -28112,28 +30715,6 @@ "This action is irreversible!", "&&Replace" ], - "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider": [ - "Unable to resolve workspace folder ({0})", - "Symbolic Link", - "Unknown File Type", - "Explorer" - ], - "vs/workbench/contrib/files/browser/views/explorerViewer": [ - "Files Explorer", - "Type file name. Press Enter to confirm or Escape to cancel.", - "Are you sure you want to change the order of multiple root folders in your workspace?", - "Are you sure you want to move the following {0} files into '{1}'?", - "Are you sure you want to change the order of root folder '{0}' in your workspace?", - "Are you sure you want to move '{0}' into '{1}'?", - "Do not ask me again", - "&&Move", - "Copy {0}", - "Copying {0}", - "Move {0}", - "Moving {0}", - "{0} folders", - "{0} files" - ], "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree": [ "Bulk Edit", "Renaming {0} to {1}, also making text edits", @@ -28152,6 +30733,13 @@ "(deleting)", "{0} - {1}" ], + "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview": [ + "Other" + ], + "vs/workbench/contrib/search/browser/replaceService": [ + "Search and Replace", + "{0} ↔ {1} (Replace Preview)" + ], "vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess": [ "To go to a symbol, first open a text editor with symbol information.", "The active text editor does not provide symbol information.", @@ -28187,11 +30775,8 @@ "fields ({0})", "constants ({0})" ], - "vs/workbench/contrib/search/browser/replaceService": [ - "Search and Replace", - "{0} ↔ {1} (Replace Preview)" - ], "vs/workbench/contrib/search/browser/searchFindInput": [ + "Use AI", "Notebook Find Filters" ], "vs/workbench/contrib/searchEditor/browser/searchEditorSerialization": [ @@ -28213,16 +30798,12 @@ "vs/workbench/contrib/debug/browser/baseDebugView": [ "Click to expand" ], - "vs/workbench/contrib/debug/common/debugSource": [ - "Unknown Source" - ], - "vs/workbench/contrib/debug/browser/debugSessionPicker": [ - "Search debug sessions by name", - "Start a New Debug Session", - "Session {0} spawned from {1}" - ], - "vs/workbench/contrib/debug/common/loadedScriptsPicker": [ - "Search loaded scripts by name" + "vs/workbench/contrib/debug/browser/debugConfigurationManager": [ + "Edit Debug Configuration in launch.json", + "Select Launch Configuration", + "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", + "workspace", + "user settings" ], "vs/workbench/contrib/debug/browser/debugAdapterManager": [ "Debugger 'type' can not be omitted and must be of type 'string'.", @@ -28238,19 +30819,13 @@ "Install extension...", "Select debugger" ], - "vs/workbench/contrib/debug/browser/debugConfigurationManager": [ - "Edit Debug Configuration in launch.json", - "Select Launch Configuration", - "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", - "workspace", - "user settings" - ], "vs/workbench/contrib/debug/browser/debugSession": [ "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", "No debugger available, can not send '{0}'", + "Session does not support breakpoints with bytes", "No debugger available, can not send '{0}'", "Session is not ready for breakpoints", "No debugger available, can not send '{0}'", @@ -28304,9 +30879,70 @@ "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined.", "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined." ], + "vs/workbench/contrib/debug/common/debugSource": [ + "Unknown Source" + ], + "vs/workbench/contrib/debug/common/loadedScriptsPicker": [ + "Search loaded scripts by name" + ], + "vs/workbench/contrib/debug/browser/debugSessionPicker": [ + "Search debug sessions by name", + "Start a New Debug Session", + "Session {0} spawned from {1}" + ], + "vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget": [ + "Icon for show next parameter hint.", + "Icon for show previous parameter hint.", + "{0} ({1})", + "Previous", + "Next" + ], + "vs/workbench/contrib/markers/browser/markersTreeViewer": [ + "Problems View", + "Icon indicating that multiple lines are shown in the markers view.", + "Icon indicating that multiple lines are collapsed in the markers view.", + "Show message in single line", + "Show message in multiple lines" + ], + "vs/workbench/contrib/markers/browser/markersTable": [ + "Code", + "Message", + "File", + "Source" + ], "vs/base/browser/ui/dropdown/dropdownActionViewItem": [ "More Actions..." ], + "vs/workbench/contrib/comments/browser/commentsModel": [ + "There are no comments in this workspace yet." + ], + "vs/workbench/contrib/comments/browser/commentColors": [ + "Icon color for resolved comments.", + "Icon color for unresolved comments.", + "Background color for comment reply input box.", + "Color of borders and arrow for resolved comments.", + "Color of borders and arrow for unresolved comments.", + "Color of background for comment ranges.", + "Color of background for currently selected or hovered comment range." + ], + "vs/workbench/contrib/comments/browser/commentsViewActions": [ + "Focus Comments view", + "Clear filter text", + "Focus comments filter", + "Show Unresolved", + "Comments", + "Show Unresolved", + "Show Resolved", + "Comments", + "Show Resolved" + ], + "vs/workbench/contrib/comments/browser/commentGlyphWidget": [ + "Editor gutter decoration color for commenting ranges. This color should be opaque.", + "Editor overview ruler decoration color for resolved comments. This color should be opaque.", + "Editor overview ruler decoration color for unresolved comments. This color should be opaque.", + "Editor gutter decoration color for commenting glyphs.", + "Editor gutter decoration color for commenting glyphs for unresolved comment threads." + ], "vs/workbench/contrib/mergeEditor/common/mergeEditor": [ "The editor is a merge editor", "The editor is a the result editor of a merge editor.", @@ -28346,18 +30982,16 @@ "The file contains unhandled conflicts.", "&&Close with Conflicts", "&&Close", - "Don't ask again" + "Do not ask me again" + ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ + "Base", + "Comparing with {0}", + "Differences are highlighted with a background color." ], "vs/workbench/contrib/mergeEditor/browser/view/viewModel": [ "There is currently no conflict focused that can be toggled." ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ - "Result", - "{0} Conflict Remaining", - "{0} Conflicts Remaining ", - "Go to next conflict", - "All conflicts handled, the merge can be completed now." - ], "vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView": [ "Input 1", "Input 2", @@ -28371,6 +31005,13 @@ "Undo accept", "Undo accept (currently second)" ], + "vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView": [ + "Result", + "{0} Conflict Remaining", + "{0} Conflicts Remaining ", + "Go to next conflict", + "All conflicts handled, the merge can be completed now." + ], "vs/workbench/contrib/mergeEditor/browser/view/colors": [ "The background color for changes.", "The background color for word changes.", @@ -28386,47 +31027,122 @@ "The background color of decorations in input 1.", "The background color of decorations in input 2." ], - "vs/base/browser/ui/findinput/replaceInput": [ - "input", - "Preserve Case" + "vs/workbench/contrib/customEditor/common/contributedCustomEditors": [ + "Built-in" ], - "vs/workbench/contrib/markers/browser/markersTreeViewer": [ - "Problems View", - "Icon indicating that multiple lines are shown in the markers view.", - "Icon indicating that multiple lines are collapsed in the markers view.", - "Show message in single line", - "Show message in multiple lines" + "vs/platform/files/browser/htmlFileSystemProvider": [ + "Rename is only supported for files.", + "Insufficient permissions. Please retry and allow the operation." ], - "vs/workbench/contrib/markers/browser/markersTable": [ - "Code", - "Message", - "File", - "Source" + "vs/workbench/contrib/extensions/browser/extensionsViewer": [ + "Error", + "Unknown Extension:", + "Extensions" ], - "vs/workbench/contrib/comments/browser/commentsController": [ - "Line {0}", - "Lines {0} to {1}", - "Editor has commenting ranges, run the command Open Accessibility Help ({0}), for more information.", - "Editor has commenting ranges, run the command Open Accessibility Help, which is currently not triggerable via keybinding, for more information.", - "Editor has commenting ranges.", - "Select Comment Provider" + "vs/workbench/contrib/extensions/browser/extensionFeaturesTab": [ + "Activation", + "Uncaught Errors ({0})", + "Messages ({0})", + "Last Request: `{0}`", + "Requests (Session) : `{0}`", + "Requests (Overall): `{0}`", + "Runtime Status", + "No features contributed.", + "Extension Features", + "No Access", + "Enable '{0}' Feature", + "Would you like to revoke '{0}' extension to access '{1}' feature?", + "Would you like to allow '{0}' extension to access '{1}' feature?", + "Revoke Access", + "Allow Access", + "Cancel", + "Revoke Access", + "Allow Access" + ], + "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ + "Average rating: {0} out of 5", + "Sponsor", + "Extension in {0}", + "This extension is ignored during sync.", + "Activation time", + "Startup", + "Sponsor", + "Workspace Extension", + "Local Extension", + "This publisher has verified ownership of {0}", + "Latest version:", + "Activation time", + "Startup", + "1 uncaught error", + "{0} uncaught errors", + "1 message", + "{0} messages", + "Show Dependencies", + "Pre-Release version", + "This extension has a {0} available", + "You have chosen not to receive recommendations for this extension.", + "The icon color for extension ratings.", + "The icon color for extension verified publisher.", + "The icon color for pre-release extension.", + "The icon color for extension sponsor." + ], + "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ + "This extension is recommended because you have {0} installed." + ], + "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ + "This extension is recommended by users of the current workspace.", + "This extension is recommended by users of the current workspace." + ], + "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ + "This extension is recommended based on the files you recently opened.", + "the {0} language" + ], + "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ + "This extension is recommended because of the current workspace configuration" ], - "vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView": [ - "Base", - "Comparing with {0}", - "Differences are highlighted with a background color." + "vs/workbench/contrib/extensions/browser/webRecommendations": [ + "This extension is recommended for {0} for the Web" ], - "vs/workbench/contrib/customEditor/common/contributedCustomEditors": [ - "Built-in" + "vs/platform/terminal/common/terminalLogService": [ + "Terminal" ], - "vs/platform/files/browser/htmlFileSystemProvider": [ - "Rename is only supported for files.", - "Insufficient permissions. Please retry and allow the operation." + "vs/workbench/contrib/terminal/browser/terminalIcons": [ + "View icon of the terminal view.", + "Icon for rename in the terminal quick menu.", + "Icon for killing a terminal instance.", + "Icon for creating a new terminal instance.", + "Icon for creating a new terminal profile.", + "Icon for a terminal decoration mark.", + "Icon for a terminal decoration of a command that was incomplete.", + "Icon for a terminal decoration of a command that errored.", + "Icon for a terminal decoration of a command that was successful.", + "Icon for removing a terminal command from command history.", + "Icon for viewing output of a terminal command.", + "Icon for toggling fuzzy search of command history." ], "vs/workbench/contrib/terminal/browser/terminalActions": [ "Show Tabs", "Select current working directory for new terminal", "Open Help", + "Only files on disk can be run in the terminal", + "There are no unattached terminals to attach to", + "The sequence of text to send to the terminal", + "The directory to start the terminal at", + "The new name for the terminal", + "No name argument provided", + "Insufficient terminals for the join action", + "All terminals are joined already", + "Sticky Scroll", + "&&Sticky Scroll", + "Providing no name will reset it to the default value", + "The name of the profile to create", + "Where to create the terminal", + "Create the terminal in the terminal view", + "Create the terminal in the editor", + "Select current working directory for new terminal", + "(Overriden) {0}", + "Select current working directory for new terminal", + "Enter terminal name", "Create New Terminal (In Active Workspace)", "Create New Terminal in Editor Area", "Create New Terminal in Editor Area", @@ -28434,8 +31150,11 @@ "Focus Previous Terminal in Terminal Group", "Focus Next Terminal in Terminal Group", "Run Recent Command...", + "Copy Last Command", "Copy Last Command Output", + "Copy Last Command and Output", "Go to Recent Directory...", + "Goes to a recent folder", "Resize Terminal Left", "Resize Terminal Right", "Resize Terminal Up", @@ -28445,7 +31164,6 @@ "Focus Previous Terminal Group", "Run Selected Text In Active Terminal", "Run Active File In Active Terminal", - "Only files on disk can be run in the terminal", "Scroll Down (Line)", "Scroll Down (Page)", "Scroll to Bottom", @@ -28455,23 +31173,14 @@ "Clear Selection", "Detach Session", "Attach to Session", - "There are no unattached terminals to attach to", "Switch Active Terminal", - "Scroll To Previous Command", - "Scroll To Next Command", "Select To Previous Command", "Select To Next Command", "Select To Previous Line", "Select To Next Line", - "The sequence of text to send to the terminal", - "The directory to start the terminal at", - "The new name for the terminal", - "No name argument provided", "Relaunch Active Terminal", "Join Terminals", - "Join Terminals", - "Insufficient terminals for the join action", - "All terminals are joined already", + "Join Terminals...", "Split Terminal (In Active Workspace)", "Select All", "Create New Terminal", @@ -28482,31 +31191,57 @@ "Select Default Profile", "Configure Terminal Settings", "Set Fixed Dimensions", - "Toggle Size to Content Width", "Clear Previous Session History", - "Select the Previous Suggestion", - "Select the Previous Page Suggestion", - "Select the Next Suggestion", - "Select the Next Page Suggestion", - "Accept Selected Suggestion", - "Hide Suggest Widget", + "Toggle Sticky Scroll", "Copy Selection", "Copy and Clear Selection", "Copy Selection as HTML", "Paste into Active Terminal", "Paste Selection into Active Terminal", "Switch Terminal", - "Providing no name will reset it to the default value", - "Create New Terminal (With Profile)", - "The name of the profile to create", - "Select current working directory for new terminal", - "(Overriden) {0}", - "Select current working directory for new terminal", - "Enter terminal name" + "Create New Terminal (With Profile)" + ], + "vs/workbench/contrib/terminal/browser/terminalMenus": [ + "&&New Terminal", + "&&Split Terminal", + "Run &&Active File", + "Run &&Selected Text", + "Copy", + "Copy as HTML", + "Paste", + "Clear", + "Select All", + "Copy", + "Copy as HTML", + "Paste", + "Clear", + "Select All", + "New Terminal With Profile...", + "Configure Terminal Settings", + "Run Task...", + "Configure Tasks...", + "Clear Terminal", + "Run Active File", + "Run Selected Text", + "Rename...", + "Change Icon...", + "Change Color...", + "Join Terminals", + "{0} (Default)", + "{0} (Default)", + "{0} (Default)", + "Split Terminal", + "Launch Profile...", + "Select Default Profile", + "Switch Terminal" + ], + "vs/workbench/contrib/terminal/browser/terminalWslRecommendationContribution": [ + "The '{0}' extension is recommended for opening a terminal in WSL.", + "Install" ], "vs/workbench/contrib/terminal/browser/terminalQuickAccess": [ "Create New Terminal", - "Create New Terminal With Profile", + "Create New Terminal With Profile...", "Rename Terminal" ], "vs/workbench/contrib/terminal/browser/terminalService": [ @@ -28516,6 +31251,37 @@ "This shell is open to a {0}local{1} folder, NOT to the virtual folder", "This shell is running on your {0}local{1} machine, NOT on the connected remote machine" ], + "vs/workbench/contrib/terminal/common/terminalStrings": [ + "Terminal", + "New Terminal", + "Do Not Show Again", + "current session", + "previous session", + "Task", + "Local", + "Kill", + "Split", + "Terminal", + "Focus Terminal", + "Focus Terminal and Hide Accessible Buffer", + "Kill Terminal", + "Move Terminal into Editor Area", + "Move Terminal into New Window", + "Move Terminal into Panel", + "Change Icon...", + "Change Color...", + "Split Terminal", + "Unsplit Terminal", + "Rename...", + "Toggle Size to Content Width", + "Focus Hover", + "Send Custom Sequence To Terminal", + "Create New Terminal Starting in a Custom Working Directory", + "Rename the Currently Active Terminal", + "Sticky Scroll", + "Scroll To Previous Command", + "Scroll To Next Command" + ], "vs/workbench/contrib/terminal/common/terminalConfiguration": [ "the terminal's current working directory", "the terminal's current working directory, displayed for multi-root workspaces or in a single root workspace when the value differs from the initial working directory. On Windows, this will only be displayed when shell integration is enabled.", @@ -28560,7 +31326,10 @@ "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux.", "If enabled, alt/option + click will reposition the prompt cursor to underneath the mouse when {0} is set to {1} (the default value). This may not work reliably depending on your shell.", "Controls whether text selected in the terminal will be copied to the clipboard.", - "Show a warning dialog when pasting multiple lines into the terminal. The dialog does not show when:\n\n- Bracketed paste mode is enabled (the shell supports multi-line paste natively)\n- The paste is handled by the shell's readline (in the case of pwsh)", + "Controls whether to show a warning dialog when pasting multiple lines into the terminal.", + "Enable the warning but do not show it when:\n\n- Bracketed paste mode is enabled (the shell supports multi-line paste natively)\n- The paste is handled by the shell's readline (in the case of pwsh)", + "Always show the warning if the text contains a new line.", + "Never show the warning.", "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant.", "Controls the font family of the terminal. Defaults to {0}'s value.", "Controls the font size in pixels of the terminal.", @@ -28596,6 +31365,9 @@ "Select the word under the cursor and show the context menu.", "Do nothing and pass event to terminal.", "Controls how terminal reacts to right click.", + "The platform default to focus the terminal. On Linux this will also paste the selection.", + "Paste on middle click.", + "Controls how terminal reacts to middle click.", "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd.", "Controls whether to confirm when the window closes if there are active terminal sessions.", "Never confirm.", @@ -28606,7 +31378,8 @@ "Confirm if the terminal is in the editor.", "Confirm if the terminal is in the panel.", "Confirm if the terminal is either in the editor or panel.", - "Controls whether the terminal bell is enabled. This shows up as a visual bell next to the terminal's name.", + "This is now deprecated. Instead use the `terminal.integrated.enableVisualBell` and `accessibility.signals.terminalBell` settings.", + "Controls whether the visual terminal bell is enabled. This shows up next to the terminal's name.", "A set of command IDs whose keybindings will not be sent to the shell but instead always be handled by VS Code. This allows keybindings that would normally be consumed by the shell to act instead the same as when the terminal is not focused, for example `Ctrl+P` to launch Quick Open.\n\n \n\nMany commands are skipped by default. To override a default and pass that command's keybinding to the shell instead, add the command prefixed with the `-` character. For example add `-workbench.action.quickOpen` to allow `Ctrl+P` to reach the shell.\n\n \n\nThe following list of default skipped commands is truncated when viewed in Settings Editor. To see the full list, {1} and search for the first command from the list below.\n\n \n\nDefault Skipped Commands:\n\n{0}", "open the default settings JSON", "Open Default Settings (JSON)", @@ -28631,6 +31404,7 @@ "Always off.", "Always on.", "Enable only when not in a remote workspace.", + "An array of strings containing the URI schemes that the terminal is allowed to open links for. By default, only a small subset of possible schemes are allowed for security reasons.", "Version 6 of Unicode. This is an older version which should work better on older systems.", "Version 11 of Unicode. This version provides better support on modern systems that use modern versions of Unicode.", "Controls what version of Unicode to use when evaluating the width of characters in the terminal. If you experience emoji or other wide characters not taking up the right amount of space or backspace either deleting too much or too little then you may want to try tweaking this setting.", @@ -28651,9 +31425,10 @@ "Only hide the terminal when there are no persistent sessions restored.", "Always hide the terminal, even when there are persistent sessions restored.", "Whether to draw custom glyphs for block element and box drawing characters instead of using the font, which typically yields better rendering with continuous lines. Note that this doesn't work when {0} is disabled.", + "Whether to rescale glyphs horizontally that are a single cell wide but have glyphs that would overlap following cell(s). This typically happens for ambiguous width characters (eg. the roman numeral characters U+2160+) which aren't featured in monospace fonts. Emoji glyphs are never rescaled.", "A set of messages that, when encountered in the terminal, will be automatically responded to. Provided the message is specific enough, this can help automate away common responses.\n\nRemarks:\n\n- Use {0} to automatically respond to the terminate batch job prompt on Windows.\n- The message includes escape sequences so the reply might not happen with styled text.\n- Each reply can only happen once every second.\n- Use {1} in the reply to mean the enter key.\n- To unset a default key, set the value to null.\n- Restart VS Code if new don't apply.", "The reply to send to the process.", - "Determines whether or not shell integration is auto-injected to support features like enhanced command tracking and current working directory detection. \n\nShell integration works by injecting the shell with a startup script. The script gives VS Code insight into what is happening within the terminal.\n\nSupported shells:\n\n- Linux/macOS: bash, fish, pwsh, zsh\n - Windows: pwsh\n\nThis setting applies only when terminals are created, so you will need to restart your terminals for it to take effect.\n\n Note that the script injection may not work if you have custom arguments defined in the terminal profile, have enabled {1}, have a [complex bash `PROMPT_COMMAND`](https://code.visualstudio.com/docs/editor/integrated-terminal#_complex-bash-promptcommand), or other unsupported setup. To disable decorations, see {0}", + "Determines whether or not shell integration is auto-injected to support features like enhanced command tracking and current working directory detection. \n\nShell integration works by injecting the shell with a startup script. The script gives VS Code insight into what is happening within the terminal.\n\nSupported shells:\n\n- Linux/macOS: bash, fish, pwsh, zsh\n - Windows: pwsh, git bash\n\nThis setting applies only when terminals are created, so you will need to restart your terminals for it to take effect.\n\n Note that the script injection may not work if you have custom arguments defined in the terminal profile, have enabled {1}, have a [complex bash `PROMPT_COMMAND`](https://code.visualstudio.com/docs/editor/integrated-terminal#_complex-bash-promptcommand), or other unsupported setup. To disable decorations, see {0}", "When shell integration is enabled, adds a decoration for each command.", "Show decorations in the gutter (left) and overview ruler (right)", "Show gutter decorations to the left of the terminal", @@ -28661,6 +31436,7 @@ "Do not show decorations", "Controls the number of recently used commands to keep in the terminal command history. Set to 0 to disable terminal command history.", "Enables experimental terminal Intellisense suggestions for supported shells when {0} is set to {1}. If shell integration is installed manually, {2} needs to be set to {3} before calling the script.", + "This is an experimental setting and may break the terminal! Use at your own risk.", "Controls whether the terminal will scroll using an animation.", "Controls whether the terminal will ignore bracketed paste mode even if the terminal was put into the mode, omitting the {0} and {1} sequences when pasting. This is useful when the shell is not respecting the mode which can happen in sub-shells for example.", "Enables image support in the terminal, this will only work when {0} is enabled. Both sixel and iTerm's inline image protocol are supported on Linux and macOS, Windows support will light up automatically when ConPTY passes through the sequences. Images will currently not be restored between window reloads/reconnects.", @@ -28668,85 +31444,12 @@ "Always focus the terminal.", "Always focus the accessible buffer.", "Do nothing.", - "Preserve the cursor position on reopen of the terminal's accessible view rather than setting it to the bottom of the buffer." - ], - "vs/workbench/contrib/terminal/browser/terminalMenus": [ - "&&New Terminal", - "&&Split Terminal", - "Run &&Active File", - "Run &&Selected Text", - "Copy", - "Copy as HTML", - "Paste", - "Clear", - "Select All", - "Copy", - "Copy as HTML", - "Paste", - "Clear", - "Select All", - "New Terminal With Profile", - "Select Default Profile", - "Configure Terminal Settings", - "Run Task...", - "Configure Tasks...", - "Switch Terminal", - "Clear Terminal", - "Run Active File", - "Run Selected Text", - "Rename...", - "Change Icon...", - "Change Color...", - "Toggle Size to Content Width", - "Join Terminals", - "{0} (Default)", - "{0} (Default)", - "{0} (Default)", - "Split Terminal" - ], - "vs/workbench/contrib/terminal/browser/terminalIcons": [ - "View icon of the terminal view.", - "Icon for rename in the terminal quick menu.", - "Icon for killing a terminal instance.", - "Icon for creating a new terminal instance.", - "Icon for creating a new terminal profile.", - "Icon for a terminal decoration mark.", - "Icon for a terminal decoration of a command that was incomplete.", - "Icon for a terminal decoration of a command that errored.", - "Icon for a terminal decoration of a command that was successful.", - "Icon for removing a terminal command from command history.", - "Icon for viewing output of a terminal command.", - "Icon for toggling fuzzy search of command history." - ], - "vs/workbench/contrib/terminal/common/terminalStrings": [ - "Terminal", - "New Terminal", - "Do Not Show Again", - "current session", - "previous session", - "Task", - "Local", - "Terminal", - "Focus Terminal", - "Focus Terminal and Hide Accessible Buffer", - "Kill Terminal", - "Kill", - "Move Terminal into Editor Area", - "Move Terminal into Panel", - "Change Icon...", - "Change Color...", - "Split Terminal", - "Split", - "Unsplit Terminal", - "Rename...", - "Toggle Size to Content Width", - "Focus Hover", - "Send Custom Sequence To Terminal", - "Create New Terminal Starting in a Custom Working Directory", - "Rename the Currently Active Terminal" - ], - "vs/platform/terminal/common/terminalLogService": [ - "Terminal" + "Preserve the cursor position on reopen of the terminal's accessible view rather than setting it to the bottom of the buffer.", + "Focus the terminal accessible view when a command is executed.", + "Shows the current command at the top of the terminal.", + "Defines the maximum number of sticky lines to show. Sticky scroll lines will never exceed 40% of the viewport regardless of this setting.", + "Zoom the font of the terminal when using mouse wheel and holding `Cmd`.", + "Zoom the font of the terminal when using mouse wheel and holding `Ctrl`." ], "vs/workbench/contrib/terminal/browser/terminalTabbedView": [ "Move Tabs Right", @@ -28761,9 +31464,10 @@ "Command line: {0}" ], "vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp": [ - "The Focus Accessible Buffer ({0}) command enables screen readers to read terminal contents.", - "The Focus Accessible Buffer command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.", + "The Focus Accessible Terminal View ({0}) command enables screen readers to read terminal contents.", + "The Focus Terminal Accessible View command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.", "Customize the behavior of the cursor when toggling between the terminal and accessible view with `terminal.integrated.accessibleViewPreserveCursorPosition.`", + "Enable `terminal.integrated.accessibleViewFocusOnCommandExecution` to automatically focus the terminal accessible view when a command is executed in the terminal.", "Consider using powershell instead of command prompt for an improved experience", "The terminal has a feature called shell integration that offers an enhanced experience and provides useful commands for screen readers such as:", "Go to Next Command ({0}) in the accessible view", @@ -28785,6 +31489,8 @@ "Configure what gets focused after running selected text in the terminal with `{0}`." ], "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager": [ + "Opening URIs can be insecure, do you want to allow opening links with the scheme {0}?", + "Allow {0}", "option + click", "alt + click", "cmd + click", @@ -28804,10 +31510,23 @@ "Folder", "Workspace Search" ], - "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ - "Run: {0}", - "Open: {0}", - "Quick Fix" + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp": [ + "Inline chat occurs within a terminal. It is useful for suggesting terminal commands. Keep in mind that AI generated code may be incorrect.", + "It can be activated using the command: Terminal: Start Chat ({0}), which will focus the input box.", + "The input box is where the user can type a request and can make the request ({0}). The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.", + "The input box is where the user can type a request and can make the request by tabbing to the Make Request button, which is not currently triggerable via keybindings. The widget will be closed and all content will be discarded when the Escape key is pressed and the terminal will regain focus.", + "The response can be inspected in the accessible view ({0}).", + "With the input box focused, inspect the response in the accessible view via the Open Accessible View command, which is currently not triggerable by a keybinding.", + "Reach the response from the input box ({0}).", + "Reach the response from the input box by tabbing or assigning a keybinding for the command: Focus Terminal Response.", + "Reach the input box from the response ({0}).", + "Reach the response from the input box by shift+tabbing or assigning a keybinding for the command: Focus Terminal Input.", + "With focus in the input box or command editor, the Terminal: Run Chat Command ({0}) action.", + "Run a command by tabbing to the button as the action is currently not triggerable by a keybinding.", + "With focus in the input box command editor, the Terminal: Insert Chat Command ({0}) action.", + "Insert a command by tabbing to the button as the action is currently not triggerable by a keybinding.", + "Use tab to reach conditional parts like commands, status, message responses and more.", + "Accessibility Signals can be changed via settings with a prefix of signals.chat. By default, if a request takes more than 4 seconds, you will hear a sound indicating that progress is still occurring." ], "vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions": [ "Free port {0}", @@ -28821,52 +31540,34 @@ "The command exit result to match on", "The kind of the resulting quick fix. This changes how the quick fix is presented. Defaults to {0}." ], - "vs/workbench/contrib/extensions/browser/extensionsWidgets": [ - "Average rating: {0} out of 5", - "Sponsor", - "Extension in {0}", - "This extension is ignored during sync.", - "Activation time", - "Startup", - "Pre-Release", - "Sponsor", - "This publisher has verified ownership of {0}", - "Latest version:", - "Activation time", - "Startup", - "1 uncaught error", - "{0} uncaught errors", - "1 message", - "{0} messages", - "Show Dependencies", - "Pre-Release version", - "This extension has a {0} available", - "You have chosen not to receive recommendations for this extension.", - "The icon color for extension ratings.", - "The icon color for extension verified publisher.", - "The icon color for pre-release extension.", - "The icon color for extension sponsor." - ], - "vs/workbench/contrib/extensions/browser/extensionsViewer": [ - "Error", - "Unknown Extension:", - "Extensions" - ], - "vs/workbench/contrib/extensions/browser/exeBasedRecommendations": [ - "This extension is recommended because you have {0} installed." - ], - "vs/workbench/contrib/extensions/browser/workspaceRecommendations": [ - "This extension is recommended by users of the current workspace." - ], - "vs/workbench/contrib/extensions/browser/fileBasedRecommendations": [ - "This extension is recommended based on the files you recently opened.", - "the {0} language" + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions": [ + "Start in Terminal", + "Close Chat", + "Focus Terminal Response", + "Focus Terminal Input", + "Discard", + "Discards the terminal current chat response, hide the chat widget, and clear the chat input.", + "Run Chat Command", + "Run", + "Run First Chat Command", + "Run First", + "Insert Chat Command", + "Insert", + "Insert First Chat Command", + "Insert First", + "View in Chat", + "Make Chat Request", + "Cancel Chat", + "Report Issue" ], - "vs/workbench/contrib/extensions/browser/configBasedRecommendations": [ - "This extension is recommended because of the current workspace configuration" + "vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon": [ + "Run: {0}", + "Open: {0}", + "Quick Fix" ], - "vs/workbench/contrib/extensions/browser/webRecommendations": [ - "This extension is recommended for {0} for the Web" + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry": [ + "The background color of the sticky scroll overlay in the terminal.", + "The background color of the sticky scroll overlay in the terminal when hovered." ], "vs/workbench/contrib/tasks/common/jsonSchemaCommon": [ "Additional command options", @@ -28935,6 +31636,10 @@ "vs/workbench/contrib/remote/browser/explorerViewItems": [ "Switch Remote" ], + "vs/base/browser/ui/findinput/replaceInput": [ + "input", + "Preserve Case" + ], "vs/workbench/contrib/snippets/browser/commands/abstractSnippetsActions": [ "Snippets" ], @@ -28965,7 +31670,6 @@ "vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent": [ "Icon used for the setup category of welcome page", "Icon used for the beginner category of welcome page", - "Icon used for the intermediate category of welcome page", "New File...", "Open a new untitled text file, notebook, or custom editor.", "Open...", @@ -28987,22 +31691,25 @@ "Open Tunnel...", "Connect to a remote machine through a Tunnel", "Get Started with VS Code", - "Discover the best customizations to make VS Code yours.", - "Personalize your VS Code", - "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", - "Backup and Sync Settings", - "Choose the look you want", - "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", + "Customize your editor, learn the basics, and start coding", + "Choose your theme", + "The right theme helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", "Browse Color Themes", - "One shortcut to access everything", - "Commands are the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequent ones to save time.\n{0}\n__Try searching for 'view toggle'.__", - "Open Command Palette", - "Limitless extensibility", + "Code with extensions", "Extensions are VS Code's power-ups. A growing number are becoming available in the web.\n{0}", "Browse Popular Web Extensions", "Rich support for all your languages", "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n{0}", "Browse Language Extensions", + "Tune your settings", + "Customize every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", + "Open Settings", + "Sync settings across devices", + "Keep your essential customizations backed up and updated across all your devices.\n{0}", + "Backup and Sync Settings", + "Unlock productivity with the Command Palette ", + "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", + "Open Command Palette", "Open up your code", "You're all set to start coding. Open a project folder to get your files into VS Code.\n{0}", "Pick a Folder", @@ -29012,26 +31719,29 @@ "Quickly navigate between your files", "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n{0}", "Quick Open a File", + "Watch video tutorials", + "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", + "Watch Tutorial", "Get Started with VS Code for the Web", - "Discover the best customizations to make VS Code for the Web yours.", - "Personalize your VS Code", - "Keep your essential VS Code customizations backed up and updated across all your devices.\n{0}", - "Backup and Sync Settings", - "Choose the look you want", - "The right color palette helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", + "Customize your editor, learn the basics, and start coding", + "Choose your theme", + "The right theme helps you focus on your code, is easy on your eyes, and is simply more fun to use.\n{0}", "Browse Color Themes", - "One shortcut to access everything", - "Commands are the keyboard way to accomplish any task in VS Code. **Practice** by looking up your frequent ones to save time.\n{0}\n__Try searching for 'view toggle'.__", - "Open Command Palette", "Just the right amount of UI", "The full menu bar is available in the dropdown menu to make room for your code. Toggle its appearance for faster access. \n{0}", "Toggle Menu Bar", - "Limitless extensibility", + "Code with extensions", "Extensions are VS Code's power-ups. A growing number are becoming available in the web.\n{0}", "Browse Popular Web Extensions", "Rich support for all your languages", "Code smarter with syntax highlighting, code completion, linting and debugging. While many languages are built-in, many more can be added as extensions.\n{0}", "Browse Language Extensions", + "Sync settings across devices", + "Keep your essential customizations backed up and updated across all your devices.\n{0}", + "Backup and Sync Settings", + "Unlock productivity with the Command Palette ", + "Run commands without reaching for your mouse to accomplish any task in VS Code.\n{0}", + "Open Command Palette", "Open up your code", "You're all set to start coding. You can open a local project or a remote repository to get your files into VS Code.\n{0}\n{1}", "Open Folder", @@ -29040,34 +31750,13 @@ "Navigate between files in an instant with one keystroke. Tip: Open multiple files by pressing the right arrow key.\n{0}", "Quick Open a File", "Learn the Fundamentals", - "Jump right into VS Code and get an overview of the must-have features.", - "Redefine your editing skills", - "Want to code faster and smarter? Practice powerful code editing features in the interactive playground.\n{0}", - "Open Editor Playground", - "Convenient built-in terminal", - "Quickly run shell commands and monitor build output, right next to your code.\n{0}", - "Show Terminal Panel", - "Limitless extensibility", + "Get an overview of the most essential features", + "Code with extensions", "Extensions are VS Code's power-ups. They range from handy productivity hacks, expanding out-of-the-box features, to adding completely new capabilities.\n{0}", - "Browse Recommended Extensions", - "Tune your settings", - "Tweak every aspect of VS Code and your extensions to your liking. Commonly used settings are listed first to get you started.\n{0}", - "Tweak my Settings", - "Customize VS Code with Profiles", - "Profiles let you create sets of VS Code customizations that include settings, extensions and UI state. Create your own profile from scratch or use the predefined set of profile templates for your specific workflow.\n{0}", - "Try Profiles", - "Safely browse and edit code", - "{0} lets you decide whether your project folders should **allow or restrict** automatic code execution __(required for extensions, debugging, etc)__.\nOpening a file/folder will prompt to grant trust. You can always {1} later.", - "Workspace Trust", - "enable trust", - "Lean back and learn", - "Watch the first in a series of short & practical video tutorials for VS Code's key features.\n{0}", - "Watch Tutorial", - "Boost your Productivity", - "Optimize your development workflow with these tips & tricks.", - "Side by side editing", - "Make the most of your screen estate by opening files side by side, vertically and horizontally.\n{0}", - "Split Editor", + "Browse Popular Extensions", + "Built-in terminal", + "Quickly run shell commands and monitor build output, right next to your code.\n{0}", + "Open Terminal", "Watch your code in action", "Accelerate your edit, build, test, and debug loop by setting up a launch configuration.\n{0}", "Run your Project", @@ -29089,22 +31778,14 @@ "Customize your shortcuts", "Once you have discovered your favorite commands, create custom keyboard shortcuts for instant access.\n{0}", "Keyboard Shortcuts", + "Safely browse and edit code", + "{0} lets you decide whether your project folders should **allow or restrict** automatic code execution __(required for extensions, debugging, etc)__.\nOpening a file/folder will prompt to grant trust. You can always {1} later.", + "Workspace Trust", + "enable trust", "Customize Notebooks", "Select the layout for your notebooks", "Get notebooks to feel just the way you prefer" ], - "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService": [ - "Recommended" - ], - "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ - "Background color for the Welcome page.", - "Background color for the tiles on the Welcome page.", - "Hover background color for the tiles on the Welcome.", - "Border color for the tiles on the Welcome page.", - "Foreground color for the Welcome page progress bars.", - "Background color for the Welcome page progress bars.", - "Foreground color of the heading of each walkthrough step" - ], "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint": [ "Title", "Contribute walkthroughs to help users getting started with your extension.", @@ -29144,6 +31825,15 @@ "Mark step done when the specified command is executed.", "Context key expression to control the visibility of this step." ], + "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors": [ + "Background color for the Welcome page.", + "Background color for the tiles on the Welcome page.", + "Hover background color for the tiles on the Welcome.", + "Border color for the tiles on the Welcome page.", + "Foreground color for the Welcome page progress bars.", + "Background color for the Welcome page progress bars.", + "Foreground color of the heading of each walkthrough step" + ], "vs/workbench/contrib/welcomeWalkthrough/common/walkThroughUtils": [ "Background color for the embedded editors on the Interactive Playground." ], @@ -29227,15 +31917,6 @@ "Sync Activity (Developer)", "Troubleshoot" ], - "vs/workbench/browser/parts/notifications/notificationsList": [ - "Inspect the response in the accessible view with {0}", - "Inspect the response in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding", - "{0}, notification, {1}", - "{0}, notification", - "{0}, source: {1}, notification, {2}", - "{0}, source: {1}, notification", - "Notifications List" - ], "vs/workbench/browser/parts/notifications/notificationsActions": [ "Icon for the clear action in notifications.", "Icon for the clear all action in notifications.", @@ -29247,12 +31928,23 @@ "Clear Notification", "Clear All Notifications", "Toggle Do Not Disturb Mode", + "Toggle Do Not Disturb Mode By Source...", + "Configure Do Not Disturb...", "Hide Notifications", "Expand Notification", "Collapse Notification", "More Actions...", "Copy Text" ], + "vs/workbench/browser/parts/notifications/notificationsList": [ + "Inspect the response in the accessible view with {0}", + "Inspect the response in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding", + "{0}, notification, {1}", + "{0}, notification", + "{0}, source: {1}, notification, {2}", + "{0}, source: {1}, notification", + "Notifications List" + ], "vs/workbench/services/textfile/common/textFileSaveParticipant": [ "Saving '{0}'" ], @@ -29267,14 +31959,14 @@ "Preferences", "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style.", "Open Settings", - "Focus Application Menu", "Check for &&Updates...", "Checking for Updates...", "D&&ownload Update", "Downloading Update...", "Install &&Update...", "Installing Update...", - "Restart to &&Update" + "Restart to &&Update", + "Focus Application Menu" ], "vs/workbench/browser/parts/titlebar/commandCenterControl": [ "Search", @@ -29284,6 +31976,10 @@ "Search {0} — {1}", "Command Center" ], + "vs/workbench/browser/parts/editor/editorTabsControl": [ + "Editor actions", + "{0} (+{1})" + ], "vs/workbench/browser/parts/globalCompositeBar": [ "Accounts icon in the view bar.", "Hide Accounts", @@ -29296,7 +31992,27 @@ "Sign Out", "You are not signed in to any accounts", "Manage", - "Manage {0} (Profile)" + "Manage {0} (Profile)", + "Hide Accounts", + "Accounts", + "Manage" + ], + "vs/workbench/browser/parts/titlebar/titlebarActions": [ + "Command Center", + "Toggle visibility of the Command Center in title bar", + "Layout Controls", + "Toggle visibility of the Layout Controls in title bar", + "Hide Custom Title Bar", + "Hide Custom Title Bar In Full Screen", + "Custom Title Bar", + "Editor Actions", + "Accounts", + "Accounts", + "Manage", + "Manage", + "Show Custom Title Bar", + "Hide Custom Title Bar", + "Hide Custom Title Bar In Full Screen" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopy": [ "Failed to save '{0}': The content of the file is newer. Do you want to overwrite the file with your changes?", @@ -29320,24 +32036,41 @@ "vs/platform/terminal/common/terminalProfiles": [ "Automatically detect the default" ], - "vs/workbench/contrib/webview/browser/webviewElement": [ - "Error loading webview: {0}" - ], "vs/platform/quickinput/browser/quickPickPin": [ "pinned", "Pin command", "Pinned command" ], + "vs/workbench/contrib/webview/browser/webviewElement": [ + "Error loading webview: {0}" + ], + "vs/editor/browser/widget/multiDiffEditor/colors": [ + "The background color of the diff editor's header", + "The background color of the multi file diff editor", + "The border color of the multi file diff editor" + ], + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChat": [ + "Whether the chat view is focused.", + "Whether the chat view is visible.", + "Whether there is an active chat request.", + "Whether the chat input has text.", + "Whether the terminal chat agent has been registered.", + "Whether the chat response contains a code block.", + "Whether the chat response contains multiple code blocks.", + "Whether the response supports issue reporting", + "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string." + ], "vs/workbench/api/common/extHostDiagnostics": [ "Not showing {0} further errors and warnings." ], + "vs/workbench/api/common/extHostNotebook": [ + "Unable to modify read-only file '{0}'", + "File Modified Since" + ], "vs/workbench/api/common/extHostLanguageFeatures": [ "Paste using '{0}' extension", "Drop using '{0}' extension" ], - "vs/workbench/api/common/extHostProgress": [ - "{0} (Extension)" - ], "vs/workbench/api/common/extHostStatusBar": [ "{0} (Extension)", "Extension Status" @@ -29345,59 +32078,11 @@ "vs/workbench/api/common/extHostTreeViews": [ "Element with id {0} is already registered" ], - "vs/workbench/api/common/extHostNotebook": [ - "Unable to modify read-only file '{0}'", - "File Modified Since" - ], - "vs/workbench/api/common/extHostChat": [ - "Provider returned null response", - "Error from provider: {0}" - ], - "vs/workbench/api/common/extHostChatAgents2": [ - "Error from provider: {0}" - ], "vs/base/browser/ui/findinput/findInputToggles": [ "Match Case", "Match Whole Word", "Use Regular Expression" ], - "vs/editor/browser/widget/diffEditor/accessibleDiffViewer": [ - "Icon for 'Insert' in accessible diff viewer.", - "Icon for 'Remove' in accessible diff viewer.", - "Icon for 'Close' in accessible diff viewer.", - "Close", - "Accessible Diff Viewer. Use arrow up and down to navigate.", - "no lines changed", - "1 line changed", - "{0} lines changed", - "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", - "blank", - "{0} unchanged line {1}", - "{0} original line {1} modified line {2}", - "+ {0} modified line {1}", - "- {0} original line {1}" - ], - "vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature": [ - "Fold Unchanged Region", - "Click or drag to show more above", - "Show all", - "Click or drag to show more below", - "{0} hidden lines", - "Double click to unfold" - ], - "vs/editor/browser/widget/diffEditor/movedBlocksLines": [ - "Code moved with changes to line {0}-{1}", - "Code moved with changes from line {0}-{1}", - "Code moved to line {0}-{1}", - "Code moved from line {0}-{1}" - ], - "vs/editor/browser/widget/diffEditor/diffEditorEditors": [ - " use {0} to open the accessibility help." - ], - "vs/editor/browser/widget/diffEditor/colors": [ - "The border color for text that got moved in the diff editor.", - "The active border color for text that got moved in the diff editor." - ], "vs/editor/browser/controller/textAreaHandler": [ "editor", "The editor is not accessible at this time.", @@ -29487,47 +32172,37 @@ "Icon for more information in the suggest widget.", "Read More" ], - "vs/workbench/contrib/comments/common/commentModel": [ - "There are no comments in this workspace yet." + "vs/workbench/contrib/chat/browser/chatFollowups": [ + "Follow up question: {0}" ], - "vs/workbench/contrib/comments/browser/commentsViewActions": [ - "Focus Comments view", - "Clear filter text", - "Focus comments filter", - "Toggle Unresolved Comments", - "Comments", - "Show Unresolved", - "Toggle Resolved Comments", - "Comments", - "Show Resolved" + "vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer": [ + "Icon for 'Insert' in accessible diff viewer.", + "Icon for 'Remove' in accessible diff viewer.", + "Icon for 'Close' in accessible diff viewer.", + "Close", + "Accessible Diff Viewer. Use arrow up and down to navigate.", + "no lines changed", + "1 line changed", + "{0} lines changed", + "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", + "blank", + "{0} unchanged line {1}", + "{0} original line {1} modified line {2}", + "+ {0} modified line {1}", + "- {0} original line {1}" ], - "vs/workbench/contrib/comments/browser/commentColors": [ - "Icon color for resolved comments.", - "Icon color for unresolved comments.", - "Color of borders and arrow for resolved comments.", - "Color of borders and arrow for unresolved comments.", - "Color of background for comment ranges.", - "Color of background for currently selected or hovered comment range." + "vs/editor/browser/widget/diffEditor/features/revertButtonsFeature": [ + "Revert Selected Changes", + "Revert Change" ], - "vs/workbench/browser/parts/editor/editorPanes": [ - "This type of editor cannot be opened in floating windows yet.", - "Close Editor", - "Unable to open '{0}'", - "&&OK" + "vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature": [ + "Code moved with changes to line {0}-{1}", + "Code moved with changes from line {0}-{1}", + "Code moved to line {0}-{1}", + "Code moved from line {0}-{1}" ], - "vs/workbench/browser/parts/editor/editorGroupWatermark": [ - "Show All Commands", - "Go to File", - "Open File", - "Open Folder", - "Open File or Folder", - "Open Recent", - "New Untitled Text File", - "Find in Files", - "Toggle Terminal", - "Start Debugging", - "Toggle Full Screen", - "Show Settings" + "vs/editor/browser/widget/diffEditor/components/diffEditorEditors": [ + " use {0} to open the accessibility help." ], "vs/workbench/browser/parts/editor/editorPlaceholder": [ "Workspace Trust Required", @@ -29540,23 +32215,38 @@ "The editor could not be opened due to an unexpected error.", "Try Again" ], + "vs/workbench/browser/parts/paneCompositeBar": [ + "Reset Location", + "Reset Location" + ], "vs/workbench/browser/parts/compositePart": [ "{0} actions", "Views and More Actions...", "{0} ({1})" ], - "vs/workbench/browser/parts/paneCompositeBar": [ - "Reset Location", - "Reset Location" + "vs/workbench/browser/parts/editor/editorPanes": [ + "Unable to open '{0}'", + "&&OK" ], - "vs/platform/quickinput/browser/quickInput": [ - "Back", - "Press 'Enter' to confirm your input or 'Escape' to cancel", - "{0}/{1}", - "Type to narrow down results.", - "{0} (Press 'Enter' to confirm or 'Escape' to cancel)" + "vs/workbench/browser/parts/editor/editorGroupWatermark": [ + "Foreground color for the labels in the editor watermark.", + "Show All Commands", + "Go to File", + "Open File", + "Open Folder", + "Open File or Folder", + "Open Recent", + "New Untitled Text File", + "Find in Files", + "Toggle Terminal", + "Start Debugging", + "Toggle Full Screen", + "Show Settings" + ], + "vs/platform/quickinput/browser/quickInputUtils": [ + "Click to execute command '{0}'" ], - "vs/platform/quickinput/browser/quickInputList": [ + "vs/platform/quickinput/browser/quickInputTree": [ "Quick Input" ], "vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators": [ @@ -29642,24 +32332,37 @@ "Item", "Value" ], - "vs/workbench/contrib/chat/browser/codeBlockPart": [ - "Code block", - "Toolbar for code block which can be reached via tab", - "Code block toolbar", - "Code block {0}" + "vs/workbench/contrib/chat/browser/chatAgentHover": [ + "View in Marketplace" + ], + "vs/workbench/contrib/inlineChat/browser/inlineChatWidget": [ + "Inline Chat Input, Use {0} for Inline Chat Accessibility Help.", + "Inline Chat Input, Run the Inline Chat Accessibility Help command for more information.", + "Inline Chat Input", + "Original", + "Modified" ], "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer": [ "Execution Order" ], - "vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll": [ - "Toggle Notebook Sticky Scroll", - "&&Toggle Notebook Sticky Scroll", - "Notebook Sticky Scroll", - "&&Notebook Sticky Scroll" + "vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider": [ + "Notebook\nUse {0} for accessibility help", + "Notebook\nRun the Open Accessibility Help command for more information", + "Notebook" ], "vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager": [ "Saving working copies" ], + "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ + "empty cell" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource": [ + "Display limit reached" + ], + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree": [ + "Notebook Variables", + "Variable {0}, value {1}" + ], "vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget": [ "Find", "Find", @@ -29678,29 +32381,8 @@ "Code Cell Source", "Code Cell Output" ], - "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory": [ - "empty cell" - ], - "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget": [ - "Exited {0} mode" - ], - "vs/platform/actions/browser/buttonbar": [ - "{0} ({1})" - ], - "vs/workbench/contrib/debug/common/debugger": [ - "Cannot find debug adapter for type '{0}'.", - "Use IntelliSense to learn about possible attributes.", - "Hover to view descriptions of existing attributes.", - "For more information, visit: {0}", - "Type of configuration.", - "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled.", - "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".", - "Request type of configuration. Can be \"launch\" or \"attach\".", - "Windows specific launch configuration attributes.", - "OS X specific launch configuration attributes.", - "Linux specific launch configuration attributes." - ], "vs/workbench/contrib/terminal/browser/xterm/decorationAddon": [ + "Toggle Visibility", "Rerun Command", "Do you want to run the command: {0}", "Yes", @@ -29711,10 +32393,8 @@ "Copy Output as HTML", "Run Recent Command", "Go To Recent Directory", - "Configure Command Decorations", "Learn About Shell Integration", "Toggle visibility", - "Toggle visibility", "Gutter command decorations", "Overview ruler command decorations" ], @@ -29759,7 +32439,23 @@ "Name of folder in which the compound is located.", "Names of configurations that will be started as part of this compound.", "Controls whether manually terminating one session will stop all of the compound sessions.", - "Task to run before any of the compound configurations start." + "Task to run before any of the compound configurations start.", + "Name", + "Type", + "Debuggers" + ], + "vs/workbench/contrib/debug/common/debugger": [ + "Cannot find debug adapter for type '{0}'.", + "Use IntelliSense to learn about possible attributes.", + "Hover to view descriptions of existing attributes.", + "For more information, visit: {0}", + "Type of configuration.", + "The debug type is not recognized. Make sure that you have a corresponding debug extension installed and that it is enabled.", + "\"node2\" is no longer supported, use \"node\" instead and set the \"protocol\" attribute to \"inspector\".", + "Request type of configuration. Can be \"launch\" or \"attach\".", + "Windows specific launch configuration attributes.", + "OS X specific launch configuration attributes.", + "Linux specific launch configuration attributes." ], "vs/workbench/contrib/debug/browser/rawDebugSession": [ "No debug adapter, can not start debug session.", @@ -29768,14 +32464,19 @@ "No debugger available found. Can not send '{0}'.", "More Info" ], - "vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel": [ - "Set Input Handled", - "Undo Mark As Handled" + "vs/workbench/contrib/comments/browser/commentThreadWidget": [ + "Comment", + "{0}, use ({1}) for accessibility help", + "{0}, run the command Open Accessibility Help which is currently not triggerable via keybinding." ], "vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController": [ "1 Conflicting Line", "{0} Conflicting Lines" ], + "vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel": [ + "Set Input Handled", + "Undo Mark As Handled" + ], "vs/workbench/contrib/mergeEditor/browser/view/conflictActions": [ "Accept {0}", "Accept {0} in the result document.", @@ -29799,40 +32500,6 @@ "Reset to base", "Reset this conflict to the common ancestor of both the right and left changes." ], - "vs/workbench/contrib/comments/browser/commentGlyphWidget": [ - "Editor gutter decoration color for commenting ranges. This color should be opaque.", - "Editor overview ruler decoration color for resolved comments. This color should be opaque.", - "Editor overview ruler decoration color for unresolved comments. This color should be opaque.", - "Editor gutter decoration color for commenting glyphs.", - "Editor gutter decoration color for commenting glyphs for unresolved comment threads." - ], - "vs/workbench/contrib/customEditor/common/extensionPoint": [ - "Contributed custom editors.", - "Identifier for the custom editor. This must be unique across all custom editors, so we recommend including your extension id as part of `viewType`. The `viewType` is used when registering custom editors with `vscode.registerCustomEditorProvider` and in the `onCustomEditor:${id}` [activation event](https://code.visualstudio.com/api/references/activation-events).", - "Human readable name of the custom editor. This is displayed to users when selecting which editor to use.", - "Set of globs that the custom editor is enabled for.", - "Glob that the custom editor is enabled for.", - "Controls if the custom editor is enabled automatically when the user opens a file. This may be overridden by users using the `workbench.editorAssociations` setting.", - "The editor is automatically used when the user opens a resource, provided that no other default custom editors are registered for that resource.", - "The editor is not automatically used when the user opens a resource, but a user can switch to the editor using the `Reopen With` command." - ], - "vs/workbench/contrib/terminal/browser/terminalConfigHelper": [ - "The '{0}' extension is recommended for opening a terminal in WSL.", - "Install" - ], - "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ - "Select the terminal profile to create", - "Select your default terminal profile", - "Enter terminal profile name", - "A terminal profile already exists with that name", - "profiles", - "contributed", - "detected", - "This terminal profile uses a potentially unsafe path that can be modified by another user: {0}. Are you sure you want to use it?", - "Yes", - "Cancel", - "Configure Terminal Profile" - ], "vs/workbench/contrib/terminal/browser/terminalInstance": [ "Terminal input", "Use the accessible buffer {0} to manually review output", @@ -29840,10 +32507,6 @@ "Bell", "Some keybindings don't go to the terminal by default and are handled by {0} instead.", "Configure Terminal Settings", - "Preview:", - "Are you sure you want to paste {0} lines of text into the terminal?", - "&&Paste", - "Do not ask me again", "Lost connection to process", "Cannot launch a terminal process in an untrusted workspace", "Cannot launch a terminal process in an untrusted workspace with cwd {0} and userHome {1}", @@ -29858,7 +32521,6 @@ "Set Fixed Dimensions: Column", "Set Fixed Dimensions: Row", "Terminal {0} environment is stale, run the 'Show Environment Information' command for more information", - "Select an icon for the terminal", "Select a color for the terminal", "The terminal process \"{0}\" failed to launch (exit code: {1}).", "The terminal process failed to launch (exit code: {0}).", @@ -29866,6 +32528,33 @@ "The terminal process terminated with exit code: {0}.", "The terminal process failed to launch: {0}." ], + "vs/workbench/contrib/customEditor/common/extensionPoint": [ + "Contributed custom editors.", + "Identifier for the custom editor. This must be unique across all custom editors, so we recommend including your extension id as part of `viewType`. The `viewType` is used when registering custom editors with `vscode.registerCustomEditorProvider` and in the `onCustomEditor:${id}` [activation event](https://code.visualstudio.com/api/references/activation-events).", + "Human readable name of the custom editor. This is displayed to users when selecting which editor to use.", + "Set of globs that the custom editor is enabled for.", + "Glob that the custom editor is enabled for.", + "Controls if the custom editor is enabled automatically when the user opens a file. This may be overridden by users using the `workbench.editorAssociations` setting.", + "The editor is automatically used when the user opens a resource, provided that no other default custom editors are registered for that resource.", + "The editor is not automatically used when the user opens a resource, but a user can switch to the editor using the `Reopen With` command.", + "View Type", + "Priority", + "Filename Pattern", + "Custom Editors" + ], + "vs/workbench/contrib/terminal/browser/terminalProfileQuickpick": [ + "Select the terminal profile to create", + "Select your default terminal profile", + "Enter terminal profile name", + "A terminal profile already exists with that name", + "profiles", + "contributed", + "detected", + "This terminal profile uses a potentially unsafe path that can be modified by another user: {0}. Are you sure you want to use it?", + "Yes", + "Cancel", + "Configure Terminal Profile" + ], "vs/workbench/contrib/terminal/browser/terminalTabsList": [ "Type terminal name. Press Enter to confirm or Escape to cancel.", "Terminal tabs", @@ -29873,18 +32562,6 @@ "Terminal {0} {1}", "Terminal" ], - "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget": [ - "Find", - "Find (⇅ for history)", - "Previous Match", - "Next Match", - "Close", - "Enter search input", - "{0} found", - "{0} found for '{1}'", - "{0} found for '{1}'", - "Border color of the sash border." - ], "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter": [ "Search workspace", "Open file in editor", @@ -29892,18 +32569,62 @@ "Open folder in new window", "Follow link" ], + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget": [ + "Ask how to do something in the terminal", + "AI-generated commands may be incorrect" + ], "vs/workbench/contrib/terminal/browser/xterm/decorationStyles": [ "Show Command Actions", + "Command executed {0}, took {1} and failed", + "Command executed {0}, took {1} and failed (Exit Code {2})", + "Command executed {0} and took {1}", "Command executed {0} and failed", "Command executed {0} and failed (Exit Code {1})", "Command executed {0}" ], - "vs/workbench/contrib/welcomeGettingStarted/common/media/theme_picker": [ - "Dark Modern", - "Light Modern", - "Dark High Contrast", - "Light High Contrast", - "See More Themes..." + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay": [ + "Navigate to Command", + "{0} ({1})", + "{0} ({1})" + ], + "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget": [ + "Find", + "Find", + "Previous Match", + "Next Match", + "Close", + "Enter search input", + "{0} found", + "{0} found for '{1}'", + "{0} found for '{1}'", + "Border color of the sash border." + ], + "vs/workbench/contrib/terminalContrib/find/browser/textInputContextMenu": [ + "Undo", + "Redo", + "Cut", + "Copy", + "Paste", + "Select All" + ], + "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ + "Suggest", + "{0}{1}, {2}", + "{0}{1}", + "{0}, {1}", + "{0}, docs: {1}" + ], + "vs/workbench/contrib/markdown/browser/markdownSettingRenderer": [ + "View in Settings", + "View \"{0}: {1}\" in Settings", + "Restore value of \"{0}: {1}\"", + "Enable \"{0}: {1}\"", + "Disable \"{0}: {1}\"", + "Set \"{0}: {1}\" to \"{2}\"", + "Set \"{0}: {1}\" to {2}", + "View or change setting", + "Copy Setting ID", + "Copy Setting ID" ], "vs/workbench/contrib/welcomeGettingStarted/common/media/notebookProfile": [ "Default", @@ -29920,15 +32641,24 @@ "Theirs", "Yours" ], + "vs/platform/languagePacks/common/localizedStrings": [ + "open", + "close", + "find" + ], "vs/workbench/browser/parts/notifications/notificationsViewer": [ "Click to execute command '{0}'", "Notification Actions", + "Turn On Notifications from '{0}'", + "Turn Off Notifications from '{0}'", "Source: {0}" ], - "vs/platform/languagePacks/common/localizedStrings": [ - "open", - "close", - "find" + "vs/workbench/contrib/welcomeGettingStarted/common/media/theme_picker": [ + "Dark Modern", + "Light Modern", + "Dark High Contrast", + "Light High Contrast", + "See More Themes..." ], "vs/base/browser/ui/menu/menubar": [ "Application Menu", @@ -29947,27 +32677,13 @@ "Toggle View Pinned", "Toggle View Badge" ], - "vs/editor/browser/widget/diffEditor/decorations": [ - "Line decoration for inserts in the diff editor.", - "Line decoration for removals in the diff editor.", - "Click to revert change" - ], "vs/editor/common/viewLayout/viewLineRenderer": [ "Show more ({0})", "{0} chars" ], - "vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin": [ - "Copy deleted lines", - "Copy deleted line", - "Copy changed lines", - "Copy changed line", - "Copy deleted line ({0})", - "Copy changed line ({0})", - "Revert this change" - ], "vs/platform/actionWidget/browser/actionList": [ - "{0} to apply, {1} to preview", - "{0} to apply", + "{0} to Apply, {1} to Preview", + "{0} to Apply", "{0}, Disabled Reason: {1}", "Action Widget" ], @@ -29976,8 +32692,17 @@ "{0} reference", "References" ], - "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ - "Tab actions" + "vs/workbench/browser/parts/compositeBar": [ + "Active View Switcher" + ], + "vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin": [ + "Copy deleted lines", + "Copy deleted line", + "Copy changed lines", + "Copy changed line", + "Copy deleted line ({0})", + "Copy changed line ({0})", + "Revert this change" ], "vs/workbench/browser/parts/editor/breadcrumbsControl": [ "Icon for the separator in the breadcrumbs.", @@ -29985,18 +32710,18 @@ "Whether breadcrumbs are currently visible", "Whether breadcrumbs have focus", "no elements", + "Toggle &&Breadcrumbs", "Toggle Breadcrumbs", "Toggle &&Breadcrumbs", - "Breadcrumbs", - "&&Breadcrumbs", + "Toggle Breadcrumbs", "Focus and Select Breadcrumbs", "Focus Breadcrumbs" ], - "vs/workbench/browser/parts/compositeBar": [ - "Active View Switcher" + "vs/workbench/browser/parts/editor/multiEditorTabsControl": [ + "Tab actions" ], - "vs/platform/quickinput/browser/quickInputUtils": [ - "Click to execute command '{0}'" + "vs/platform/actions/browser/buttonbar": [ + "{0} ({1})" ], "vs/workbench/contrib/notebook/browser/diff/diffElementOutputs": [ "Choose a different output mimetype, available mimetypes: {0}", @@ -30009,9 +32734,9 @@ ], "vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions": [ "Controls the display of line numbers in the cell editor.", - "Toggle Notebook Line Numbers", "Notebook Line Numbers", - "Show Cell Line Numbers" + "Show Cell Line Numbers", + "Toggle Notebook Line Numbers" ], "vs/workbench/contrib/notebook/browser/view/cellParts/codeCell": [ "Double-click to expand cell input ({0})", @@ -30020,30 +32745,28 @@ "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar": [ "More..." ], + "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ + "1 cell hidden", + "{0} cells hidden" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/collapsedCellOutput": [ "Outputs are collapsed", "Double-click to expand cell output ({0})", "Expand Cell Output (${0})" ], - "vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint": [ - "1 cell hidden", - "{0} cells hidden" - ], "vs/workbench/contrib/notebook/browser/view/cellParts/markupCell": [ "Double-click to expand cell input ({0})", "Expand Cell Input ({0})" ], - "vs/workbench/services/suggest/browser/simpleSuggestWidget": [ - "Suggest", - "{0}{1}, {2}", - "{0}{1}", - "{0}, {1}", - "{0}, docs: {1}" + "vs/workbench/contrib/comments/browser/commentThreadHeader": [ + "Icon to collapse a review comment.", + "Collapse", + "Start discussion" ], - "vs/workbench/contrib/comments/browser/commentThreadWidget": [ - "Comment", - "{0}, use ({1}) for accessibility help", - "{0}, run the command Open Accessibility Help which is currently not triggerable via keybinding." + "vs/workbench/contrib/comments/browser/commentThreadBody": [ + "Comment thread with {0} comments on lines {1} through {2}. {3}.", + "Comment thread with {0} comments on the entire document. {1}.", + "Comment thread with {0} comments. {1}." ], "vs/workbench/contrib/terminal/browser/terminalProcessManager": [ "Could not kill process listening on port {0}, command exited with error {1}", @@ -30058,9 +32781,12 @@ "Select a directory to go to (hold Option-key to edit the command)", "Select a directory to go to (hold Alt-key to edit the command)" ], - "vs/workbench/browser/parts/editor/editorTabsControl": [ - "Editor actions", - "{0} (+{1})" + "vs/workbench/contrib/terminal/common/terminalClipboard": [ + "Preview:", + "Are you sure you want to paste {0} lines of text into the terminal?", + "&&Paste", + "Paste as &&one line", + "Do not ask me again" ], "vs/workbench/browser/parts/editor/breadcrumbs": [ "Breadcrumb Navigation", @@ -30118,22 +32844,21 @@ "Select mimetype to render for current output", "renderer not available" ], + "vs/workbench/contrib/comments/browser/commentNode": [ + "Toggle Reaction", + "Toggling the comment reaction failed: {0}.", + "Toggling the comment reaction failed", + "Deleting the comment reaction failed: {0}.", + "Deleting the comment reaction failed", + "Deleting the comment reaction failed: {0}.", + "Deleting the comment reaction failed" + ], "vs/workbench/contrib/notebook/browser/view/cellParts/codeCellExecutionIcon": [ "Success", - "Failed", + "Failure", "Pending", "Executing" ], - "vs/workbench/contrib/comments/browser/commentThreadBody": [ - "Comment thread with {0} comments on lines {1} through {2}. {3}.", - "Comment thread with {0} comments on the entire document. {1}.", - "Comment thread with {0} comments. {1}." - ], - "vs/workbench/contrib/comments/browser/commentThreadHeader": [ - "Icon to collapse a review comment.", - "Collapse", - "Start discussion" - ], "vs/workbench/contrib/terminal/browser/environmentVariableInfo": [ "The following extensions want to relaunch the terminal to contribute to its environment:", "Relaunch terminal", @@ -30141,21 +32866,14 @@ "Show environment contributions", "workspace" ], - "vs/workbench/contrib/comments/browser/commentNode": [ - "Toggle Reaction", - "Toggling the comment reaction failed: {0}.", - "Toggling the comment reaction failed", - "Deleting the comment reaction failed: {0}.", - "Deleting the comment reaction failed", - "Deleting the comment reaction failed: {0}.", - "Deleting the comment reaction failed" - ], "vs/workbench/contrib/comments/browser/reactionsAction": [ "Pick Reactions...", "Toggle reaction, ", "{0}{1} reaction", "{0}1 reaction with {1}", - "{0}{1} reactions with {2}" + "{0}{1} reactions with {2}", + "{0}{1} reacted with {2}", + "{0}{1} and {2} more reacted with {3}" ] }, "bundles": { @@ -30168,7 +32886,6 @@ "vs/base/browser/ui/findinput/findInputToggles", "vs/base/browser/ui/findinput/replaceInput", "vs/base/browser/ui/hover/hoverWidget", - "vs/base/browser/ui/iconLabel/iconLabelHover", "vs/base/browser/ui/icons/iconSelectBox", "vs/base/browser/ui/inputbox/inputBox", "vs/base/browser/ui/keybindingLabel/keybindingLabel", @@ -30187,15 +32904,19 @@ "vs/editor/browser/controller/textAreaHandler", "vs/editor/browser/coreCommands", "vs/editor/browser/editorExtensions", - "vs/editor/browser/widget/codeEditorWidget", - "vs/editor/browser/widget/diffEditor/accessibleDiffViewer", - "vs/editor/browser/widget/diffEditor/colors", - "vs/editor/browser/widget/diffEditor/decorations", + "vs/editor/browser/services/hoverService/hoverWidget", + "vs/editor/browser/services/hoverService/updatableHoverWidget", + "vs/editor/browser/widget/codeEditor/codeEditorWidget", + "vs/editor/browser/widget/diffEditor/commands", + "vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer", + "vs/editor/browser/widget/diffEditor/components/diffEditorEditors", + "vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin", "vs/editor/browser/widget/diffEditor/diffEditor.contribution", - "vs/editor/browser/widget/diffEditor/diffEditorEditors", - "vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature", - "vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin", - "vs/editor/browser/widget/diffEditor/movedBlocksLines", + "vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature", + "vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature", + "vs/editor/browser/widget/diffEditor/features/revertButtonsFeature", + "vs/editor/browser/widget/diffEditor/registrations.contribution", + "vs/editor/browser/widget/multiDiffEditor/colors", "vs/editor/common/config/editorConfigurationSchema", "vs/editor/common/config/editorOptions", "vs/editor/common/core/editorColorRegistry", @@ -30243,7 +32964,7 @@ "vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget", "vs/editor/contrib/gotoSymbol/browser/referencesModel", "vs/editor/contrib/gotoSymbol/browser/symbolNavigation", - "vs/editor/contrib/hover/browser/hover", + "vs/editor/contrib/hover/browser/hoverActions", "vs/editor/contrib/hover/browser/markdownHoverParticipant", "vs/editor/contrib/hover/browser/markerHoverParticipant", "vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace", @@ -30267,7 +32988,7 @@ "vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess", "vs/editor/contrib/readOnlyMessage/browser/contribution", "vs/editor/contrib/rename/browser/rename", - "vs/editor/contrib/rename/browser/renameInputField", + "vs/editor/contrib/rename/browser/renameWidget", "vs/editor/contrib/smartSelect/browser/smartSelect", "vs/editor/contrib/snippet/browser/snippetController2", "vs/editor/contrib/snippet/browser/snippetVariables", @@ -30286,6 +33007,7 @@ "vs/editor/contrib/wordHighlighter/browser/highlightDecorations", "vs/editor/contrib/wordHighlighter/browser/wordHighlighter", "vs/editor/contrib/wordOperations/browser/wordOperations", + "vs/platform/accessibilitySignal/browser/accessibilitySignalService", "vs/platform/action/common/actionCommonCategories", "vs/platform/actionWidget/browser/actionList", "vs/platform/actionWidget/browser/actionWidget", @@ -30294,7 +33016,6 @@ "vs/platform/actions/browser/toolbar", "vs/platform/actions/common/menuResetAction", "vs/platform/actions/common/menuService", - "vs/platform/audioCues/browser/audioCueService", "vs/platform/configuration/common/configurationRegistry", "vs/platform/contextkey/browser/contextKeyService", "vs/platform/contextkey/common/contextkey", @@ -30317,12 +33038,13 @@ "vs/platform/languagePacks/common/languagePacks", "vs/platform/languagePacks/common/localizedStrings", "vs/platform/list/browser/listService", + "vs/platform/log/common/log", "vs/platform/markers/common/markers", "vs/platform/quickinput/browser/commandsQuickAccess", "vs/platform/quickinput/browser/helpQuickAccess", "vs/platform/quickinput/browser/quickInput", "vs/platform/quickinput/browser/quickInputController", - "vs/platform/quickinput/browser/quickInputList", + "vs/platform/quickinput/browser/quickInputTree", "vs/platform/quickinput/browser/quickInputUtils", "vs/platform/quickinput/browser/quickPickPin", "vs/platform/remoteTunnel/common/remoteTunnel", @@ -30331,7 +33053,16 @@ "vs/platform/terminal/common/terminalLogService", "vs/platform/terminal/common/terminalPlatformConfiguration", "vs/platform/terminal/common/terminalProfiles", - "vs/platform/theme/common/colorRegistry", + "vs/platform/theme/common/colors/baseColors", + "vs/platform/theme/common/colors/chartsColors", + "vs/platform/theme/common/colors/editorColors", + "vs/platform/theme/common/colors/inputColors", + "vs/platform/theme/common/colors/listColors", + "vs/platform/theme/common/colors/menuColors", + "vs/platform/theme/common/colors/minimapColors", + "vs/platform/theme/common/colors/miscColors", + "vs/platform/theme/common/colors/quickpickColors", + "vs/platform/theme/common/colors/searchColors", "vs/platform/theme/common/iconRegistry", "vs/platform/theme/common/tokenClassificationRegistry", "vs/platform/undoRedo/common/undoRedoService", @@ -30342,7 +33073,6 @@ "vs/platform/userDataSync/common/userDataSyncLog", "vs/platform/userDataSync/common/userDataSyncMachines", "vs/platform/workspace/common/workspace", - "vs/platform/workspace/common/workspaceTrust", "vs/workbench/api/browser/mainThreadAuthentication", "vs/workbench/api/browser/mainThreadCLICommands", "vs/workbench/api/browser/mainThreadComments", @@ -30350,6 +33080,7 @@ "vs/workbench/api/browser/mainThreadEditSessionIdentityParticipant", "vs/workbench/api/browser/mainThreadExtensionService", "vs/workbench/api/browser/mainThreadFileSystemEventService", + "vs/workbench/api/browser/mainThreadLanguageModels", "vs/workbench/api/browser/mainThreadMessageService", "vs/workbench/api/browser/mainThreadNotebookSaveParticipant", "vs/workbench/api/browser/mainThreadProgress", @@ -30367,6 +33098,7 @@ "vs/workbench/browser/actions/developerActions", "vs/workbench/browser/actions/helpActions", "vs/workbench/browser/actions/layoutActions", + "vs/workbench/browser/actions/listCommands", "vs/workbench/browser/actions/navigationActions", "vs/workbench/browser/actions/quickAccessActions", "vs/workbench/browser/actions/textInputActions", @@ -30374,6 +33106,7 @@ "vs/workbench/browser/actions/workspaceActions", "vs/workbench/browser/actions/workspaceCommands", "vs/workbench/browser/editor", + "vs/workbench/browser/labels", "vs/workbench/browser/parts/activitybar/activitybarPart", "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions", "vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart", @@ -30387,6 +33120,7 @@ "vs/workbench/browser/parts/editor/breadcrumbs", "vs/workbench/browser/parts/editor/breadcrumbsControl", "vs/workbench/browser/parts/editor/breadcrumbsPicker", + "vs/workbench/browser/parts/editor/diffEditorCommands", "vs/workbench/browser/parts/editor/editor.contribution", "vs/workbench/browser/parts/editor/editorActions", "vs/workbench/browser/parts/editor/editorCommands", @@ -30424,6 +33158,7 @@ "vs/workbench/browser/parts/statusbar/statusbarPart", "vs/workbench/browser/parts/titlebar/commandCenterControl", "vs/workbench/browser/parts/titlebar/menubarControl", + "vs/workbench/browser/parts/titlebar/titlebarActions", "vs/workbench/browser/parts/titlebar/titlebarPart", "vs/workbench/browser/parts/titlebar/windowTitle", "vs/workbench/browser/parts/views/checkbox", @@ -30431,8 +33166,8 @@ "vs/workbench/browser/parts/views/viewFilter", "vs/workbench/browser/parts/views/viewPane", "vs/workbench/browser/parts/views/viewPaneContainer", - "vs/workbench/browser/parts/views/viewsService", "vs/workbench/browser/quickaccess", + "vs/workbench/browser/window", "vs/workbench/browser/workbench", "vs/workbench/browser/workbench.contribution", "vs/workbench/common/configuration", @@ -30444,14 +33179,17 @@ "vs/workbench/common/theme", "vs/workbench/common/views", "vs/workbench/contrib/accessibility/browser/accessibilityConfiguration", - "vs/workbench/contrib/accessibility/browser/accessibilityContributions", "vs/workbench/contrib/accessibility/browser/accessibilityStatus", - "vs/workbench/contrib/accessibility/browser/accessibleNotificationService", "vs/workbench/contrib/accessibility/browser/accessibleView", "vs/workbench/contrib/accessibility/browser/accessibleViewActions", + "vs/workbench/contrib/accessibility/browser/accessibleViewContributions", + "vs/workbench/contrib/accessibility/browser/audioCueConfiguration", + "vs/workbench/contrib/accessibilitySignals/browser/commands", + "vs/workbench/contrib/accessibilitySignals/browser/openDiffEditorAnnouncement", "vs/workbench/contrib/accountEntitlements/browser/accountsEntitlements.contribution", - "vs/workbench/contrib/audioCues/browser/audioCues.contribution", - "vs/workbench/contrib/audioCues/browser/commands", + "vs/workbench/contrib/authentication/browser/actions/manageTrustedExtensionsForAccountAction", + "vs/workbench/contrib/authentication/browser/actions/signOutOfAccountAction", + "vs/workbench/contrib/authentication/browser/authentication.contribution", "vs/workbench/contrib/bulkEdit/browser/bulkEditService", "vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution", "vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane", @@ -30471,28 +33209,33 @@ "vs/workbench/contrib/chat/browser/actions/chatMoveActions", "vs/workbench/contrib/chat/browser/actions/chatQuickInputActions", "vs/workbench/contrib/chat/browser/actions/chatTitleActions", + "vs/workbench/contrib/chat/browser/chat", "vs/workbench/contrib/chat/browser/chat.contribution", - "vs/workbench/contrib/chat/browser/chatContributionServiceImpl", + "vs/workbench/contrib/chat/browser/chatAccessibilityProvider", + "vs/workbench/contrib/chat/browser/chatAgentHover", "vs/workbench/contrib/chat/browser/chatEditorInput", + "vs/workbench/contrib/chat/browser/chatFollowups", "vs/workbench/contrib/chat/browser/chatInputPart", "vs/workbench/contrib/chat/browser/chatListRenderer", - "vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget", + "vs/workbench/contrib/chat/browser/chatParticipantContributions", "vs/workbench/contrib/chat/browser/codeBlockPart", + "vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables", "vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib", "vs/workbench/contrib/chat/common/chatColors", "vs/workbench/contrib/chat/common/chatContextKeys", "vs/workbench/contrib/chat/common/chatServiceImpl", + "vs/workbench/contrib/chat/common/languageModelStats", "vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions", "vs/workbench/contrib/codeActions/browser/codeActionsContribution", "vs/workbench/contrib/codeActions/common/codeActionsExtensionPoint", "vs/workbench/contrib/codeActions/common/documentationExtensionPoint", "vs/workbench/contrib/codeEditor/browser/accessibility/accessibility", + "vs/workbench/contrib/codeEditor/browser/dictation/editorDictation", "vs/workbench/contrib/codeEditor/browser/diffEditorHelper", "vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint", "vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget", "vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens", "vs/workbench/contrib/codeEditor/browser/inspectKeybindings", - "vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint", "vs/workbench/contrib/codeEditor/browser/largeFileOptimizations", "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline", "vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree", @@ -30505,6 +33248,7 @@ "vs/workbench/contrib/codeEditor/browser/toggleRenderControlCharacter", "vs/workbench/contrib/codeEditor/browser/toggleRenderWhitespace", "vs/workbench/contrib/codeEditor/browser/toggleWordWrap", + "vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint", "vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard", "vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate", "vs/workbench/contrib/commands/common/commands.contribution", @@ -30516,14 +33260,16 @@ "vs/workbench/contrib/comments/browser/commentThreadHeader", "vs/workbench/contrib/comments/browser/commentThreadWidget", "vs/workbench/contrib/comments/browser/comments.contribution", + "vs/workbench/contrib/comments/browser/commentsAccessibility", "vs/workbench/contrib/comments/browser/commentsController", "vs/workbench/contrib/comments/browser/commentsEditorContribution", + "vs/workbench/contrib/comments/browser/commentsModel", "vs/workbench/contrib/comments/browser/commentsTreeViewer", "vs/workbench/contrib/comments/browser/commentsView", "vs/workbench/contrib/comments/browser/commentsViewActions", "vs/workbench/contrib/comments/browser/reactionsAction", "vs/workbench/contrib/comments/common/commentContextKeys", - "vs/workbench/contrib/comments/common/commentModel", + "vs/workbench/contrib/customEditor/browser/customEditorInput", "vs/workbench/contrib/customEditor/common/contributedCustomEditors", "vs/workbench/contrib/customEditor/common/customEditor", "vs/workbench/contrib/customEditor/common/extensionPoint", @@ -30587,6 +33333,7 @@ "vs/workbench/contrib/extensions/browser/exeBasedRecommendations", "vs/workbench/contrib/extensions/browser/extensionEditor", "vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant", + "vs/workbench/contrib/extensions/browser/extensionFeaturesTab", "vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService", "vs/workbench/contrib/extensions/browser/extensions.contribution", "vs/workbench/contrib/extensions/browser/extensionsActions", @@ -30629,6 +33376,7 @@ "vs/workbench/contrib/files/browser/fileConstants", "vs/workbench/contrib/files/browser/fileImportExport", "vs/workbench/contrib/files/browser/files.contribution", + "vs/workbench/contrib/files/browser/views/emptyView", "vs/workbench/contrib/files/browser/views/explorerDecorationsProvider", "vs/workbench/contrib/files/browser/views/explorerView", "vs/workbench/contrib/files/browser/views/explorerViewer", @@ -30644,12 +33392,15 @@ "vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty", "vs/workbench/contrib/inlineChat/browser/inlineChatActions", "vs/workbench/contrib/inlineChat/browser/inlineChatController", - "vs/workbench/contrib/inlineChat/browser/inlineChatDecorations", + "vs/workbench/contrib/inlineChat/browser/inlineChatSavingServiceImpl", "vs/workbench/contrib/inlineChat/browser/inlineChatStrategies", "vs/workbench/contrib/inlineChat/browser/inlineChatWidget", + "vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget", "vs/workbench/contrib/inlineChat/common/inlineChat", + "vs/workbench/contrib/inlineChat/electron-sandbox/inlineChatActions", "vs/workbench/contrib/interactive/browser/interactive.contribution", "vs/workbench/contrib/interactive/browser/interactiveEditor", + "vs/workbench/contrib/issue/browser/issueQuickAccess", "vs/workbench/contrib/issue/common/issue.contribution", "vs/workbench/contrib/issue/electron-sandbox/issue.contribution", "vs/workbench/contrib/keybindings/browser/keybindings.contribution", @@ -30667,6 +33418,7 @@ "vs/workbench/contrib/logs/common/logs.contribution", "vs/workbench/contrib/logs/common/logsActions", "vs/workbench/contrib/logs/electron-sandbox/logsActions", + "vs/workbench/contrib/markdown/browser/markdownSettingRenderer", "vs/workbench/contrib/markers/browser/markers.contribution", "vs/workbench/contrib/markers/browser/markersFileDecorations", "vs/workbench/contrib/markers/browser/markersTable", @@ -30689,6 +33441,11 @@ "vs/workbench/contrib/mergeEditor/browser/view/viewModel", "vs/workbench/contrib/mergeEditor/common/mergeEditor", "vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands", + "vs/workbench/contrib/multiDiffEditor/browser/actions", + "vs/workbench/contrib/multiDiffEditor/browser/icons.contribution", + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.contribution", + "vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput", + "vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver", "vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands", "vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController", "vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders", @@ -30701,23 +33458,34 @@ "vs/workbench/contrib/notebook/browser/contrib/gettingStarted/notebookGettingStarted", "vs/workbench/contrib/notebook/browser/contrib/layout/layoutActions", "vs/workbench/contrib/notebook/browser/contrib/navigation/arrow", + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariableCommands", + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariables", + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesDataSource", + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesTree", + "vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView", "vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline", "vs/workbench/contrib/notebook/browser/contrib/profile/notebookProfile", "vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants", "vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout", "vs/workbench/contrib/notebook/browser/controller/cellOperations", "vs/workbench/contrib/notebook/browser/controller/cellOutputActions", + "vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions", + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext", + "vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController", "vs/workbench/contrib/notebook/browser/controller/coreActions", "vs/workbench/contrib/notebook/browser/controller/editActions", "vs/workbench/contrib/notebook/browser/controller/executeActions", "vs/workbench/contrib/notebook/browser/controller/foldingController", "vs/workbench/contrib/notebook/browser/controller/insertCellActions", "vs/workbench/contrib/notebook/browser/controller/layoutActions", + "vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions", + "vs/workbench/contrib/notebook/browser/controller/sectionActions", "vs/workbench/contrib/notebook/browser/diff/diffElementOutputs", "vs/workbench/contrib/notebook/browser/diff/notebookDiffActions", "vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor", "vs/workbench/contrib/notebook/browser/notebook.contribution", "vs/workbench/contrib/notebook/browser/notebookAccessibility", + "vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider", "vs/workbench/contrib/notebook/browser/notebookEditor", "vs/workbench/contrib/notebook/browser/notebookEditorWidget", "vs/workbench/contrib/notebook/browser/notebookExtensionPoint", @@ -30738,7 +33506,6 @@ "vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView", "vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer", "vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory", - "vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll", "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy", "vs/workbench/contrib/notebook/browser/viewParts/notebookKernelView", "vs/workbench/contrib/notebook/common/notebookEditorInput", @@ -30774,6 +33541,7 @@ "vs/workbench/contrib/relauncher/browser/relauncher.contribution", "vs/workbench/contrib/remote/browser/explorerViewItems", "vs/workbench/contrib/remote/browser/remote", + "vs/workbench/contrib/remote/browser/remoteConnectionHealth", "vs/workbench/contrib/remote/browser/remoteExplorer", "vs/workbench/contrib/remote/browser/remoteIcons", "vs/workbench/contrib/remote/browser/remoteIndicator", @@ -30789,9 +33557,9 @@ "vs/workbench/contrib/scm/browser/menus", "vs/workbench/contrib/scm/browser/scm.contribution", "vs/workbench/contrib/scm/browser/scmRepositoriesViewPane", - "vs/workbench/contrib/scm/browser/scmSyncViewPane", "vs/workbench/contrib/scm/browser/scmViewPane", "vs/workbench/contrib/scm/browser/scmViewPaneContainer", + "vs/workbench/contrib/scrollLocking/browser/scrollLocking", "vs/workbench/contrib/search/browser/anythingQuickAccess", "vs/workbench/contrib/search/browser/patternInputWidget", "vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess", @@ -30829,6 +33597,7 @@ "vs/workbench/contrib/snippets/browser/snippets.contribution", "vs/workbench/contrib/snippets/browser/snippetsFile", "vs/workbench/contrib/snippets/browser/snippetsService", + "vs/workbench/contrib/speech/browser/speechService", "vs/workbench/contrib/speech/common/speechService", "vs/workbench/contrib/surveys/browser/ces.contribution", "vs/workbench/contrib/surveys/browser/languageSurveys.contribution", @@ -30854,7 +33623,6 @@ "vs/workbench/contrib/terminal/browser/environmentVariableInfo", "vs/workbench/contrib/terminal/browser/terminal.contribution", "vs/workbench/contrib/terminal/browser/terminalActions", - "vs/workbench/contrib/terminal/browser/terminalConfigHelper", "vs/workbench/contrib/terminal/browser/terminalEditorInput", "vs/workbench/contrib/terminal/browser/terminalIcons", "vs/workbench/contrib/terminal/browser/terminalInstance", @@ -30868,10 +33636,12 @@ "vs/workbench/contrib/terminal/browser/terminalTabsList", "vs/workbench/contrib/terminal/browser/terminalTooltip", "vs/workbench/contrib/terminal/browser/terminalView", + "vs/workbench/contrib/terminal/browser/terminalWslRecommendationContribution", "vs/workbench/contrib/terminal/browser/xterm/decorationAddon", "vs/workbench/contrib/terminal/browser/xterm/decorationStyles", "vs/workbench/contrib/terminal/browser/xterm/xtermTerminal", "vs/workbench/contrib/terminal/common/terminal", + "vs/workbench/contrib/terminal/common/terminalClipboard", "vs/workbench/contrib/terminal/common/terminalColorRegistry", "vs/workbench/contrib/terminal/common/terminalConfiguration", "vs/workbench/contrib/terminal/common/terminalContextKey", @@ -30879,9 +33649,14 @@ "vs/workbench/contrib/terminal/electron-sandbox/terminalRemote", "vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution", "vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp", + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChat", + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp", + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions", + "vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget", "vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution", "vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution", "vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution", + "vs/workbench/contrib/terminalContrib/find/browser/textInputContextMenu", "vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution", "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter", "vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager", @@ -30890,7 +33665,14 @@ "vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution", "vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixBuiltinActions", "vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService", + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry", + "vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay", + "vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution", + "vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution", + "vs/workbench/contrib/testing/browser/codeCoverageDecorations", "vs/workbench/contrib/testing/browser/icons", + "vs/workbench/contrib/testing/browser/testCoverageBars", + "vs/workbench/contrib/testing/browser/testCoverageView", "vs/workbench/contrib/testing/browser/testExplorerActions", "vs/workbench/contrib/testing/browser/testing.contribution", "vs/workbench/contrib/testing/browser/testingConfigurationUi", @@ -30932,7 +33714,6 @@ "vs/workbench/contrib/webviewPanel/browser/webviewCommands", "vs/workbench/contrib/webviewPanel/browser/webviewEditor", "vs/workbench/contrib/webviewPanel/browser/webviewPanel.contribution", - "vs/workbench/contrib/welcomeGettingStarted/browser/featuredExtensionService", "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted", "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution", "vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors", @@ -30964,8 +33745,10 @@ "vs/workbench/electron-sandbox/window", "vs/workbench/services/actions/common/menusExtensionPoint", "vs/workbench/services/assignment/common/assignmentService", + "vs/workbench/services/authentication/browser/authenticationExtensionsService", "vs/workbench/services/authentication/browser/authenticationService", "vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService", + "vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService", "vs/workbench/services/configuration/browser/configurationService", "vs/workbench/services/configuration/common/configurationEditing", "vs/workbench/services/configuration/common/jsonEditingService", @@ -30980,6 +33763,8 @@ "vs/workbench/services/editor/common/editorResolverService", "vs/workbench/services/extensionManagement/browser/extensionBisect", "vs/workbench/services/extensionManagement/browser/extensionEnablementService", + "vs/workbench/services/extensionManagement/common/extensionFeaturesManagemetService", + "vs/workbench/services/extensionManagement/common/extensionManagement", "vs/workbench/services/extensionManagement/common/extensionManagementService", "vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService", "vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService", @@ -30992,9 +33777,9 @@ "vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner", "vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost", "vs/workbench/services/extensions/electron-sandbox/nativeExtensionService", + "vs/workbench/services/files/electron-sandbox/diskFileSystemProvider", "vs/workbench/services/filesConfiguration/common/filesConfigurationService", "vs/workbench/services/history/browser/historyService", - "vs/workbench/services/hover/browser/hoverWidget", "vs/workbench/services/integrity/electron-sandbox/integrityService", "vs/workbench/services/issue/browser/issueTroubleshoot", "vs/workbench/services/keybinding/browser/keybindingService", @@ -31036,6 +33821,7 @@ "vs/workbench/services/themes/common/themeConfiguration", "vs/workbench/services/themes/common/themeExtensionPoints", "vs/workbench/services/themes/common/tokenClassificationExtensionPoint", + "vs/workbench/services/themes/electron-sandbox/themes.contribution", "vs/workbench/services/userDataProfile/browser/extensionsResource", "vs/workbench/services/userDataProfile/browser/globalStateResource", "vs/workbench/services/userDataProfile/browser/keybindingsResource", @@ -31049,6 +33835,8 @@ "vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService", "vs/workbench/services/userDataSync/common/userDataSync", "vs/workbench/services/views/browser/viewDescriptorService", + "vs/workbench/services/views/browser/viewsService", + "vs/workbench/services/views/common/viewContainerModel", "vs/workbench/services/workingCopy/common/fileWorkingCopyManager", "vs/workbench/services/workingCopy/common/storedFileWorkingCopy", "vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager", @@ -31079,30 +33867,34 @@ "vs/platform/extensionManagement/common/extensionManagement", "vs/platform/extensions/common/extensionValidator", "vs/platform/files/common/files", + "vs/platform/log/common/log", "vs/platform/markers/common/markers", + "vs/platform/request/common/request", "vs/platform/theme/common/iconRegistry", + "vs/platform/userDataProfile/common/userDataProfile", "vs/platform/workspace/common/workspace", - "vs/workbench/api/common/extHostChat", - "vs/workbench/api/common/extHostChatAgents2", "vs/workbench/api/common/extHostDiagnostics", "vs/workbench/api/common/extHostExtensionService", "vs/workbench/api/common/extHostLanguageFeatures", + "vs/workbench/api/common/extHostLanguageModels", "vs/workbench/api/common/extHostLogService", "vs/workbench/api/common/extHostNotebook", - "vs/workbench/api/common/extHostProgress", "vs/workbench/api/common/extHostStatusBar", "vs/workbench/api/common/extHostTelemetry", "vs/workbench/api/common/extHostTerminalService", "vs/workbench/api/common/extHostTreeViews", "vs/workbench/api/common/extHostTunnelService", "vs/workbench/api/common/extHostWorkspace", + "vs/workbench/common/configuration", "vs/workbench/common/editor", "vs/workbench/common/views", + "vs/workbench/contrib/chat/common/chatContextKeys", "vs/workbench/contrib/debug/common/abstractDebugAdapter", "vs/workbench/contrib/debug/common/debug", "vs/workbench/contrib/tasks/common/taskDefinitionRegistry", "vs/workbench/contrib/tasks/common/tasks", "vs/workbench/services/configurationResolver/common/variableResolver", + "vs/workbench/services/editor/common/editorResolverService", "vs/workbench/services/extensions/common/extensionsRegistry", "vs/workbench/services/search/common/queryBuilder" ], @@ -31126,6 +33918,7 @@ "vs/platform/contextkey/common/scanner", "vs/platform/environment/node/argv", "vs/platform/files/common/files", + "vs/platform/log/common/log", "vs/platform/terminal/node/ptyHostMain", "vs/platform/terminal/node/ptyService", "vs/platform/terminal/node/terminalProcess" @@ -31147,18 +33940,18 @@ "vs/platform/files/common/files", "vs/platform/files/common/io", "vs/platform/files/node/diskFileSystemProvider", + "vs/platform/log/common/log", "vs/platform/markers/common/markers", + "vs/platform/request/common/request", "vs/platform/theme/common/iconRegistry", "vs/platform/userDataProfile/common/userDataProfile", "vs/platform/workspace/common/workspace", - "vs/workbench/api/common/extHostChat", - "vs/workbench/api/common/extHostChatAgents2", "vs/workbench/api/common/extHostDiagnostics", "vs/workbench/api/common/extHostExtensionService", "vs/workbench/api/common/extHostLanguageFeatures", + "vs/workbench/api/common/extHostLanguageModels", "vs/workbench/api/common/extHostLogService", "vs/workbench/api/common/extHostNotebook", - "vs/workbench/api/common/extHostProgress", "vs/workbench/api/common/extHostStatusBar", "vs/workbench/api/common/extHostTelemetry", "vs/workbench/api/common/extHostTerminalService", @@ -31166,14 +33959,17 @@ "vs/workbench/api/common/extHostTunnelService", "vs/workbench/api/common/extHostWorkspace", "vs/workbench/api/node/extHostDebugService", + "vs/workbench/common/configuration", "vs/workbench/common/editor", "vs/workbench/common/views", + "vs/workbench/contrib/chat/common/chatContextKeys", "vs/workbench/contrib/debug/common/abstractDebugAdapter", "vs/workbench/contrib/debug/common/debug", "vs/workbench/contrib/debug/node/debugAdapter", "vs/workbench/contrib/tasks/common/taskDefinitionRegistry", "vs/workbench/contrib/tasks/common/tasks", "vs/workbench/services/configurationResolver/common/variableResolver", + "vs/workbench/services/editor/common/editorResolverService", "vs/workbench/services/extensions/common/extensionsRegistry", "vs/workbench/services/remote/common/tunnelModel", "vs/workbench/services/search/common/queryBuilder" @@ -31203,6 +33999,7 @@ "vs/platform/files/electron-main/diskFileSystemProviderServer", "vs/platform/files/node/diskFileSystemProvider", "vs/platform/issue/electron-main/issueMainService", + "vs/platform/log/common/log", "vs/platform/menubar/electron-main/menubar", "vs/platform/native/electron-main/nativeHostMainService", "vs/platform/request/common/request", @@ -31245,6 +34042,7 @@ "vs/platform/files/common/io", "vs/platform/files/node/diskFileSystemProvider", "vs/platform/languagePacks/common/languagePacks", + "vs/platform/log/common/log", "vs/platform/request/common/request", "vs/platform/shell/node/shellEnv", "vs/platform/telemetry/common/telemetryService", @@ -31281,6 +34079,7 @@ "vs/platform/files/common/io", "vs/platform/files/node/diskFileSystemProvider", "vs/platform/languagePacks/common/languagePacks", + "vs/platform/log/common/log", "vs/platform/remoteTunnel/common/remoteTunnel", "vs/platform/remoteTunnel/node/remoteTunnelService", "vs/platform/request/common/request", @@ -31301,7 +34100,6 @@ "vs/base/browser/ui/actionbar/actionViewItems", "vs/base/browser/ui/findinput/findInput", "vs/base/browser/ui/findinput/findInputToggles", - "vs/base/browser/ui/iconLabel/iconLabelHover", "vs/base/browser/ui/inputbox/inputBox", "vs/base/browser/ui/selectBox/selectBoxCustom", "vs/base/browser/ui/tree/abstractTree", diff --git a/packages/core/src/electron-browser/window/electron-window-preferences.ts b/packages/core/src/electron-browser/window/electron-window-preferences.ts index 1cc08cc11e5d7..d8addf1f7fb8c 100644 --- a/packages/core/src/electron-browser/window/electron-window-preferences.ts +++ b/packages/core/src/electron-browser/window/electron-window-preferences.ts @@ -38,7 +38,7 @@ export const electronWindowPreferencesSchema: PreferenceSchema = { 'maximum': ZoomLevel.MAX, 'scope': 'application', // eslint-disable-next-line max-len - 'description': nls.localizeByDefault('Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.') + 'description': nls.localizeByDefault("Adjust the default zoom level for all windows. Each increment above `0` (e.g. `1`) or below (e.g. `-1`) represents zooming `20%` larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity. See {0} for configuring if the 'Zoom In' and 'Zoom Out' commands apply the zoom level to all windows or only the active window.") }, 'window.titleBarStyle': { type: 'string', diff --git a/packages/editor/src/browser/editor-generated-preference-schema.ts b/packages/editor/src/browser/editor-generated-preference-schema.ts index 4f00af0a3071f..b75a3502b27a7 100644 --- a/packages/editor/src/browser/editor-generated-preference-schema.ts +++ b/packages/editor/src/browser/editor-generated-preference-schema.ts @@ -31,6 +31,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 4, "minimum": 1, "markdownDescription": nls.localize("theia/editor/editor.tabSize", "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), + "scope": "language-overridable", "restricted": false }, "editor.indentSize": { @@ -48,36 +49,42 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "tabSize", "markdownDescription": nls.localizeByDefault("The number of spaces used for indentation or `\"tabSize\"` to use the value from `#editor.tabSize#`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), + "scope": "language-overridable", "restricted": false }, "editor.insertSpaces": { "type": "boolean", "default": true, "markdownDescription": nls.localize("theia/editor/editor.insertSpaces", "Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), + "scope": "language-overridable", "restricted": false }, "editor.detectIndentation": { "type": "boolean", "default": true, "markdownDescription": nls.localize("theia/editor/editor.detectIndentation", "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents."), + "scope": "language-overridable", "restricted": false }, "editor.trimAutoWhitespace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Remove trailing auto inserted whitespace."), + "scope": "language-overridable", "restricted": false }, "editor.largeFileOptimizations": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Special handling for large files to disable certain memory intensive features."), + "scope": "language-overridable", "restricted": false }, "editor.wordBasedSuggestions": { "type": "boolean", "default": true, - "description": nls.localizeByDefault("Controls whether completions should be computed based on words in the document."), + "description": nls.localize("theia/editor/editor.wordBasedSuggestions", "Controls whether completions should be computed based on words in the document."), + "scope": "language-overridable", "restricted": false }, "editor.wordBasedSuggestionsMode": { @@ -92,7 +99,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Suggest words from all open documents of the same language."), nls.localizeByDefault("Suggest words from all open documents.") ], - "description": nls.localizeByDefault("Controls from which documents word based completions are computed."), + "description": nls.localize("theia/editor/editor.wordBasedSuggestionsMode", "Controls from which documents word based completions are computed."), + "scope": "language-overridable", "restricted": false }, "editor.semanticHighlighting.enabled": { @@ -108,18 +116,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "configuredByTheme", "description": nls.localizeByDefault("Controls whether the semanticHighlighting is shown for the languages that support it."), + "scope": "language-overridable", "restricted": false }, "editor.stablePeek": { "type": "boolean", "default": false, "markdownDescription": nls.localizeByDefault("Keep peek editors open even when double-clicking their content or when hitting `Escape`."), + "scope": "language-overridable", "restricted": false }, "editor.maxTokenizationLineLength": { "type": "integer", "default": 20000, "description": nls.localizeByDefault("Lines above this length will not be tokenized for performance reasons"), + "scope": "language-overridable", "restricted": false }, "editor.experimental.asyncTokenization": { @@ -129,12 +140,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "tags": [ "experimental" ], + "scope": "language-overridable", "restricted": false }, "editor.experimental.asyncTokenizationLogging": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether async tokenization should be logged. For debugging only."), + "scope": "language-overridable", "restricted": false }, "editor.experimental.asyncTokenizationVerification": { @@ -144,6 +157,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "tags": [ "experimental" ], + "scope": "language-overridable", "restricted": false }, "editor.language.brackets": { @@ -166,6 +180,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] } ] }, + "scope": "language-overridable", "restricted": false }, "editor.language.colorizedBracketPairs": { @@ -188,60 +203,70 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] } ] }, + "scope": "language-overridable", "restricted": false }, "diffEditor.maxComputationTime": { "type": "number", "default": 5000, "description": nls.localizeByDefault("Timeout in milliseconds after which diff computation is cancelled. Use 0 for no timeout."), + "scope": "language-overridable", "restricted": false }, "diffEditor.maxFileSize": { "type": "number", "default": 50, "description": nls.localizeByDefault("Maximum file size in MB for which to compute diffs. Use 0 for no limit."), + "scope": "language-overridable", "restricted": false }, "diffEditor.renderSideBySide": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the diff editor shows the diff side by side or inline."), + "scope": "language-overridable", "restricted": false }, "diffEditor.renderSideBySideInlineBreakpoint": { "type": "number", "default": 900, "description": nls.localizeByDefault("If the diff editor width is smaller than this value, the inline view is used."), + "scope": "language-overridable", "restricted": false }, "diffEditor.useInlineViewWhenSpaceIsLimited": { "type": "boolean", "default": true, "description": nls.localizeByDefault("If enabled and the editor width is too small, the inline view is used."), + "scope": "language-overridable", "restricted": false }, "diffEditor.renderMarginRevertIcon": { "type": "boolean", "default": true, "description": nls.localizeByDefault("When enabled, the diff editor shows arrows in its glyph margin to revert changes."), + "scope": "language-overridable", "restricted": false }, "diffEditor.ignoreTrimWhitespace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("When enabled, the diff editor ignores changes in leading or trailing whitespace."), + "scope": "language-overridable", "restricted": false }, "diffEditor.renderIndicators": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the diff editor shows +/- indicators for added/removed changes."), + "scope": "language-overridable", "restricted": false }, "diffEditor.codeLens": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether the editor shows CodeLens."), + "scope": "language-overridable", "restricted": false }, "diffEditor.wordWrap": { @@ -257,6 +282,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Lines will wrap at the viewport width."), nls.localize("theia/editor/diffEditor.wordWrap2", "Lines will wrap according to the `#editor.wordWrap#` setting.") ], + "scope": "language-overridable", "restricted": false }, "diffEditor.diffAlgorithm": { @@ -273,12 +299,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "tags": [ "experimental" ], + "scope": "language-overridable", "restricted": false }, "diffEditor.hideUnchangedRegions.enabled": { "type": "boolean", "default": false, "markdownDescription": nls.localizeByDefault("Controls whether the diff editor shows unchanged regions."), + "scope": "language-overridable", "restricted": false }, "diffEditor.hideUnchangedRegions.revealLineCount": { @@ -286,6 +314,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 20, "markdownDescription": nls.localizeByDefault("Controls how many lines are used for unchanged regions."), "minimum": 1, + "scope": "language-overridable", "restricted": false }, "diffEditor.hideUnchangedRegions.minimumLineCount": { @@ -293,6 +322,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 3, "markdownDescription": nls.localizeByDefault("Controls how many lines are used as a minimum for unchanged regions."), "minimum": 1, + "scope": "language-overridable", "restricted": false }, "diffEditor.hideUnchangedRegions.contextLineCount": { @@ -300,24 +330,28 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 3, "markdownDescription": nls.localizeByDefault("Controls how many lines are used as context when comparing unchanged regions."), "minimum": 1, + "scope": "language-overridable", "restricted": false }, "diffEditor.experimental.showMoves": { "type": "boolean", "default": false, "markdownDescription": nls.localizeByDefault("Controls whether the diff editor should show detected code moves."), + "scope": "language-overridable", "restricted": false }, "diffEditor.experimental.showEmptyDecorations": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the diff editor shows empty decorations to see where characters got inserted or deleted."), + "scope": "language-overridable", "restricted": false }, "editor.acceptSuggestionOnCommitCharacter": { "markdownDescription": nls.localizeByDefault("Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.acceptSuggestionOnEnter": { @@ -334,6 +368,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "off" ], "default": "on", + "scope": "language-overridable", "restricted": false }, "editor.accessibilitySupport": { @@ -353,6 +388,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "accessibility" ], "description": nls.localizeByDefault("Controls if the UI should run in a mode where it is optimized for screen readers."), + "scope": "language-overridable", "restricted": false }, "editor.accessibilityPageSize": { @@ -364,6 +400,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 10, "minimum": 1, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.autoClosingBrackets": { @@ -382,6 +419,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", + "scope": "language-overridable", "restricted": false }, "editor.autoClosingComments": { @@ -400,6 +438,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", + "scope": "language-overridable", "restricted": false }, "editor.screenReaderAnnounceInlineSuggestion": { @@ -409,6 +448,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.autoClosingDelete": { @@ -425,6 +465,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "auto", + "scope": "language-overridable", "restricted": false }, "editor.autoClosingOvertype": { @@ -441,6 +482,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "auto", + "scope": "language-overridable", "restricted": false }, "editor.autoClosingQuotes": { @@ -459,6 +501,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", + "scope": "language-overridable", "restricted": false }, "editor.autoIndent": { @@ -479,6 +522,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "full" ], "default": "full", + "scope": "language-overridable", "restricted": false }, "editor.autoSurround": { @@ -497,18 +541,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "languageDefined", + "scope": "language-overridable", "restricted": false }, "editor.bracketPairColorization.enabled": { "type": "boolean", "default": true, "markdownDescription": nls.localize("theia/editor/editor.bracketPairColorization.enabled", "Controls whether bracket pair colorization is enabled or not. Use `#workbench.colorCustomizations#` to override the bracket highlight colors."), + "scope": "language-overridable", "restricted": false }, "editor.bracketPairColorization.independentColorPoolPerBracketType": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether each bracket type has its own independent color pool."), + "scope": "language-overridable", "restricted": false }, "editor.guides.bracketPairs": { @@ -528,6 +575,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": false, "description": nls.localizeByDefault("Controls whether bracket pair guides are enabled or not."), + "scope": "language-overridable", "restricted": false }, "editor.guides.bracketPairsHorizontal": { @@ -547,18 +595,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "active", "description": nls.localizeByDefault("Controls whether horizontal bracket pair guides are enabled or not."), + "scope": "language-overridable", "restricted": false }, "editor.guides.highlightActiveBracketPair": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the editor should highlight the active bracket pair."), + "scope": "language-overridable", "restricted": false }, "editor.guides.indentation": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the editor should render indent guides."), + "scope": "language-overridable", "restricted": false }, "editor.guides.highlightActiveIndentation": { @@ -578,18 +629,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": true, "description": nls.localizeByDefault("Controls whether the editor should highlight the active indent guide."), + "scope": "language-overridable", "restricted": false }, "editor.codeLens": { "description": nls.localizeByDefault("Controls whether the editor shows CodeLens."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.codeLensFontFamily": { "description": nls.localizeByDefault("Controls the font family for CodeLens."), "type": "string", "default": "", + "scope": "language-overridable", "restricted": false }, "editor.codeLensFontSize": { @@ -598,12 +652,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 100, "markdownDescription": nls.localizeByDefault("Controls the font size in pixels for CodeLens. When set to 0, 90% of `#editor.fontSize#` is used."), + "scope": "language-overridable", "restricted": false }, "editor.colorDecorators": { "description": nls.localizeByDefault("Controls whether the editor should render the inline color decorators and color picker."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.colorDecoratorsLimit": { @@ -612,30 +668,35 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 500, "minimum": 1, "maximum": 1000000, + "scope": "language-overridable", "restricted": false }, "editor.columnSelection": { "description": nls.localizeByDefault("Enable that the selection with the mouse and keys is doing column selection."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.comments.insertSpace": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether a space character is inserted when commenting."), + "scope": "language-overridable", "restricted": false }, "editor.comments.ignoreEmptyLines": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls if empty lines should be ignored with toggle, add or remove actions for line comments."), + "scope": "language-overridable", "restricted": false }, "editor.copyWithSyntaxHighlighting": { "description": nls.localizeByDefault("Controls whether syntax highlighting should be copied into the clipboard."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.cursorBlinking": { @@ -649,6 +710,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "solid" ], "default": "blink", + "scope": "language-overridable", "restricted": false }, "editor.cursorSmoothCaretAnimation": { @@ -665,6 +727,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "on" ], "default": "off", + "scope": "language-overridable", "restricted": false }, "editor.cursorStyle": { @@ -679,6 +742,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "underline-thin" ], "default": "line", + "scope": "language-overridable", "restricted": false }, "editor.cursorSurroundingLines": { @@ -687,6 +751,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.cursorSurroundingLinesStyle": { @@ -694,13 +759,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("`cursorSurroundingLines` is enforced only when triggered via the keyboard or API."), nls.localizeByDefault("`cursorSurroundingLines` is enforced always.") ], - "markdownDescription": nls.localizeByDefault("Controls when `#cursorSurroundingLines#` should be enforced."), + "markdownDescription": nls.localize("theia/editor/editor.cursorSurroundingLinesStyle", "Controls when `#cursorSurroundingLines#` should be enforced."), "type": "string", "enum": [ "default", "all" ], "default": "default", + "scope": "language-overridable", "restricted": false }, "editor.cursorWidth": { @@ -709,18 +775,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.dragAndDrop": { "description": nls.localizeByDefault("Controls whether the editor should allow moving selections via drag and drop."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.dropIntoEditor.enabled": { "type": "boolean", "default": true, - "markdownDescription": nls.localizeByDefault("Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor)."), + "markdownDescription": nls.localize("theia/editor/editor.dropIntoEditor.enabled", "Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor)."), + "scope": "language-overridable", "restricted": false }, "editor.dropIntoEditor.showDropSelector": { @@ -735,12 +804,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Never show the drop selector widget. Instead the default drop provider is always used.") ], "default": "afterDrop", + "scope": "language-overridable", "restricted": false }, "editor.emptySelectionClipboard": { "description": nls.localizeByDefault("Controls whether copying without a selection copies the current line."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.experimentalWhitespaceRendering": { @@ -757,18 +828,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "off" ], "default": "svg", + "scope": "language-overridable", "restricted": false }, "editor.fastScrollSensitivity": { "markdownDescription": nls.localizeByDefault("Scrolling speed multiplier when pressing `Alt`."), "type": "number", "default": 5, + "scope": "language-overridable", "restricted": false }, "editor.find.cursorMoveOnType": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the cursor should jump to find matches while typing."), + "scope": "language-overridable", "restricted": false }, "editor.find.seedSearchStringFromSelection": { @@ -785,6 +859,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Only seed search string from the editor selection.") ], "description": nls.localizeByDefault("Controls whether the search string in the Find Widget is seeded from the editor selection."), + "scope": "language-overridable", "restricted": false }, "editor.find.autoFindInSelection": { @@ -801,24 +876,28 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Turn on Find in Selection automatically when multiple lines of content are selected.") ], "description": nls.localizeByDefault("Controls the condition for turning on Find in Selection automatically."), + "scope": "language-overridable", "restricted": false }, "editor.find.addExtraSpaceOnTop": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the Find Widget should add extra lines on top of the editor. When true, you can scroll beyond the first line when the Find Widget is visible."), + "scope": "language-overridable", "restricted": false }, "editor.find.loop": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the search automatically restarts from the beginning (or the end) when no further matches can be found."), + "scope": "language-overridable", "restricted": false }, "editor.folding": { "description": nls.localizeByDefault("Controls whether the editor has code folding enabled."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.foldingStrategy": { @@ -833,18 +912,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "indentation" ], "default": "auto", + "scope": "language-overridable", "restricted": false }, "editor.foldingHighlight": { "description": nls.localizeByDefault("Controls whether the editor should highlight folded ranges."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.foldingImportsByDefault": { "description": nls.localizeByDefault("Controls whether the editor automatically collapses import ranges."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.foldingMaximumRegions": { @@ -853,18 +935,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 5000, "minimum": 10, "maximum": 65000, + "scope": "language-overridable", "restricted": false }, "editor.unfoldOnClickAfterEndOfLine": { "description": nls.localizeByDefault("Controls whether clicking on the empty content after a folded line will unfold the line."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.fontFamily": { "description": nls.localizeByDefault("Controls the font family."), "type": "string", "default": isOSX ? 'Menlo, Monaco, \'Courier New\', monospace' : isWindows ? 'Consolas, \'Courier New\', monospace' : '\'Droid Sans Mono\', \'monospace\', monospace', + "scope": "language-overridable", "restricted": false }, "editor.fontLigatures": { @@ -880,6 +965,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "description": nls.localizeByDefault("Configures font ligatures or font features. Can be either a boolean to enable/disable ligatures or a string for the value of the CSS 'font-feature-settings' property."), "default": false, + "scope": "language-overridable", "restricted": false }, "editor.fontSize": { @@ -888,6 +974,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "maximum": 100, "default": isOSX ? 12 : 14, "description": nls.localizeByDefault("Controls the font size in pixels."), + "scope": "language-overridable", "restricted": false }, "editor.fontWeight": { @@ -920,6 +1007,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "normal", "description": nls.localizeByDefault("Controls the font weight. Accepts \"normal\" and \"bold\" keywords or numbers between 1 and 1000."), + "scope": "language-overridable", "restricted": false }, "editor.fontVariations": { @@ -935,29 +1023,34 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "description": nls.localizeByDefault("Configures font variations. Can be either a boolean to enable/disable the translation from font-weight to font-variation-settings or a string for the value of the CSS 'font-variation-settings' property."), "default": false, + "scope": "language-overridable", "restricted": false }, "editor.formatOnPaste": { "description": nls.localizeByDefault("Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.formatOnType": { "description": nls.localizeByDefault("Controls whether the editor should automatically format the line after typing."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.glyphMargin": { "description": nls.localizeByDefault("Controls whether the editor should render the vertical glyph margin. Glyph margin is mostly used for debugging."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multiple": { "deprecationMessage": "This setting is deprecated, please use separate settings like 'editor.editor.gotoLocation.multipleDefinitions' or 'editor.editor.gotoLocation.multipleImplementations' instead.", "default": null, + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleDefinitions": { @@ -974,6 +1067,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Go to the primary result and show a Peek view"), nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleTypeDefinitions": { @@ -990,6 +1084,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Go to the primary result and show a Peek view"), nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleDeclarations": { @@ -1006,6 +1101,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Go to the primary result and show a Peek view"), nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleImplementations": { @@ -1022,6 +1118,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Go to the primary result and show a Peek view"), nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.multipleReferences": { @@ -1038,6 +1135,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Go to the primary result and show a Peek view"), nls.localizeByDefault("Go to the primary result and enable Peek-less navigation to others") ], + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeDefinitionCommand": { @@ -1058,6 +1156,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Definition' is the current location."), + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeTypeDefinitionCommand": { @@ -1078,6 +1177,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Type Definition' is the current location."), + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeDeclarationCommand": { @@ -1098,6 +1198,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Declaration' is the current location."), + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeImplementationCommand": { @@ -1118,6 +1219,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Implementation' is the current location."), + "scope": "language-overridable", "restricted": false }, "editor.gotoLocation.alternativeReferenceCommand": { @@ -1138,18 +1240,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor.action.revealDefinition" ], "description": nls.localizeByDefault("Alternative command id that is being executed when the result of 'Go to Reference' is the current location."), + "scope": "language-overridable", "restricted": false }, "editor.hideCursorInOverviewRuler": { "description": nls.localizeByDefault("Controls whether the cursor should be hidden in the overview ruler."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.hover.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the hover is shown."), + "scope": "language-overridable", "restricted": false }, "editor.hover.delay": { @@ -1158,12 +1263,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 10000, "description": nls.localizeByDefault("Controls the delay in milliseconds after which the hover is shown."), + "scope": "language-overridable", "restricted": false }, "editor.hover.sticky": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the hover should remain visible when mouse is moved over it."), + "scope": "language-overridable", "restricted": false }, "editor.hover.hidingDelay": { @@ -1171,18 +1278,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "default": 300, "description": nls.localize("theia/editor/editor.hover.hidingDelay", "Controls the delay in milliseconds after thich the hover is hidden. Requires `editor.hover.sticky` to be enabled."), + "scope": "language-overridable", "restricted": false }, "editor.hover.above": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Prefer showing hovers above the line, if there's space."), + "scope": "language-overridable", "restricted": false }, "editor.inlineSuggest.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether to automatically show inline suggestions in the editor."), + "scope": "language-overridable", "restricted": false }, "editor.inlineSuggest.showToolbar": { @@ -1197,30 +1307,35 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Show the inline suggestion toolbar when hovering over an inline suggestion.") ], "description": nls.localizeByDefault("Controls when to show the inline suggestion toolbar."), + "scope": "language-overridable", "restricted": false }, "editor.inlineSuggest.suppressSuggestions": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls how inline suggestions interact with the suggest widget. If enabled, the suggest widget is not shown automatically when inline suggestions are available."), + "scope": "language-overridable", "restricted": false }, "editor.letterSpacing": { "description": nls.localizeByDefault("Controls the letter spacing in pixels."), "type": "number", "default": 0, + "scope": "language-overridable", "restricted": false }, "editor.lightbulb.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Enables the Code Action lightbulb in the editor."), + "scope": "language-overridable", "restricted": false }, "editor.lineHeight": { "markdownDescription": nls.localizeByDefault("Controls the line height. \n - Use 0 to automatically compute the line height from the font size.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values."), "type": "number", "default": 0, + "scope": "language-overridable", "restricted": false }, "editor.lineNumbers": { @@ -1239,18 +1354,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "on", "description": nls.localizeByDefault("Controls the display of line numbers."), + "scope": "language-overridable", "restricted": false }, "editor.linkedEditing": { "description": nls.localizeByDefault("Controls whether the editor has linked editing enabled. Depending on the language, related symbols such as HTML tags, are updated while editing."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.links": { "description": nls.localizeByDefault("Controls whether the editor should detect links and make them clickable."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.matchBrackets": { @@ -1262,18 +1380,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "never" ], "default": "always", + "scope": "language-overridable", "restricted": false }, "editor.minimap.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the minimap is shown."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.autohide": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether the minimap is hidden automatically."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.size": { @@ -1290,6 +1411,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "proportional", "description": nls.localizeByDefault("Controls the size of the minimap."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.side": { @@ -1300,6 +1422,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "right", "description": nls.localizeByDefault("Controls the side where to render the minimap."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.showSlider": { @@ -1310,6 +1433,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "mouseover", "description": nls.localizeByDefault("Controls when the minimap slider is shown."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.scale": { @@ -1323,36 +1447,42 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] 3 ], "description": nls.localizeByDefault("Scale of content drawn in the minimap: 1, 2 or 3."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.renderCharacters": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Render the actual characters on a line as opposed to color blocks."), + "scope": "language-overridable", "restricted": false }, "editor.minimap.maxColumn": { "type": "number", "default": 120, "description": nls.localizeByDefault("Limit the width of the minimap to render at most a certain number of columns."), + "scope": "language-overridable", "restricted": false }, "editor.mouseWheelScrollSensitivity": { "markdownDescription": nls.localizeByDefault("A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events."), "type": "number", "default": 1, + "scope": "language-overridable", "restricted": false }, "editor.mouseWheelZoom": { "markdownDescription": nls.localizeByDefault("Zoom the font of the editor when using mouse wheel and holding `Ctrl`."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.multiCursorMergeOverlapping": { "description": nls.localizeByDefault("Merge multiple cursors when they are overlapping."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.multiCursorModifier": { @@ -1367,6 +1497,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "alt" ], "default": "alt", + "scope": "language-overridable", "restricted": false }, "editor.multiCursorPaste": { @@ -1381,6 +1512,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "full" ], "default": "spread", + "scope": "language-overridable", "restricted": false }, "editor.multiCursorLimit": { @@ -1389,18 +1521,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 10000, "minimum": 1, "maximum": 100000, + "scope": "language-overridable", "restricted": false }, "editor.occurrencesHighlight": { - "description": nls.localizeByDefault("Controls whether the editor should highlight semantic symbol occurrences."), + "description": nls.localize("theia/editor/editor.occurrencesHighlight", "Controls whether the editor should highlight semantic symbol occurrences."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.overviewRulerBorder": { "description": nls.localizeByDefault("Controls whether a border should be drawn around the overview ruler."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.padding.top": { @@ -1409,6 +1544,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 1000, "description": nls.localizeByDefault("Controls the amount of space between the top edge of the editor and the first line."), + "scope": "language-overridable", "restricted": false }, "editor.padding.bottom": { @@ -1417,12 +1553,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 0, "maximum": 1000, "description": nls.localizeByDefault("Controls the amount of space between the bottom edge of the editor and the last line."), + "scope": "language-overridable", "restricted": false }, "editor.pasteAs.enabled": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("Controls whether you can paste content in different ways."), + "scope": "language-overridable", "restricted": false }, "editor.pasteAs.showPasteSelector": { @@ -1437,18 +1575,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localizeByDefault("Never show the paste selector widget. Instead the default pasting behavior is always used.") ], "default": "afterPaste", + "scope": "language-overridable", "restricted": false }, "editor.parameterHints.enabled": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Enables a pop-up that shows parameter documentation and type information as you type."), + "scope": "language-overridable", "restricted": false }, "editor.parameterHints.cycle": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether the parameter hints menu cycles or closes when reaching the end of the list."), + "scope": "language-overridable", "restricted": false }, "editor.peekWidgetDefaultFocus": { @@ -1463,12 +1604,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "editor" ], "default": "tree", + "scope": "language-overridable", "restricted": false }, "editor.definitionLinkOpensInPeek": { "description": nls.localizeByDefault("Controls whether the Go to Definition mouse gesture always opens the peek widget."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.quickSuggestions": { @@ -1548,6 +1691,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "strings": "off" }, "markdownDescription": nls.localize("theia/editor/editor.quickSuggestions", "Controls whether suggestions should automatically show up while typing. This can be controlled for typing in comments, strings, and other code. Quick suggestion can be configured to show as ghost text or with the suggest widget. Also be aware of the '#editor.suggestOnTriggerCharacters#'-setting which controls if suggestions are triggered by special characters."), + "scope": "language-overridable", "restricted": false }, "editor.quickSuggestionsDelay": { @@ -1556,6 +1700,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 10, "minimum": 0, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.renameOnType": { @@ -1563,6 +1708,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "markdownDeprecationMessage": "Deprecated, use `editor.linkedEditing` instead.", "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false, "deprecationMessage": "Deprecated, use `editor.linkedEditing` instead." }, @@ -1570,7 +1716,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "description": nls.localizeByDefault("Controls whether the editor should render control characters."), "restricted": true, "type": "boolean", - "default": true + "default": true, + "scope": "language-overridable" }, "editor.renderFinalNewline": { "description": nls.localizeByDefault("Render last line number when the file ends with a newline."), @@ -1581,6 +1728,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "dimmed" ], "default": "on", + "scope": "language-overridable", "restricted": false }, "editor.renderLineHighlight": { @@ -1599,12 +1747,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "all" ], "default": "line", + "scope": "language-overridable", "restricted": false }, "editor.renderLineHighlightOnlyWhenFocus": { "description": nls.localizeByDefault("Controls if the editor should render the current line highlight only when the editor is focused."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.renderWhitespace": { @@ -1625,12 +1775,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "all" ], "default": "selection", + "scope": "language-overridable", "restricted": false }, "editor.roundedSelection": { "description": nls.localizeByDefault("Controls whether selections should have rounded corners."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.rulers": { @@ -1661,6 +1813,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] }, "default": [], "description": nls.localizeByDefault("Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty."), + "scope": "language-overridable", "restricted": false }, "editor.scrollbar.vertical": { @@ -1677,6 +1830,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "auto", "description": nls.localizeByDefault("Controls the visibility of the vertical scrollbar."), + "scope": "language-overridable", "restricted": false }, "editor.scrollbar.horizontal": { @@ -1693,24 +1847,28 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "auto", "description": nls.localizeByDefault("Controls the visibility of the horizontal scrollbar."), + "scope": "language-overridable", "restricted": false }, "editor.scrollbar.verticalScrollbarSize": { "type": "number", "default": 14, "description": nls.localizeByDefault("The width of the vertical scrollbar."), + "scope": "language-overridable", "restricted": false }, "editor.scrollbar.horizontalScrollbarSize": { "type": "number", "default": 12, "description": nls.localizeByDefault("The height of the horizontal scrollbar."), + "scope": "language-overridable", "restricted": false }, "editor.scrollbar.scrollByPage": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether clicks scroll by page or jump to click position."), + "scope": "language-overridable", "restricted": false }, "editor.scrollBeyondLastColumn": { @@ -1719,24 +1877,28 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 4, "minimum": 0, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.scrollBeyondLastLine": { "description": nls.localizeByDefault("Controls whether the editor will scroll beyond the last line."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.scrollPredominantAxis": { "description": nls.localizeByDefault("Scroll only along the predominant axis when scrolling both vertically and horizontally at the same time. Prevents horizontal drift when scrolling vertically on a trackpad."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.selectionHighlight": { "description": nls.localizeByDefault("Controls whether the editor should highlight matches similar to the selection."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.showFoldingControls": { @@ -1753,12 +1915,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "mouseover" ], "default": "mouseover", + "scope": "language-overridable", "restricted": false }, "editor.showUnused": { "description": nls.localizeByDefault("Controls fading out of unused code."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.snippetSuggestions": { @@ -1777,30 +1941,35 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "none" ], "default": "inline", + "scope": "language-overridable", "restricted": false }, "editor.smartSelect.selectLeadingAndTrailingWhitespace": { "description": nls.localizeByDefault("Whether leading and trailing whitespace should always be selected."), "default": true, "type": "boolean", + "scope": "language-overridable", "restricted": false }, "editor.smartSelect.selectSubwords": { "description": nls.localizeByDefault("Whether subwords (like 'foo' in 'fooBar' or 'foo_bar') should be selected."), "default": true, "type": "boolean", + "scope": "language-overridable", "restricted": false }, "editor.smoothScrolling": { "description": nls.localizeByDefault("Controls whether the editor will scroll using an animation."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.enabled": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Shows the nested current scopes during the scroll at the top of the editor."), + "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.maxLineCount": { @@ -1809,6 +1978,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "minimum": 1, "maximum": 10, "description": nls.localizeByDefault("Defines the maximum number of sticky lines to show."), + "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.defaultModel": { @@ -1820,18 +1990,21 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "outlineModel", "description": nls.localizeByDefault("Defines the model to use for determining which lines to stick. If the outline model does not exist, it will fall back on the folding provider model which falls back on the indentation model. This order is respected in all three cases."), + "scope": "language-overridable", "restricted": false }, "editor.stickyScroll.scrollWithEditor": { "type": "boolean", "default": true, "description": nls.localize("theia/editor/editor.stickyScroll.scrollWithEditor", "Enable scrolling of the sticky scroll widget with the editor's horizontal scrollbar."), + "scope": "language-overridable", "restricted": false }, "editor.stickyTabStops": { "description": nls.localizeByDefault("Emulate selection behavior of tab characters when using spaces for indentation. Selection will stick to tab stops."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.suggest.insertMode": { @@ -1846,24 +2019,28 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "insert", "description": nls.localizeByDefault("Controls whether words are overwritten when accepting completions. Note that this depends on extensions opting into this feature."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.filterGraceful": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether filtering and sorting suggestions accounts for small typos."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.localityBonus": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether sorting favors words that appear close to the cursor."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.shareSuggestSelections": { "type": "boolean", "default": false, "markdownDescription": nls.localizeByDefault("Controls whether remembered suggestion selections are shared between multiple workspaces and windows (needs `#editor.suggestSelection#`)."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.selectionMode": { @@ -1882,228 +2059,266 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "always", "markdownDescription": nls.localizeByDefault("Controls whether a suggestion is selected when the widget shows. Note that this only applies to automatically triggered suggestions (`#editor.quickSuggestions#` and `#editor.suggestOnTriggerCharacters#`) and that a suggestion is always selected when explicitly invoked, e.g via `Ctrl+Space`."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.snippetsPreventQuickSuggestions": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether an active snippet prevents quick suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showIcons": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether to show or hide icons in suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showStatusBar": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls the visibility of the status bar at the bottom of the suggest widget."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.preview": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Controls whether to preview the suggestion outcome in the editor."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showInlineDetails": { "type": "boolean", "default": true, "description": nls.localizeByDefault("Controls whether suggest details show inline with the label or only in the details widget."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.maxVisibleSuggestions": { "type": "number", "deprecationMessage": "This setting is deprecated. The suggest widget can now be resized.", "default": 0, + "scope": "language-overridable", "restricted": false }, "editor.suggest.filteredTypes": { "type": "object", "deprecationMessage": "This setting is deprecated, please use separate settings like 'editor.suggest.showKeywords' or 'editor.suggest.showSnippets' instead.", "default": {}, + "scope": "language-overridable", "restricted": false }, "editor.suggest.showMethods": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `method`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showFunctions": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `function`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showConstructors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `constructor`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showDeprecated": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `deprecated`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.matchOnWordStartOnly": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense filtering requires that the first character matches on a word start. For example, `c` on `Console` or `WebContext` but _not_ on `description`. When disabled IntelliSense will show more results but still sorts them by match quality."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showFields": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `field`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showVariables": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `variable`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showClasses": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `class`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showStructs": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `struct`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showInterfaces": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `interface`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showModules": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `module`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showProperties": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `property`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showEvents": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `event`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showOperators": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `operator`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showUnits": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `unit`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showValues": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `value`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showConstants": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `constant`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showEnums": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `enum`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showEnumMembers": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `enumMember`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showKeywords": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `keyword`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showWords": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `text`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showColors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `color`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showFiles": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `file`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showReferences": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `reference`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showCustomcolors": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `customcolor`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showFolders": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `folder`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showTypeParameters": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `typeParameter`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showSnippets": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `snippet`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showUsers": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `user`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggest.showIssues": { "type": "boolean", "default": true, "markdownDescription": nls.localizeByDefault("When enabled IntelliSense shows `issues`-suggestions."), + "scope": "language-overridable", "restricted": false }, "editor.suggestFontSize": { @@ -2112,6 +2327,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 1000, + "scope": "language-overridable", "restricted": false }, "editor.suggestLineHeight": { @@ -2120,12 +2336,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 0, "minimum": 0, "maximum": 1000, + "scope": "language-overridable", "restricted": false }, "editor.suggestOnTriggerCharacters": { "description": nls.localizeByDefault("Controls whether suggestions should automatically show up when typing trigger characters."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.suggestSelection": { @@ -2142,6 +2360,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "recentlyUsedByPrefix" ], "default": "first", + "scope": "language-overridable", "restricted": false }, "editor.tabCompletion": { @@ -2158,6 +2377,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "onlySnippets" ], "default": "off", + "scope": "language-overridable", "restricted": false }, "editor.unicodeHighlight.nonBasicASCII": { @@ -2172,19 +2392,22 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": "inUntrustedWorkspace", - "description": nls.localizeByDefault("Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII.") + "description": nls.localizeByDefault("Controls whether all non-basic ASCII characters are highlighted. Only characters between U+0020 and U+007E, tab, line-feed and carriage-return are considered basic ASCII."), + "scope": "language-overridable" }, "editor.unicodeHighlight.invisibleCharacters": { "restricted": true, "type": "boolean", "default": true, - "description": nls.localizeByDefault("Controls whether characters that just reserve space or have no width at all are highlighted.") + "description": nls.localizeByDefault("Controls whether characters that just reserve space or have no width at all are highlighted."), + "scope": "language-overridable" }, "editor.unicodeHighlight.ambiguousCharacters": { "restricted": true, "type": "boolean", "default": true, - "description": nls.localizeByDefault("Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale.") + "description": nls.localizeByDefault("Controls whether characters are highlighted that can be confused with basic ASCII characters, except those that are common in the current user locale."), + "scope": "language-overridable" }, "editor.unicodeHighlight.includeComments": { "restricted": true, @@ -2198,7 +2421,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": "inUntrustedWorkspace", - "description": nls.localizeByDefault("Controls whether characters in comments should also be subject to Unicode highlighting.") + "description": nls.localizeByDefault("Controls whether characters in comments should also be subject to Unicode highlighting."), + "scope": "language-overridable" }, "editor.unicodeHighlight.includeStrings": { "restricted": true, @@ -2212,7 +2436,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "inUntrustedWorkspace" ], "default": true, - "description": nls.localizeByDefault("Controls whether characters in strings should also be subject to Unicode highlighting.") + "description": nls.localizeByDefault("Controls whether characters in strings should also be subject to Unicode highlighting."), + "scope": "language-overridable" }, "editor.unicodeHighlight.allowedCharacters": { "restricted": true, @@ -2221,7 +2446,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "description": nls.localizeByDefault("Defines allowed characters that are not being highlighted."), "additionalProperties": { "type": "boolean" - } + }, + "scope": "language-overridable" }, "editor.unicodeHighlight.allowedLocales": { "restricted": true, @@ -2233,7 +2459,8 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "_os": true, "_vscode": true }, - "description": nls.localizeByDefault("Unicode characters that are common in allowed locales are not being highlighted.") + "description": nls.localizeByDefault("Unicode characters that are common in allowed locales are not being highlighted."), + "scope": "language-overridable" }, "editor.unusualLineTerminators": { "enumDescriptions": [ @@ -2249,12 +2476,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "prompt" ], "default": "prompt", + "scope": "language-overridable", "restricted": false }, "editor.useTabStops": { - "description": nls.localizeByDefault("Inserting and deleting whitespace follows tab stops."), + "description": nls.localize("theia/editor/editor.useTabStops", "Inserting and deleting whitespace follows tab stops."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.wordBreak": { @@ -2269,12 +2498,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "keepAll" ], "default": "normal", + "scope": "language-overridable", "restricted": false }, "editor.wordSeparators": { "description": nls.localizeByDefault("Characters that will be used as word separators when doing word related navigations or operations."), "type": "string", "default": "`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?", + "scope": "language-overridable", "restricted": false }, "editor.wordWrap": { @@ -2293,6 +2524,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "bounded" ], "default": "off", + "scope": "language-overridable", "restricted": false }, "editor.wordWrapColumn": { @@ -2301,6 +2533,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "default": 80, "minimum": 1, "maximum": 1073741824, + "scope": "language-overridable", "restricted": false }, "editor.wrappingIndent": { @@ -2319,6 +2552,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "description": nls.localizeByDefault("Controls the indentation of wrapped lines."), "default": "same", + "scope": "language-overridable", "restricted": false }, "editor.wrappingStrategy": { @@ -2333,12 +2567,14 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] ], "default": "simple", "description": nls.localizeByDefault("Controls the algorithm that computes wrapping points. Note that when in accessibility mode, advanced will be used for the best experience."), + "scope": "language-overridable", "restricted": false }, "editor.showDeprecated": { "description": nls.localizeByDefault("Controls strikethrough deprecated variables."), "type": "boolean", "default": true, + "scope": "language-overridable", "restricted": false }, "editor.inlayHints.enabled": { @@ -2357,36 +2593,42 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] nls.localize("theia/editor/editor.inlayHints.enabled2", "Inlay hints are hidden by default and show when holding Ctrl+Alt"), nls.localizeByDefault("Inlay hints are disabled") ], + "scope": "language-overridable", "restricted": false }, "editor.inlayHints.fontSize": { "type": "number", "default": 0, "markdownDescription": nls.localize("theia/editor/editor.inlayHints.fontSize", "Controls font size of inlay hints in the editor. As default the `#editor.fontSize#` is used when the configured value is less than `5` or greater than the editor font size."), + "scope": "language-overridable", "restricted": false }, "editor.inlayHints.fontFamily": { "type": "string", "default": "", "markdownDescription": nls.localize("theia/editor/editor.inlayHints.fontFamily", "Controls font family of inlay hints in the editor. When set to empty, the `#editor.fontFamily#` is used."), + "scope": "language-overridable", "restricted": false }, "editor.inlayHints.padding": { "type": "boolean", "default": false, "description": nls.localizeByDefault("Enables the padding around the inlay hints in the editor."), + "scope": "language-overridable", "restricted": false }, "editor.tabFocusMode": { "markdownDescription": nls.localizeByDefault("Controls whether the editor receives tabs or defers them to the workbench for navigation."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.defaultColorDecorators": { "markdownDescription": nls.localizeByDefault("Controls whether inline color decorations should be shown using the default document color provider"), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.colorDecoratorsActivatedOn": { @@ -2403,28 +2645,33 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "click" ], "default": "clickAndHover", + "scope": "language-overridable", "restricted": false }, "editor.inlineCompletionsAccessibilityVerbose": { "description": nls.localizeByDefault("Controls whether the accessibility hint should be provided to screen reader users when an inline completion is shown."), "type": "boolean", "default": false, + "scope": "language-overridable", "restricted": false }, "editor.codeActionWidget.showHeaders": { "type": "boolean", + "scope": "language-overridable", "description": nls.localizeByDefault("Enable/disable showing group headers in the Code Action menu."), "default": true, "restricted": false }, "editor.codeActionWidget.includeNearbyQuickfixes": { "type": "boolean", + "scope": "language-overridable", "description": nls.localize("theia/editor/editor.codeActionWidget.includeNearbyQuickfixes", "Enable/disable showing nearest quickfix within a line when not currently on a diagnostic."), "default": false, "restricted": false }, "editor.experimental.dropIntoEditor.defaultProvider": { "type": "object", + "scope": "language-overridable", "description": nls.localizeByDefault("Configures the default drop provider to use for content of a given mime type."), "default": {}, "additionalProperties": { @@ -2433,6 +2680,7 @@ export const editorGeneratedPreferenceProperties: PreferenceSchema['properties'] "restricted": false }, "editor.rename.enablePreview": { + "scope": "language-overridable", "description": nls.localizeByDefault("Enable/disable the ability to preview changes before renaming"), "default": true, "type": "boolean", @@ -2543,6 +2791,7 @@ export interface GeneratedEditorPreferences { 'editor.fontLigatures': boolean | string; 'editor.fontSize': number; 'editor.fontWeight': number | string | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; + 'editor.fontVariations': boolean | string; 'editor.formatOnPaste': boolean; 'editor.formatOnType': boolean; 'editor.glyphMargin': boolean; diff --git a/packages/markers/src/browser/problem/problem-preferences.ts b/packages/markers/src/browser/problem/problem-preferences.ts index b778d514ed9e4..66ca217bff6f4 100644 --- a/packages/markers/src/browser/problem/problem-preferences.ts +++ b/packages/markers/src/browser/problem/problem-preferences.ts @@ -23,7 +23,7 @@ export const ProblemConfigSchema: PreferenceSchema = { 'properties': { 'problems.decorations.enabled': { 'type': 'boolean', - 'description': nls.localizeByDefault('Show Errors & Warnings on files and folder.'), + 'description': nls.localizeByDefault('Show Errors & Warnings on files and folder. Overwritten by `#problems.visibility#` when it is off.'), 'default': true, }, 'problems.decorations.tabbar.enabled': { diff --git a/packages/navigator/src/browser/navigator-widget.tsx b/packages/navigator/src/browser/navigator-widget.tsx index 030102a9f85e1..e8d2b0a9f6cb7 100644 --- a/packages/navigator/src/browser/navigator-widget.tsx +++ b/packages/navigator/src/browser/navigator-widget.tsx @@ -30,7 +30,7 @@ import { nls } from '@theia/core/lib/common/nls'; import { AbstractNavigatorTreeWidget } from './abstract-navigator-tree-widget'; export const FILE_NAVIGATOR_ID = 'files'; -export const LABEL = nls.localize('theia/navigator/noFolderOpened', 'No Folder Opened'); +export const LABEL = nls.localizeByDefault('No Folder Opened'); export const CLASS = 'theia-Files'; @injectable() diff --git a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx index 9ec19a6cd31fd..78b69f66dfa11 100644 --- a/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx +++ b/packages/search-in-workspace/src/browser/search-in-workspace-widget.tsx @@ -502,7 +502,7 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge
    ; } protected handleFocusSearchInputBox = (event: React.FocusEvent) => { - event.target.placeholder = `${SearchInWorkspaceWidget.LABEL} (⇅ ${nls.localizeByDefault('for history')})`; + event.target.placeholder = SearchInWorkspaceWidget.LABEL + nls.localizeByDefault(' ({0} for history)', '⇅'); this.contextKeyService.setSearchInputBoxFocus(true); }; protected handleBlurSearchInputBox = (event: React.FocusEvent) => { @@ -541,7 +541,7 @@ export class SearchInWorkspaceWidget extends BaseWidget implements StatefulWidge } protected handleFocusReplaceInputBox = (event: React.FocusEvent) => { - event.target.placeholder = `${nls.localizeByDefault('Replace')} (⇅ ${nls.localizeByDefault('for history')})`; + event.target.placeholder = nls.localizeByDefault('Replace') + nls.localizeByDefault(' ({0} for history)', '⇅'); this.contextKeyService.setReplaceInputBoxFocus(true); }; protected handleBlurReplaceInputBox = (event: React.FocusEvent) => { diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts index 39c8dc8f4044d..fbc5dd49632dc 100644 --- a/packages/terminal/src/browser/terminal-preferences.ts +++ b/packages/terminal/src/browser/terminal-preferences.ts @@ -34,7 +34,7 @@ const commonProfileProperties: PreferenceSchemaProperties = { }, overrideName: { type: 'boolean', - description: nls.localizeByDefault('Controls whether or not the profile name overrides the auto detected one.') + description: nls.localizeByDefault('Whether or not to replace the dynamic terminal title that detects what program is running with the static profile name.') }, icon: { type: 'string', diff --git a/packages/vsx-registry/src/browser/vsx-extension.tsx b/packages/vsx-registry/src/browser/vsx-extension.tsx index 47f6ca92ad31c..4a0784ddb7d72 100644 --- a/packages/vsx-registry/src/browser/vsx-extension.tsx +++ b/packages/vsx-registry/src/browser/vsx-extension.tsx @@ -452,7 +452,7 @@ export abstract class AbstractVSXExtensionComponent { outOfSynch - ? + ? : } From 0941a8ffd9cacceb24b4cab2a003beee2ccf75e8 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Wed, 29 May 2024 15:54:45 +0200 Subject: [PATCH 250/441] Improve dev-container label Change-Id: I1bb27d0caea9f8a72bbdc2fa5a8dc7ee13bd807b --- .../remote-container-connection-provider.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts index 08b2b9ac1f2a9..1efa6abd60613 100644 --- a/packages/dev-container/src/electron-node/remote-container-connection-provider.ts +++ b/packages/dev-container/src/electron-node/remote-container-connection-provider.ts @@ -80,12 +80,13 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection }); try { const container = await this.containerService.getOrCreateContainer(dockerConnection, options.devcontainerFile, options.lastContainerInfo, this.outputProvider); + const devContainerConfig = await this.devContainerFileService.getConfiguration(options.devcontainerFile); // create actual connection const report: RemoteStatusReport = message => progress.report({ message }); report('Connecting to remote system...'); - const remote = await this.createContainerConnection(container, dockerConnection); + const remote = await this.createContainerConnection(container, dockerConnection, devContainerConfig.name); const result = await this.remoteSetup.setup({ connection: remote, report, @@ -124,11 +125,11 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection return this.devContainerFileService.getAvailableFiles(); } - async createContainerConnection(container: Docker.Container, docker: Docker): Promise { + async createContainerConnection(container: Docker.Container, docker: Docker, name?: string): Promise { return Promise.resolve(new RemoteDockerContainerConnection({ id: generateUuid(), - name: 'dev-container', - type: 'container', + name: name ?? 'dev-container', + type: 'Dev Container', docker, container, })); @@ -139,7 +140,7 @@ export class DevContainerConnectionProvider implements RemoteContainerConnection if (!connection || !(connection instanceof RemoteDockerContainerConnection)) { return undefined; } - return await connection.container.inspect(); + return connection.container.inspect(); } dispose(): void { From 63585530b2e32af0ed4bac4edb5ff443bfc248b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 12:21:22 +0200 Subject: [PATCH 251/441] Translation update for version 1.50.0 (#13749) --- packages/core/i18n/nls.cs.json | 13 ++++++++++++- packages/core/i18n/nls.de.json | 13 ++++++++++++- packages/core/i18n/nls.es.json | 13 ++++++++++++- packages/core/i18n/nls.fr.json | 13 ++++++++++++- packages/core/i18n/nls.hu.json | 13 ++++++++++++- packages/core/i18n/nls.it.json | 13 ++++++++++++- packages/core/i18n/nls.ja.json | 13 ++++++++++++- packages/core/i18n/nls.json | 13 ++++++++++++- packages/core/i18n/nls.pl.json | 13 ++++++++++++- packages/core/i18n/nls.pt-br.json | 13 ++++++++++++- packages/core/i18n/nls.pt-pt.json | 13 ++++++++++++- packages/core/i18n/nls.ru.json | 13 ++++++++++++- packages/core/i18n/nls.zh-cn.json | 13 ++++++++++++- 13 files changed, 156 insertions(+), 13 deletions(-) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index b469dbdb06c8f..4e6df727e5e04 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Předpokládejme, že není připojena čtečka obrazovky", "editor.bracketPairColorization.enabled": "Ovládá, zda je obarvení dvojice závorek povoleno, nebo ne. Pro přepsání barev zvýraznění závorek použijte `#workbench.colorCustomizations#`.", "editor.codeActionWidget.includeNearbyQuickfixes": "Povolení/zakázání zobrazení nejbližší opravy v rámci řádku, pokud se zrovna neprovádí diagnostika.", + "editor.cursorSurroundingLinesStyle": "Řídí, kdy se má vynutit `#cursorSurroundingLines#`.", "editor.detectIndentation": "Řídí, zda se při otevření souboru automaticky zjistí `#editor.tabSize#` a `#editor.insertSpaces#` na základě obsahu souboru.", + "editor.dropIntoEditor.enabled": "Ovládá, zda můžete soubor přetáhnout do textového editoru podržením klávesy `shift` (namísto otevření souboru v editoru).", "editor.formatOnSaveMode.modificationsIfAvailable": "Pokusí se formátovat pouze změny (vyžaduje kontrolu zdrojů). Pokud nelze použít kontrolu zdrojů, bude formátován celý soubor.", "editor.hover.hidingDelay": "Řídí prodlevu v milisekundách, po které se hover skryje. Vyžaduje, aby bylo povoleno `editor.hover.sticky`.", "editor.inlayHints.enabled1": "Vložené nápovědy se zobrazují ve výchozím nastavení a skrývají se při podržení `Ctrl+Alt`", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Řídí rodinu písma vložených nápověd v editoru. Je-li nastaveno na prázdnou hodnotu, použije se `#editor.fontFamily#`.", "editor.inlayHints.fontSize": "Ovládá velikost písma vložených nápověd v editoru. Ve výchozím nastavení se použije `#editor.fontSize#`, pokud je nastavená hodnota menší než `5` nebo větší než velikost písma editoru.", "editor.insertSpaces": "Vkládání mezer při stisknutí `Tab`. Toto nastavení je přepsáno na základě obsahu souboru, pokud je zapnuta funkce `#editor.detectIndentation#`.", + "editor.occurrencesHighlight": "Řídí, zda má editor zvýrazňovat výskyty sémantických symbolů.", "editor.quickSuggestions": "Ovládá, zda se mají při psaní automaticky zobrazovat návrhy. To lze ovládat při psaní komentářů, řetězců a dalšího kódu. Rychlé návrhy lze nakonfigurovat tak, aby se zobrazovaly jako text ducha nebo s widgetem návrhu. Mějte také na paměti nastavení '#editor.suggestOnTriggerCharacters#', které řídí, zda se návrhy spouštějí pomocí speciálních znaků.", "editor.stickyScroll.scrollWithEditor": "Povolení posouvání widgetu sticky scroll pomocí horizontálního posuvníku editoru.", "editor.suggestFontSize": "Velikost písma widgetu pro návrh. Je-li nastavena hodnota `0`, použije se hodnota `#editor.fontSize#`.", "editor.suggestLineHeight": "Výška řádku pro widget návrhu. Je-li nastavena hodnota `0`, použije se hodnota `#editor.lineHeight#`. Minimální hodnota je 8.", "editor.tabSize": "Počet mezer, kterým se rovná tabulátor. Toto nastavení je přepsáno na základě obsahu souboru, pokud je zapnuta funkce `#editor.detectIndentation#`.", + "editor.useTabStops": "Vkládání a odstraňování bílých znaků následuje po zarážkách tabulátoru.", + "editor.wordBasedSuggestions": "Řídí, zda se doplnění mají vypočítávat na základě slov v dokumentu.", + "editor.wordBasedSuggestionsMode": "Řídí, z jakých dokumentů se vypočítávají doplnění na základě slov.", "files.autoSave": "Ovládá [automatické ukládání](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) editorů, které mají neuložené změny.", "files.autoSave.afterDelay": "Editor se změnami se automaticky uloží po uplynutí nastavené doby `#files.autoSaveDelay#`.", "files.autoSave.off": "Editor se změnami se nikdy automaticky neuloží.", @@ -336,11 +342,16 @@ "autoReveal": "Automatické odhalení", "clipboardWarn": "Přístup do schránky je odepřen. Zkontrolujte oprávnění prohlížeče.", "clipboardWarnFirefox": "Rozhraní API schránky není k dispozici. Lze ji povolit pomocí předvolby '{0}' na stránce '{1}'. Poté znovu načtěte aplikaci Theia. Všimněte si, že to umožní FireFoxu získat plný přístup k systémové schránce.", - "noFolderOpened": "Žádná otevřená složka", "refresh": "Obnovení v Průzkumníku", "reveal": "Odhalení v Průzkumníkovi", "toggleHiddenFiles": "Přepínání skrytých souborů" }, + "notebook": { + "dragGhostImage": { + "codeText": "Vybraná buňka kódu", + "markdownText": "Vybraná buňka Mardown" + } + }, "output": { "clearOutputChannel": "Jasný výstupní kanál...", "closeOutputChannel": "Zavřít výstupní kanál...", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index 2a98cdd7f6d04..6d494ce66b343 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Angenommen, ein Bildschirmlesegerät ist nicht angeschlossen", "editor.bracketPairColorization.enabled": "Steuert, ob die Einfärbung von Klammerpaaren aktiviert ist oder nicht. Verwenden Sie `#workbench.colorCustomizations#`, um die Farben der Klammerhervorhebung zu überschreiben.", "editor.codeActionWidget.includeNearbyQuickfixes": "Aktivieren/Deaktivieren der Anzeige des nächstgelegenen Quickfix innerhalb einer Zeile, wenn keine Diagnose durchgeführt wird.", + "editor.cursorSurroundingLinesStyle": "Steuert, wann `#cursorSurroundingLines#` erzwungen werden soll.", "editor.detectIndentation": "Steuert, ob `#editor.tabSize#` und `#editor.insertSpaces#` automatisch erkannt werden, wenn eine Datei geöffnet wird, basierend auf dem Inhalt der Datei.", + "editor.dropIntoEditor.enabled": "Steuert, ob Sie eine Datei durch Ziehen und Ablegen in einen Texteditor ziehen können, indem Sie die \"Umschalttaste\" gedrückt halten (anstatt die Datei in einem Editor zu öffnen).", "editor.formatOnSaveMode.modificationsIfAvailable": "Es wird versucht, nur Änderungen zu formatieren (erfordert Quellensicherung). Wenn die Versionskontrolle nicht verwendet werden kann, wird die gesamte Datei formatiert.", "editor.hover.hidingDelay": "Steuert die Verzögerung in Millisekunden, nach der der Hover ausgeblendet wird. Erfordert, dass `editor.hover.sticky` aktiviert ist.", "editor.inlayHints.enabled1": "Inlay-Hinweise werden standardmäßig angezeigt und bei gedrückter Tastenkombination \"Strg+Alt\" ausgeblendet", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Steuert die Schriftfamilie der Inlay-Hinweise im Editor. Wenn leer, wird die `#editor.fontFamily#` verwendet.", "editor.inlayHints.fontSize": "Steuert die Schriftgröße der Inlay-Hinweise im Editor. Als Vorgabe wird die `#editor.fontSize#` verwendet, wenn der konfigurierte Wert kleiner als `5` oder größer als die Editor-Schriftgröße ist.", "editor.insertSpaces": "Leerzeichen einfügen, wenn `Tab` gedrückt wird. Diese Einstellung wird auf der Grundlage des Dateiinhalts außer Kraft gesetzt, wenn `#editor.detectIndentation#` eingeschaltet ist.", + "editor.occurrencesHighlight": "Steuert, ob der Editor das Auftreten von semantischen Symbolen hervorheben soll.", "editor.quickSuggestions": "Legt fest, ob während der Eingabe automatisch Vorschläge angezeigt werden sollen. Dies kann für die Eingabe von Kommentaren, Strings und anderem Code gesteuert werden. Schnellvorschläge können so konfiguriert werden, dass sie als Ghosttext oder mit dem Suggest-Widget angezeigt werden. Beachten Sie auch die '#editor.suggestOnTriggerCharacters#'-Einstellung, die steuert, ob Vorschläge durch Sonderzeichen ausgelöst werden.", "editor.stickyScroll.scrollWithEditor": "Aktivieren Sie den Bildlauf des Sticky-Scroll-Widgets mit der horizontalen Bildlaufleiste des Editors.", "editor.suggestFontSize": "Schriftgröße für das Vorschlags-Widget. Wenn auf `0` gesetzt, wird der Wert von `#editor.fontSize#` verwendet.", "editor.suggestLineHeight": "Zeilenhöhe für das Vorschlags-Widget. Wenn auf `0` gesetzt, wird der Wert von `#editor.lineHeight#` verwendet. Der Mindestwert ist 8.", "editor.tabSize": "Die Anzahl der Leerzeichen, die ein Tabulator ausmacht. Diese Einstellung wird basierend auf dem Inhalt der Datei überschrieben, wenn `#editor.detectIndentation#` eingeschaltet ist.", + "editor.useTabStops": "Das Einfügen und Löschen von Leerzeichen erfolgt nach Tabulatorstopps.", + "editor.wordBasedSuggestions": "Steuert, ob Vervollständigungen auf der Grundlage von Wörtern im Dokument berechnet werden sollen.", + "editor.wordBasedSuggestionsMode": "Steuert, aus welchen Dokumenten die wortbasierten Vervollständigungen berechnet werden.", "files.autoSave": "Steuert das [automatische Speichern](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) von Editoren, die ungespeicherte Änderungen haben.", "files.autoSave.afterDelay": "Ein Editor mit Änderungen wird automatisch nach der konfigurierten `#files.autoSaveDelay#` gespeichert.", "files.autoSave.off": "Ein Editor mit Änderungen wird nie automatisch gespeichert.", @@ -336,11 +342,16 @@ "autoReveal": "Auto-Enthüllung", "clipboardWarn": "Der Zugriff auf die Zwischenablage wird verweigert. Überprüfen Sie die Berechtigung Ihres Browsers.", "clipboardWarnFirefox": "Die Zwischenablage-API ist nicht verfügbar. Sie kann durch die Einstellung '{0}' auf der Seite '{1}' aktiviert werden. Dann laden Sie Theia neu. Beachten Sie, dass FireFox dadurch vollen Zugriff auf die Systemzwischenablage erhält.", - "noFolderOpened": "Kein Ordner geöffnet", "refresh": "Aktualisieren im Explorer", "reveal": "Enthüllen im Explorer", "toggleHiddenFiles": "Versteckte Dateien umschalten" }, + "notebook": { + "dragGhostImage": { + "codeText": "Codezelle ausgewählt", + "markdownText": "Mardown-Zelle ausgewählt" + } + }, "output": { "clearOutputChannel": "Ausgangskanal löschen...", "closeOutputChannel": "Ausgangskanal schließen...", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index eecd92e087dd9..32ea6b48d8d4f 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Supongamos que no hay un lector de pantalla conectado", "editor.bracketPairColorization.enabled": "Controla si la coloración del par de corchetes está activada o no. Utilice `#workbench.colorCustomizations#` para anular los colores de resaltado de los corchetes.", "editor.codeActionWidget.includeNearbyQuickfixes": "Activar/desactivar la visualización del quickfix más cercano dentro de una línea cuando no se está actualmente en un diagnóstico.", + "editor.cursorSurroundingLinesStyle": "Controla cuándo debe aplicarse `#cursorSurroundingLines#`.", "editor.detectIndentation": "Controla si `#editor.tabSize#` y `#editor.insertSpaces#` se detectarán automáticamente al abrir un archivo en función de su contenido.", + "editor.dropIntoEditor.enabled": "Controla si puedes arrastrar y soltar un archivo en un editor de texto manteniendo pulsada la tecla `shift` (en lugar de abrir el archivo en un editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Intentará formatear sólo las modificaciones (requiere control de origen). Si no se puede utilizar el control de origen, se formateará todo el archivo.", "editor.hover.hidingDelay": "Controla el retardo en milisegundos tras el cual el hover se oculta. Requiere que `editor.hover.sticky` esté activado.", "editor.inlayHints.enabled1": "Los consejos de incrustación se muestran por defecto y se ocultan cuando se mantiene `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Controla la familia de fuentes de las sugerencias de incrustación en el editor. Si está vacío, se utiliza `#editor.fontFamily#`.", "editor.inlayHints.fontSize": "Controla el tamaño de la fuente de las sugerencias de incrustación en el editor. Por defecto se utiliza `#editor.fontSize#` cuando el valor configurado es menor que `5` o mayor que el tamaño de fuente del editor.", "editor.insertSpaces": "Insertar espacios al pulsar `Tab`. Este ajuste se anula en función del contenido del archivo cuando `#editor.detectIndentation#` está activado.", + "editor.occurrencesHighlight": "Controla si el editor debe resaltar las apariciones de símbolos semánticos.", "editor.quickSuggestions": "Controla si las sugerencias deben aparecer automáticamente mientras se escribe. Esto puede controlarse para escribir comentarios, cadenas y otros códigos. La sugerencia rápida puede ser configurada para mostrarse como texto fantasma o con el widget de sugerencia. También hay que tener en cuenta la configuración '#editor.suggestOnTriggerCharacters#' que controla si las sugerencias son activadas por caracteres especiales.", "editor.stickyScroll.scrollWithEditor": "Habilitar el desplazamiento del widget de desplazamiento pegajoso con la barra de desplazamiento horizontal del editor.", "editor.suggestFontSize": "Tamaño de fuente para el widget de sugerencia. Cuando se establece en `0`, se utiliza el valor de `#editor.fontSize#`.", "editor.suggestLineHeight": "Altura de línea para el widget de sugerencia. Cuando se establece en `0`, se utiliza el valor de `#editor.lineHeight#`. El valor mínimo es 8.", "editor.tabSize": "El número de espacios a los que equivale un tabulador. Este ajuste se anula en función del contenido del archivo cuando `#editor.detectIndentation#` está activado.", + "editor.useTabStops": "Inserción y supresión de espacios en blanco después de los tabuladores.", + "editor.wordBasedSuggestions": "Controla si las terminaciones deben calcularse en función de las palabras del documento.", + "editor.wordBasedSuggestionsMode": "Controla a partir de qué documentos se calculan las terminaciones basadas en palabras.", "files.autoSave": "Controla el [autoguardado](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) de los editores que tienen cambios sin guardar.", "files.autoSave.afterDelay": "Un editor con cambios se guarda automáticamente después del `#files.autoSaveDelay#` configurado.", "files.autoSave.off": "Un editor con cambios nunca se guarda automáticamente.", @@ -336,11 +342,16 @@ "autoReveal": "Auto Reveal", "clipboardWarn": "El acceso al portapapeles está denegado. Comprueba los permisos de tu navegador.", "clipboardWarnFirefox": "La API del portapapeles no está disponible. Se puede activar mediante la preferencia '{0}' en la página '{1}'. A continuación, vuelva a cargar Theia. Tenga en cuenta que esto permitirá a FireFox tener acceso completo al portapapeles del sistema.", - "noFolderOpened": "Ninguna carpeta abierta", "refresh": "Actualizar en el Explorador", "reveal": "Revelar en el Explorador", "toggleHiddenFiles": "Activar los archivos ocultos" }, + "notebook": { + "dragGhostImage": { + "codeText": "Celda de código seleccionada", + "markdownText": "Celda de Mardown seleccionada" + } + }, "output": { "clearOutputChannel": "Canal de salida claro...", "closeOutputChannel": "Cerrar canal de salida...", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index ae2afdac91b2d..57fd23e510cc1 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Supposons qu'il n'y ait pas de lecteur d'écran", "editor.bracketPairColorization.enabled": "Contrôle si la colorisation des paires de crochets est activée ou non. Utilisez `#workbench.colorCustomizations#` pour surcharger les couleurs de mise en évidence des crochets.", "editor.codeActionWidget.includeNearbyQuickfixes": "Activer/désactiver l'affichage de la réparation rapide la plus proche dans une ligne lorsqu'il n'y a pas de diagnostic en cours.", + "editor.cursorSurroundingLinesStyle": "Contrôle quand `#cursorSurroundingLines#` doit être appliqué.", "editor.detectIndentation": "Contrôle si `#editor.tabSize#` et `#editor.insertSpaces#` seront automatiquement détectés lors de l'ouverture d'un fichier en fonction de son contenu.", + "editor.dropIntoEditor.enabled": "Contrôle si vous pouvez glisser-déposer un fichier dans un éditeur de texte en maintenant la touche \"Maj\" enfoncée (au lieu d'ouvrir le fichier dans un éditeur).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tentera de formater uniquement les modifications (nécessite le contrôle de la source). Si le contrôle de la source ne peut pas être utilisé, alors le fichier entier sera formaté.", "editor.hover.hidingDelay": "Contrôle le délai en millisecondes après lequel le survol est caché. Nécessite que `editor.hover.sticky` soit activé.", "editor.inlayHints.enabled1": "Les conseils d'incrustation sont affichés par défaut et sont masqués lorsque vous maintenez les touches `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Contrôle la famille de police des indices d'incrustation dans l'éditeur. Si la valeur est vide, c'est la police `#editor.fontFamily#` qui est utilisée.", "editor.inlayHints.fontSize": "Contrôle la taille de la police des indices d'incrustation dans l'éditeur. Par défaut, `#editor.fontSize#` est utilisé lorsque la valeur configurée est inférieure à `5` ou supérieure à la taille de la police de l'éditeur.", "editor.insertSpaces": "Insérer des espaces lorsque l'on appuie sur `Tab`. Ce paramètre est modifié en fonction du contenu du fichier lorsque `#editor.detectIndentation#` est activé.", + "editor.occurrencesHighlight": "Contrôle si l'éditeur doit mettre en évidence les occurrences de symboles sémantiques.", "editor.quickSuggestions": "Contrôle si les suggestions doivent s'afficher automatiquement pendant la saisie. Cela peut être contrôlé pour la saisie de commentaires, de chaînes de caractères et d'autres codes. La suggestion rapide peut être configurée pour s'afficher sous forme de texte fantôme ou avec le widget de suggestion. Tenez également compte du paramètre '#editor.suggestOnTriggerCharacters#' qui contrôle si les suggestions sont déclenchées par des caractères spéciaux.", "editor.stickyScroll.scrollWithEditor": "Active le défilement du widget de défilement collant avec la barre de défilement horizontale de l'éditeur.", "editor.suggestFontSize": "Taille de la police pour le widget de suggestion. Si elle vaut `0`, la valeur de `#editor.fontSize#` est utilisée.", "editor.suggestLineHeight": "Hauteur de ligne pour le widget de suggestion. Lorsqu'il vaut `0`, la valeur de `#editor.lineHeight#` est utilisée. La valeur minimale est de 8.", "editor.tabSize": "Le nombre d'espaces que représente une tabulation. Ce paramètre est modifié en fonction du contenu du fichier lorsque `#editor.detectIndentation#` est activé.", + "editor.useTabStops": "L'insertion et la suppression d'espaces blancs suivent les taquets de tabulation.", + "editor.wordBasedSuggestions": "Contrôle si les compléments doivent être calculés sur la base des mots du document.", + "editor.wordBasedSuggestionsMode": "Contrôle les documents à partir desquels les complétions basées sur les mots sont calculées.", "files.autoSave": "Contrôle la [sauvegarde automatique](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) des éditeurs dont les modifications n'ont pas été sauvegardées.", "files.autoSave.afterDelay": "Un éditeur avec des modifications est automatiquement enregistré après le `#files.autoSaveDelay#` configuré.", "files.autoSave.off": "Un éditeur avec des modifications n'est jamais automatiquement sauvegardé.", @@ -336,11 +342,16 @@ "autoReveal": "Révélation automobile", "clipboardWarn": "L'accès au presse-papiers est refusé. Vérifiez les autorisations de votre navigateur.", "clipboardWarnFirefox": "L'API Presse-papiers n'est pas disponible. Elle peut être activée par la préférence '{0}' sur la page '{1}'. Rechargez ensuite Theia. Notez que cela permettra à FireFox d'avoir un accès complet au presse-papiers du système.", - "noFolderOpened": "Aucun dossier ouvert", "refresh": "Rafraîchir dans l'Explorateur", "reveal": "Révéler dans Explorer", "toggleHiddenFiles": "Basculer les fichiers cachés" }, + "notebook": { + "dragGhostImage": { + "codeText": "Cellule de code sélectionnée", + "markdownText": "Cellule Mardown sélectionnée" + } + }, "output": { "clearOutputChannel": "Effacer le canal de sortie...", "closeOutputChannel": "Fermer le canal de sortie...", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index dea9c9f30191c..8216dcf460df7 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Feltételezzük, hogy a képernyőolvasó nincs csatlakoztatva", "editor.bracketPairColorization.enabled": "Szabályozza, hogy a zárójelpár színezése engedélyezve legyen-e vagy sem. Használja a `#workbench.colorCustomizations#` parancsot a zárójelek kiemelési színeinek felülbírálásához.", "editor.codeActionWidget.includeNearbyQuickfixes": "A legközelebbi gyorsjavítás megjelenítésének engedélyezése/letiltása egy soron belül, ha éppen nem diagnosztikán van.", + "editor.cursorSurroundingLinesStyle": "Szabályozza, hogy mikor legyen érvényes a `#cursorSurroundingLines#`.", "editor.detectIndentation": "Szabályozza, hogy a `#editor.tabSize#` és a `#editor.insertSpaces#` automatikusan felismerésre kerüljön-e egy fájl megnyitásakor a fájl tartalma alapján.", + "editor.dropIntoEditor.enabled": "Azt szabályozza, hogy a \"shift\" lenyomva tartásával húzhat-e egy fájlt egy szövegszerkesztőbe (ahelyett, hogy megnyitná a fájlt egy szerkesztőprogramban).", "editor.formatOnSaveMode.modificationsIfAvailable": "Csak a módosítások formázására tesz kísérletet (forrásellenőrzés szükséges). Ha a forrásellenőrzés nem használható, akkor a teljes fájl lesz formázva.", "editor.hover.hidingDelay": "Ezredmásodpercben határozza meg a késleltetést, amely után a lebegő elrejtődik. A `editor.hover.sticky` engedélyezése szükséges.", "editor.inlayHints.enabled1": "Az inlay tippek alapértelmezés szerint megjelennek, és elrejtődnek, ha a \"Ctrl+Alt\" billentyűkombinációt nyomva tartjuk.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "A betűcsaládot vezérli a betétfájlok betűtípusát a szerkesztőben. Ha üres, akkor a `#editor.fontFamily#`-t használja.", "editor.inlayHints.fontSize": "A betűméretet szabályozza a szerkesztőben megjelenő betűjelzések betűméretét. Alapértelmezés szerint az `#editor.fontSize#` értéket használja, ha a beállított érték kisebb, mint `5` vagy nagyobb, mint a szerkesztő betűmérete.", "editor.insertSpaces": "Szóközök beillesztése a \"Tab\" billentyű lenyomásakor. Ez a beállítás a fájl tartalma alapján felülíródik, ha a `#editor.detectIndentation#` be van kapcsolva.", + "editor.occurrencesHighlight": "Szabályozza, hogy a szerkesztő kiemelje-e a szemantikus szimbólumok előfordulását.", "editor.quickSuggestions": "Szabályozza, hogy a javaslatok automatikusan megjelenjenek-e gépelés közben. Ezt a megjegyzések, karakterláncok és más kódok beírása esetén lehet szabályozni. A gyors javaslatok beállíthatók úgy, hogy szellemszövegként vagy a javaslat widget segítségével jelenjenek meg. Figyeljen a '#editor.suggestOnTriggerCharacters#'-beállításra is, amely azt szabályozza, hogy a javaslatok speciális karakterek hatására aktiválódjanak-e.", "editor.stickyScroll.scrollWithEditor": "A ragadós görgető widget görgetésének engedélyezése a szerkesztő vízszintes görgetősávjával.", "editor.suggestFontSize": "A javaslat widget betűmérete. Ha `0`-ra van állítva, akkor a `#editor.fontSize#` értékét használja.", "editor.suggestLineHeight": "A javaslat widget vonalmagassága. Ha `0`-ra van állítva, akkor a `#editor.lineHeight#` értékét használja. A minimális érték 8.", "editor.tabSize": "A tabulátor szóközök száma. Ez a beállítás a fájl tartalma alapján felülíródik, ha a `#editor.detectIndentation#` be van kapcsolva.", + "editor.useTabStops": "A szóközök beillesztése és törlése a tabulátormegállókat követi.", + "editor.wordBasedSuggestions": "Szabályozza, hogy a kiegészítések a dokumentumban található szavak alapján kerüljenek-e kiszámításra.", + "editor.wordBasedSuggestionsMode": "Azt vezérli, hogy mely dokumentumokból számolja ki a szóalapú kiegészítéseket.", "files.autoSave": "Az [automatikus mentés](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) vezérli a mentetlen módosításokkal rendelkező szerkesztőket.", "files.autoSave.afterDelay": "A szerkesztő a változtatásokat automatikusan elmenti a beállított `#files.autoSaveDelay#` után.", "files.autoSave.off": "Egy szerkesztő a módosításokkal soha nem kerül automatikusan mentésre.", @@ -336,11 +342,16 @@ "autoReveal": "Automatikus felfedés", "clipboardWarn": "A vágólaphoz való hozzáférés megtagadva. Ellenőrizze a böngésző engedélyeit.", "clipboardWarnFirefox": "A vágólap API nem érhető el. A '{0}' beállítással engedélyezhető a '{1}' oldalon. Ezután töltse be újra a Theia-t. Figyelem, ez lehetővé teszi, hogy a FireFox teljes hozzáférést kapjon a rendszer vágólapjához.", - "noFolderOpened": "Nincs megnyitott mappa", "refresh": "Frissítés az Explorerben", "reveal": "Feltárása az Explorerben", "toggleHiddenFiles": "Rejtett fájlok kapcsolása" }, + "notebook": { + "dragGhostImage": { + "codeText": "Kijelölt kódcella", + "markdownText": "Mardown cella kiválasztva" + } + }, "output": { "clearOutputChannel": "Tiszta kimeneti csatorna...", "closeOutputChannel": "Kimeneti csatorna bezárása...", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index c2acda6f6aec9..0d6a2a5eecc83 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Supponiamo che un lettore di schermo non sia collegato", "editor.bracketPairColorization.enabled": "Controlla se la colorazione delle coppie di parentesi è abilitata o meno. Usare `#workbench.colorCustomizations#` per sovrascrivere i colori di evidenziazione delle parentesi.", "editor.codeActionWidget.includeNearbyQuickfixes": "Abilita/disabilita la visualizzazione della soluzione rapida più vicina all'interno di una linea quando non è in corso una diagnostica.", + "editor.cursorSurroundingLinesStyle": "Controlla quando `#cursorSurroundingLines#` deve essere applicato.", "editor.detectIndentation": "Controlla se `#editor.tabSize#` e `#editor.insertSpaces#` saranno rilevati automaticamente all'apertura di un file, in base al suo contenuto.", + "editor.dropIntoEditor.enabled": "Controlla se è possibile trascinare e rilasciare un file in un editor di testo tenendo premuto `shift` (invece di aprire il file in un editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tenterà di formattare solo le modifiche (richiede il controllo della fonte). Se il controllo della fonte non può essere usato, allora l'intero file sarà formattato.", "editor.hover.hidingDelay": "Controlla il ritardo, in millisecondi, dopo il quale il passaggio del mouse viene nascosto. Richiede che `editor.hover.sticky` sia abilitato.", "editor.inlayHints.enabled1": "I suggerimenti per l'intarsio sono visualizzati per impostazione predefinita e si nascondono quando si tiene premuto `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Controlla la famiglia di caratteri dei suggerimenti di inlay nell'editor. Se impostato a vuoto, viene utilizzata la `#editor.fontFamily#`.", "editor.inlayHints.fontSize": "Controlla la dimensione dei caratteri dei suggerimenti inlay nell'editor. Come impostazione predefinita, viene utilizzato `#editor.fontSize#` quando il valore configurato è inferiore a `5` o superiore alla dimensione dei caratteri dell'editor.", "editor.insertSpaces": "Inserisce spazi quando si preme `Tab`. Questa impostazione viene sovrascritta in base al contenuto del file quando `#editor.detectIndentation#` è attivo.", + "editor.occurrencesHighlight": "Controlla se l'editor deve evidenziare le occorrenze di simboli semantici.", "editor.quickSuggestions": "Controlla se i suggerimenti devono essere visualizzati automaticamente durante la digitazione. Questo può essere controllato per la digitazione di commenti, stringhe e altro codice. Il suggerimento rapido può essere configurato per essere visualizzato come testo fantasma o con il widget Suggerimento. Si tenga presente anche l'impostazione '#editor.suggestOnTriggerCharacters#', che controlla se i suggerimenti vengono attivati da caratteri speciali.", "editor.stickyScroll.scrollWithEditor": "Abilita lo scorrimento del widget di scorrimento appiccicoso con la barra di scorrimento orizzontale dell'editor.", "editor.suggestFontSize": "Dimensione del carattere per il widget Suggerimento. Se impostato a `0`, viene utilizzato il valore di `#editor.fontSize#`.", "editor.suggestLineHeight": "Altezza della riga per il widget di suggerimento. Se impostato a `0`, viene utilizzato il valore di `#editor.lineHeight#`. Il valore minimo è 8.", "editor.tabSize": "Il numero di spazi a cui corrisponde una tabulazione. Questa impostazione viene sovrascritta in base al contenuto del file quando `#editor.detectIndentation#` è attivo.", + "editor.useTabStops": "L'inserimento e l'eliminazione di spazi bianchi segue gli stop di tabulazione.", + "editor.wordBasedSuggestions": "Controlla se i completamenti devono essere calcolati in base alle parole del documento.", + "editor.wordBasedSuggestionsMode": "Controlla da quali documenti vengono calcolati i completamenti basati sulle parole.", "files.autoSave": "Controlla il [salvataggio automatico](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) degli editor che hanno modifiche non salvate.", "files.autoSave.afterDelay": "Un editor con modifiche viene salvato automaticamente dopo il `#files.autoSaveDelay#` configurato.", "files.autoSave.off": "Un editor con modifiche non viene mai salvato automaticamente.", @@ -336,11 +342,16 @@ "autoReveal": "Rivelazione auto", "clipboardWarn": "L'accesso agli appunti è negato. Controllare i permessi del browser.", "clipboardWarnFirefox": "L'API Appunti non è disponibile. È possibile abilitarla tramite la preferenza '{0}' nella pagina '{1}'. Quindi ricaricare Theia. Ciò consentirà a FireFox di avere pieno accesso agli appunti di sistema.", - "noFolderOpened": "Nessuna cartella aperta", "refresh": "Aggiorna in Explorer", "reveal": "Rivelare in Explorer", "toggleHiddenFiles": "Toggle Hidden Files" }, + "notebook": { + "dragGhostImage": { + "codeText": "Codice cella selezionato", + "markdownText": "Cella di Mardown selezionato" + } + }, "output": { "clearOutputChannel": "Cancella il canale di uscita...", "closeOutputChannel": "Chiudere il canale di uscita...", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 41318df05a9ce..f259de3b53cdd 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "スクリーンリーダーが添付されていないと仮定する", "editor.bracketPairColorization.enabled": "ブラケットペアのカラー化を有効にするかどうかを制御する。ブラケットのハイライト色を上書きするには `#workbench.colorCustomizations#` を使用してください。", "editor.codeActionWidget.includeNearbyQuickfixes": "現在診断中でない場合に、行内に最も近いクイックフィックスを表示するかどうかを設定します。", + "editor.cursorSurroundingLinesStyle": "cursorSurroundingLines#`を実行するタイミングを制御する。", "editor.detectIndentation": "ファイルを開いたときに、ファイルの内容に基づいて `#editor.tabSize#` と `#editor.insertSpaces#` を自動的に検出するかどうかを制御する。", + "editor.dropIntoEditor.enabled": "shift`を押しながらファイルをテキストエディタにドラッグ&ドロップできるかどうかをコントロールします(エディタでファイルを開く代わりに)。", "editor.formatOnSaveMode.modificationsIfAvailable": "変更点のみをフォーマットしようとします(ソースコントロールが必要です)。ソースコントロールが使用できない場合は、ファイル全体がフォーマットされます。", "editor.hover.hidingDelay": "ホバーが非表示になるまでの遅延をミリ秒単位で制御する。editor.hover.sticky`が有効になっている必要があります。", "editor.inlayHints.enabled1": "インレイのヒントはデフォルトで表示され、`Ctrl+Alt`を押すと隠れます。", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "エディタのインレイヒントのフォントファミリーを制御する。空に設定すると、`#editor.fontFamily#`が使用されます。", "editor.inlayHints.fontSize": "エディタのインレイヒントのフォントサイズを制御する。デフォルトでは`#editor.fontSize#`が使用され、設定値が`5`未満またはエディタのフォントサイズより大きい場合に使用されます。", "editor.insertSpaces": "Tab`を押したときにスペースを挿入する。この設定は、`#editor.detectIndentation#`がオンの場合、ファイルの内容によって上書きされる。", + "editor.occurrencesHighlight": "エディターが意味記号の出現をハイライトするかどうかを制御する。", "editor.quickSuggestions": "入力中に自動的にサジェストを表示するかどうかを制御します。コメント、文字列、その他のコードを入力する際に制御できます。クイックサジェストは、ゴーストテキストとして表示するか、サジェストウィジェットで表示するかを設定できます。また、'#editor.suggestOnTriggerCharacters#'設定は、特殊な文字でサジェストが発生するかどうかを制御します。", "editor.stickyScroll.scrollWithEditor": "エディターの水平スクロールバーでスティッキースクロールウィジェットのスクロールを有効にする。", "editor.suggestFontSize": "サジェストウィジェットのフォントサイズ。0`に設定すると、`#editor.fontSize#`の値が使用される。", "editor.suggestLineHeight": "提案ウィジェットの行の高さ。0`に設定すると、`#editor.lineHeight#`の値が使用される。最小値は8。", "editor.tabSize": "タブのスペース数。この設定は、`#editor.detectIndentation#`がオンの場合、ファイルの内容によって上書きされる。", + "editor.useTabStops": "空白の挿入と削除はタブストップに従う。", + "editor.wordBasedSuggestions": "文書内の単語に基づいて補完を計算するかどうかを制御する。", + "editor.wordBasedSuggestionsMode": "どの文書から単語ベースの補完を計算するかを制御する。", "files.autoSave": "未保存の変更があるエディターの[自動保存](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)を制御します。", "files.autoSave.afterDelay": "変更されたエディターは、設定された `#files.autoSaveDelay#` の後に自動的に保存されます。", "files.autoSave.off": "変更されたエディタは、自動的に保存されることはありません。", @@ -336,11 +342,16 @@ "autoReveal": "オートリヴェール", "clipboardWarn": "クリップボードへのアクセスが拒否されました。ブラウザのアクセス許可を確認してください。", "clipboardWarnFirefox": "クリップボードAPIは使用できません。これは、'{1}' ページの '{0}' 環境設定によって有効にすることができます。その後Theiaをリロードしてください。これにより、FireFoxがシステムクリップボードにフルアクセスできるようになります。", - "noFolderOpened": "フォルダが開かない", "refresh": "エクスプローラーでの更新", "reveal": "エクスプローラーでの表示", "toggleHiddenFiles": "隠しファイルのトグル" }, + "notebook": { + "dragGhostImage": { + "codeText": "選択されたコードセル", + "markdownText": "マーダウンのセルを選択" + } + }, "output": { "clearOutputChannel": "クリア出力チャンネル...", "closeOutputChannel": "出力チャンネルを閉じる...", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index ebac886adc918..e4ed5adf0f707 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Assume a screen reader is not attached", "editor.bracketPairColorization.enabled": "Controls whether bracket pair colorization is enabled or not. Use `#workbench.colorCustomizations#` to override the bracket highlight colors.", "editor.codeActionWidget.includeNearbyQuickfixes": "Enable/disable showing nearest quickfix within a line when not currently on a diagnostic.", + "editor.cursorSurroundingLinesStyle": "Controls when `#cursorSurroundingLines#` should be enforced.", "editor.detectIndentation": "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents.", + "editor.dropIntoEditor.enabled": "Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Will attempt to format modifications only (requires source control). If source control can't be used, then the whole file will be formatted.", "editor.hover.hidingDelay": "Controls the delay in milliseconds after thich the hover is hidden. Requires `editor.hover.sticky` to be enabled.", "editor.inlayHints.enabled1": "Inlay hints are showing by default and hide when holding Ctrl+Alt", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Controls font family of inlay hints in the editor. When set to empty, the `#editor.fontFamily#` is used.", "editor.inlayHints.fontSize": "Controls font size of inlay hints in the editor. As default the `#editor.fontSize#` is used when the configured value is less than `5` or greater than the editor font size.", "editor.insertSpaces": "Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.", + "editor.occurrencesHighlight": "Controls whether the editor should highlight semantic symbol occurrences.", "editor.quickSuggestions": "Controls whether suggestions should automatically show up while typing. This can be controlled for typing in comments, strings, and other code. Quick suggestion can be configured to show as ghost text or with the suggest widget. Also be aware of the '#editor.suggestOnTriggerCharacters#'-setting which controls if suggestions are triggered by special characters.", "editor.stickyScroll.scrollWithEditor": "Enable scrolling of the sticky scroll widget with the editor's horizontal scrollbar.", "editor.suggestFontSize": "Font size for the suggest widget. When set to `0`, the value of `#editor.fontSize#` is used.", "editor.suggestLineHeight": "Line height for the suggest widget. When set to `0`, the value of `#editor.lineHeight#` is used. The minimum value is 8.", "editor.tabSize": "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on.", + "editor.useTabStops": "Inserting and deleting whitespace follows tab stops.", + "editor.wordBasedSuggestions": "Controls whether completions should be computed based on words in the document.", + "editor.wordBasedSuggestionsMode": "Controls from which documents word based completions are computed.", "files.autoSave": "Controls [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors that have unsaved changes.", "files.autoSave.afterDelay": "An editor with changes is automatically saved after the configured `#files.autoSaveDelay#`.", "files.autoSave.off": "An editor with changes is never automatically saved.", @@ -336,11 +342,16 @@ "autoReveal": "Auto Reveal", "clipboardWarn": "Access to the clipboard is denied. Check your browser's permission.", "clipboardWarnFirefox": "Clipboard API is not available. It can be enabled by '{0}' preference on '{1}' page. Then reload Theia. Note, it will allow FireFox getting full access to the system clipboard.", - "noFolderOpened": "No Folder Opened", "refresh": "Refresh in Explorer", "reveal": "Reveal in Explorer", "toggleHiddenFiles": "Toggle Hidden Files" }, + "notebook": { + "dragGhostImage": { + "codeText": "Code cell selected", + "markdownText": "Mardown cell selected" + } + }, "output": { "clearOutputChannel": "Clear Output Channel...", "closeOutputChannel": "Close Output Channel...", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 35c19def759d2..a0acc4defe849 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Załóżmy, że czytnik ekranu nie jest podłączony", "editor.bracketPairColorization.enabled": "Kontroluje, czy kolorowanie par nawiasów jest włączone, czy nie. Użyj `#workbench.colorCustomizations#`, aby zastąpić kolory podświetlenia nawiasów.", "editor.codeActionWidget.includeNearbyQuickfixes": "Włączenie/wyłączenie wyświetlania najbliższego quickfixa w linii, gdy nie jest on aktualnie w diagnostyce.", + "editor.cursorSurroundingLinesStyle": "Kontroluje, kiedy `#cursorSurroundingLines#` powinien być wymuszany.", "editor.detectIndentation": "Kontroluje, czy `#editor.tabSize#` i `#editor.insertSpaces#` będą automatycznie wykrywane podczas otwierania pliku na podstawie jego zawartości.", + "editor.dropIntoEditor.enabled": "Kontroluje, czy można przeciągnąć i upuścić plik do edytora tekstu, przytrzymując `shift` (zamiast otwierać plik w edytorze).", "editor.formatOnSaveMode.modificationsIfAvailable": "Spowoduje próbę sformatowania tylko modyfikacji (wymaga kontroli źródła). Jeśli kontrola źródła nie może być użyta, sformatowany zostanie cały plik.", "editor.hover.hidingDelay": "Kontroluje opóźnienie w milisekundach, po którym hover zostanie ukryty. Wymaga włączenia `editor.hover.sticky`.", "editor.inlayHints.enabled1": "Podpowiedzi są domyślnie wyświetlane i ukrywają się po przytrzymaniu `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Kontroluje rodzinę czcionek podpowiedzi inlay w edytorze. Gdy ustawiona na pustą, używana jest `#editor.fontFamily#`.", "editor.inlayHints.fontSize": "Kontroluje rozmiar czcionki podpowiedzi inlay w edytorze. Domyślnie używana jest wartość `#editor.fontSize#`, gdy skonfigurowana wartość jest mniejsza niż `5` lub większa niż rozmiar czcionki edytora.", "editor.insertSpaces": "Wstawia spacje po naciśnięciu `Tab`. To ustawienie jest nadpisywane na podstawie zawartości pliku, gdy włączona jest funkcja `#editor.detectIndentation#`.", + "editor.occurrencesHighlight": "Kontroluje, czy edytor powinien podświetlać wystąpienia symboli semantycznych.", "editor.quickSuggestions": "Kontroluje czy sugestie powinny być automatycznie wyświetlane podczas pisania. Można to kontrolować w przypadku wpisywania komentarzy, ciągów znaków i innego kodu. Szybkie sugestie mogą być skonfigurowane tak, aby pokazywały się jako tekst widma lub z widżetem sugestii. Należy również pamiętać o ustawieniu '#editor.suggestOnTriggerCharacters#', które kontroluje czy sugestie są uruchamiane przez znaki specjalne.", "editor.stickyScroll.scrollWithEditor": "Włącza przewijanie widżetu sticky scroll za pomocą poziomego paska przewijania edytora.", "editor.suggestFontSize": "Rozmiar czcionki dla widżetu sugestii. Gdy ustawione na `0`, używana jest wartość `#editor.fontSize#`.", "editor.suggestLineHeight": "Wysokość linii dla widżetu sugestii. Gdy ustawione na `0`, używana jest wartość `#editor.lineHeight#`. Minimalna wartość to 8.", "editor.tabSize": "Liczba spacji równa tabulatorowi. To ustawienie jest nadpisywane na podstawie zawartości pliku, gdy włączona jest funkcja `#editor.detectIndentation#`.", + "editor.useTabStops": "Wstawianie i usuwanie białych znaków następuje po tabulatorach.", + "editor.wordBasedSuggestions": "Kontroluje, czy uzupełnienia powinny być obliczane na podstawie słów w dokumencie.", + "editor.wordBasedSuggestionsMode": "Określa, na podstawie których dokumentów uzupełniane są uzupełnienia oparte na słowach.", "files.autoSave": "Steruje funkcją [automatycznego zapisywania](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) edytorów, w których nie zapisano zmian.", "files.autoSave.afterDelay": "Edytor ze zmianami jest automatycznie zapisywany po upływie skonfigurowanego czasu `#files.autoSaveDelay#`.", "files.autoSave.off": "Edytor ze zmianami nigdy nie jest automatycznie zapisywany.", @@ -336,11 +342,16 @@ "autoReveal": "Auto ujawnienie", "clipboardWarn": "Dostęp do schowka jest zablokowany. Sprawdź uprawnienia przeglądarki.", "clipboardWarnFirefox": "Interfejs API schowka nie jest dostępny. Można go włączyć za pomocą opcji '{0}' na stronie '{1}'. Następnie należy ponownie załadować Theia. Uwaga, pozwoli to FireFox uzyskać pełny dostęp do schowka systemowego.", - "noFolderOpened": "Folder nie został otwarty", "refresh": "Odśwież w Eksploratorze", "reveal": "Ujawnij w Eksploratorze", "toggleHiddenFiles": "Przełącz ukryte pliki" }, + "notebook": { + "dragGhostImage": { + "codeText": "Wybrana komórka kodu", + "markdownText": "Wybrano komórkę Mardown" + } + }, "output": { "clearOutputChannel": "Wyczyść kanał wyjściowy...", "closeOutputChannel": "Zamknij kanał wyjściowy...", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index 585af09cc8955..976fe48f4c6fa 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Suponha que um leitor de tela não esteja conectado", "editor.bracketPairColorization.enabled": "Controla se a colorização de pares de colchetes está ativada ou não. Use `#workbench.colorCustomizations#` para substituir as cores de destaque dos colchetes.", "editor.codeActionWidget.includeNearbyQuickfixes": "Ativar/desativar a exibição do reparo rápido mais próximo em uma linha quando não estiver em um diagnóstico no momento.", + "editor.cursorSurroundingLinesStyle": "Controla quando o `#cursorSurroundingLines#` deve ser aplicado.", "editor.detectIndentation": "Controla se `#editor.tabSize#` e `#editor.insertSpaces#` serão detectados automaticamente quando um arquivo for aberto com base no conteúdo do arquivo.", + "editor.dropIntoEditor.enabled": "Controla se você pode arrastar e soltar um arquivo em um editor de texto mantendo pressionada a tecla `shift` (em vez de abrir o arquivo em um editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tentará formatar modificações apenas (requer controle de fonte). Se o controle da fonte não puder ser usado, então o arquivo inteiro será formatado.", "editor.hover.hidingDelay": "Controla o atraso, em milissegundos, após o qual o hover fica oculto. Requer que a opção `editor.hover.sticky` esteja ativada.", "editor.inlayHints.enabled1": "Dicas de Inlay estão mostrando por padrão e se escondem ao segurar `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Controla a família de fontes das dicas de inlay no editor. Quando definido como vazio, a `#editor.fontFamily#` é usada.", "editor.inlayHints.fontSize": "Controla o tamanho da fonte das dicas de inlay no editor. Por padrão, o `#editor.fontSize#` é usado quando o valor configurado é menor que `5` ou maior que o tamanho da fonte do editor.", "editor.insertSpaces": "Inserir espaços ao pressionar `Tab`. Essa configuração é substituída com base no conteúdo do arquivo quando `#editor.detectIndentation#` está ativado.", + "editor.occurrencesHighlight": "Controla se o editor deve destacar as ocorrências de símbolos semânticos.", "editor.quickSuggestions": "Controla se as sugestões devem aparecer automaticamente durante a digitação. Isto pode ser controlado para digitação de comentários, strings, e outros códigos. A sugestão rápida pode ser configurada para aparecer como texto fantasma ou com o widget de sugestão. Esteja ciente também do '#editor.suggestOnTriggerCharacters#'-setting que controla se as sugestões são acionadas por caracteres especiais.", "editor.stickyScroll.scrollWithEditor": "Ative a rolagem do widget de rolagem fixa com a barra de rolagem horizontal do editor.", "editor.suggestFontSize": "Tamanho da fonte para o widget de sugestão. Quando definido como `0`, o valor de `#editor.fontSize#` é usado.", "editor.suggestLineHeight": "Altura da linha para o widget de sugestão. Quando definido como `0`, o valor de `#editor.lineHeight#` é usado. O valor mínimo é 8.", "editor.tabSize": "O número de espaços a que uma tabulação é igual. Essa configuração é substituída com base no conteúdo do arquivo quando `#editor.detectIndentation#` está ativado.", + "editor.useTabStops": "A inserção e a exclusão de espaços em branco seguem as paradas de tabulação.", + "editor.wordBasedSuggestions": "Controla se as conclusões devem ser computadas com base nas palavras do documento.", + "editor.wordBasedSuggestionsMode": "Controla a partir de quais documentos as conclusões baseadas em palavras são computadas.", "files.autoSave": "Controles [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) de editores que não salvaram mudanças.", "files.autoSave.afterDelay": "Um editor com mudanças é automaticamente salvo após o `#files.autoSaveDelay#` configurado.", "files.autoSave.off": "Um editor com mudanças nunca é salvo automaticamente.", @@ -336,11 +342,16 @@ "autoReveal": "Auto Revelação", "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão de seu navegador.", "clipboardWarnFirefox": "A API da área de transferência não está disponível. Ela pode ser ativada pela preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Observe que isso permitirá que o FireFox obtenha acesso total à área de transferência do sistema.", - "noFolderOpened": "Nenhuma pasta foi aberta", "refresh": "Refrescar no Explorer", "reveal": "Revelar no Explorer", "toggleHiddenFiles": "Alternar arquivos ocultos" }, + "notebook": { + "dragGhostImage": { + "codeText": "Célula de código selecionada", + "markdownText": "Célula Mardown selecionada" + } + }, "output": { "clearOutputChannel": "Canal de saída livre...", "closeOutputChannel": "Fechar canal de saída...", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json index 9b0ac58bfb796..aedb0f0b201b4 100644 --- a/packages/core/i18n/nls.pt-pt.json +++ b/packages/core/i18n/nls.pt-pt.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Assumir que um leitor de ecrã não está ligado", "editor.bracketPairColorization.enabled": "Controla se a colorização de pares de colchetes está habilitada ou não. Utilize `#workbench.colorCustomizations#` para substituir as cores de destaque dos colchetes.", "editor.codeActionWidget.includeNearbyQuickfixes": "Ativar/desativar a apresentação da reparação rápida mais próxima dentro de uma linha quando não se está a fazer um diagnóstico.", + "editor.cursorSurroundingLinesStyle": "Controla quando o `#cursorSurroundingLines#` deve ser aplicado.", "editor.detectIndentation": "Controla se `#editor.tabSize#` e `#editor.insertSpaces#` serão automaticamente detectados quando um arquivo é aberto com base no conteúdo do arquivo.", + "editor.dropIntoEditor.enabled": "Controla se é possível arrastar e soltar um arquivo em um editor de texto mantendo pressionada a tecla `shift` (em vez de abrir o arquivo em um editor).", "editor.formatOnSaveMode.modificationsIfAvailable": "Tentará apenas formatar modificações (requer controlo da fonte). Se o controlo da fonte não puder ser utilizado, então o ficheiro inteiro será formatado.", "editor.hover.hidingDelay": "Controla o atraso em milissegundos após o qual o hover é escondido. Requer que `editor.hover.sticky` esteja habilitado.", "editor.inlayHints.enabled1": "Dicas de Inlay estão a mostrar por defeito e escondem-se quando se segura \"Ctrl+Alt\".", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Controla a família da fonte das dicas de inlay no editor. Quando definido como vazio, a `#editor.fontFamily#` é utilizada.", "editor.inlayHints.fontSize": "Controla o tamanho da fonte das dicas de inlay no editor. Como padrão o `#editor.fontSize#` é utilizado quando o valor configurado é menor que `5` ou maior que o tamanho da fonte do editor.", "editor.insertSpaces": "Inserir espaços ao pressionar `Tab`. Esta configuração é substituída com base no conteúdo do ficheiro quando `#editor.detectIndentation#` está ligado.", + "editor.occurrencesHighlight": "Controla se o editor deve realçar as ocorrências de símbolos semânticos.", "editor.quickSuggestions": "Controla se as sugestões devem aparecer automaticamente durante a dactilografia. Isto pode ser controlado para digitação de comentários, strings, e outros códigos. A sugestão rápida pode ser configurada para aparecer como texto fantasma ou com o widget de sugestão. Esteja também ciente do '#editor.suggestOnTriggerCharacters#'-setting que controla se as sugestões são accionadas por caracteres especiais.", "editor.stickyScroll.scrollWithEditor": "Ativar a deslocação do widget de deslocação autocolante com a barra de deslocação horizontal do editor.", "editor.suggestFontSize": "Tamanho da fonte para o widget de sugestão. Quando definido como `0`, o valor de `#editor.fontSize#` é utilizado.", "editor.suggestLineHeight": "Altura da linha para o widget de sugestão. Quando definido como `0`, o valor de `#editor.lineHeight#` é utilizado. O valor mínimo é 8.", "editor.tabSize": "O número de espaços a que uma tabulação é igual. Esta configuração é substituída com base no conteúdo do ficheiro quando `#editor.detectIndentation#` está ligado.", + "editor.useTabStops": "A inserção e eliminação de espaços em branco segue as paragens de tabulação.", + "editor.wordBasedSuggestions": "Controla se as conclusões devem ser calculadas com base nas palavras do documento.", + "editor.wordBasedSuggestionsMode": "Controla a partir de que documentos são calculadas as conclusões baseadas em palavras.", "files.autoSave": "Controla [auto guardar](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) de editores que tenham alterações não guardadas.", "files.autoSave.afterDelay": "Um editor com alterações é automaticamente guardado após a configuração `#files.autoSaveDelay#``.", "files.autoSave.off": "Um editor com alterações nunca é automaticamente guardado.", @@ -336,11 +342,16 @@ "autoReveal": "Auto Revelação", "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão do seu browser.", "clipboardWarnFirefox": "A API da área de transferência não está disponível. Pode ser activada através da preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Note que isso permitirá ao FireFox obter acesso total à área de transferência do sistema.", - "noFolderOpened": "Nenhuma pasta aberta", "refresh": "Actualizar no Explorer", "reveal": "Revelar no Explorer", "toggleHiddenFiles": "Alternar ficheiros escondidos" }, + "notebook": { + "dragGhostImage": { + "codeText": "Célula de código selecionada", + "markdownText": "Célula Mardown selecionada" + } + }, "output": { "clearOutputChannel": "Canal de saída transparente...", "closeOutputChannel": "Fechar canal de saída...", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index 788a3251d5164..7e34f194d380e 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "Предположим, что устройство чтения с экрана не подключено", "editor.bracketPairColorization.enabled": "Управляет тем, включена или нет раскраска пар скобок. Используйте `#workbench.colorCustomizations#`, чтобы переопределить цвета выделения скобок.", "editor.codeActionWidget.includeNearbyQuickfixes": "Включить/выключить показ ближайшего быстрого исправления в линии, если в данный момент нет диагностики.", + "editor.cursorSurroundingLinesStyle": "Контролирует, когда `#cursorSurroundingLines#` должен быть применен.", "editor.detectIndentation": "Определяет, будут ли `#editor.tabSize#` и `#editor.insertSpaces#` автоматически определяться при открытии файла на основе его содержимого.", + "editor.dropIntoEditor.enabled": "Позволяет перетащить файл в текстовый редактор, удерживая клавишу `shift` (вместо открытия файла в редакторе).", "editor.formatOnSaveMode.modificationsIfAvailable": "Попытается отформатировать только модификации (требуется контроль исходного текста). Если контроль источника не может быть использован, то будет отформатирован весь файл.", "editor.hover.hidingDelay": "Управляет задержкой в миллисекундах, после которой наведение будет скрыто. Требуется, чтобы `editor.hover.sticky` был включен.", "editor.inlayHints.enabled1": "Подсказки инкрустации отображаются по умолчанию и скрываются при нажатии `Ctrl+Alt`.", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "Управляет семейством шрифтов подсказок инлея в редакторе. Если установлено значение empty, используется `#editor.fontFamily#`.", "editor.inlayHints.fontSize": "Управляет размером шрифта подсказок инлея в редакторе. По умолчанию используется `#editor.fontSize#`, если настроенное значение меньше `5` или больше размера шрифта редактора.", "editor.insertSpaces": "Вставлять пробелы при нажатии клавиши `Tab`. Эта настройка переопределяется в зависимости от содержимого файла, если включена опция `#editor.detectIndentation#`.", + "editor.occurrencesHighlight": "Управляет тем, должен ли редактор выделять вхождения семантических символов.", "editor.quickSuggestions": "Управляет тем, должны ли предложения автоматически появляться при вводе текста. Этим можно управлять при вводе комментариев, строк и другого кода. Быстрые предложения могут быть настроены на отображение в виде призрачного текста или виджета предложений. Также обратите внимание на настройку '#editor.suggestOnTriggerCharacters#', которая определяет, будут ли появляться предложения при использовании специальных символов.", "editor.stickyScroll.scrollWithEditor": "Включите прокрутку виджета липкой прокрутки с помощью горизонтальной полосы прокрутки редактора.", "editor.suggestFontSize": "Размер шрифта для виджета предложения. Если установлено значение `0`, используется значение `#editor.fontSize#`.", "editor.suggestLineHeight": "Высота строки для виджета предложения. Если установлено значение `0`, используется значение `#editor.lineHeight#`. Минимальное значение - 8.", "editor.tabSize": "Количество пробелов, которым равна табуляция. Эта настройка переопределяется в зависимости от содержимого файла, если включена функция `#editor.detectIndentation#`.", + "editor.useTabStops": "Вставка и удаление пробельных символов следует за остановками табуляции.", + "editor.wordBasedSuggestions": "Указывает, следует ли вычислять завершения на основе слов в документе.", + "editor.wordBasedSuggestionsMode": "Служит для управления тем, на основе каких документов вычисляются словосочетания.", "files.autoSave": "Управляет [автосохранением](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) редакторов, в которых есть несохраненные изменения.", "files.autoSave.afterDelay": "Редактор с изменениями автоматически сохраняется по истечении настроенной `#files.autoSaveDelay#`.", "files.autoSave.off": "Редактор с изменениями никогда не сохраняется автоматически.", @@ -336,11 +342,16 @@ "autoReveal": "Автоматическое раскрытие", "clipboardWarn": "Доступ к буферу обмена запрещен. Проверьте разрешение вашего браузера.", "clipboardWarnFirefox": "API буфера обмена недоступен. Его можно включить с помощью '{0}' предпочтения на странице '{1}'. Затем перезагрузите Theia. Обратите внимание, это позволит FireFox получить полный доступ к системному буферу обмена.", - "noFolderOpened": "Папка не открыта", "refresh": "Обновить в Проводнике", "reveal": "Раскрытие в Проводнике", "toggleHiddenFiles": "Переключение скрытых файлов" }, + "notebook": { + "dragGhostImage": { + "codeText": "Выбранная ячейка кода", + "markdownText": "Выбранная клетка Мардауна" + } + }, "output": { "clearOutputChannel": "Очистить выходной канал...", "closeOutputChannel": "Закрыть выходной канал...", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index fd5dc52c42350..604f18555ce5d 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -112,7 +112,9 @@ "editor.accessibilitySupport2": "假设未连接屏幕阅读器", "editor.bracketPairColorization.enabled": "控制是否启用括号对着色。使用 `#workbench.colorCustomizations#` 覆盖括号高亮颜色。", "editor.codeActionWidget.includeNearbyQuickfixes": "启用/禁用在当前未进行诊断时显示行内最近的快速修复。", + "editor.cursorSurroundingLinesStyle": "控制何时执行 `#cursorSurroundingLines#` 。", "editor.detectIndentation": "控制打开文件时是否根据文件内容自动检测 `#editor.tabSize#` 和 `#editor.insertSpaces#`。", + "editor.dropIntoEditor.enabled": "控制是否可以按住 `shift` 将文件拖放到文本编辑器中(而不是在编辑器中打开文件)。", "editor.formatOnSaveMode.modificationsIfAvailable": "将尝试只对修改部分进行格式化(需要源代码控制)。如果不能使用源码控制,那么整个文件将被格式化。", "editor.hover.hidingDelay": "控制隐藏悬停后的延迟时间(毫秒)。需要启用 `editor.hover.sticky`。", "editor.inlayHints.enabled1": "镶嵌提示默认显示,按住 \"Ctrl+Alt \"时隐藏。", @@ -120,11 +122,15 @@ "editor.inlayHints.fontFamily": "控制编辑器中镶嵌提示的字体家族。设置为空时,将使用 `#editor.fontFamily#`。", "editor.inlayHints.fontSize": "控制编辑器中镶嵌提示的字体大小。默认情况下,当配置值小于 \"5 \"或大于编辑器字体大小时,将使用 \"#editor.fontSize#\"。", "editor.insertSpaces": "按 `Tab` 键时插入空格。当启用 `#editor.detectIndentation#`时,该设置会根据文件内容被覆盖。", + "editor.occurrencesHighlight": "控制编辑器是否高亮显示语义符号。", "editor.quickSuggestions": "控制在输入时是否应该自动显示建议。这可以在输入评论、字符串和其他代码时加以控制。快速建议可以被配置为显示为幽灵文本或建议小部件。还要注意'#editor.suggestOnTriggerCharacters#'设置,它控制建议是否被特殊字符触发。", "editor.stickyScroll.scrollWithEditor": "使用编辑器的水平滚动条启用粘性滚动 widget 的滚动功能。", "editor.suggestFontSize": "建议 widget 的字体大小。设置为 \"0 \"时,将使用 \"#editor.fontSize#\"的值。", "editor.suggestLineHeight": "建议 widget 的行高。设置为 \"0 \"时,将使用 \"#editor.lineHeight#\"的值。最小值为 8。", "editor.tabSize": "制表符等于的空格数。当启用 `#editor.detectIndentation#`时,该设置会根据文件内容被覆盖。", + "editor.useTabStops": "在制表符后插入和删除空白。", + "editor.wordBasedSuggestions": "控制是否根据文档中的单词计算补全。", + "editor.wordBasedSuggestionsMode": "控制从哪些文档中计算基于单词的补全。", "files.autoSave": "控制有未保存的修改的编辑器的[自动保存](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)。", "files.autoSave.afterDelay": "在配置的 `#files.autoSaveDelay#`之后,有改动的编辑器会自动保存。", "files.autoSave.off": "有变化的编辑器永远不会被自动保存。", @@ -336,11 +342,16 @@ "autoReveal": "自动显示", "clipboardWarn": "对剪贴板的访问被拒绝了。检查你的浏览器的权限。", "clipboardWarnFirefox": "剪贴板API是不可用的。它可以通过'{0}'页面上的'{1}'偏好启用。然后重新加载Theia。注意,这将允许FireFox获得对系统剪贴板的完全访问。", - "noFolderOpened": "未打开文件夹", "refresh": "在资源管理器中刷新", "reveal": "在资源管理器中显示", "toggleHiddenFiles": "切换隐藏文件" }, + "notebook": { + "dragGhostImage": { + "codeText": "已选择代码单元", + "markdownText": "已选定 Mardown 牢房" + } + }, "output": { "clearOutputChannel": "清除输出通道...", "closeOutputChannel": "关闭输出通道...", From 433adffb25def975edaa9ebd9d1341de56aa09e0 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 3 Jun 2024 14:01:55 +0200 Subject: [PATCH 252/441] core: update re-exports for 1.50.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index c6887f0664ac1..6ba076ea9874b 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.49.0`](https://www.npmjs.com/package/@theia/application-package/v/1.49.0)) - - `@theia/request` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.49.0`](https://www.npmjs.com/package/@theia/request/v/1.49.0)) + - `@theia/application-package` (from [`@theia/application-package@1.50.0`](https://www.npmjs.com/package/@theia/application-package/v/1.50.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.50.0`](https://www.npmjs.com/package/@theia/application-package/v/1.50.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.50.0`](https://www.npmjs.com/package/@theia/application-package/v/1.50.0)) + - `@theia/request` (from [`@theia/request@1.50.0`](https://www.npmjs.com/package/@theia/request/v/1.50.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.50.0`](https://www.npmjs.com/package/@theia/request/v/1.50.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.50.0`](https://www.npmjs.com/package/@theia/request/v/1.50.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From 0ce85db6ef8b461c58f2e24cc82c7fc621bd7718 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 3 Jun 2024 14:02:13 +0200 Subject: [PATCH 253/441] v1.50.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 90 ++++++++-------- examples/browser/package.json | 102 +++++++++--------- examples/electron/package.json | 100 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 28 ++--- packages/dev-container/package.json | 12 +-- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 14 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 +++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 12 +-- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 18 ++-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 72 files changed, 516 insertions(+), 516 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 5f153fa5ef03f..041c955afa0bb 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.49.0", - "@theia/ffmpeg": "1.49.0", - "@theia/native-webpack-plugin": "1.49.0", + "@theia/application-package": "1.50.0", + "@theia/ffmpeg": "1.50.0", + "@theia/native-webpack-plugin": "1.50.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 10cc21e0a8240..474a2d7fc8ef9 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.49.0", + "@theia/request": "1.50.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index e8283d617d5f4..5bb7ad72ae9c5 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,12 +32,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.49.0", - "@theia/application-package": "1.49.0", - "@theia/ffmpeg": "1.49.0", - "@theia/localization-manager": "1.49.0", - "@theia/ovsx-client": "1.49.0", - "@theia/request": "1.49.0", + "@theia/application-manager": "1.50.0", + "@theia/application-package": "1.50.0", + "@theia/ffmpeg": "1.50.0", + "@theia/localization-manager": "1.50.0", + "@theia/ovsx-client": "1.50.0", + "@theia/request": "1.50.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index ebbfba69ed3ab..f73000d39ffac 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index e4fec1c2065a4..83b945c0b5ff7 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~5.4.5" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index 6502832c2165a..498788bfa69fb 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.49.0", + "version": "1.50.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index fb6d6a0cbf67d..de6baab58fb4b 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.49.0", + "@theia/request": "1.50.0", "semver": "^7.5.4", "tslib": "^2.6.2" } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index c96faee5ffd49..153ba4c87e15f 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.49.0", + "version": "1.50.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.49.0", - "@theia/ext-scripts": "1.49.0", - "@theia/re-exports": "1.49.0", + "@theia/core": "1.50.0", + "@theia/ext-scripts": "1.50.0", + "@theia/re-exports": "1.50.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index 896541e54e442..fa6959c1defec 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.49.0", + "version": "1.50.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index e532099c08618..9f3603a3c1577 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index adddf0c9ac13e..ba19567587f18 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index ce21ee47c43ee..a4676f2f41c96 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/plugin-ext-headless": "1.49.0" + "@theia/core": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/plugin-ext-headless": "1.50.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index cb9a09b45b939..e29dd873c4bfc 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.49.0", - "@theia/ovsx-client": "1.49.0", - "@theia/search-in-workspace": "1.49.0", - "@theia/test": "1.49.0", - "@theia/toolbar": "1.49.0", - "@theia/vsx-registry": "1.49.0", - "@theia/workspace": "1.49.0" + "@theia/output": "1.50.0", + "@theia/ovsx-client": "1.50.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/test": "1.50.0", + "@theia/toolbar": "1.50.0", + "@theia/vsx-registry": "1.50.0", + "@theia/workspace": "1.50.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 95dc5c56a778c..9a3dd1d9e6b67 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.49.0" + "@theia/core": "1.50.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index d9617d165a0ba..7cca04dd0d1a5 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.49.0", + "version": "1.50.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,49 +15,49 @@ } }, "dependencies": { - "@theia/api-samples": "1.49.0", - "@theia/bulk-edit": "1.49.0", - "@theia/callhierarchy": "1.49.0", - "@theia/console": "1.49.0", - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/editor-preview": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/getting-started": "1.49.0", - "@theia/git": "1.49.0", - "@theia/keymaps": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/memory-inspector": "1.49.0", - "@theia/messages": "1.49.0", - "@theia/metrics": "1.49.0", - "@theia/mini-browser": "1.49.0", - "@theia/monaco": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/outline-view": "1.49.0", - "@theia/output": "1.49.0", - "@theia/plugin-dev": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/plugin-ext-vscode": "1.49.0", - "@theia/plugin-metrics": "1.49.0", - "@theia/preferences": "1.49.0", - "@theia/preview": "1.49.0", - "@theia/process": "1.49.0", - "@theia/property-view": "1.49.0", - "@theia/scm": "1.49.0", - "@theia/scm-extra": "1.49.0", - "@theia/search-in-workspace": "1.49.0", - "@theia/secondary-window": "1.49.0", - "@theia/task": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/timeline": "1.49.0", - "@theia/toolbar": "1.49.0", - "@theia/typehierarchy": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/vsx-registry": "1.49.0", - "@theia/workspace": "1.49.0" + "@theia/api-samples": "1.50.0", + "@theia/bulk-edit": "1.50.0", + "@theia/callhierarchy": "1.50.0", + "@theia/console": "1.50.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/editor-preview": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/getting-started": "1.50.0", + "@theia/git": "1.50.0", + "@theia/keymaps": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/memory-inspector": "1.50.0", + "@theia/messages": "1.50.0", + "@theia/metrics": "1.50.0", + "@theia/mini-browser": "1.50.0", + "@theia/monaco": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/outline-view": "1.50.0", + "@theia/output": "1.50.0", + "@theia/plugin-dev": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/plugin-ext-vscode": "1.50.0", + "@theia/plugin-metrics": "1.50.0", + "@theia/preferences": "1.50.0", + "@theia/preview": "1.50.0", + "@theia/process": "1.50.0", + "@theia/property-view": "1.50.0", + "@theia/scm": "1.50.0", + "@theia/scm-extra": "1.50.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/secondary-window": "1.50.0", + "@theia/task": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/timeline": "1.50.0", + "@theia/toolbar": "1.50.0", + "@theia/typehierarchy": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/vsx-registry": "1.50.0", + "@theia/workspace": "1.50.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -73,6 +73,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.49.0" + "@theia/cli": "1.50.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index 1c2a64f21647f..91be9a7a8df2a 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.49.0", + "version": "1.50.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,54 +20,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.49.0", - "@theia/api-samples": "1.49.0", - "@theia/bulk-edit": "1.49.0", - "@theia/callhierarchy": "1.49.0", - "@theia/console": "1.49.0", - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", - "@theia/dev-container": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/editor-preview": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/getting-started": "1.49.0", - "@theia/keymaps": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/memory-inspector": "1.49.0", - "@theia/messages": "1.49.0", - "@theia/metrics": "1.49.0", - "@theia/mini-browser": "1.49.0", - "@theia/monaco": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/notebook": "1.49.0", - "@theia/outline-view": "1.49.0", - "@theia/output": "1.49.0", - "@theia/plugin-dev": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/plugin-ext-headless": "1.49.0", - "@theia/plugin-ext-vscode": "1.49.0", - "@theia/plugin-metrics": "1.49.0", - "@theia/preferences": "1.49.0", - "@theia/preview": "1.49.0", - "@theia/process": "1.49.0", - "@theia/property-view": "1.49.0", - "@theia/remote": "1.49.0", - "@theia/scm": "1.49.0", - "@theia/scm-extra": "1.49.0", - "@theia/search-in-workspace": "1.49.0", - "@theia/secondary-window": "1.49.0", - "@theia/task": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/test": "1.49.0", - "@theia/timeline": "1.49.0", - "@theia/toolbar": "1.49.0", - "@theia/typehierarchy": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/vsx-registry": "1.49.0", - "@theia/workspace": "1.49.0" + "@theia/api-provider-sample": "1.50.0", + "@theia/api-samples": "1.50.0", + "@theia/bulk-edit": "1.50.0", + "@theia/callhierarchy": "1.50.0", + "@theia/console": "1.50.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", + "@theia/dev-container": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/editor-preview": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/getting-started": "1.50.0", + "@theia/keymaps": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/memory-inspector": "1.50.0", + "@theia/messages": "1.50.0", + "@theia/metrics": "1.50.0", + "@theia/mini-browser": "1.50.0", + "@theia/monaco": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/notebook": "1.50.0", + "@theia/outline-view": "1.50.0", + "@theia/output": "1.50.0", + "@theia/plugin-dev": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/plugin-ext-headless": "1.50.0", + "@theia/plugin-ext-vscode": "1.50.0", + "@theia/plugin-metrics": "1.50.0", + "@theia/preferences": "1.50.0", + "@theia/preview": "1.50.0", + "@theia/process": "1.50.0", + "@theia/property-view": "1.50.0", + "@theia/remote": "1.50.0", + "@theia/scm": "1.50.0", + "@theia/scm-extra": "1.50.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/secondary-window": "1.50.0", + "@theia/task": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/test": "1.50.0", + "@theia/timeline": "1.50.0", + "@theia/toolbar": "1.50.0", + "@theia/typehierarchy": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/vsx-registry": "1.50.0", + "@theia/workspace": "1.50.0" }, "scripts": { "clean": "theia clean", @@ -90,6 +90,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.49.0" + "@theia/cli": "1.50.0" } -} \ No newline at end of file +} diff --git a/examples/electron/package.json b/examples/electron/package.json index 4f38f0295383e..91d3396805e1f 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.49.0", + "version": "1.50.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -26,53 +26,53 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.49.0", - "@theia/api-samples": "1.49.0", - "@theia/bulk-edit": "1.49.0", - "@theia/callhierarchy": "1.49.0", - "@theia/console": "1.49.0", - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", - "@theia/dev-container": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/editor-preview": "1.49.0", - "@theia/electron": "1.49.0", - "@theia/external-terminal": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/getting-started": "1.49.0", - "@theia/keymaps": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/memory-inspector": "1.49.0", - "@theia/messages": "1.49.0", - "@theia/metrics": "1.49.0", - "@theia/mini-browser": "1.49.0", - "@theia/monaco": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/outline-view": "1.49.0", - "@theia/output": "1.49.0", - "@theia/plugin-dev": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/plugin-ext-headless": "1.49.0", - "@theia/plugin-ext-vscode": "1.49.0", - "@theia/preferences": "1.49.0", - "@theia/preview": "1.49.0", - "@theia/process": "1.49.0", - "@theia/property-view": "1.49.0", - "@theia/remote": "1.49.0", - "@theia/scm": "1.49.0", - "@theia/scm-extra": "1.49.0", - "@theia/search-in-workspace": "1.49.0", - "@theia/secondary-window": "1.49.0", - "@theia/task": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/timeline": "1.49.0", - "@theia/toolbar": "1.49.0", - "@theia/typehierarchy": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/vsx-registry": "1.49.0", - "@theia/workspace": "1.49.0" + "@theia/api-provider-sample": "1.50.0", + "@theia/api-samples": "1.50.0", + "@theia/bulk-edit": "1.50.0", + "@theia/callhierarchy": "1.50.0", + "@theia/console": "1.50.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", + "@theia/dev-container": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/editor-preview": "1.50.0", + "@theia/electron": "1.50.0", + "@theia/external-terminal": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/getting-started": "1.50.0", + "@theia/keymaps": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/memory-inspector": "1.50.0", + "@theia/messages": "1.50.0", + "@theia/metrics": "1.50.0", + "@theia/mini-browser": "1.50.0", + "@theia/monaco": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/outline-view": "1.50.0", + "@theia/output": "1.50.0", + "@theia/plugin-dev": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/plugin-ext-headless": "1.50.0", + "@theia/plugin-ext-vscode": "1.50.0", + "@theia/preferences": "1.50.0", + "@theia/preview": "1.50.0", + "@theia/process": "1.50.0", + "@theia/property-view": "1.50.0", + "@theia/remote": "1.50.0", + "@theia/scm": "1.50.0", + "@theia/scm-extra": "1.50.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/secondary-window": "1.50.0", + "@theia/task": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/timeline": "1.50.0", + "@theia/toolbar": "1.50.0", + "@theia/typehierarchy": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/vsx-registry": "1.50.0", + "@theia/workspace": "1.50.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -90,7 +90,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.49.0", + "@theia/cli": "1.50.0", "electron": "^28.2.8" } -} \ No newline at end of file +} diff --git a/examples/playwright/package.json b/examples/playwright/package.json index b685ab8597b8e..93a837a23e8cd 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.49.0", + "version": "1.50.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index 715a667ab608b..6ca786b2d2d76 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.49.0", + "version": "1.50.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index a63c50027fa26..cd6d8d575bcd9 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.49.0", + "@theia/workspace": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index ff55e82e52ca8..11a89161d73a6 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index c3ba587ce32f2..74fdeefbecf98 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index 837d9c05d7c45..f5d6941838133 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.49.0", - "@theia/request": "1.49.0", + "@theia/application-package": "1.50.0", + "@theia/request": "1.50.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -209,8 +209,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", - "@theia/re-exports": "1.49.0", + "@theia/ext-scripts": "1.50.0", + "@theia/re-exports": "1.50.0", "minimist": "^1.2.0" }, "nyc": { diff --git a/packages/debug/package.json b/packages/debug/package.json index d25c0532758b3..e3bed8a2557c4 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,21 +1,21 @@ { "name": "@theia/debug", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.49.0", - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/console": "1.50.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.49.0", - "@theia/process": "1.49.0", - "@theia/task": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/output": "1.50.0", + "@theia/process": "1.50.0", + "@theia/task": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/workspace": "1.50.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -58,7 +58,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index d51c2170b0a11..6f4e55fa71dda 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,12 +1,12 @@ { "name": "@theia/dev-container", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/output": "1.49.0", - "@theia/remote": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/output": "1.50.0", + "@theia/remote": "1.50.0", + "@theia/workspace": "1.50.0", "dockerode": "^4.0.2", "jsonc-parser": "^2.2.0", "uuid": "^8.0.0" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index ff800ee986882..79f3fdd993363 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/navigator": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/navigator": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index 4e76681cb87f7..71e3a75b3c0d3 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/variable-resolver": "1.49.0", + "@theia/core": "1.50.0", + "@theia/variable-resolver": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index a31c805bc1252..fe1a4f10fc16f 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", - "@theia/re-exports": "1.49.0" + "@theia/ext-scripts": "1.50.0", + "@theia/re-exports": "1.50.0" }, "peerDependencies": { "electron": "^28.2.8" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index a4054ef33ba19..a1c7f6e2fe818 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/workspace": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 052c376758d58..d4e2b469a6d14 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/process": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/process": "1.50.0", + "@theia/workspace": "1.50.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index 3d8a65d90ba1f..504d0ea9d7566 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -73,7 +73,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 0992a56fd6767..75b32bcdeb60c 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/keymaps": "1.49.0", - "@theia/preview": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/keymaps": "1.50.0", + "@theia/preview": "1.50.0", + "@theia/workspace": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 2c9a0ceded313..8d4224ec9deaf 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.49.0", - "@theia/scm": "1.49.0", - "@theia/scm-extra": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/navigator": "1.50.0", + "@theia/scm": "1.50.0", + "@theia/scm-extra": "1.50.0", + "@theia/workspace": "1.50.0", "@types/diff": "^3.2.2", "@types/p-queue": "^2.3.1", "diff": "^3.4.0", @@ -67,7 +67,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index 5879738cc886e..05167cfc29f75 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.49.0", - "@theia/userstorage": "1.49.0", + "@theia/preferences": "1.50.0", + "@theia/userstorage": "1.50.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index 0815d6cc2fc74..45db4103633ee 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/workspace": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 304b95c255c43..d8d1d1f8dacc2 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index b9e20be90a58f..c08435ec7bde9 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index d6d836f49753e..e71613da26a3e 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 5f18723ddd0c7..df57df917e048 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 885304f399a11..e7c7247de58e7 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/markers": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/markers": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/outline-view": "1.50.0", + "@theia/workspace": "1.50.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index da73f3da4a724..529e908a544c4 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/workspace": "1.50.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 4230c133036ce..b650fe73a789e 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,14 +1,14 @@ { "name": "@theia/notebook", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.49.0", + "@theia/outline-view": "1.50.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/markdown-it": "^12.2.3", "@types/vscode-notebook-renderer": "^1.72.0" }, diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index a2be8bf2631e9..517bafa4e7a4e 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index 84100459dd984..4defd62cfa78b 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index 7939a13bab50c..30bdcf37f7844 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/output": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/output": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/workspace": "1.50.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index d881283142dd8..99c82f69e7994 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/terminal": "1.49.0", + "@theia/core": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/terminal": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index e679a78cf59f6..c854201edc640 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,22 +1,22 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.49.0", - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/callhierarchy": "1.50.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.49.0", - "@theia/outline-view": "1.49.0", - "@theia/plugin": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/typehierarchy": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/navigator": "1.50.0", + "@theia/outline-view": "1.50.0", + "@theia/plugin": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/typehierarchy": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/workspace": "1.50.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index f88dff0ba7958..5082ac7976658 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.49.0", - "@theia/callhierarchy": "1.49.0", - "@theia/console": "1.49.0", - "@theia/core": "1.49.0", - "@theia/debug": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/editor-preview": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/messages": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/bulk-edit": "1.50.0", + "@theia/callhierarchy": "1.50.0", + "@theia/console": "1.50.0", + "@theia/core": "1.50.0", + "@theia/debug": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/editor-preview": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/messages": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.49.0", - "@theia/notebook": "1.49.0", - "@theia/output": "1.49.0", - "@theia/plugin": "1.49.0", - "@theia/preferences": "1.49.0", - "@theia/scm": "1.49.0", - "@theia/search-in-workspace": "1.49.0", - "@theia/task": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/test": "1.49.0", - "@theia/timeline": "1.49.0", - "@theia/typehierarchy": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/navigator": "1.50.0", + "@theia/notebook": "1.50.0", + "@theia/output": "1.50.0", + "@theia/plugin": "1.50.0", + "@theia/preferences": "1.50.0", + "@theia/scm": "1.50.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/task": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/test": "1.50.0", + "@theia/timeline": "1.50.0", + "@theia/typehierarchy": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/workspace": "1.50.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index c843d7bd50184..9cee256920354 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.49.0", - "@theia/metrics": "1.49.0", + "@theia/core": "1.50.0", + "@theia/metrics": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.49.0", - "@theia/plugin-ext": "1.49.0", + "@theia/plugin": "1.50.0", + "@theia/plugin-ext": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 36e44f7bcd3c4..cd14cca6a5eb1 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 67c9f1529209d..d8f3b4ddc021d 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/userstorage": "1.50.0", + "@theia/workspace": "1.50.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index e21dd7cb3d48b..c8e3ef8679a47 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/mini-browser": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/mini-browser": "1.50.0", + "@theia/monaco": "1.50.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index 0a83f10498a15..bd159fc2c4dca 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index a82e63a3e76a7..ef6f37a453a96 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 624e74771e02e..afc63314566a2 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index e346b7b069682..bec5e2d689d2c 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/scm": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/scm": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index 1ec63d0bc4927..552b7669c3ee2 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,12 +1,12 @@ { "name": "@theia/scm", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^3.2.2", "diff": "^3.4.0", @@ -48,7 +48,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 8875bdc22a436..d1ccc5fbe1072 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/process": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/process": "1.50.0", + "@theia/workspace": "1.50.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index 23543736851a9..d593e7ca9d3cc 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index f4ada852b7b60..fe51482b1b8a4 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/markers": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/markers": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.49.0", - "@theia/terminal": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/process": "1.50.0", + "@theia/terminal": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/workspace": "1.50.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index d01054f84426a..aa6c00f8f090f 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/process": "1.49.0", - "@theia/variable-resolver": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/process": "1.50.0", + "@theia/variable-resolver": "1.50.0", + "@theia/workspace": "1.50.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index c9d180784203d..431dd55a4aa48 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/terminal": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/terminal": "1.50.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index d5e19db776992..7cf2f16c4af29 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/navigator": "1.49.0", + "@theia/core": "1.50.0", + "@theia/navigator": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index ca0fdb37f31a3..074071814618e 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", - "@theia/file-search": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/monaco": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", + "@theia/file-search": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.49.0", - "@theia/userstorage": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/search-in-workspace": "1.50.0", + "@theia/userstorage": "1.50.0", + "@theia/workspace": "1.50.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 564e8d2d37839..972734a6e43a2 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/editor": "1.49.0", + "@theia/core": "1.50.0", + "@theia/editor": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 114d89c74f34c..27beacd3f2530 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index 072dc08158c1d..ae4497f92624e 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.49.0", + "@theia/core": "1.50.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index bc4602e1e2e67..004b066dee6b0 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/navigator": "1.49.0", - "@theia/ovsx-client": "1.49.0", - "@theia/plugin-ext": "1.49.0", - "@theia/plugin-ext-vscode": "1.49.0", - "@theia/preferences": "1.49.0", - "@theia/workspace": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/navigator": "1.50.0", + "@theia/ovsx-client": "1.50.0", + "@theia/plugin-ext": "1.50.0", + "@theia/plugin-ext-vscode": "1.50.0", + "@theia/preferences": "1.50.0", + "@theia/workspace": "1.50.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -54,7 +54,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0", + "@theia/ext-scripts": "1.50.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index b0ba638c5efbb..081d0089f7e50 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.49.0", + "version": "1.50.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.49.0", - "@theia/filesystem": "1.49.0", - "@theia/variable-resolver": "1.49.0", + "@theia/core": "1.50.0", + "@theia/filesystem": "1.50.0", + "@theia/variable-resolver": "1.50.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.49.0" + "@theia/ext-scripts": "1.50.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index e9ebb8dd7bdd5..c771b32f789f3 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.49.0", + "version": "1.50.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 3adeb5e79e204..936b6509a806f 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.49.0", + "version": "1.50.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index caada66babf7e..3bc1c4fce9b1b 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.49.0", + "version": "1.50.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.49.0" + "@theia/api-provider-sample": "1.50.0" }, "scripts": { "prepare": "yarn -s package", From 249fd1347f51d3b183d511ce60cc65a38dab5769 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 3 Jun 2024 12:49:27 +0200 Subject: [PATCH 254/441] docs: updated changelog for 1.50.0 Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8f79af9a3761..f135fb348667b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,49 @@ -## 1.50.0 +## 1.50.0 - 06/03/2024 - [application-package] bumped the default supported API from `1.88.1` to `1.89.1` [#13738](https://github.com/eclipse-theia/theia/pull/13738) - contributed on behalf of STMicroelectronics -- [plugin] Support WindowState active API [#13718](https://github.com/eclipse-theia/theia/pull/13718) - contributed on behalf of STMicroelectronics +- [cli] upgrade the Theia build to use Typescript 5.4.5 [#13628](https://github.com/eclipse-theia/theia/pull/13628) - Contributed on behalf of STMicroelectronics +- [core] added logic to delegate showing help to the back end process. [#13729](https://github.com/eclipse-theia/theia/pull/13729) - Contributed on behalf of STMicroelectronics +- [core] added logic to don't reveal the focused element when updating the tree rows [#13703](https://github.com/eclipse-theia/theia/pull/13703) - Contributed on behalf of STMicroelectronics +- [core] added logic to ensure globalSelection is correctly set when opening context menu on a tree widget [#13710](https://github.com/eclipse-theia/theia/pull/13710) +- [core] added to logic to ensure usage of user-defined `THEIA_CONFIG_DIR` [#13708](https://github.com/eclipse-theia/theia/pull/13708) - Contributed on behalf of STMicroelectronics +- [core] fixed hex editor by updating `msgpckr` to 1.10.2 [#13722](https://github.com/eclipse-theia/theia/pull/13722) +- [core] improved `WebSocketConnectionProvider` deprecation message [#13713](https://github.com/eclipse-theia/theia/pull/13713) - Contributed on behalf of STMicroelectronics +- [core] refactored auto save mechanism via a central service [#13683](https://github.com/eclipse-theia/theia/pull/13683) +- [core] updated logic to make browserWindow of splashScreen transparent [#13699](https://github.com/eclipse-theia/theia/pull/13699) +- [dev-container] added support for four previously unsupported dev container properties [#13714](https://github.com/eclipse-theia/theia/pull/13714) +- [dev-container] improved logic to show dev-container label in status bar [#13744](https://github.com/eclipse-theia/theia/pull/13744) +- [electron] updated electron to ^28.2.8 [#13580](https://github.com/eclipse-theia/theia/pull/13580) +- [navigator] added logic to handle `isFileSystemResource` context key [#13664](https://github.com/eclipse-theia/theia/pull/13664) +- [navigator] added logic to not show the new "Open With..." command on folders [#13678](https://github.com/eclipse-theia/theia/pull/13678) +- [notebook] added additional css to notebook output webviews [#13666](https://github.com/eclipse-theia/theia/pull/13666) +- [notebook] added basics for notebook cell drag image renderers [#13698](https://github.com/eclipse-theia/theia/pull/13698) +- [notebook] added logic to select next notebook cell on first or last line of editor [#13656](https://github.com/eclipse-theia/theia/pull/13656) +- [notebook] added logic to select the last cell when deleting selected last cell [#13715](https://github.com/eclipse-theia/theia/pull/13715) +- [notebook] added logic to stop execution when deleting cell [#13701](https://github.com/eclipse-theia/theia/pull/13701) +- [notebook] added responsive design for the main notebook toolbar [#13663](https://github.com/eclipse-theia/theia/pull/13663) +- [notebook] aligned commands with vscode notebook commands [#13645](https://github.com/eclipse-theia/theia/pull/13645) +- [notebook] aligned notebook scroll into view behaviour with vscode [#13742](https://github.com/eclipse-theia/theia/pull/13742) +- [notebook] fixed focus loss of the notebook editor widget when bluring a cell editor [#13741](https://github.com/eclipse-theia/theia/pull/13741) +- [notebook] fixed notebook cell divider size [#13745](https://github.com/eclipse-theia/theia/pull/13745) +- [notebook] fixed storing of the notebook-outlineview state data [#13648](https://github.com/eclipse-theia/theia/pull/13648) +- [notebook] improved notebook cell model lifecycle [#13675](https://github.com/eclipse-theia/theia/pull/13675) +- [notebook] improved support for creating new notebooks [#13696](https://github.com/eclipse-theia/theia/pull/13696) +- [plugin] added stub for `registerMappedEditProvider` [#13681](https://github.com/eclipse-theia/theia/pull/13681) - Contributed on behalf of STMicroelectronics +- [plugin] added support `WindowState` active API [#13718](https://github.com/eclipse-theia/theia/pull/13718) - contributed on behalf of STMicroelectronics +- [plugin] fixed github authentication built-in for electron case [#13611](https://github.com/eclipse-theia/theia/pull/13611) - Contributed on behalof of STMicroelectronics +- [plugin] fixed incorrect URI conversions in custom-editors-main [#13653](https://github.com/eclipse-theia/theia/pull/13653) +- [plugin] fixed quick pick separators from plugins [#13740](https://github.com/eclipse-theia/theia/pull/13740) +- [plugin] improved vscode tab API [#13730](https://github.com/eclipse-theia/theia/pull/13730) - [plugin] updated `DropMetada` and `documentPaste` proposed API for 1.89 compatibility [#13733](https://github.com/eclipse-theia/theia/pull/13733) - contributed on behalf of STMicroelectronics +- [plugin] updated nls metadata for VSCode API 1.89.0 [#13743](https://github.com/eclipse-theia/theia/pull/13743) +- [remote] added logic to support plugin copying for remote feature [#13369](https://github.com/eclipse-theia/theia/pull/13369) +- [terminal] fixed performance issues in terminal [#13735](https://github.com/eclipse-theia/theia/pull/13735) - Contributed on behalf of STMicroelectronics +- [terminal] updated logic to allow transitive binding for TerminalFrontendContribution [#13667](https://github.com/eclipse-theia/theia/pull/13667) [Breaking Changes:](#breaking_changes_1.50.0) From 4456fde0f9d3a30cba8656d5683db28d726872c0 Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Tue, 4 Jun 2024 12:53:04 +0200 Subject: [PATCH 255/441] Fix overflow behavior of sidebars (#13483) * Fix overflow behavior of sidebars - Ensure that last visible element also gets added to `...` overflow menu if there is not enough space - Remove special behavior for only one overflowing element - This behavior would always add a second element to the `overflow` menu if only one element is currently overflowing. This did not work for the case where only two tabs where open in the first place. In addition, it unnecessarily hide a tab in some cases even if there was enough space to render it. - Extract logic to compute and hide hidden tabs into `hideOverflowingTabs` method - Invoke this method from `computeOverflowingTabsData`. At this point the actual tabs bar is already rendered so we can use the actual position/height information to compute overflowing tabs instead of manually composing the available height with the hidden tabs bar in `updateTabs` - Update `AdditionalViewsMenuWidget` to use a dedicated menu path for each side. This ensures that only the hidden tabs of the corresponding sidebar are displayed when clicking the '...' button Fixes #13482 Contributed on behalf of STMicroelectronics --- .../browser/frontend-application-module.ts | 11 ++- .../shell/additional-views-menu-widget.tsx | 10 +- .../src/browser/shell/side-panel-handler.ts | 3 +- packages/core/src/browser/shell/tab-bars.ts | 95 ++++++++----------- 4 files changed, 52 insertions(+), 67 deletions(-) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 3c35877a40668..6ab58faec8bf9 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -35,7 +35,8 @@ import { MenuCommandAdapterRegistry, MenuCommandExecutor, MenuCommandAdapterRegistryImpl, - MenuCommandExecutorImpl + MenuCommandExecutorImpl, + MenuPath } from '../common'; import { KeybindingRegistry, KeybindingContext, KeybindingContribution } from './keybinding'; import { FrontendApplication } from './frontend-application'; @@ -137,7 +138,7 @@ import { MarkdownRenderer, MarkdownRendererFactory, MarkdownRendererImpl } from import { StylingParticipant, StylingService } from './styling-service'; import { bindCommonStylingParticipants } from './common-styling-participants'; import { HoverService } from './hover-service'; -import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './shell/additional-views-menu-widget'; +import { AdditionalViewsMenuPath, AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './shell/additional-views-menu-widget'; import { LanguageIconLabelProvider } from './language-icon-provider'; import { bindTreePreferences } from './tree'; import { OpenWithService } from './open-with-service'; @@ -177,9 +178,9 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(SidebarBottomMenuWidgetFactory).toAutoFactory(SidebarBottomMenuWidget); bind(AdditionalViewsMenuWidget).toSelf(); bind(AdditionalViewsMenuWidgetFactory).toFactory(ctx => (side: 'left' | 'right') => { - const widget = ctx.container.resolve(AdditionalViewsMenuWidget); - widget.side = side; - return widget; + const childContainer = ctx.container.createChild(); + childContainer.bind(AdditionalViewsMenuPath).toConstantValue(['additional_views_menu', side]); + return childContainer.resolve(AdditionalViewsMenuWidget); }); bind(SplitPositionHandler).toSelf().inSingletonScope(); diff --git a/packages/core/src/browser/shell/additional-views-menu-widget.tsx b/packages/core/src/browser/shell/additional-views-menu-widget.tsx index 5a4f0bb9b9f15..05f57919faf30 100644 --- a/packages/core/src/browser/shell/additional-views-menu-widget.tsx +++ b/packages/core/src/browser/shell/additional-views-menu-widget.tsx @@ -23,13 +23,13 @@ import { SideTabBar } from './tab-bars'; export const AdditionalViewsMenuWidgetFactory = Symbol('AdditionalViewsMenuWidgetFactory'); export type AdditionalViewsMenuWidgetFactory = (side: 'left' | 'right') => AdditionalViewsMenuWidget; -export const ADDITIONAL_VIEWS_MENU_PATH: MenuPath = ['additional_views_menu']; - +export const AdditionalViewsMenuPath = Symbol('AdditionalViewsMenuPath'); @injectable() export class AdditionalViewsMenuWidget extends SidebarMenuWidget { static readonly ID = 'sidebar.additional.views'; - side: 'left' | 'right'; + @inject(AdditionalViewsMenuPath) + protected menuPath: MenuPath; @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; @@ -47,7 +47,7 @@ export class AdditionalViewsMenuWidget extends SidebarMenuWidget { title: nls.localizeByDefault('Additional Views'), iconClass: codicon('ellipsis'), id: AdditionalViewsMenuWidget.ID, - menuPath: ADDITIONAL_VIEWS_MENU_PATH, + menuPath: this.menuPath, order: 0 }); } @@ -66,6 +66,6 @@ export class AdditionalViewsMenuWidget extends SidebarMenuWidget { }); } })); - this.menuDisposables.push(this.menuModelRegistry.registerMenuAction(ADDITIONAL_VIEWS_MENU_PATH, { commandId: command.id, order: index.toString() })); + this.menuDisposables.push(this.menuModelRegistry.registerMenuAction(this.menuPath, { commandId: command.id, order: index.toString() })); } } diff --git a/packages/core/src/browser/shell/side-panel-handler.ts b/packages/core/src/browser/shell/side-panel-handler.ts index 6bfdb5e066486..ec38f8f2201ff 100644 --- a/packages/core/src/browser/shell/side-panel-handler.ts +++ b/packages/core/src/browser/shell/side-panel-handler.ts @@ -211,6 +211,7 @@ export class SidePanelHandler { protected createAdditionalViewsWidget(): AdditionalViewsMenuWidget { const widget = this.additionalViewsMenuFactory(this.side); widget.addClass('theia-sidebar-menu'); + widget.addClass('theia-additional-views-menu'); return widget; } @@ -653,7 +654,7 @@ export class SidePanelHandler { } protected onTabsOverflowChanged(sender: SideTabBar, event: { titles: Title[], startIndex: number }): void { - if (event.startIndex >= 0 && event.startIndex <= sender.currentIndex) { + if (event.startIndex > 0 && event.startIndex <= sender.currentIndex) { sender.revealTab(sender.currentIndex); } else { this.additionalViewsMenu.updateAdditionalViews(sender, event); diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts index b85c4368f35de..8bb9cd292082e 100644 --- a/packages/core/src/browser/shell/tab-bars.ts +++ b/packages/core/src/browser/shell/tab-bars.ts @@ -1084,8 +1084,6 @@ export class SideTabBar extends ScrollableTabBar { startIndex: number }; - protected _rowGap: number; - constructor(options?: TabBar.IOptions & PerfectScrollbar.Options) { super(options); @@ -1142,31 +1140,6 @@ export class SideTabBar extends ScrollableTabBar { } } - // Queries the tabRowGap value of the content node. Needed to properly compute overflowing - // tabs that should be hidden - protected get tabRowGap(): number { - // We assume that the tab row gap is static i.e. we compute it once an then cache it - if (!this._rowGap) { - this._rowGap = this.computeTabRowGap(); - } - return this._rowGap; - - } - - protected computeTabRowGap(): number { - const style = window.getComputedStyle(this.contentNode); - const rowGapStyle = style.getPropertyValue('row-gap'); - const numericValue = parseFloat(rowGapStyle); - const unit = rowGapStyle.match(/[a-zA-Z]+/)?.[0]; - - const tempDiv = document.createElement('div'); - tempDiv.style.height = '1' + unit; - document.body.appendChild(tempDiv); - const rowGapValue = numericValue * tempDiv.offsetHeight; - document.body.removeChild(tempDiv); - return rowGapValue; - } - /** * Reveal the tab with the given index by moving it into the non-overflowing tabBar section * if necessary. @@ -1207,18 +1180,13 @@ export class SideTabBar extends ScrollableTabBar { const hiddenContent = this.hiddenContentNode; const n = hiddenContent.children.length; const renderData = new Array>(n); - const availableWidth = this.node.clientHeight - this.tabRowGap; - let actualWidth = 0; - let overflowStartIndex = -1; for (let i = 0; i < n; i++) { const hiddenTab = hiddenContent.children[i]; - // Extract tab padding from the computed style + // Extract tab padding, and margin from the computed style const tabStyle = window.getComputedStyle(hiddenTab); - const paddingTop = parseFloat(tabStyle.paddingTop!); - const paddingBottom = parseFloat(tabStyle.paddingBottom!); const rd: Partial = { - paddingTop, - paddingBottom + paddingTop: parseFloat(tabStyle.paddingTop!), + paddingBottom: parseFloat(tabStyle.paddingBottom!) }; // Extract label size from the DOM const labelElements = hiddenTab.getElementsByClassName('p-TabBar-tabLabel'); @@ -1231,38 +1199,21 @@ export class SideTabBar extends ScrollableTabBar { if (iconElements.length === 1) { const icon = iconElements[0]; rd.iconSize = { width: icon.clientWidth, height: icon.clientHeight }; - actualWidth += icon.clientHeight + paddingTop + paddingBottom + this.tabRowGap; - - if (actualWidth > availableWidth && i !== 0) { - rd.visible = false; - if (overflowStartIndex === -1) { - overflowStartIndex = i; - } - } - renderData[i] = rd; } - } - // Special handling if only one element is overflowing. - if (overflowStartIndex === n - 1 && renderData[overflowStartIndex]) { - if (!this.tabsOverflowData) { - overflowStartIndex--; - renderData[overflowStartIndex].visible = false; - } else { - renderData[overflowStartIndex].visible = true; - overflowStartIndex = -1; - } + renderData[i] = rd; } // Render into the visible node this.renderTabs(this.contentNode, renderData); - this.computeOverflowingTabsData(overflowStartIndex); + this.computeOverflowingTabsData(); }); } } - protected computeOverflowingTabsData(startIndex: number): void { + protected computeOverflowingTabsData(): void { // ensure that render tabs has completed window.requestAnimationFrame(() => { + const startIndex = this.hideOverflowingTabs(); if (startIndex === -1) { if (this.tabsOverflowData) { this.tabsOverflowData = undefined; @@ -1286,6 +1237,38 @@ export class SideTabBar extends ScrollableTabBar { }); } + /** + * Hide overflowing tabs and return the index of the first hidden tab. + */ + protected hideOverflowingTabs(): number { + const availableHeight = this.node.clientHeight; + const invisibleClass = 'p-mod-invisible'; + let startIndex = -1; + const n = this.contentNode.children.length; + for (let i = 0; i < n; i++) { + const tab = this.contentNode.children[i] as HTMLLIElement; + if (tab.offsetTop + tab.offsetHeight >= availableHeight) { + tab.classList.add(invisibleClass); + if (startIndex === -1) { + startIndex = i; + /* If only one element is overflowing and the additional menu widget is visible (i.e. this.tabsOverflowData is set) + * there might already be enough space to show the last tab. In this case, we need to include the size of the + * additional menu widget and recheck if the last tab is visible */ + if (startIndex === n - 1 && this.tabsOverflowData) { + const additionalViewsMenu = this.node.parentElement?.querySelector('.theia-additional-views-menu') as HTMLDivElement; + if (tab.offsetTop + tab.offsetHeight < availableHeight + additionalViewsMenu.offsetHeight) { + tab.classList.remove(invisibleClass); + startIndex = -1; + } + } + } + } else { + tab.classList.remove(invisibleClass); + } + } + return startIndex; + } + /** * Render the tab bar using the given DOM element as host. The optional `renderData` is forwarded * to the TabBarRenderer. From a6fb8829705796ee43cf05aa220a61137e01c369 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 4 Jun 2024 16:19:01 +0200 Subject: [PATCH 256/441] fix editors theme change and widget not attached error (#13757) Signed-off-by: Jonah Iden --- packages/monaco/src/browser/monaco-standalone-theme-service.ts | 1 + .../src/main/browser/notebooks/renderers/cell-output-webview.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/monaco/src/browser/monaco-standalone-theme-service.ts b/packages/monaco/src/browser/monaco-standalone-theme-service.ts index f36d7083d6b48..cae2bffd3f3c7 100644 --- a/packages/monaco/src/browser/monaco-standalone-theme-service.ts +++ b/packages/monaco/src/browser/monaco-standalone-theme-service.ts @@ -41,6 +41,7 @@ export class MonacoStandaloneThemeService extends StandaloneThemeService { for (let i = 0; i < this.styleElements.length; i++) { if (this.styleElements[i] === style) { this.styleElements.splice(i, 1); + style.remove(); return; } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index efd1845c92e6f..98cac69ef734c 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -187,6 +187,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); + this.webviewWidget.parent = this.editor ?? null; this.webviewWidget.setContentOptions({ allowScripts: true, // eslint-disable-next-line max-len From a3c89068b959b1386328f859ff0b8e1eabb2a42a Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 6 Jun 2024 12:03:02 +0200 Subject: [PATCH 257/441] Improved Upload Command (#13775) - if nothing is selected uploads to the workspace root - if a folder is selected uploads to that folder - if a file is selected uploads to the parent folder Signed-off-by: Jonah Iden --- .../src/browser/filesystem-frontend-contribution.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts index 836ccd5a65731..6a39696d63e4f 100644 --- a/packages/filesystem/src/browser/filesystem-frontend-contribution.ts +++ b/packages/filesystem/src/browser/filesystem-frontend-contribution.ts @@ -145,7 +145,7 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri commands.registerCommand(FileSystemCommands.UPLOAD, { isEnabled: (...args: unknown[]) => { const selection = this.getSelection(...args); - return !!selection && this.canUpload(selection); + return !!selection && !environment.electron.is(); }, isVisible: () => !environment.electron.is(), execute: (...args: unknown[]) => { @@ -162,14 +162,10 @@ export class FileSystemFrontendContribution implements FrontendApplicationContri }); } - protected canUpload({ fileStat }: FileSelection): boolean { - return !environment.electron.is() && fileStat.isDirectory; - } - protected async upload(selection: FileSelection): Promise { try { const source = TreeWidgetSelection.getSource(this.selectionService.selection); - const fileUploadResult = await this.uploadService.upload(selection.fileStat.resource); + const fileUploadResult = await this.uploadService.upload(selection.fileStat.isDirectory ? selection.fileStat.resource : selection.fileStat.resource.parent); if (ExpandableTreeNode.is(selection) && source) { await source.model.expandNode(selection); } From e0517ad4e68cc5b97fb09cd9b74568065941c50c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 6 Jun 2024 12:12:40 +0200 Subject: [PATCH 258/441] Stop moving to next cell when suggestion widget is visible (#13774) Signed-off-by: Jonah Iden --- .../browser/contributions/notebook-actions-contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index a261b6dfa934f..f62ebdef7d5b7 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -315,13 +315,13 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon command: NotebookCommands.CHANGE_SELECTED_CELL.id, keybinding: 'up', args: CellChangeDirection.Up, - when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_FIRST_LINE}) && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_FIRST_LINE}) && !suggestWidgetVisible && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, { command: NotebookCommands.CHANGE_SELECTED_CELL.id, keybinding: 'down', args: CellChangeDirection.Down, - when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_LAST_LINE}) && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `(!editorTextFocus || ${NOTEBOOK_CELL_CURSOR_LAST_LINE}) && !suggestWidgetVisible && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` }, { command: NotebookCommands.CUT_SELECTED_CELL.id, From fcd227ae4e92fd8573fbaacbacd32e5bcb485caf Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 6 Jun 2024 16:30:31 +0200 Subject: [PATCH 259/441] Improved ability to overwrite notebook services (#13776) * improved ability to overwrite notebook services private fields and methods to protected Signed-off-by: Jonah Iden * more private to protected Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../service/notebook-editor-widget-service.ts | 8 +++---- .../service/notebook-execution-service.ts | 4 ++-- .../notebook-execution-state-service.ts | 24 +++++++++---------- .../notebook-kernel-quick-pick-service.ts | 10 ++++---- .../service/notebook-kernel-service.ts | 8 +++---- .../notebook-renderer-messaging-service.ts | 12 +++++----- .../view-model/notebook-cell-output-model.ts | 2 +- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index 5c64eb35e2c0f..1502adcbabdaf 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -35,14 +35,14 @@ export class NotebookEditorWidgetService { @inject(ContextKeyService) protected contextKeyService: ContextKeyService; - private readonly notebookEditors = new Map(); + protected readonly notebookEditors = new Map(); - private readonly onNotebookEditorAddEmitter = new Emitter(); - private readonly onNotebookEditorRemoveEmitter = new Emitter(); + protected readonly onNotebookEditorAddEmitter = new Emitter(); + protected readonly onNotebookEditorRemoveEmitter = new Emitter(); readonly onDidAddNotebookEditor = this.onNotebookEditorAddEmitter.event; readonly onDidRemoveNotebookEditor = this.onNotebookEditorRemoveEmitter.event; - private readonly onDidChangeFocusedEditorEmitter = new Emitter(); + protected readonly onDidChangeFocusedEditorEmitter = new Emitter(); readonly onDidChangeFocusedEditor = this.onDidChangeFocusedEditorEmitter.event; focusedEditor?: NotebookEditorWidget = undefined; diff --git a/packages/notebook/src/browser/service/notebook-execution-service.ts b/packages/notebook/src/browser/service/notebook-execution-service.ts index 58c64e2bfafad..643ea2ae6785a 100644 --- a/packages/notebook/src/browser/service/notebook-execution-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-service.ts @@ -50,7 +50,7 @@ export class NotebookExecutionService { @inject(NotebookKernelQuickPickService) protected notebookKernelQuickPickService: NotebookKernelQuickPickService; - private readonly cellExecutionParticipants = new Set(); + protected readonly cellExecutionParticipants = new Set(); async executeNotebookCells(notebook: NotebookModel, cells: Iterable): Promise { const cellsArr = Array.from(cells) @@ -117,7 +117,7 @@ export class NotebookExecutionService { return Disposable.create(() => this.cellExecutionParticipants.delete(participant)); } - private async runExecutionParticipants(executions: CellExecution[]): Promise { + protected async runExecutionParticipants(executions: CellExecution[]): Promise { for (const participant of this.cellExecutionParticipants) { await participant.onWillExecuteCell(executions); } diff --git a/packages/notebook/src/browser/service/notebook-execution-state-service.ts b/packages/notebook/src/browser/service/notebook-execution-state-service.ts index 5ac7645373be9..88e44be22e0f0 100644 --- a/packages/notebook/src/browser/service/notebook-execution-state-service.ts +++ b/packages/notebook/src/browser/service/notebook-execution-state-service.ts @@ -69,10 +69,10 @@ export class NotebookExecutionStateService implements Disposable { protected readonly executions = new Map>(); - private readonly onDidChangeExecutionEmitter = new Emitter(); + protected readonly onDidChangeExecutionEmitter = new Emitter(); onDidChangeExecution = this.onDidChangeExecutionEmitter.event; - private readonly onDidChangeLastRunFailStateEmitter = new Emitter(); + protected readonly onDidChangeLastRunFailStateEmitter = new Emitter(); onDidChangeLastRunFailState = this.onDidChangeLastRunFailStateEmitter.event; getOrCreateCellExecution(notebookUri: URI, cellHandle: number): CellExecution { @@ -98,7 +98,7 @@ export class NotebookExecutionStateService implements Disposable { } - private createNotebookCellExecution(notebook: NotebookModel, cellHandle: number): CellExecution { + protected createNotebookCellExecution(notebook: NotebookModel, cellHandle: number): CellExecution { const notebookUri = notebook.uri; const execution = new CellExecution(cellHandle, notebook); execution.toDispose.push(execution.onDidUpdate(() => this.onDidChangeExecutionEmitter.fire(new CellExecutionStateChangedEvent(notebookUri, cellHandle, execution)))); @@ -107,7 +107,7 @@ export class NotebookExecutionStateService implements Disposable { return execution; } - private onCellExecutionDidComplete(notebookUri: URI, cellHandle: number, exe: CellExecution, lastRunSuccess?: boolean): void { + protected onCellExecutionDidComplete(notebookUri: URI, cellHandle: number, exe: CellExecution, lastRunSuccess?: boolean): void { const notebookExecutions = this.executions.get(notebookUri.toString())?.get(cellHandle); if (!notebookExecutions) { throw new Error('Notebook Cell Execution not found while trying to complete it'); @@ -138,15 +138,15 @@ export class NotebookExecutionStateService implements Disposable { } export class CellExecution implements Disposable { - private readonly onDidUpdateEmitter = new Emitter(); + protected readonly onDidUpdateEmitter = new Emitter(); readonly onDidUpdate = this.onDidUpdateEmitter.event; - private readonly onDidCompleteEmitter = new Emitter(); + protected readonly onDidCompleteEmitter = new Emitter(); readonly onDidComplete = this.onDidCompleteEmitter.event; toDispose = new DisposableCollection(); - private _state: NotebookCellExecutionState = NotebookCellExecutionState.Unconfirmed; + protected _state: NotebookCellExecutionState = NotebookCellExecutionState.Unconfirmed; get state(): NotebookCellExecutionState { return this._state; } @@ -155,19 +155,19 @@ export class CellExecution implements Disposable { return this.notebook.uri; } - private _didPause = false; + protected _didPause = false; get didPause(): boolean { return this._didPause; } - private _isPaused = false; + protected _isPaused = false; get isPaused(): boolean { return this._isPaused; } constructor( readonly cellHandle: number, - private readonly notebook: NotebookModel, + protected readonly notebook: NotebookModel, ) { console.debug(`CellExecution#ctor ${this.getCellLog()}`); } @@ -188,7 +188,7 @@ export class CellExecution implements Disposable { this.applyCellExecutionEditsToNotebook([startExecuteEdit]); } - private getCellLog(): string { + protected getCellLog(): string { return `${this.notebookURI.toString()}, ${this.cellHandle}`; } @@ -254,7 +254,7 @@ export class CellExecution implements Disposable { this.toDispose.dispose(); } - private applyCellExecutionEditsToNotebook(edits: CellEditOperation[]): void { + protected applyCellExecutionEditsToNotebook(edits: CellEditOperation[]): void { this.notebook.applyEdits(edits, false); } } diff --git a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts index 7b999993202d1..e4285eea344f7 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-quick-pick-service.ts @@ -277,7 +277,7 @@ export class NotebookKernelQuickPickService { return true; } - private async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise { + protected async displaySelectAnotherQuickPick(editor: NotebookModel, kernelListEmpty: boolean): Promise { const notebook: NotebookModel = editor; const disposables = new DisposableCollection(); const quickPick = this.quickInputService.createQuickPick(); @@ -399,11 +399,11 @@ export class NotebookKernelQuickPickService { return false; } - private isUri(value: string): boolean { + protected isUri(value: string): boolean { return /^(?\w[\w\d+.-]*):/.test(value); } - private async calculateKernelSources(editor: NotebookModel): Promise[]> { + protected async calculateKernelSources(editor: NotebookModel): Promise[]> { const notebook: NotebookModel = editor; const actions = await this.notebookKernelService.getKernelSourceActionsFromProviders(notebook); @@ -448,7 +448,7 @@ export class NotebookKernelQuickPickService { return quickPickItems; } - private async selectOneKernel(notebook: NotebookModel, source: string, kernels: NotebookKernel[]): Promise { + protected async selectOneKernel(notebook: NotebookModel, source: string, kernels: NotebookKernel[]): Promise { const quickPickItems: QuickPickInput[] = kernels.map(kernel => toKernelQuickPick(kernel, undefined)); const quickPick = this.quickInputService.createQuickPick(); quickPick.items = quickPickItems; @@ -472,7 +472,7 @@ export class NotebookKernelQuickPickService { quickPick.show(); } - private async executeCommand(notebook: NotebookModel, command: NotebookCommand): Promise { + protected async executeCommand(notebook: NotebookModel, command: NotebookCommand): Promise { const args = (command.arguments || []).concat([NotebookModelResource.create(notebook.uri)]); return this.commandService.executeCommand(command.id, ...args); } diff --git a/packages/notebook/src/browser/service/notebook-kernel-service.ts b/packages/notebook/src/browser/service/notebook-kernel-service.ts index f6271b2cbcb4d..86a1159757051 100644 --- a/packages/notebook/src/browser/service/notebook-kernel-service.ts +++ b/packages/notebook/src/browser/service/notebook-kernel-service.ts @@ -84,7 +84,7 @@ export interface NotebookTextModelLike { uri: URI; viewType: string } class KernelInfo { - private static instanceCounter = 0; + protected static instanceCounter = 0; score: number; readonly kernel: NotebookKernel; @@ -130,7 +130,7 @@ export class SourceCommand implements Disposable { this.onDidChangeStateEmitter.fire(); } - private async runCommand(commandService: CommandService): Promise { + protected async runCommand(commandService: CommandService): Promise { try { await commandService.executeCommand(this.command.id, { uri: this.model.uri, @@ -278,7 +278,7 @@ export class NotebookKernelService { return this.kernels.get(id)?.kernel; } - private static score(kernel: NotebookKernel, notebook: NotebookTextModelLike): number { + protected static score(kernel: NotebookKernel, notebook: NotebookTextModelLike): number { if (kernel.viewType === notebook.viewType) { return 10; } else if (kernel.viewType === '*') { @@ -288,7 +288,7 @@ export class NotebookKernelService { } } - private tryAutoBindNotebook(notebook: NotebookModel, onlyThisKernel?: NotebookKernel): void { + protected tryAutoBindNotebook(notebook: NotebookModel, onlyThisKernel?: NotebookKernel): void { const id = this.notebookBindings[`${notebook.viewType}/${notebook.uri}`]; if (!id) { diff --git a/packages/notebook/src/browser/service/notebook-renderer-messaging-service.ts b/packages/notebook/src/browser/service/notebook-renderer-messaging-service.ts index d70de22aae07f..ba35e516265b8 100644 --- a/packages/notebook/src/browser/service/notebook-renderer-messaging-service.ts +++ b/packages/notebook/src/browser/service/notebook-renderer-messaging-service.ts @@ -45,17 +45,17 @@ export interface RendererMessaging extends Disposable { @injectable() export class NotebookRendererMessagingService implements Disposable { - private readonly postMessageEmitter = new Emitter(); + protected readonly postMessageEmitter = new Emitter(); readonly onPostMessage = this.postMessageEmitter.event; - private readonly willActivateRendererEmitter = new Emitter(); + protected readonly willActivateRendererEmitter = new Emitter(); readonly onWillActivateRenderer = this.willActivateRendererEmitter.event; @inject(NotebookEditorWidgetService) - private readonly editorWidgetService: NotebookEditorWidgetService; + protected readonly editorWidgetService: NotebookEditorWidgetService; - private readonly activations = new Map(); - private readonly scopedMessaging = new Map(); + protected readonly activations = new Map(); + protected readonly scopedMessaging = new Map(); receiveMessage(editorId: string | undefined, rendererId: string, message: unknown): Promise { if (editorId === undefined) { @@ -101,7 +101,7 @@ export class NotebookRendererMessagingService implements Disposable { return messaging; } - private postMessage(editorId: string, rendererId: string, message: unknown): void { + protected postMessage(editorId: string, rendererId: string, message: unknown): void { if (!this.activations.has(rendererId)) { this.prepare(rendererId); } diff --git a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts index 9171dc70323bd..846d5efb5e00f 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts @@ -38,7 +38,7 @@ export class NotebookCellOutputModel implements Disposable { return this.rawOutput.metadata; } - constructor(private rawOutput: CellOutput) { } + constructor(protected rawOutput: CellOutput) { } replaceData(rawData: CellOutput): void { this.rawOutput = rawData; From d79b7101adef9db71e74c13744a3fde4f79a343c Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 7 Jun 2024 08:41:58 +0200 Subject: [PATCH 260/441] Added notebook output options and tag preference search (#13773) * Added notebook output options and the ability to search for tags in settings Signed-off-by: Jonah Iden * review changes Signed-off-by: Jonah Iden * review fixes Signed-off-by: Jonah Iden * improved tag search Signed-off-by: Jonah Iden * fixed line height when set to 0 Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../common/preferences/preference-schema.ts | 1 + .../contributions/notebook-preferences.ts | 56 ++++++- .../src/browser/notebook-frontend-module.ts | 2 + .../src/browser/service/notebook-options.ts | 154 ++++++++++++++++++ .../browser/view-model/notebook-cell-model.ts | 8 +- .../browser/view/notebook-code-cell-view.tsx | 26 +-- .../renderers/cell-output-webview.tsx | 36 +++- .../renderers/output-webview-internal.ts | 18 ++ .../renderers/webview-communication.ts | 13 +- .../src/browser/preference-tree-model.ts | 10 +- 10 files changed, 289 insertions(+), 35 deletions(-) create mode 100644 packages/notebook/src/browser/service/notebook-options.ts diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts index 3d1e15ca0411a..b77c01a1891f9 100644 --- a/packages/core/src/common/preferences/preference-schema.ts +++ b/packages/core/src/common/preferences/preference-schema.ts @@ -75,6 +75,7 @@ export interface PreferenceSchemaProperty extends PreferenceItem { description?: string; markdownDescription?: string; scope?: 'application' | 'machine' | 'window' | 'resource' | 'language-overridable' | 'machine-overridable' | PreferenceScope; + tags?: string[]; } export interface PreferenceDataProperty extends PreferenceItem { diff --git a/packages/notebook/src/browser/contributions/notebook-preferences.ts b/packages/notebook/src/browser/contributions/notebook-preferences.ts index 9fd02a08dc653..bb947fb756dc3 100644 --- a/packages/notebook/src/browser/contributions/notebook-preferences.ts +++ b/packages/notebook/src/browser/contributions/notebook-preferences.ts @@ -13,19 +13,71 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ import { nls } from '@theia/core'; import { PreferenceSchema } from '@theia/core/lib/browser'; -export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers'; +export namespace NotebookPreferences { + export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers'; + export const OUTPUT_LINE_HEIGHT = 'notebook.output.lineHeight'; + export const OUTPUT_FONT_SIZE = 'notebook.output.fontSize'; + export const OUTPUT_FONT_FAMILY = 'notebook.output.fontFamily'; + export const OUTPUT_SCROLLING = 'notebook.output.scrolling'; + export const OUTPUT_WORD_WRAP = 'notebook.output.wordWrap'; + export const OUTPUT_LINE_LIMIT = 'notebook.output.textLineLimit'; +} export const notebookPreferenceSchema: PreferenceSchema = { properties: { - [NOTEBOOK_LINE_NUMBERS]: { + [NotebookPreferences.NOTEBOOK_LINE_NUMBERS]: { type: 'string', enum: ['on', 'off'], default: 'off', description: nls.localizeByDefault('Controls the display of line numbers in the cell editor.') }, + [NotebookPreferences.OUTPUT_LINE_HEIGHT]: { + // eslint-disable-next-line max-len + markdownDescription: nls.localizeByDefault('Line height of the output text within notebook cells.\n - When set to 0, editor line height is used.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values.'), + type: 'number', + default: 0, + tags: ['notebookLayout', 'notebookOutputLayout'] + }, + [NotebookPreferences.OUTPUT_FONT_SIZE]: { + markdownDescription: nls.localizeByDefault('Font size for the output text within notebook cells. When set to 0, {0} is used.', '`#editor.fontSize#`'), + type: 'number', + default: 0, + tags: ['notebookLayout', 'notebookOutputLayout'] + }, + [NotebookPreferences.OUTPUT_FONT_FAMILY]: { + markdownDescription: nls.localizeByDefault('The font family of the output text within notebook cells. When set to empty, the {0} is used.', '`#editor.fontFamily#`'), + type: 'string', + tags: ['notebookLayout', 'notebookOutputLayout'] + }, + [NotebookPreferences.OUTPUT_SCROLLING]: { + markdownDescription: nls.localizeByDefault('Initially render notebook outputs in a scrollable region when longer than the limit.'), + type: 'boolean', + tags: ['notebookLayout', 'notebookOutputLayout'], + default: false + }, + [NotebookPreferences.OUTPUT_WORD_WRAP]: { + markdownDescription: nls.localizeByDefault('Controls whether the lines in output should wrap.'), + type: 'boolean', + tags: ['notebookLayout', 'notebookOutputLayout'], + default: false + }, + [NotebookPreferences.OUTPUT_LINE_LIMIT]: { + markdownDescription: nls.localizeByDefault( + 'Controls how many lines of text are displayed in a text output. If {0} is enabled, this setting is used to determine the scroll height of the output.', + '`#notebook.output.scrolling#`'), + type: 'number', + default: 30, + tags: ['notebookLayout', 'notebookOutputLayout'], + minimum: 1, + }, + } }; diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index c79f729c4e143..681780f512655 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -45,6 +45,7 @@ import { NotebookLabelProviderContribution } from './contributions/notebook-labe import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution'; import { NotebookClipboardService } from './service/notebook-clipboard-service'; import { notebookPreferenceSchema } from './contributions/notebook-preferences'; +import { NotebookOptionsService } from './service/notebook-options'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -106,4 +107,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(LabelProviderContribution).toService(NotebookLabelProviderContribution); bind(PreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema }); + bind(NotebookOptionsService).toSelf().inSingletonScope(); }); diff --git a/packages/notebook/src/browser/service/notebook-options.ts b/packages/notebook/src/browser/service/notebook-options.ts new file mode 100644 index 0000000000000..c642e97821e3e --- /dev/null +++ b/packages/notebook/src/browser/service/notebook-options.ts @@ -0,0 +1,154 @@ + +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { Emitter } from '@theia/core'; +import { NotebookPreferences, notebookPreferenceSchema } from '../contributions/notebook-preferences'; +import { EditorPreferences } from '@theia/editor/lib/browser'; +import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo'; +import { PixelRatio } from '@theia/monaco-editor-core/esm/vs/base/browser/browser'; + +const notebookOutputOptionsRelevantPreferences = [ + 'editor.fontSize', + 'editor.fontFamily', + NotebookPreferences.NOTEBOOK_LINE_NUMBERS, + NotebookPreferences.OUTPUT_LINE_HEIGHT, + NotebookPreferences.OUTPUT_FONT_SIZE, + NotebookPreferences.OUTPUT_FONT_FAMILY, + NotebookPreferences.OUTPUT_SCROLLING, + NotebookPreferences.OUTPUT_WORD_WRAP, + NotebookPreferences.OUTPUT_LINE_LIMIT +]; + +export interface NotebookOutputOptions { + // readonly outputNodePadding: number; + // readonly outputNodeLeftPadding: number; + // readonly previewNodePadding: number; + // readonly markdownLeftMargin: number; + // readonly leftMargin: number; + // readonly rightMargin: number; + // readonly runGutter: number; + // readonly dragAndDropEnabled: boolean; + readonly fontSize: number; + readonly outputFontSize?: number; + readonly fontFamily: string; + readonly outputFontFamily?: string; + // readonly markupFontSize: number; + // readonly markdownLineHeight: number; + readonly outputLineHeight: number; + readonly outputScrolling: boolean; + readonly outputWordWrap: boolean; + readonly outputLineLimit: number; + // readonly outputLinkifyFilePaths: boolean; + // readonly minimalError: boolean; + +} + +@injectable() +export class NotebookOptionsService { + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + @inject(EditorPreferences) + protected readonly editorPreferences: EditorPreferences; + + protected outputOptionsChangedEmitter = new Emitter(); + onDidChangeOutputOptions = this.outputOptionsChangedEmitter.event; + + protected fontInfo?: BareFontInfo; + get editorFontInfo(): BareFontInfo { + return this.getOrCreateMonacoFontInfo(); + } + + @postConstruct() + protected init(): void { + this.preferenceService.onPreferencesChanged(async preferenceChanges => { + if (notebookOutputOptionsRelevantPreferences.some(p => p in preferenceChanges)) { + this.outputOptionsChangedEmitter.fire(this.computeOutputOptions()); + } + }); + } + + computeOutputOptions(): NotebookOutputOptions { + const outputLineHeight = this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_LINE_HEIGHT); + + const fontSize = this.preferenceService.get('editor.fontSize')!; + const outputFontSize = this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_FONT_SIZE); + + return { + fontSize, + outputFontSize: outputFontSize, + fontFamily: this.preferenceService.get('editor.fontFamily')!, + outputFontFamily: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_FONT_FAMILY), + outputLineHeight: this.computeOutputLineHeight(outputLineHeight, outputFontSize ?? fontSize), + outputScrolling: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_SCROLLING)!, + outputWordWrap: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_WORD_WRAP)!, + outputLineLimit: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_LINE_LIMIT)! + }; + } + + protected getNotebookPreferenceWithDefault(key: string): T { + return this.preferenceService.get(key, notebookPreferenceSchema.properties?.[key]?.default as T); + } + + protected computeOutputLineHeight(lineHeight: number, outputFontSize: number): number { + const minimumLineHeight = 9; + + if (lineHeight === 0) { + // use editor line height + lineHeight = this.editorFontInfo.lineHeight; + } else if (lineHeight < minimumLineHeight) { + // Values too small to be line heights in pixels are in ems. + let fontSize = outputFontSize; + if (fontSize === 0) { + fontSize = this.preferenceService.get('editor.fontSize')!; + } + + lineHeight = lineHeight * fontSize; + } + + // Enforce integer, minimum constraints + lineHeight = Math.round(lineHeight); + if (lineHeight < minimumLineHeight) { + lineHeight = minimumLineHeight; + } + + return lineHeight; + } + + protected getOrCreateMonacoFontInfo(): BareFontInfo { + if (!this.fontInfo) { + this.fontInfo = this.createFontInfo(); + this.editorPreferences.onPreferenceChanged(e => this.fontInfo = this.createFontInfo()); + } + return this.fontInfo; + } + + protected createFontInfo(): BareFontInfo { + return BareFontInfo.createFromRawSettings({ + fontFamily: this.editorPreferences['editor.fontFamily'], + fontWeight: String(this.editorPreferences['editor.fontWeight']), + fontSize: this.editorPreferences['editor.fontSize'], + fontLigatures: this.editorPreferences['editor.fontLigatures'], + lineHeight: this.editorPreferences['editor.lineHeight'], + letterSpacing: this.editorPreferences['editor.letterSpacing'], + }, PixelRatio.value); + } + +} diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 038666cd24d21..df4b2cf96c2cb 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -30,7 +30,7 @@ import { NotebookCellOutputsSplice } from '../notebook-types'; import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service'; import { NotebookCellOutputModel } from './notebook-cell-output-model'; import { PreferenceService } from '@theia/core/lib/browser'; -import { NOTEBOOK_LINE_NUMBERS } from '../contributions/notebook-preferences'; +import { NotebookPreferences } from '../contributions/notebook-preferences'; import { LanguageService } from '@theia/core/lib/browser/language-service'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); @@ -245,13 +245,13 @@ export class NotebookCellModel implements NotebookCell, Disposable { this._internalMetadata = this.props.internalMetadata ?? {}; this.editorOptions = { - lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS) + lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS) }; this.toDispose.push(this.preferenceService.onPreferenceChanged(e => { - if (e.preferenceName === NOTEBOOK_LINE_NUMBERS) { + if (e.preferenceName === NotebookPreferences.NOTEBOOK_LINE_NUMBERS) { this.editorOptions = { ...this.editorOptions, - lineNumbers: this.preferenceService.get(NOTEBOOK_LINE_NUMBERS) + lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS) }; } })); diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 596cc22af4c26..9e5fc25129973 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -32,8 +32,7 @@ import { CommandRegistry, DisposableCollection, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; import { NotebookViewportService } from './notebook-viewport-service'; import { EditorPreferences } from '@theia/editor/lib/browser'; -import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo'; -import { PixelRatio } from '@theia/monaco-editor-core/esm/vs/base/browser/browser'; +import { NotebookOptionsService } from '../service/notebook-options'; @injectable() export class NotebookCodeCellRenderer implements CellRenderer { @@ -64,7 +63,8 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry; - protected fontInfo: BareFontInfo | undefined; + @inject(NotebookOptionsService) + protected readonly notebookOptionsService: NotebookOptionsService; render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { return
    @@ -81,7 +81,7 @@ export class NotebookCodeCellRenderer implements CellRenderer { monacoServices={this.monacoServices} notebookContextManager={this.notebookContextManager} notebookViewportService={this.notebookViewportService} - fontInfo={this.getOrCreateMonacoFontInfo()} /> + fontInfo={this.notebookOptionsService.editorFontInfo} /> this.fontInfo = this.createFontInfo()); - } - return this.fontInfo; - } - - protected createFontInfo(): BareFontInfo { - return BareFontInfo.createFromRawSettings({ - fontFamily: this.editorPreferences['editor.fontFamily'], - fontWeight: String(this.editorPreferences['editor.fontWeight']), - fontSize: this.editorPreferences['editor.fontSize'], - fontLigatures: this.editorPreferences['editor.fontLigatures'], - lineHeight: this.editorPreferences['editor.lineHeight'], - letterSpacing: this.editorPreferences['editor.letterSpacing'], - }, PixelRatio.value); - } } export interface NotebookCodeCellStatusProps { diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 98cac69ef734c..c2f7555e7bbf3 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -35,6 +35,7 @@ import { CellUri } from '@theia/notebook/lib/common'; import { Disposable, DisposableCollection, nls, QuickPickService } from '@theia/core'; import { NotebookCellOutputModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-output-model'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; +import { NotebookOptionsService, NotebookOutputOptions } from '@theia/notebook/lib/browser/service/notebook-options'; const CellModel = Symbol('CellModel'); const Notebook = Symbol('NotebookModel'); @@ -147,6 +148,11 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @inject(AdditionalNotebookCellOutputCss) protected readonly additionalOutputCss: string; + @inject(NotebookOptionsService) + protected readonly notebookOptionsService: NotebookOptionsService; + + protected options: NotebookOutputOptions; + readonly id = generateUuid(); protected editor: NotebookEditorWidget | undefined; @@ -161,6 +167,11 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @postConstruct() protected async init(): Promise { this.editor = this.notebookEditorWidgetService.getNotebookEditor(NOTEBOOK_EDITOR_ID_PREFIX + CellUri.parse(this.cell.uri)?.notebook); + this.options = this.notebookOptionsService.computeOutputOptions(); + this.toDispose.push(this.notebookOptionsService.onDidChangeOutputOptions(options => { + this.options = options; + this.updateStyles(); + })); this.toDispose.push(this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange))); this.toDispose.push(this.cell.onDidChangeOutputItems(output => { @@ -272,6 +283,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { switch (message.type) { case 'initialized': this.updateOutput({ newOutputs: this.cell.outputs, start: 0, deleteCount: 0 }); + this.updateStyles(); break; case 'customRendererMessage': // console.log('from webview customRendererMessage ', message.rendererId, '', JSON.stringify(message.message)); @@ -302,6 +314,22 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { return kernelPreloads.concat(staticPreloads); } + protected updateStyles(): void { + this.webviewWidget.sendMessage({ + type: 'notebookStyles', + styles: this.generateStyles() + }); + } + + protected generateStyles(): { [key: string]: string } { + return { + 'notebook-cell-output-font-size': `${this.options.outputFontSize || this.options.fontSize}px`, + 'notebook-cell-output-line-height': `${this.options.outputLineHeight}px`, + 'notebook-cell-output-max-height': `${this.options.outputLineHeight * this.options.outputLineLimit}px`, + 'notebook-cell-output-font-family': this.options.outputFontFamily || this.options.fontFamily, + }; + } + private async createWebviewContent(): Promise { const isWorkspaceTrusted = await this.workspaceTrustService.getWorkspaceTrust(); const preloads = this.preloadsScriptString(isWorkspaceTrusted); @@ -325,10 +353,10 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { const ctx: PreloadContext = { isWorkspaceTrusted, rendererData: this.notebookRendererRegistry.notebookRenderers, - renderOptions: { // TODO these should be changeable in the settings - lineLimit: 30, - outputScrolling: false, - outputWordWrap: false, + renderOptions: { + lineLimit: this.options.outputLineLimit, + outputScrolling: this.options.outputScrolling, + outputWordWrap: this.options.outputWordWrap, }, staticPreloadsData: this.getPreloads() }; diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 8818378ed4446..7e01a55751954 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -569,6 +569,24 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } break; } + case 'notebookStyles': { + const documentStyle = window.document.documentElement.style; + + for (let i = documentStyle.length - 1; i >= 0; i--) { + const property = documentStyle[i]; + + // Don't remove properties that the webview might have added separately + if (property && property.startsWith('--notebook-')) { + documentStyle.removeProperty(property); + } + } + + // Re-add new properties + for (const [name, value] of Object.entries(event.data.styles)) { + documentStyle.setProperty(`--${name}`, value); + } + break; + } } }); window.addEventListener('wheel', handleWheel); diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts index 4989fbef9df2e..442c7baf86ef1 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts @@ -59,7 +59,18 @@ export interface PreloadMessage { readonly resources: string[]; } -export type ToWebviewMessage = UpdateRenderersMessage | OutputChangedMessage | ChangePreferredMimetypeMessage | CustomRendererMessage | KernelMessage | PreloadMessage; +export interface notebookStylesMessage { + readonly type: 'notebookStyles'; + styles: Record; +} + +export type ToWebviewMessage = UpdateRenderersMessage + | OutputChangedMessage + | ChangePreferredMimetypeMessage + | CustomRendererMessage + | KernelMessage + | PreloadMessage + | notebookStylesMessage; export interface WebviewInitialized { readonly type: 'initialized'; diff --git a/packages/preferences/src/browser/preference-tree-model.ts b/packages/preferences/src/browser/preference-tree-model.ts index c03ecf19afb96..689e186641fba 100644 --- a/packages/preferences/src/browser/preference-tree-model.ts +++ b/packages/preferences/src/browser/preference-tree-model.ts @@ -67,6 +67,7 @@ export class PreferenceTreeModel extends TreeModelImpl { protected lastSearchedFuzzy: string = ''; protected lastSearchedLiteral: string = ''; + protected lastSearchedTags: string[] = []; protected _currentScope: number = Number(Preference.DEFAULT_SCOPE.scope); protected _isFiltered: boolean = false; protected _currentRows: Map = new Map(); @@ -110,8 +111,10 @@ export class PreferenceTreeModel extends TreeModelImpl { this.updateFilteredRows(PreferenceFilterChangeSource.Scope); }), this.filterInput.onFilterChanged(newSearchTerm => { - this.lastSearchedLiteral = newSearchTerm; - this.lastSearchedFuzzy = newSearchTerm.replace(/\s/g, ''); + this.lastSearchedTags = Array.from(newSearchTerm.matchAll(/@tag:([^\s]+)/g)).map(match => match[0].slice(5)); + const newSearchTermWithoutTags = newSearchTerm.replace(/@tag:[^\s]+/g, ''); + this.lastSearchedLiteral = newSearchTermWithoutTags; + this.lastSearchedFuzzy = newSearchTermWithoutTags.replace(/\s/g, ''); this._isFiltered = newSearchTerm.length > 2; if (this.isFiltered) { this.expandAll(); @@ -183,6 +186,9 @@ export class PreferenceTreeModel extends TreeModelImpl { if (node.id.startsWith(COMMONLY_USED_SECTION_PREFIX)) { return false; } + if (!this.lastSearchedTags.every(tag => node.preference.data.tags?.includes(tag))) { + return false; + } return fuzzy.test(this.lastSearchedFuzzy, prefID) // search matches preference name. // search matches description. Fuzzy isn't ideal here because the score depends on the order of discovery. || (node.preference.data.description ?? '').includes(this.lastSearchedLiteral); From d50029f1aa6af641445d514bf1c8ff8d8ca966b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 11 Jun 2024 15:09:52 +0200 Subject: [PATCH 261/441] Update jsdiff(#13787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13772 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/git/package.json | 4 ++-- packages/scm/package.json | 5 +++-- yarn.lock | 15 +++++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/git/package.json b/packages/git/package.json index 8d4224ec9deaf..121f9d5d5c1e0 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -11,9 +11,9 @@ "@theia/scm": "1.50.0", "@theia/scm-extra": "1.50.0", "@theia/workspace": "1.50.0", - "@types/diff": "^3.2.2", + "@types/diff": "^5.2.1", "@types/p-queue": "^2.3.1", - "diff": "^3.4.0", + "diff": "^5.2.0", "dugite-extra": "0.1.17", "find-git-exec": "^0.0.4", "find-git-repositories": "^0.1.1", diff --git a/packages/scm/package.json b/packages/scm/package.json index 552b7669c3ee2..4a995ee612b18 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -8,8 +8,8 @@ "@theia/filesystem": "1.50.0", "@theia/monaco": "1.50.0", "@theia/monaco-editor-core": "1.83.101", - "@types/diff": "^3.2.2", - "diff": "^3.4.0", + "@types/diff": "^5.2.1", + "diff": "^5.2.0", "p-debounce": "^2.1.0", "react-autosize-textarea": "^7.0.0", "ts-md5": "^1.2.2", @@ -45,6 +45,7 @@ "compile": "theiaext compile", "docs": "theiaext docs", "lint": "theiaext lint", + "test": "theiaext test", "watch": "theiaext watch" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 7e743ace87f5e..f90c3d48d0083 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1861,10 +1861,10 @@ dependencies: "@types/node" "*" -"@types/diff@^3.2.2": - version "3.5.8" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-3.5.8.tgz#24434e47c2ef1cbcdf96afa43c6ea2fd8e4add93" - integrity sha512-CZ5vepL87+M8PxRIvJjR181Erahch2w7Jev/XJm+Iot/SOvJh8QqH/N79b+vsKtYF6fFzoPieiiq2c5tzmXR9A== +"@types/diff@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.2.1.tgz#cceae9c4b2dae5c6b8ab1ce1263601c255d87fb3" + integrity sha512-uxpcuwWJGhe2AR1g8hD9F5OYGCqjqWnBUQFD8gMZsDbv8oPHzxJF6iMO6n8Tk0AdzlxoaaoQhOYlIg/PukVU8g== "@types/docker-modem@*": version "3.0.6" @@ -4740,17 +4740,12 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -diff@^3.4.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.0.0: +diff@^5.0.0, diff@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== From 8d2d0c6883e36a0145656657778745f1488794b8 Mon Sep 17 00:00:00 2001 From: Tobias Ortmayr Date: Tue, 11 Jun 2024 19:14:25 +0200 Subject: [PATCH 262/441] Fix type definition of `TheiaAppFactory` (#13799) Ensure that the `TheiaAppFactory` type does match the constructor function of TheiaApp. `InitialWorkspace` is required in the TheiaApp constructor butwas optional in `TheiaAppFactory` This ensures that (sub)classes of TheiaApp can properly be used as app factory without type casting Contributed on behalf of STMicroelectronics --- examples/playwright/src/theia-app-loader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/playwright/src/theia-app-loader.ts b/examples/playwright/src/theia-app-loader.ts index 08b9084c5d3ba..f420d95c329a2 100644 --- a/examples/playwright/src/theia-app-loader.ts +++ b/examples/playwright/src/theia-app-loader.ts @@ -22,7 +22,7 @@ import { TheiaWorkspace } from './theia-workspace'; import { OSUtil } from './util'; export interface TheiaAppFactory { - new(page: Page, initialWorkspace?: TheiaWorkspace, isElectron?: boolean): T; + new(page: Page, initialWorkspace: TheiaWorkspace, isElectron?: boolean): T; } // TODO this is just a sketch, we need a proper way to configure tests and pass this configuration to the `TheiaAppLoader`: From fdfb59d92e7ff8bf9fc1573910d4b8300857f455 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 13 Jun 2024 09:51:07 +0200 Subject: [PATCH 263/441] Improved notebook cell drag images (#13791) * IMproved notebook cell drag images Signed-off-by: Jonah Iden * review improvements Signed-off-by: Jonah Iden * fixed small issue where the drag over indicator stayed when dropping outside of the notebook editor Signed-off-by: Jonah Iden * remove triple backticks from code block Signed-off-by: Jonah Iden * Fix code escaping * Optimize algorithm --------- Signed-off-by: Jonah Iden Co-authored-by: Mark Sujew --- packages/notebook/src/browser/style/index.css | 5 ++- .../browser/view/notebook-cell-list-view.tsx | 19 ++++----- .../browser/view/notebook-code-cell-view.tsx | 40 ++++++++++++++++++- .../view/notebook-markdown-cell-view.tsx | 6 ++- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 447e3fc1fbe9b..1ac9a9db976b6 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -286,6 +286,7 @@ position: absolute; top: -99999px; left: -99999px; - height: 500px; - width: 500px; + max-height: 500px; + min-height: 100px; + background-color: var(--theia-editor-background); } diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 8fcc4ce0b8384..4ac04b13d785c 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -45,7 +45,7 @@ export class NotebookCellListView extends React.Component this.onDragStart(e, index, cell)} + onDragEnd={e => { + NotebookCellListView.dragGhost?.remove(); + this.setState({ ...this.state, dragOverIndicator: undefined }); + }} onDragOver={e => this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} draggable={true} @@ -137,14 +141,11 @@ export class NotebookCellListView extends React.Component
    @@ -102,10 +107,43 @@ export class NotebookCodeCellRenderer implements CellRenderer { renderDragImage(cell: NotebookCellModel): HTMLElement { const dragImage = document.createElement('div'); dragImage.className = 'theia-notebook-drag-image'; - dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/codeText', 'Code cell selected'); + dragImage.style.width = this.notebookContextManager.context?.clientWidth + 'px'; + dragImage.style.height = '100px'; + dragImage.style.display = 'flex'; + + const fakeRunButton = document.createElement('span'); + fakeRunButton.className = `${codicon('play')} theia-notebook-cell-status-item`; + dragImage.appendChild(fakeRunButton); + + const fakeEditor = document.createElement('div'); + dragImage.appendChild(fakeEditor); + const lines = cell.source.split('\n').slice(0, 5).join('\n'); + const codeSequence = this.getMarkdownCodeSequence(lines); + const firstLine = new MarkdownString(`${codeSequence}${cell.language}\n${lines}\n${codeSequence}`, { supportHtml: true, isTrusted: false }); + fakeEditor.appendChild(this.markdownRenderer.render(firstLine).element); + fakeEditor.classList.add('theia-notebook-cell-editor-container'); + fakeEditor.style.padding = '10px'; return dragImage; } + protected getMarkdownCodeSequence(input: string): string { + // We need a minimum of 3 backticks to start a code block. + let longest = 2; + let current = 0; + for (let i = 0; i < input.length; i++) { + const char = input.charAt(i); + if (char === '`') { + current++; + if (current > longest) { + longest = current; + } + } else { + current = 0; + } + } + return Array(longest + 1).fill('`').join(''); + } + } export interface NotebookCodeCellStatusProps { diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index 1b5cd348dde63..c67e380b51833 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -44,8 +44,10 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { renderDragImage(cell: NotebookCellModel): HTMLElement { const dragImage = document.createElement('div'); - dragImage.className = 'theia-notebook-drag-image'; - dragImage.textContent = nls.localize('theia/notebook/dragGhostImage/markdownText', 'Mardown cell selected'); + dragImage.style.width = this.notebookContextManager.context?.clientWidth + 'px'; + const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true }); + const markdownElement = this.markdownRenderer.render(markdownString).element; + dragImage.appendChild(markdownElement); return dragImage; } } From 63fb7e9fd7d42a6daea8d9bfcc26e356ab323a18 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Fri, 14 Jun 2024 18:41:42 +0200 Subject: [PATCH 264/441] Fix programmatic save for custom editors (#13684) --- .../plugin-ext/src/common/plugin-api-rpc.ts | 1 - .../src/main/browser/documents-main.ts | 24 +++---------------- .../src/main/browser/main-context.ts | 4 +--- .../main/browser/text-editor-model-service.ts | 9 +++++++ 4 files changed, 13 insertions(+), 25 deletions(-) diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index c63e8ca5d0309..54bd49125d8ea 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1388,7 +1388,6 @@ export interface DocumentsMain { $tryShowDocument(uri: UriComponents, options?: TextDocumentShowOptions): Promise; $tryOpenDocument(uri: UriComponents): Promise; $trySaveDocument(uri: UriComponents): Promise; - $tryCloseDocument(uri: UriComponents): Promise; } export interface EnvMain { diff --git a/packages/plugin-ext/src/main/browser/documents-main.ts b/packages/plugin-ext/src/main/browser/documents-main.ts index deed64a60d2d5..b9f4b64782b14 100644 --- a/packages/plugin-ext/src/main/browser/documents-main.ts +++ b/packages/plugin-ext/src/main/browser/documents-main.ts @@ -20,10 +20,10 @@ import { DisposableCollection, Disposable, UntitledResourceResolver } from '@the import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; import { RPCProtocol } from '../../common/rpc-protocol'; import { EditorModelService } from './text-editor-model-service'; -import { EditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser'; +import { EditorOpenerOptions } from '@theia/editor/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { URI as CodeURI } from '@theia/core/shared/vscode-uri'; -import { ApplicationShell, Saveable } from '@theia/core/lib/browser'; +import { ApplicationShell } from '@theia/core/lib/browser'; import { TextDocumentShowOptions } from '../../common/plugin-api-rpc-model'; import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; import { OpenerService } from '@theia/core/lib/browser/opener-service'; @@ -94,7 +94,6 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { notebookDocuments: NotebookDocumentsMainImpl, private readonly modelService: EditorModelService, rpc: RPCProtocol, - private editorManager: EditorManager, private openerService: OpenerService, private shell: ApplicationShell, private untitledResourceResolver: UntitledResourceResolver, @@ -206,13 +205,7 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { } async $trySaveDocument(uri: UriComponents): Promise { - const widget = await this.editorManager.getByUri(new URI(CodeURI.revive(uri))); - if (widget) { - await Saveable.save(widget); - return true; - } - - return false; + return this.modelService.save(new URI(CodeURI.revive(uri))); } async $tryOpenDocument(uri: UriComponents): Promise { @@ -226,17 +219,6 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable { } } - async $tryCloseDocument(uri: UriComponents): Promise { - const widget = await this.editorManager.getByUri(new URI(CodeURI.revive(uri))); - if (widget) { - await Saveable.save(widget); - widget.close(); - return true; - } - - return false; - } - static toEditorOpenerOptions(shell: ApplicationShell, options?: TextDocumentShowOptions): EditorOpenerOptions | undefined { if (!options) { return undefined; diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index e0bdf7d826df5..2f11bd40b8045 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -40,7 +40,6 @@ import { DecorationsMainImpl } from './decorations/decorations-main'; import { ClipboardMainImpl } from './clipboard-main'; import { DocumentsMainImpl } from './documents-main'; import { TextEditorsMainImpl } from './text-editors-main'; -import { EditorManager } from '@theia/editor/lib/browser'; import { EditorModelService } from './text-editor-model-service'; import { OpenerService } from '@theia/core/lib/browser/opener-service'; import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; @@ -94,13 +93,12 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain); const modelService = container.get(EditorModelService); - const editorManager = container.get(EditorManager); const openerService = container.get(OpenerService); const shell = container.get(ApplicationShell); const untitledResourceResolver = container.get(UntitledResourceResolver); const languageService = container.get(MonacoLanguages); const documentsMain = new DocumentsMainImpl(editorsAndDocuments, notebookDocumentsMain, modelService, rpc, - editorManager, openerService, shell, untitledResourceResolver, languageService); + openerService, shell, untitledResourceResolver, languageService); rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, new NotebooksMainImpl(rpc, container, commandRegistryMain)); diff --git a/packages/plugin-ext/src/main/browser/text-editor-model-service.ts b/packages/plugin-ext/src/main/browser/text-editor-model-service.ts index 693aca0f4ba4b..5e03e16601571 100644 --- a/packages/plugin-ext/src/main/browser/text-editor-model-service.ts +++ b/packages/plugin-ext/src/main/browser/text-editor-model-service.ts @@ -76,6 +76,15 @@ export class EditorModelService { return this.monacoModelService.models; } + async save(uri: URI): Promise { + const model = this.monacoModelService.get(uri.toString()); + if (model) { + await model.save(); + return true; + } + return false; + } + async saveAll(includeUntitled?: boolean): Promise { const saves = []; for (const model of this.monacoModelService.models) { From d6be64bba4c048f9bc75f9ccdd4880585902086a Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Wed, 19 Jun 2024 13:38:22 +0200 Subject: [PATCH 265/441] fix: load correct messaging module in browser-only (#13827) The frontend generator erroneously included the Electron messaging module instead of the browser messaging module. Consequently, the browser-only environment attempted to establish a WebSocket connection to the backend. This issue has now been resolved. Fixes #13820 --- .../application-manager/src/generator/frontend-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index bf586df8db3e5..bbfd134bdb3b8 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -108,7 +108,7 @@ ${Array.from(frontendPreloadModules.values(), jsModulePath => `\ } module.exports = (async () => { - const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowser() + const { messagingFrontendModule } = require('@theia/core/lib/${this.pck.isBrowser() || this.pck.isBrowserOnly() ? 'browser/messaging/messaging-frontend-module' : 'electron-browser/messaging/electron-messaging-frontend-module'}'); const container = new Container(); From 7bb356fb14f6e0a9f2654372c7099b51b003d0c7 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 20 Jun 2024 08:39:13 +0200 Subject: [PATCH 266/441] set notebook editor as active when opening in foreground (#13828) Signed-off-by: Jonah Iden --- .../src/browser/service/notebook-editor-widget-service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index 1502adcbabdaf..b585834ce6013 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -62,6 +62,9 @@ export class NotebookEditorWidgetService { } this.notebookEditors.set(editor.id, editor); this.onNotebookEditorAddEmitter.fire(editor); + if (editor.isVisible) { + this.notebookEditorFocusChanged(editor, true); + } } removeNotebookEditor(editor: NotebookEditorWidget): void { From 7767e5444a7a7385094a20d8a05957f10475b8b1 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 20 Jun 2024 10:30:55 +0200 Subject: [PATCH 267/441] wip (#13810) Send notification with a Promise to respect order between requests and notifications Test run profile constructor updates observable properties that send a notification to the main test run profile, without main being told to create this profile. This leads to a profile not found issue when activating the test extension. fixes #13811 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- packages/plugin-ext/src/common/proxy-handler.ts | 4 +++- packages/plugin-ext/src/plugin/tests.ts | 13 ++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts index 9a3f362ca7deb..6a96eaf97c934 100644 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ b/packages/plugin-ext/src/common/proxy-handler.ts @@ -135,7 +135,9 @@ export class RpcInvocationHandler { } protected onNotification(method: string, args: any[]): void { - this.target[method](...args); + this.rpcDeferred.promise.then(() => { + this.target[method](...args); + }); } } diff --git a/packages/plugin-ext/src/plugin/tests.ts b/packages/plugin-ext/src/plugin/tests.ts index 70ae52f38a907..d39d6f68ab5d6 100644 --- a/packages/plugin-ext/src/plugin/tests.ts +++ b/packages/plugin-ext/src/plugin/tests.ts @@ -469,13 +469,7 @@ export class TestRunProfile implements theia.TestRunProfile { isDefault = false, tag: theia.TestTag | undefined = undefined, ) { - this.proxy = proxy; - this.label = label; - this.tag = tag; - this.label = label; - this.isDefault = isDefault; - - this.proxy.$notifyTestRunProfileCreated(controllerId, { + proxy.$notifyTestRunProfileCreated(controllerId, { id: profileId, kind: kind, tag: tag ? tag.toString() : '', @@ -483,6 +477,11 @@ export class TestRunProfile implements theia.TestRunProfile { isDefault: isDefault, canConfigure: false, }); + this.proxy = proxy; + this.label = label; + this.tag = tag; + this.label = label; + this.isDefault = isDefault; } // eslint-disable-next-line @typescript-eslint/no-explicit-any From c2be137d95ef2be5ea6f7167a20a0d8d87743dd1 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 20 Jun 2024 10:41:14 +0200 Subject: [PATCH 268/441] Always resolve existing before showing new notification (#13668) --- .../src/browser/notifications-manager.ts | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/messages/src/browser/notifications-manager.ts b/packages/messages/src/browser/notifications-manager.ts index c074720d7fab2..fcec7704e827d 100644 --- a/packages/messages/src/browser/notifications-manager.ts +++ b/packages/messages/src/browser/notifications-manager.ts @@ -180,18 +180,23 @@ export class NotificationManager extends MessageClient { override showMessage(plainMessage: PlainMessage): Promise { const messageId = this.getMessageId(plainMessage); - let notification = this.notifications.get(messageId); - if (!notification) { - const message = this.contentRenderer.renderMessage(plainMessage.text); - const type = this.toNotificationType(plainMessage.type); - const actions = Array.from(new Set(plainMessage.actions)); - const source = plainMessage.source; - const expandable = this.isExpandable(message, source, actions); - const collapsed = expandable; - notification = { messageId, message, type, actions, expandable, collapsed }; - this.notifications.set(messageId, notification); + this.toasts.delete(messageId); + this.notifications.delete(messageId); + const existingDeferred = this.deferredResults.get(messageId); + if (existingDeferred) { + this.deferredResults.delete(messageId); + existingDeferred.resolve(undefined); } - const result = this.deferredResults.get(messageId) || new Deferred(); + + const message = this.contentRenderer.renderMessage(plainMessage.text); + const type = this.toNotificationType(plainMessage.type); + const actions = Array.from(new Set(plainMessage.actions)); + const source = plainMessage.source; + const expandable = this.isExpandable(message, source, actions); + const collapsed = expandable; + const notification = { messageId, message, type, actions, expandable, collapsed }; + this.notifications.set(messageId, notification); + const result = new Deferred(); this.deferredResults.set(messageId, result); if (!this.centerVisible) { From 6dfbe6f3c8d1ab9c88931cf7b1e1fd87d4a8f382 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 20 Jun 2024 11:14:51 +0200 Subject: [PATCH 269/441] Propagate "Save As" operation to plugin host (#13689) --- CHANGELOG.md | 6 ++- packages/core/src/browser/saveable.ts | 10 ++++- .../src/browser/shell/application-shell.ts | 19 +++++++-- .../src/browser/shell/theia-dock-panel.ts | 12 +++++- .../src/browser/widgets/react-renderer.tsx | 5 ++- .../browser/filesystem-saveable-service.ts | 40 +++++++++---------- .../browser/location/location-renderer.tsx | 4 +- .../monaco/src/browser/monaco-editor-model.ts | 2 +- .../src/browser/view-model/notebook-model.ts | 2 +- 9 files changed, 69 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f135fb348667b..3f4cd7eff0935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,11 @@ +[Breaking Changes:](#breaking_changes_not_yet_released) + +- [filesystem] Adjusted the "Save As" mechanism. It now assumes that `Saveable.getSnapshot()` returns a full snapshot of the editor model [#13689](https://github.com/eclipse-theia/theia/pull/13689). + +--> ## 1.50.0 - 06/03/2024 diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index 2f9390ca2b8fd..f65a6a29162af 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -46,7 +46,7 @@ export interface Saveable { */ revert?(options?: Saveable.RevertOptions): Promise; /** - * Creates a snapshot of the dirty state. + * Creates a snapshot of the dirty state. See also {@link Saveable.Snapshot}. */ createSnapshot?(): Saveable.Snapshot; /** @@ -114,7 +114,15 @@ export namespace Saveable { soft?: boolean } + /** + * A snapshot of a saveable item. Contains the full content of the saveable file in a string serializable format. + */ export type Snapshot = { value: string } | { read(): string | null }; + export namespace Snapshot { + export function read(snapshot: Snapshot): string | undefined { + return 'value' in snapshot ? snapshot.value : (snapshot.read() ?? undefined); + } + } export function isSource(arg: unknown): arg is SaveableSource { return isObject(arg) && is(arg.saveable); } diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index c752bffdf1827..cfb5a72f8af68 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -960,7 +960,7 @@ export class ApplicationShell extends Widget { } } - getInsertionOptions(options?: Readonly): { area: string; addOptions: DockLayout.IAddOptions; } { + getInsertionOptions(options?: Readonly): { area: string; addOptions: TheiaDockPanel.AddOptions; } { let ref: Widget | undefined = options?.ref; let area: ApplicationShell.Area = options?.area || 'main'; if (!ref && (area === 'main' || area === 'bottom')) { @@ -969,7 +969,7 @@ export class ApplicationShell extends Widget { } // make sure that ref belongs to area area = ref && this.getAreaFor(ref) || area; - const addOptions: DockPanel.IAddOptions = {}; + const addOptions: TheiaDockPanel.AddOptions = {}; if (ApplicationShell.isOpenToSideMode(options?.mode)) { const areaPanel = area === 'main' ? this.mainPanel : area === 'bottom' ? this.bottomPanel : undefined; const sideRef = areaPanel && ref && (options?.mode === 'open-to-left' ? @@ -981,6 +981,10 @@ export class ApplicationShell extends Widget { addOptions.ref = ref; addOptions.mode = options?.mode === 'open-to-left' ? 'split-left' : 'split-right'; } + } else if (ApplicationShell.isReplaceMode(options?.mode)) { + addOptions.ref = options?.ref; + addOptions.closeRef = true; + addOptions.mode = 'tab-after'; } else { addOptions.ref = ref; addOptions.mode = options?.mode; @@ -2172,6 +2176,15 @@ export namespace ApplicationShell { return mode === 'open-to-left' || mode === 'open-to-right'; } + /** + * Whether the `ref` of the options widget should be replaced. + */ + export type ReplaceMode = 'tab-replace'; + + export function isReplaceMode(mode: unknown): mode is ReplaceMode { + return mode === 'tab-replace'; + } + /** * Options for adding a widget to the application shell. */ @@ -2185,7 +2198,7 @@ export namespace ApplicationShell { * * The default is `'tab-after'`. */ - mode?: DockLayout.InsertMode | OpenToSideMode + mode?: DockLayout.InsertMode | OpenToSideMode | ReplaceMode /** * The reference widget for the insert location. * diff --git a/packages/core/src/browser/shell/theia-dock-panel.ts b/packages/core/src/browser/shell/theia-dock-panel.ts index f5818217ec98f..19c2b60e92c77 100644 --- a/packages/core/src/browser/shell/theia-dock-panel.ts +++ b/packages/core/src/browser/shell/theia-dock-panel.ts @@ -133,11 +133,14 @@ export class TheiaDockPanel extends DockPanel { } } - override addWidget(widget: Widget, options?: DockPanel.IAddOptions): void { + override addWidget(widget: Widget, options?: TheiaDockPanel.AddOptions): void { if (this.mode === 'single-document' && widget.parent === this) { return; } super.addWidget(widget, options); + if (options?.closeRef) { + options.ref?.close(); + } this.widgetAdded.emit(widget); this.markActiveTabBar(widget.title); } @@ -252,4 +255,11 @@ export namespace TheiaDockPanel { export interface Factory { (options?: DockPanel.IOptions): TheiaDockPanel; } + + export interface AddOptions extends DockPanel.IAddOptions { + /** + * Whether to also close the widget referenced by `ref`. + */ + closeRef?: boolean + } } diff --git a/packages/core/src/browser/widgets/react-renderer.tsx b/packages/core/src/browser/widgets/react-renderer.tsx index f24325072ac13..ff8176405e2d2 100644 --- a/packages/core/src/browser/widgets/react-renderer.tsx +++ b/packages/core/src/browser/widgets/react-renderer.tsx @@ -41,7 +41,10 @@ export class ReactRenderer implements Disposable { } render(): void { - this.hostRoot.render({this.doRender()}); + // Ignore all render calls after the host element has unmounted + if (!this.toDispose.disposed) { + this.hostRoot.render({this.doRender()}); + } } protected doRender(): React.ReactNode { diff --git a/packages/filesystem/src/browser/filesystem-saveable-service.ts b/packages/filesystem/src/browser/filesystem-saveable-service.ts index 39fe1e3eb3f98..e7aaa51ce389e 100644 --- a/packages/filesystem/src/browser/filesystem-saveable-service.ts +++ b/packages/filesystem/src/browser/filesystem-saveable-service.ts @@ -16,7 +16,7 @@ import { environment, MessageService, nls } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { Navigatable, Saveable, SaveableSource, SaveOptions, Widget, open, OpenerService, ConfirmDialog, FormatType, CommonCommands } from '@theia/core/lib/browser'; +import { Navigatable, Saveable, SaveableSource, SaveOptions, Widget, open, OpenerService, ConfirmDialog, CommonCommands, LabelProvider } from '@theia/core/lib/browser'; import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import URI from '@theia/core/lib/common/uri'; import { FileService } from './file-service'; @@ -33,6 +33,8 @@ export class FilesystemSaveableService extends SaveableService { protected readonly fileDialogService: FileDialogService; @inject(OpenerService) protected readonly openerService: OpenerService; + @inject(LabelProvider) + protected readonly labelProvider: LabelProvider; /** * This method ensures a few things about `widget`: @@ -76,7 +78,7 @@ export class FilesystemSaveableService extends SaveableService { return this.save(sourceWidget, options); } else if (selected) { try { - await this.copyAndSave(sourceWidget, selected, overwrite); + await this.saveSnapshot(sourceWidget, selected, overwrite); return selected; } catch (e) { console.warn(e); @@ -85,30 +87,26 @@ export class FilesystemSaveableService extends SaveableService { } /** + * Saves the current snapshot of the {@link sourceWidget} to the target file + * and replaces the widget with a new one that contains the snapshot content + * * @param sourceWidget widget to save as `target`. * @param target The new URI for the widget. * @param overwrite */ - private async copyAndSave(sourceWidget: Widget & SaveableSource & Navigatable, target: URI, overwrite: boolean): Promise { - const snapshot = sourceWidget.saveable.createSnapshot!(); - if (!await this.fileService.exists(target)) { - const sourceUri = sourceWidget.getResourceUri()!; - if (this.fileService.canHandleResource(sourceUri)) { - await this.fileService.copy(sourceUri, target, { overwrite }); - } else { - await this.fileService.createFile(target); - } - } - const targetWidget = await open(this.openerService, target, { widgetOptions: { ref: sourceWidget } }); - const targetSaveable = Saveable.get(targetWidget); - if (targetWidget && targetSaveable && targetSaveable.applySnapshot) { - targetSaveable.applySnapshot(snapshot); - await sourceWidget.saveable.revert!(); - sourceWidget.close(); - Saveable.save(targetWidget, { formatType: FormatType.ON }); + protected async saveSnapshot(sourceWidget: Widget & SaveableSource & Navigatable, target: URI, overwrite: boolean): Promise { + const saveable = sourceWidget.saveable; + const snapshot = saveable.createSnapshot!(); + const content = Saveable.Snapshot.read(snapshot) ?? ''; + if (await this.fileService.exists(target)) { + // Do not fire the `onDidCreate` event as the file already exists. + await this.fileService.write(target, content); } else { - this.messageService.error(nls.localize('theia/workspace/failApply', 'Could not apply changes to new file')); + // Ensure to actually call `create` as that fires the `onDidCreate` event. + await this.fileService.create(target, content, { overwrite }); } + await saveable.revert!(); + await open(this.openerService, target, { widgetOptions: { ref: sourceWidget, mode: 'tab-replace' } }); } async confirmOverwrite(uri: URI): Promise { @@ -119,7 +117,7 @@ export class FilesystemSaveableService extends SaveableService { // Prompt users for confirmation before overwriting. const confirmed = await new ConfirmDialog({ title: nls.localizeByDefault('Overwrite'), - msg: nls.localizeByDefault('{0} already exists. Are you sure you want to overwrite it?', uri.toString()) + msg: nls.localizeByDefault('{0} already exists. Are you sure you want to overwrite it?', this.labelProvider.getName(uri)) }).open(); return !!confirmed; } diff --git a/packages/filesystem/src/browser/location/location-renderer.tsx b/packages/filesystem/src/browser/location/location-renderer.tsx index bf89c946fb7c5..b6f61a60564b2 100644 --- a/packages/filesystem/src/browser/location/location-renderer.tsx +++ b/packages/filesystem/src/browser/location/location-renderer.tsx @@ -122,7 +122,9 @@ export class LocationListRenderer extends ReactRenderer { } override render(): void { - this.hostRoot.render(this.doRender()); + if (!this.toDispose.disposed) { + this.hostRoot.render(this.doRender()); + } } protected initResolveDirectoryCache(): void { diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index eef64d64aef89..e8f6fa2629ef8 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -651,7 +651,7 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo } applySnapshot(snapshot: Saveable.Snapshot): void { - const value = 'value' in snapshot ? snapshot.value : snapshot.read() ?? ''; + const value = Saveable.Snapshot.read(snapshot) ?? ''; this.model.setValue(value); } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 5099cee977e46..b1f7f06a403c6 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -190,7 +190,7 @@ export class NotebookModel implements Saveable, Disposable { } async applySnapshot(snapshot: Saveable.Snapshot): Promise { - const rawData = 'read' in snapshot ? snapshot.read() : snapshot.value; + const rawData = Saveable.Snapshot.read(snapshot); if (!rawData) { throw new Error('could not read notebook snapshot'); } From a5f03f84c93aca94e6c24a0bbd8b52928241306d Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 20 Jun 2024 13:17:43 +0200 Subject: [PATCH 270/441] improved shown keybindings in context menu (#13830) * improved shown keybindings in context menu Signed-off-by: Jonah Iden * small performance improvement Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- packages/core/src/browser/menu/browser-menu-plugin.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/menu/browser-menu-plugin.ts b/packages/core/src/browser/menu/browser-menu-plugin.ts index 931b0719d931e..392c2003eac26 100644 --- a/packages/core/src/browser/menu/browser-menu-plugin.ts +++ b/packages/core/src/browser/menu/browser-menu-plugin.ts @@ -468,9 +468,11 @@ export class MenuCommandRegistry extends PhosphorCommandRegistry { }); const bindings = keybindingRegistry.getKeybindingsForCommand(id); - // Only consider the first keybinding. + // Only consider the first active keybinding. if (bindings.length) { - const binding = bindings[0]; + const binding = bindings.length > 1 ? + bindings.find(b => !b.when || this.services.contextKeyService.match(b.when)) ?? bindings[0] : + bindings[0]; const keys = keybindingRegistry.acceleratorFor(binding, ' ', true); this.addKeyBinding({ command: id, From c7af87240cfa035a6b53512d195a9d6a888b657c Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 20 Jun 2024 14:23:02 +0200 Subject: [PATCH 271/441] Serialize saveables to disk for "Save As" (#13833) --- packages/core/src/browser/saveable.ts | 10 ++++++++- .../browser/filesystem-saveable-service.ts | 22 ++++++++++++++----- .../monaco/src/browser/monaco-editor-model.ts | 5 +++++ .../src/browser/view-model/notebook-model.ts | 8 +++++-- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index f65a6a29162af..ecb4728a1c955 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -22,6 +22,7 @@ import { Key } from './keyboard/keys'; import { AbstractDialog } from './dialogs'; import { nls } from '../common/nls'; import { DisposableCollection, isObject } from '../common'; +import { BinaryBuffer } from '../common/buffer'; export type AutoSaveMode = 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; @@ -53,6 +54,10 @@ export interface Saveable { * Applies the given snapshot to the dirty state. */ applySnapshot?(snapshot: object): void; + /** + * Serializes the full state of the saveable item to a binary buffer. + */ + serialize?(): Promise; } export interface SaveableSource { @@ -79,6 +84,7 @@ export class DelegatingSaveable implements Saveable { revert?(options?: Saveable.RevertOptions): Promise; createSnapshot?(): Saveable.Snapshot; applySnapshot?(snapshot: object): void; + serialize?(): Promise; protected _delegate?: Saveable; protected toDispose = new DisposableCollection(); @@ -101,6 +107,7 @@ export class DelegatingSaveable implements Saveable { this.revert = delegate.revert?.bind(delegate); this.createSnapshot = delegate.createSnapshot?.bind(delegate); this.applySnapshot = delegate.applySnapshot?.bind(delegate); + this.serialize = delegate.serialize?.bind(delegate); } } @@ -115,7 +122,8 @@ export namespace Saveable { } /** - * A snapshot of a saveable item. Contains the full content of the saveable file in a string serializable format. + * A snapshot of a saveable item. + * Applying a snapshot of a saveable on another (of the same type) using the `applySnapshot` should yield the state of the original saveable. */ export type Snapshot = { value: string } | { read(): string | null }; export namespace Snapshot { diff --git a/packages/filesystem/src/browser/filesystem-saveable-service.ts b/packages/filesystem/src/browser/filesystem-saveable-service.ts index e7aaa51ce389e..804fad348b800 100644 --- a/packages/filesystem/src/browser/filesystem-saveable-service.ts +++ b/packages/filesystem/src/browser/filesystem-saveable-service.ts @@ -21,6 +21,7 @@ import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import URI from '@theia/core/lib/common/uri'; import { FileService } from './file-service'; import { FileDialogService } from './file-dialog'; +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; @injectable() export class FilesystemSaveableService extends SaveableService { @@ -39,13 +40,13 @@ export class FilesystemSaveableService extends SaveableService { /** * This method ensures a few things about `widget`: * - `widget.getResourceUri()` actually returns a URI. - * - `widget.saveable.createSnapshot` is defined. + * - `widget.saveable.createSnapshot` or `widget.saveable.serialize` is defined. * - `widget.saveable.revert` is defined. */ override canSaveAs(widget: Widget | undefined): widget is Widget & SaveableSource & Navigatable { return widget !== undefined && Saveable.isSource(widget) - && typeof widget.saveable.createSnapshot === 'function' + && (typeof widget.saveable.createSnapshot === 'function' || typeof widget.saveable.serialize === 'function') && typeof widget.saveable.revert === 'function' && Navigatable.is(widget) && widget.getResourceUri() !== undefined; @@ -96,14 +97,23 @@ export class FilesystemSaveableService extends SaveableService { */ protected async saveSnapshot(sourceWidget: Widget & SaveableSource & Navigatable, target: URI, overwrite: boolean): Promise { const saveable = sourceWidget.saveable; - const snapshot = saveable.createSnapshot!(); - const content = Saveable.Snapshot.read(snapshot) ?? ''; + let buffer: BinaryBuffer; + if (saveable.serialize) { + buffer = await saveable.serialize(); + } else if (saveable.createSnapshot) { + const snapshot = saveable.createSnapshot(); + const content = Saveable.Snapshot.read(snapshot) ?? ''; + buffer = BinaryBuffer.fromString(content); + } else { + throw new Error('Cannot save the widget as the saveable does not provide a snapshot or a serialize method.'); + } + if (await this.fileService.exists(target)) { // Do not fire the `onDidCreate` event as the file already exists. - await this.fileService.write(target, content); + await this.fileService.writeFile(target, buffer); } else { // Ensure to actually call `create` as that fires the `onDidCreate` event. - await this.fileService.create(target, content, { overwrite }); + await this.fileService.createFile(target, buffer, { overwrite }); } await saveable.revert!(); await open(this.openerService, target, { widgetOptions: { ref: sourceWidget, mode: 'tab-replace' } }); diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index e8f6fa2629ef8..7d05e457d0d02 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -33,6 +33,7 @@ import { IModelService } from '@theia/monaco-editor-core/esm/vs/editor/common/se import { createTextBufferFactoryFromStream } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel'; import { editorGeneratedPreferenceProperties } from '@theia/editor/lib/browser/editor-generated-preference-schema'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; export { TextDocumentSaveReason @@ -655,6 +656,10 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo this.model.setValue(value); } + async serialize(): Promise { + return BinaryBuffer.fromString(this.model.getValue()); + } + protected trace(loggable: Loggable): void { if (this.logger) { this.logger.debug((log: Log) => diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index b1f7f06a403c6..68e901157286b 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -34,6 +34,7 @@ import { inject, injectable, interfaces, postConstruct } from '@theia/core/share import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import type { NotebookModelResolverService } from '../service/notebook-model-resolver-service'; +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; export const NotebookModelFactory = Symbol('NotebookModelFactory'); @@ -176,8 +177,7 @@ export class NotebookModel implements Saveable, Disposable { this.dirtyCells = []; this.dirty = false; - const data = this.getData(); - const serializedNotebook = await this.props.serializer.fromNotebook(data); + const serializedNotebook = await this.serialize(); this.fileService.writeFile(this.uri, serializedNotebook); this.onDidSaveNotebookEmitter.fire(); @@ -189,6 +189,10 @@ export class NotebookModel implements Saveable, Disposable { }; } + serialize(): Promise { + return this.props.serializer.fromNotebook(this.getData()); + } + async applySnapshot(snapshot: Saveable.Snapshot): Promise { const rawData = Saveable.Snapshot.read(snapshot); if (!rawData) { From 611d83d437fa50453b514f052b90ddea32e57b56 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 20 Jun 2024 18:48:05 +0200 Subject: [PATCH 272/441] Fix tab group API event order (#13812) --- examples/api-tests/src/typescript.spec.js | 3 +- .../src/browser/shell/theia-dock-panel.ts | 4 +- .../browser/editors-and-documents-main.ts | 18 +++++-- .../src/main/browser/main-context.ts | 8 +-- .../src/main/browser/tabs/tabs-main.ts | 54 +++++++++++++------ 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index a2432070f1f77..ac22ca97abb03 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -103,10 +103,9 @@ describe('TypeScript', function () { const editorWidget = widget instanceof EditorWidget ? widget : undefined; const editor = MonacoEditor.get(editorWidget); assert.isDefined(editor); - await timeout(1000); // workaround for https://github.com/eclipse-theia/theia/issues/13679 // wait till tsserver is running, see: // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103 - // await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile')); + await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile')); // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911 await waitForAnimation(() => !progressStatusBarItem.currentProgress); return /** @type {MonacoEditor} */ (editor); diff --git a/packages/core/src/browser/shell/theia-dock-panel.ts b/packages/core/src/browser/shell/theia-dock-panel.ts index 19c2b60e92c77..13bc989e6ce91 100644 --- a/packages/core/src/browser/shell/theia-dock-panel.ts +++ b/packages/core/src/browser/shell/theia-dock-panel.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { find, toArray, ArrayExt } from '@phosphor/algorithm'; +import { find, toArray } from '@phosphor/algorithm'; import { TabBar, Widget, DockPanel, Title, DockLayout } from '@phosphor/widgets'; import { Signal } from '@phosphor/signaling'; import { Disposable, DisposableCollection } from '../../common/disposable'; @@ -103,7 +103,7 @@ export class TheiaDockPanel extends DockPanel { } findTabBar(title: Title): TabBar | undefined { - return find(this.tabBars(), bar => ArrayExt.firstIndexOf(bar.titles, title) > -1); + return find(this.tabBars(), bar => bar.titles.includes(title)); } protected readonly toDisposeOnMarkAsCurrent = new DisposableCollection(); diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index 0078b76ba4c39..f0e8c8b633e29 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -33,6 +33,7 @@ import { TextEditorMain } from './text-editor-main'; import { DisposableCollection, Emitter, URI } from '@theia/core'; import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; import { SaveableService } from '@theia/core/lib/browser/saveable-service'; +import { TabsMainImpl } from './tabs/tabs-main'; export class EditorsAndDocumentsMain implements Disposable { @@ -59,14 +60,14 @@ export class EditorsAndDocumentsMain implements Disposable { Disposable.create(() => this.textEditors.clear()) ); - constructor(rpc: RPCProtocol, container: interfaces.Container) { + constructor(rpc: RPCProtocol, container: interfaces.Container, tabsMain: TabsMainImpl) { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT); this.editorManager = container.get(EditorManager); this.modelService = container.get(EditorModelService); this.saveResourceService = container.get(SaveableService); - this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService); + this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService, tabsMain); this.toDispose.push(this.stateComputer); this.toDispose.push(this.onTextEditorAddEmitter); this.toDispose.push(this.onTextEditorRemoveEmitter); @@ -217,18 +218,25 @@ class EditorAndDocumentStateComputer implements Disposable { constructor( private callback: (delta: EditorAndDocumentStateDelta) => void, private readonly editorService: EditorManager, - private readonly modelService: EditorModelService + private readonly modelService: EditorModelService, + private readonly tabsMain: TabsMainImpl ) { } listen(): void { if (this.toDispose.disposed) { return; } - this.toDispose.push(this.editorService.onCreated(widget => { + this.toDispose.push(this.editorService.onCreated(async widget => { + await this.tabsMain.waitForWidget(widget); this.onTextEditorAdd(widget); this.update(); })); - this.toDispose.push(this.editorService.onCurrentEditorChanged(() => this.update())); + this.toDispose.push(this.editorService.onCurrentEditorChanged(async widget => { + if (widget) { + await this.tabsMain.waitForWidget(widget); + } + this.update(); + })); this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); this.toDispose.push(this.modelService.onModelRemoved(() => this.update())); diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index 2f11bd40b8045..262926efc80d9 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -87,7 +87,10 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container const preferenceRegistryMain = new PreferenceRegistryMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN, preferenceRegistryMain); - const editorsAndDocuments = new EditorsAndDocumentsMain(rpc, container); + const tabsMain = new TabsMainImpl(rpc, container); + rpc.set(PLUGIN_RPC_CONTEXT.TABS_MAIN, tabsMain); + + const editorsAndDocuments = new EditorsAndDocumentsMain(rpc, container, tabsMain); const notebookDocumentsMain = new NotebookDocumentsMainImpl(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain); @@ -198,9 +201,6 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container const commentsMain = new CommentsMainImp(rpc, container); rpc.set(PLUGIN_RPC_CONTEXT.COMMENTS_MAIN, commentsMain); - const tabsMain = new TabsMainImpl(rpc, container); - rpc.set(PLUGIN_RPC_CONTEXT.TABS_MAIN, tabsMain); - const localizationMain = new LocalizationMainImpl(container); rpc.set(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN, localizationMain); } diff --git a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts index bd8f83885aad4..ca38088b8f297 100644 --- a/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts +++ b/packages/plugin-ext/src/main/browser/tabs/tabs-main.ts @@ -25,7 +25,7 @@ import { toUriComponents } from '../hierarchy/hierarchy-types-converters'; import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { DisposableCollection } from '@theia/core'; import { NotebookEditorWidget } from '@theia/notebook/lib/browser'; -import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service'; +import { Deferred } from '@theia/core/lib/common/promise-util'; interface TabInfo { tab: TabDto; @@ -38,17 +38,17 @@ export class TabsMainImpl implements TabsMain, Disposable { private readonly proxy: TabsExt; private tabGroupModel = new Map, TabGroupDto>(); private tabInfoLookup = new Map, TabInfo>(); + private waitQueue = new Map(); private applicationShell: ApplicationShell; private disposableTabBarListeners: DisposableCollection = new DisposableCollection(); private toDisposeOnDestroy: DisposableCollection = new DisposableCollection(); - private groupIdCounter = 0; + private groupIdCounter = 1; private currentActiveGroup: TabGroupDto; private tabGroupChanged: boolean = false; - private viewColumnService: ViewColumnService; private readonly defaultTabGroup: TabGroupDto = { groupId: 0, @@ -64,11 +64,10 @@ export class TabsMainImpl implements TabsMain, Disposable { this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TABS_EXT); this.applicationShell = container.get(ApplicationShell); - this.viewColumnService = container.get(ViewColumnService); this.createTabsModel(); const tabBars = this.applicationShell.mainPanel.tabBars(); - for (let tabBar; tabBar = tabBars.next();) { + for (let tabBar: TabBar | undefined; tabBar = tabBars.next();) { this.attachListenersToTabBar(tabBar); } @@ -110,6 +109,21 @@ export class TabsMainImpl implements TabsMain, Disposable { }); } + waitForWidget(widget: Widget): Promise { + const deferred = new Deferred(); + this.waitQueue.set(widget, deferred); + + const timeout = setTimeout(() => { + deferred.resolve(); // resolve to unblock the event + }, 1000); + + deferred.promise.then(() => { + clearTimeout(timeout); + }); + + return deferred.promise; + } + protected createTabsModel(): void { if (this.applicationShell.mainAreaTabBars.length === 0) { this.proxy.$acceptEditorTabModel([this.defaultTabGroup]); @@ -119,9 +133,9 @@ export class TabsMainImpl implements TabsMain, Disposable { this.tabInfoLookup.clear(); this.disposableTabBarListeners.dispose(); this.applicationShell.mainAreaTabBars - .forEach((tabBar, i) => { + .forEach(tabBar => { this.attachListenersToTabBar(tabBar); - const groupDto = this.createTabGroupDto(tabBar, i); + const groupDto = this.createTabGroupDto(tabBar); tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index })); newTabGroupModel.set(tabBar, groupDto); }); @@ -131,31 +145,38 @@ export class TabsMainImpl implements TabsMain, Disposable { } this.tabGroupModel = newTabGroupModel; this.proxy.$acceptEditorTabModel(Array.from(this.tabGroupModel.values())); + // Resolve all waiting widget promises + this.waitQueue.forEach(deferred => deferred.resolve()); + this.waitQueue.clear(); } - protected createTabDto(tabTitle: Title, groupId: number): TabDto { + protected createTabDto(tabTitle: Title, groupId: number, newTab = false): TabDto { const widget = tabTitle.owner; + const active = newTab || this.getTabBar(tabTitle)?.currentTitle === tabTitle; return { id: this.createTabId(tabTitle, groupId), label: tabTitle.label, input: this.evaluateTabDtoInput(widget), - isActive: tabTitle.owner.isVisible, + isActive: active, isPinned: tabTitle.className.includes(PINNED_CLASS), isDirty: Saveable.isDirty(widget), isPreview: widget instanceof EditorPreviewWidget && widget.isPreview }; } + protected getTabBar(tabTitle: Title): TabBar | undefined { + return this.applicationShell.mainPanel.findTabBar(tabTitle); + } + protected createTabId(tabTitle: Title, groupId: number): string { return `${groupId}~${tabTitle.owner.id}`; } - protected createTabGroupDto(tabBar: TabBar, index: number): TabGroupDto { - const oldDto = index === 0 ? this.defaultTabGroup : this.tabGroupModel.get(tabBar); + protected createTabGroupDto(tabBar: TabBar): TabGroupDto { + const oldDto = this.tabGroupModel.get(tabBar); const groupId = oldDto?.groupId ?? this.groupIdCounter++; const tabs = tabBar.titles.map(title => this.createTabDto(title, groupId)); - const viewColumn = this.viewColumnService.getViewColumn(tabBar.id) - ?? this.applicationShell.allTabBars.indexOf(tabBar); + const viewColumn = 0; // TODO: Implement correct viewColumn handling return { groupId, tabs, @@ -190,7 +211,6 @@ export class TabsMainImpl implements TabsMain, Disposable { uri: toUriComponents(widget.editor.uri.toString()) }; } - // TODO notebook support when implemented } else if (widget instanceof ViewContainer) { return { kind: TabInputKind.WebviewEditorInput, @@ -238,7 +258,7 @@ export class TabsMainImpl implements TabsMain, Disposable { private onTabCreated(tabBar: TabBar, args: TabBar.ITabActivateRequestedArgs): void { const group = this.getOrRebuildModel(this.tabGroupModel, tabBar); this.connectToSignal(this.disposableTabBarListeners, args.title.changed, this.onTabTitleChanged); - const tabDto = this.createTabDto(args.title, group.groupId); + const tabDto = this.createTabDto(args.title, group.groupId, true); this.tabInfoLookup.set(args.title, { group, tab: tabDto, tabIndex: args.index }); group.tabs.splice(args.index, 0, tabDto); this.proxy.$acceptTabOperation({ @@ -247,6 +267,8 @@ export class TabsMainImpl implements TabsMain, Disposable { tabDto, groupId: group.groupId }); + this.waitQueue.get(args.title.owner)?.resolve(); + this.waitQueue.delete(args.title.owner); } private onTabTitleChanged(title: Title): void { @@ -275,6 +297,8 @@ export class TabsMainImpl implements TabsMain, Disposable { groupId: tabInfo.group.groupId }); } + this.waitQueue.get(title.owner)?.resolve(); + this.waitQueue.delete(title.owner); } private onTabClosed(tabInfo: TabInfo, title: Title): void { From b151610ec7703ac5ee088eaad888d8e1093dc907 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Fri, 21 Jun 2024 13:16:59 +0200 Subject: [PATCH 273/441] Disable cell editor search widget (#13836) --- packages/monaco/src/browser/simple-monaco-editor.ts | 13 ++++++------- .../src/browser/view/notebook-cell-editor.tsx | 4 +++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/monaco/src/browser/simple-monaco-editor.ts b/packages/monaco/src/browser/simple-monaco-editor.ts index de2a1d525f2e7..3ef6322f59efb 100644 --- a/packages/monaco/src/browser/simple-monaco-editor.ts +++ b/packages/monaco/src/browser/simple-monaco-editor.ts @@ -16,7 +16,7 @@ import { EditorServiceOverrides, MonacoEditor, MonacoEditorServices } from './monaco-editor'; -import { CodeEditorWidget } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/codeEditorWidget'; +import { CodeEditorWidget, ICodeEditorWidgetOptions } from '@theia/monaco-editor-core/esm/vs/editor/browser/widget/codeEditorWidget'; import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { ServiceCollection } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/serviceCollection'; @@ -51,7 +51,8 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab readonly node: HTMLElement, services: MonacoEditorServices, options?: MonacoEditor.IOptions, - override?: EditorServiceOverrides + override?: EditorServiceOverrides, + widgetOptions?: ICodeEditorWidgetOptions ) { super(services); this.toDispose.pushAll([ @@ -66,7 +67,7 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab this.toDispose.push(this.create({ ...MonacoEditor.createReadOnlyOptions(document.readOnly), ...options - }, override)); + }, override, widgetOptions)); this.addHandlers(this.editor); this.editor.setModel(document.textEditorModel); } @@ -75,7 +76,7 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab return this.editor; } - protected create(options?: MonacoEditor.IOptions, override?: EditorServiceOverrides): Disposable { + protected create(options?: MonacoEditor.IOptions, override?: EditorServiceOverrides, widgetOptions?: ICodeEditorWidgetOptions): Disposable { const combinedOptions = { ...options, lightbulb: { enabled: true }, @@ -97,9 +98,7 @@ export class SimpleMonacoEditor extends MonacoEditorServices implements Disposab width: 0, height: 0 }, - }, { - - }); + }, widgetOptions ?? {}); } protected addHandlers(codeEditor: CodeEditorWidget): void { diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 6e5e3711b1a55..cc7260abc2e5c 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -26,6 +26,7 @@ import { DisposableCollection, OS } from '@theia/core'; import { NotebookViewportService } from './notebook-viewport-service'; import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo'; import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys'; +import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorExtensions'; interface CellEditorProps { notebookModel: NotebookModel, @@ -117,7 +118,8 @@ export class CellEditor extends React.Component { editorNode, monacoServices, { ...DEFAULT_EDITOR_OPTIONS, ...cell.editorOptions }, - [[IContextKeyService, this.props.notebookContextManager.scopedStore]]); + [[IContextKeyService, this.props.notebookContextManager.scopedStore]], + { contributions: EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== 'editor.contrib.findController') }); this.toDispose.push(this.editor); this.editor.setLanguage(cell.language); this.toDispose.push(this.editor.getControl().onDidContentSizeChange(() => { From 49b8735733712df2945c5a3f60660688e888cb6a Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Fri, 21 Jun 2024 15:38:25 +0200 Subject: [PATCH 274/441] Fix account menu order, icon and badge (#13771) --- .../src/browser/authentication-service.ts | 20 ++++- .../browser/common-frontend-contribution.ts | 16 ++-- .../src/browser/shell/sidebar-menu-widget.tsx | 83 ++++++++++++++----- packages/core/src/browser/style/sidepanel.css | 9 +- packages/core/src/browser/style/tabs.css | 2 +- 5 files changed, 95 insertions(+), 35 deletions(-) diff --git a/packages/core/src/browser/authentication-service.ts b/packages/core/src/browser/authentication-service.ts index e9595b816248f..a1f1c7120ff01 100644 --- a/packages/core/src/browser/authentication-service.ts +++ b/packages/core/src/browser/authentication-service.ts @@ -133,6 +133,7 @@ export interface AuthenticationService { readonly onDidUnregisterAuthenticationProvider: Event; readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationProviderAuthenticationSessionsChangeEvent }>; + readonly onDidUpdateSignInCount: Event; getSessions(providerId: string, scopes?: string[]): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; @@ -157,15 +158,18 @@ export class AuthenticationServiceImpl implements AuthenticationService { protected authenticationProviders: Map = new Map(); - private onDidRegisterAuthenticationProviderEmitter: Emitter = new Emitter(); + private readonly onDidRegisterAuthenticationProviderEmitter: Emitter = new Emitter(); readonly onDidRegisterAuthenticationProvider: Event = this.onDidRegisterAuthenticationProviderEmitter.event; - private onDidUnregisterAuthenticationProviderEmitter: Emitter = new Emitter(); + private readonly onDidUnregisterAuthenticationProviderEmitter: Emitter = new Emitter(); readonly onDidUnregisterAuthenticationProvider: Event = this.onDidUnregisterAuthenticationProviderEmitter.event; - private onDidChangeSessionsEmitter: Emitter = new Emitter(); + private readonly onDidChangeSessionsEmitter: Emitter = new Emitter(); readonly onDidChangeSessions: Event = this.onDidChangeSessionsEmitter.event; + private readonly onDidChangeSignInCountEmitter: Emitter = new Emitter(); + readonly onDidUpdateSignInCount: Event = this.onDidChangeSignInCountEmitter.event; + @inject(MenuModelRegistry) protected readonly menus: MenuModelRegistry; @inject(CommandRegistry) protected readonly commands: CommandRegistry; @inject(StorageService) protected readonly storageService: StorageService; @@ -295,6 +299,7 @@ export class AuthenticationServiceImpl implements AuthenticationService { return; } + const previousSize = this.signInRequestItems.size; const sessions = await provider.getSessions(); Object.keys(existingRequestsForProvider).forEach(requestedScopes => { if (sessions.some(session => session.scopes.slice().sort().join('') === requestedScopes)) { @@ -311,6 +316,9 @@ export class AuthenticationServiceImpl implements AuthenticationService { } } }); + if (previousSize !== this.signInRequestItems.size) { + this.onDidChangeSignInCountEmitter.fire(this.signInRequestItems.size); + } } async requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise { @@ -341,7 +349,7 @@ export class AuthenticationServiceImpl implements AuthenticationService { } const menuItem = this.menus.registerMenuAction(ACCOUNTS_SUBMENU, { - label: `Sign in to use ${extensionName} (1)`, + label: nls.localizeByDefault('Sign in with {0} to use {1} (1)', provider.label, extensionName), order: '1', commandId: `${extensionId}signIn`, }); @@ -362,6 +370,7 @@ export class AuthenticationServiceImpl implements AuthenticationService { } }); + const previousSize = this.signInRequestItems.size; if (providerRequests) { const existingRequest = providerRequests[scopesList] || { disposables: [], requestingExtensionIds: [] }; @@ -378,6 +387,9 @@ export class AuthenticationServiceImpl implements AuthenticationService { } }); } + if (previousSize !== this.signInRequestItems.size) { + this.onDidChangeSignInCountEmitter.fire(this.signInRequestItems.size); + } } } diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index aa7d7bd85e299..fb9cd421f9eea 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -61,11 +61,12 @@ import { ConfirmDialog, confirmExit, ConfirmSaveDialog, Dialog } from './dialogs import { WindowService } from './window/window-service'; import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; import { DecorationStyle } from './decoration-style'; -import { isPinned, Title, togglePinned, Widget } from './widgets'; +import { codicon, isPinned, Title, togglePinned, Widget } from './widgets'; import { SaveableService } from './saveable-service'; import { UserWorkingDirectoryProvider } from './user-working-directory-provider'; import { UNTITLED_SCHEME, UntitledResourceResolver } from '../common'; import { LanguageQuickPickService } from './i18n/language-quick-pick-service'; +import { SidebarMenu } from './shell/sidebar-menu-widget'; export namespace CommonMenus { @@ -472,17 +473,18 @@ export class CommonFrontendContribution implements FrontendApplicationContributi app.shell.leftPanelHandler.addBottomMenu({ id: 'settings-menu', - iconClass: 'codicon codicon-settings-gear', + iconClass: codicon('settings-gear'), title: nls.localizeByDefault(CommonCommands.MANAGE_CATEGORY), menuPath: MANAGE_MENU, - order: 1, + order: 0, }); - const accountsMenu = { + const accountsMenu: SidebarMenu = { id: 'accounts-menu', - iconClass: 'codicon codicon-person', + iconClass: codicon('account'), title: nls.localizeByDefault('Accounts'), menuPath: ACCOUNTS_MENU, - order: 0, + order: 1, + onDidBadgeChange: this.authenticationService.onDidUpdateSignInCount }; this.authenticationService.onDidRegisterAuthenticationProvider(() => { app.shell.leftPanelHandler.addBottomMenu(accountsMenu); @@ -530,7 +532,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi if (newValue === 'compact') { this.shell.leftPanelHandler.addTopMenu({ id: mainMenuId, - iconClass: 'codicon codicon-menu', + iconClass: `theia-compact-menu ${codicon('menu')}`, title: nls.localizeByDefault('Application Menu'), menuPath: MAIN_MENU_BAR, order: 0, diff --git a/packages/core/src/browser/shell/sidebar-menu-widget.tsx b/packages/core/src/browser/shell/sidebar-menu-widget.tsx index 8e75b50fff5d4..b2faa89240fa9 100644 --- a/packages/core/src/browser/shell/sidebar-menu-widget.tsx +++ b/packages/core/src/browser/shell/sidebar-menu-widget.tsx @@ -20,6 +20,7 @@ import { ReactWidget } from '../widgets'; import { ContextMenuRenderer } from '../context-menu-renderer'; import { MenuPath } from '../../common/menu'; import { HoverService } from '../hover-service'; +import { Event, Disposable, Emitter, DisposableCollection } from '../../common'; export const SidebarTopMenuWidgetFactory = Symbol('SidebarTopMenuWidgetFactory'); export const SidebarBottomMenuWidgetFactory = Symbol('SidebarBottomMenuWidgetFactory'); @@ -29,18 +30,54 @@ export interface SidebarMenu { iconClass: string; title: string; menuPath: MenuPath; + onDidBadgeChange?: Event; /* * Used to sort menus. The lower the value the lower they are placed in the sidebar. */ order: number; } +export class SidebarMenuItem implements Disposable { + + readonly menu: SidebarMenu; + get badge(): string { + if (this._badge <= 0) { + return ''; + } else if (this._badge > 99) { + return '99+'; + } else { + return this._badge.toString(); + } + }; + protected readonly onDidBadgeChangeEmitter = new Emitter(); + readonly onDidBadgeChange: Event = this.onDidBadgeChangeEmitter.event; + protected _badge = 0; + + protected readonly toDispose = new DisposableCollection(); + + constructor(menu: SidebarMenu) { + this.menu = menu; + if (menu.onDidBadgeChange) { + this.toDispose.push(menu.onDidBadgeChange(value => { + this._badge = value; + this.onDidBadgeChangeEmitter.fire(value); + })); + } + } + + dispose(): void { + this.toDispose.dispose(); + this.onDidBadgeChangeEmitter.dispose(); + } + +} + /** * The menu widget placed on the sidebar. */ @injectable() export class SidebarMenuWidget extends ReactWidget { - protected readonly menus: SidebarMenu[]; + protected readonly items: SidebarMenuItem[]; /** * The element that had focus when a menu rendered by this widget was activated. */ @@ -58,27 +95,27 @@ export class SidebarMenuWidget extends ReactWidget { constructor() { super(); - this.menus = []; + this.items = []; } addMenu(menu: SidebarMenu): void { - const exists = this.menus.find(m => m.id === menu.id); + const exists = this.items.find(item => item.menu.id === menu.id); if (exists) { return; } - this.menus.push(menu); - this.menus.sort((a, b) => a.order - b.order); + const newItem = new SidebarMenuItem(menu); + newItem.onDidBadgeChange(() => this.update()); + this.items.push(newItem); + this.items.sort((a, b) => a.menu.order - b.menu.order); this.update(); } removeMenu(menuId: string): void { - const menu = this.menus.find(m => m.id === menuId); - if (menu) { - const index = this.menus.indexOf(menu); - if (index !== -1) { - this.menus.splice(index, 1); - this.update(); - } + const index = this.items.findIndex(m => m.menu.id === menuId); + if (index !== -1) { + this.items[index].dispose(); + this.items.splice(index, 1); + this.update(); } } @@ -127,14 +164,20 @@ export class SidebarMenuWidget extends ReactWidget { protected render(): React.ReactNode { return - {this.menus.map(menu => this.onClick(e, menu.menuPath)} - onMouseDown={this.onMouseDown} - onMouseEnter={e => this.onMouseEnter(e, menu.title)} - onMouseLeave={this.onMouseOut} - />)} + {this.items.map(item => this.renderItem(item))} ; } + + protected renderItem(item: SidebarMenuItem): React.ReactNode { + return
    this.onClick(e, item.menu.menuPath)} + onMouseDown={this.onMouseDown} + onMouseEnter={e => this.onMouseEnter(e, item.menu.title)} + onMouseLeave={this.onMouseOut}> + + {item.badge &&
    {item.badge}
    } +
    ; + } } diff --git a/packages/core/src/browser/style/sidepanel.css b/packages/core/src/browser/style/sidepanel.css index 374dd47d9e932..64d5a59532a3f 100644 --- a/packages/core/src/browser/style/sidepanel.css +++ b/packages/core/src/browser/style/sidepanel.css @@ -186,23 +186,26 @@ flex-direction: column-reverse; } +.p-Widget .theia-sidebar-menu-item { + cursor: pointer; +} + .p-Widget.theia-sidebar-menu i { padding: var(--theia-private-sidebar-tab-padding-top-and-bottom) var(--theia-private-sidebar-tab-padding-left-and-right); display: flex; justify-content: center; align-items: center; - cursor: pointer; color: var(--theia-activityBar-inactiveForeground); background-color: var(--theia-activityBar-background); font-size: var(--theia-private-sidebar-icon-size); } -.theia-sidebar-menu i:hover { +.theia-sidebar-menu .theia-sidebar-menu-item:hover i { color: var(--theia-activityBar-foreground); } -.theia-sidebar-menu > i.codicon-menu { +.theia-sidebar-menu i.theia-compact-menu { font-size: 16px; } diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css index a77ae0ad9f172..92c48350cd691 100644 --- a/packages/core/src/browser/style/tabs.css +++ b/packages/core/src/browser/style/tabs.css @@ -305,7 +305,7 @@ display: none !important; } -.p-TabBar .theia-badge-decorator-sidebar { +.theia-badge-decorator-sidebar { background-color: var(--theia-activityBarBadge-background); border-radius: 20px; color: var(--theia-activityBarBadge-foreground); From 447e38e4432a32edf7f1e0b2d9d46315bed4c5df Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 24 Jun 2024 15:17:36 +0200 Subject: [PATCH 275/441] Return empty appRoot in web plugin host (#13762) --- .../plugin-ext/src/hosted/browser/worker/worker-env-ext.ts | 6 ++---- packages/plugin/src/theia.d.ts | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts index 8599f1d8667d6..d94a3b3ba51a7 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-env-ext.ts @@ -28,11 +28,9 @@ export class WorkerEnvExtImpl extends EnvExtImpl { super(); } - /** - * Throw error for app-root as there is no filesystem in worker context - */ override get appRoot(): string { - throw new Error('There is no app root in worker context'); + // The documentation indicates that this should be an empty string + return ''; } get isNewAppInstall(): boolean { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 535109db71e61..93093b6ab6a76 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -7734,6 +7734,9 @@ export module '@theia/plugin' { /** * The application root folder from which the editor is running. + * + * *Note* that the value is the empty string when running in an + * environment that has no representation of an application root folder. */ export const appRoot: string; From 1f08797d9a459a701a3eb69ed9c41586b46771e5 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 24 Jun 2024 15:19:23 +0200 Subject: [PATCH 276/441] Support `PluginExt#extensionKind` properly (#13763) --- packages/core/src/browser/browser.ts | 7 ++++++- packages/plugin-ext/src/common/plugin-api-rpc.ts | 14 ++++++++++++++ .../plugin-ext/src/hosted/browser/hosted-plugin.ts | 4 +++- packages/plugin-ext/src/plugin/plugin-context.ts | 2 +- packages/plugin-ext/src/plugin/plugin-manager.ts | 9 ++++++++- 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/core/src/browser/browser.ts b/packages/core/src/browser/browser.ts index c39a76057af7b..88291aaa9ac91 100644 --- a/packages/core/src/browser/browser.ts +++ b/packages/core/src/browser/browser.ts @@ -34,9 +34,14 @@ export const isSafari = (userAgent.indexOf('Chrome') === -1) && (userAgent.index export const isIPad = (userAgent.indexOf('iPad') >= 0); // eslint-disable-next-line @typescript-eslint/no-explicit-any /** - * @deprecated us Environment.electron.is + * @deprecated use Environment.electron.is */ export const isNative = environment.electron.is(); +/** + * Determines whether the backend is running in a remote environment. + * I.e. we use the browser version or connect to a remote Theia instance in Electron. + */ +export const isRemote = !environment.electron.is() || new URL(location.href).searchParams.has('localPort'); // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isBasicWasmSupported = typeof (window as any).WebAssembly !== 'undefined'; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 54bd49125d8ea..e9fdea0194be9 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -159,6 +159,18 @@ export enum UIKind { Web = 2 } +export enum ExtensionKind { + /** + * Extension runs where the UI runs. + */ + UI = 1, + + /** + * Extension runs where the remote extension host runs. + */ + Workspace = 2 +} + export interface EnvInit { queryParams: QueryParameters; language: string; @@ -178,6 +190,7 @@ export interface PluginManager { getAllPlugins(): Plugin[]; getPluginById(pluginId: string): Plugin | undefined; getPluginExport(pluginId: string): PluginAPI | undefined; + getPluginKind(): theia.ExtensionKind; isRunning(pluginId: string): boolean; isActive(pluginId: string): boolean; activatePlugin(pluginId: string): PromiseLike; @@ -233,6 +246,7 @@ export interface PluginManagerInitializeParams { globalState: KeysToKeysToAnyValue workspaceState: KeysToKeysToAnyValue env: EnvInit + pluginKind: ExtensionKind extApi?: ExtPluginApi[] webview: WebviewInitData jsonValidation: PluginJsonValidationContribution[] diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index e972a1603f36a..913fa538f1a47 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -26,7 +26,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify' import { PluginWorker } from './plugin-worker'; import { getPluginId, DeployedPlugin, HostedPluginServer } from '../../common/plugin-protocol'; import { HostedPluginWatcher } from './hosted-plugin-watcher'; -import { MAIN_RPC_CONTEXT, PluginManagerExt, UIKind } from '../../common/plugin-api-rpc'; +import { ExtensionKind, MAIN_RPC_CONTEXT, PluginManagerExt, UIKind } from '../../common/plugin-api-rpc'; import { setUpPluginApi } from '../../main/browser/main-context'; import { RPCProtocol, RPCProtocolImpl } from '../../common/rpc-protocol'; import { @@ -71,6 +71,7 @@ import { AbstractHostedPluginSupport, PluginContributions, PluginHost, ALL_ACTIVATION_EVENT, isConnectionScopedBackendPlugin } from '../common/hosted-plugin'; +import { isRemote } from '@theia/core/lib/browser/browser'; export type DebugActivationEvent = 'onDebugResolve' | 'onDebugInitialConfigurations' | 'onDebugAdapterProtocolTracker' | 'onDebugDynamicConfigurations'; @@ -334,6 +335,7 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport extends Plugin implements ExtensionPlugin { this.extensionPath = this.pluginPath; this.extensionUri = this.pluginUri; - this.extensionKind = ExtensionKind.UI; // stub as a local extension (not running on a remote workspace) + this.extensionKind = pluginManager.getPluginKind(); this.isFromDifferentExtensionHost = isFromDifferentExtensionHost; } diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index c9ea874c98537..bc84aee65e84e 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -29,7 +29,8 @@ import { PluginManagerInitializeParams, PluginManagerStartParams, TerminalServiceExt, - LocalizationExt + LocalizationExt, + ExtensionKind } from '../common/plugin-api-rpc'; import { PluginMetadata, PluginJsonValidationContribution } from '../common/plugin-protocol'; import * as theia from '@theia/plugin'; @@ -121,6 +122,7 @@ export abstract class AbstractPluginManagerExtImpl

    private notificationMain: NotificationMain; protected jsonValidation: PluginJsonValidationContribution[] = []; + protected pluginKind = ExtensionKind.UI; protected ready = new Deferred(); @postConstruct() @@ -410,6 +412,10 @@ export abstract class AbstractPluginManagerExtImpl

    } } + getPluginKind(): theia.ExtensionKind { + return this.pluginKind; + } + getAllPlugins(): Plugin[] { return Array.from(this.registry.values()); } @@ -477,6 +483,7 @@ export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl Date: Tue, 25 Jun 2024 11:32:35 +0200 Subject: [PATCH 277/441] Use `openWithSystemApp` to open uri on `vscode.env.openExternal`(#13676) --- .../core/src/browser/http-open-handler.ts | 4 +- packages/core/src/electron-browser/preload.ts | 4 +- .../window/electron-window-module.ts | 18 ++++---- .../window/external-app-open-handler.ts | 42 +++++++++++++++++++ .../core/src/electron-common/electron-api.ts | 6 ++- .../src/main/browser/window-state-main.ts | 2 +- 6 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 packages/core/src/electron-browser/window/external-app-open-handler.ts diff --git a/packages/core/src/browser/http-open-handler.ts b/packages/core/src/browser/http-open-handler.ts index e31b1f9bdc89d..d8f467a549372 100644 --- a/packages/core/src/browser/http-open-handler.ts +++ b/packages/core/src/browser/http-open-handler.ts @@ -27,6 +27,8 @@ export interface HttpOpenHandlerOptions { @injectable() export class HttpOpenHandler implements OpenHandler { + static readonly PRIORITY: number = 500; + readonly id = 'http'; @inject(WindowService) @@ -36,7 +38,7 @@ export class HttpOpenHandler implements OpenHandler { protected readonly externalUriService: ExternalUriService; canHandle(uri: URI, options?: HttpOpenHandlerOptions): number { - return ((options && options.openExternal) || uri.scheme.startsWith('http') || uri.scheme.startsWith('mailto')) ? 500 : 0; + return ((options && options.openExternal) || uri.scheme.startsWith('http') || uri.scheme.startsWith('mailto')) ? HttpOpenHandler.PRIORITY : 0; } async open(uri: URI): Promise { diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index b0903d1a764af..78d0d29a93f3c 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -79,8 +79,8 @@ const api: TheiaCoreAPI = { showItemInFolder: fsPath => { ipcRenderer.send(CHANNEL_SHOW_ITEM_IN_FOLDER, fsPath); }, - openWithSystemApp: fsPath => { - ipcRenderer.send(CHANNEL_OPEN_WITH_SYSTEM_APP, fsPath); + openWithSystemApp: location => { + ipcRenderer.send(CHANNEL_OPEN_WITH_SYSTEM_APP, location); }, attachSecurityToken: (endpoint: string) => ipcRenderer.invoke(CHANNEL_ATTACH_SECURITY_TOKEN, endpoint), diff --git a/packages/core/src/electron-browser/window/electron-window-module.ts b/packages/core/src/electron-browser/window/electron-window-module.ts index b50e21267b4cd..78f490c1c98db 100644 --- a/packages/core/src/electron-browser/window/electron-window-module.ts +++ b/packages/core/src/electron-browser/window/electron-window-module.ts @@ -15,18 +15,20 @@ // ***************************************************************************** import { ContainerModule } from 'inversify'; -import { WindowService } from '../../browser/window/window-service'; -import { ElectronWindowService } from './electron-window-service'; -import { FrontendApplicationContribution } from '../../browser/frontend-application-contribution'; -import { ElectronClipboardService } from '../electron-clipboard-service'; +import { OpenHandler } from '../../browser'; import { ClipboardService } from '../../browser/clipboard-service'; +import { FrontendApplicationContribution } from '../../browser/frontend-application-contribution'; +import { FrontendApplicationStateService } from '../../browser/frontend-application-state'; +import { SecondaryWindowService } from '../../browser/window/secondary-window-service'; +import { WindowService } from '../../browser/window/window-service'; import { ElectronMainWindowService, electronMainWindowServicePath } from '../../electron-common/electron-main-window-service'; +import { ElectronClipboardService } from '../electron-clipboard-service'; import { ElectronIpcConnectionProvider } from '../messaging/electron-ipc-connection-source'; -import { bindWindowPreferences } from './electron-window-preferences'; -import { FrontendApplicationStateService } from '../../browser/frontend-application-state'; import { ElectronFrontendApplicationStateService } from './electron-frontend-application-state'; import { ElectronSecondaryWindowService } from './electron-secondary-window-service'; -import { SecondaryWindowService } from '../../browser/window/secondary-window-service'; +import { bindWindowPreferences } from './electron-window-preferences'; +import { ElectronWindowService } from './electron-window-service'; +import { ExternalAppOpenHandler } from './external-app-open-handler'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ElectronMainWindowService).toDynamicValue(context => @@ -38,4 +40,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ClipboardService).to(ElectronClipboardService).inSingletonScope(); rebind(FrontendApplicationStateService).to(ElectronFrontendApplicationStateService).inSingletonScope(); bind(SecondaryWindowService).to(ElectronSecondaryWindowService).inSingletonScope(); + bind(ExternalAppOpenHandler).toSelf().inSingletonScope(); + bind(OpenHandler).toService(ExternalAppOpenHandler); }); diff --git a/packages/core/src/electron-browser/window/external-app-open-handler.ts b/packages/core/src/electron-browser/window/external-app-open-handler.ts new file mode 100644 index 0000000000000..d74c3a2378e1d --- /dev/null +++ b/packages/core/src/electron-browser/window/external-app-open-handler.ts @@ -0,0 +1,42 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from 'inversify'; +import { OpenHandler } from '../../browser/opener-service'; +import URI from '../../common/uri'; +import { HttpOpenHandler } from '../../browser/http-open-handler'; + +export interface ExternalAppOpenHandlerOptions { + openExternalApp?: boolean +} + +@injectable() +export class ExternalAppOpenHandler implements OpenHandler { + + static readonly PRIORITY: number = HttpOpenHandler.PRIORITY + 100; + readonly id = 'external-app'; + + canHandle(uri: URI, options?: ExternalAppOpenHandlerOptions): number { + return (options && options.openExternalApp) ? ExternalAppOpenHandler.PRIORITY : -1; + } + + async open(uri: URI): Promise { + // For files 'file:' scheme, system accepts only the path. + // For other protocols e.g. 'vscode:' we use the full URI to propagate target app information. + window.electronTheiaCore.openWithSystemApp(uri.scheme === 'file' ? uri.path.fsPath() : uri.toString(true)); + return undefined; + } +} diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 6bcde6a4fb333..833b44d6070e2 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -56,7 +56,11 @@ export interface TheiaCoreAPI { focusWindow(name?: string): void; showItemInFolder(fsPath: string): void; - openWithSystemApp(fsPath: string): void; + + /** + * @param location The location to open with the system app. This can be a file path or a URL. + */ + openWithSystemApp(location: string): void; getTitleBarStyleAtStartup(): Promise; setTitleBarStyle(style: string): void; diff --git a/packages/plugin-ext/src/main/browser/window-state-main.ts b/packages/plugin-ext/src/main/browser/window-state-main.ts index 1a48ea1f50e56..64e9439cf2e37 100644 --- a/packages/plugin-ext/src/main/browser/window-state-main.ts +++ b/packages/plugin-ext/src/main/browser/window-state-main.ts @@ -69,7 +69,7 @@ export class WindowStateMain implements WindowMain, Disposable { const uri = URI.revive(uriComponent); const url = new CoreURI(encodeURI(uri.toString(true))); try { - await open(this.openerService, url); + await open(this.openerService, url, { openExternalApp: true }); return true; } catch (e) { return false; From adf13a07727dd65744fff998e11303e65cdd9062 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 25 Jun 2024 11:36:10 +0200 Subject: [PATCH 278/441] Support dynamic menu contributions (#13720) --- examples/api-tests/src/menus.spec.js | 7 +++- .../src/browser/menu/browser-menu-plugin.ts | 41 +++++++++++-------- .../src/common/menu/menu-model-registry.ts | 41 ++++++++++++++++--- .../menu/electron-main-menu-factory.ts | 3 ++ 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/examples/api-tests/src/menus.spec.js b/examples/api-tests/src/menus.spec.js index 542c533bc6ff9..905e60422dfaa 100644 --- a/examples/api-tests/src/menus.spec.js +++ b/examples/api-tests/src/menus.spec.js @@ -44,8 +44,8 @@ describe('Menus', function () { const container = window.theia.container; const shell = container.get(ApplicationShell); + /** @type {BrowserMenuBarContribution} */ const menuBarContribution = container.get(BrowserMenuBarContribution); - const menuBar = /** @type {import('@theia/core/lib/browser/menu/browser-menu-plugin').MenuBarWidget} */ (menuBarContribution.menuBar); const pluginService = container.get(HostedPluginSupport); const menus = container.get(MenuModelRegistry); const commands = container.get(CommandRegistry); @@ -54,6 +54,9 @@ describe('Menus', function () { before(async function () { await pluginService.didStart; await pluginService.activateByViewContainer('explorer'); + // Updating the menu interferes with our ability to programmatically test it + // We simply disable the menu updating + menus.isReady = false; }); const toTearDown = new DisposableCollection(); @@ -73,7 +76,7 @@ describe('Menus', function () { ]) { it(`should toggle '${contribution.viewLabel}' view`, async () => { await contribution.closeView(); - await menuBar.triggerMenuItem('View', contribution.viewLabel); + await menuBarContribution.menuBar.triggerMenuItem('View', contribution.viewLabel); await shell.waitForActivation(contribution.viewId); }); } diff --git a/packages/core/src/browser/menu/browser-menu-plugin.ts b/packages/core/src/browser/menu/browser-menu-plugin.ts index 392c2003eac26..d1ceac06b8220 100644 --- a/packages/core/src/browser/menu/browser-menu-plugin.ts +++ b/packages/core/src/browser/menu/browser-menu-plugin.ts @@ -71,25 +71,30 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory { const menuBar = new DynamicMenuBarWidget(); menuBar.id = 'theia:menubar'; this.corePreferences.ready.then(() => { - this.showMenuBar(menuBar, this.corePreferences.get('window.menuBarVisibility', 'classic')); - }); - const preferenceListener = this.corePreferences.onPreferenceChanged(preference => { - if (preference.preferenceName === 'window.menuBarVisibility') { - this.showMenuBar(menuBar, preference.newValue); - } - }); - const keybindingListener = this.keybindingRegistry.onKeybindingsChanged(() => { - const preference = this.corePreferences['window.menuBarVisibility']; - this.showMenuBar(menuBar, preference); - }); - menuBar.disposed.connect(() => { - preferenceListener.dispose(); - keybindingListener.dispose(); + this.showMenuBar(menuBar); }); + const disposable = new DisposableCollection( + this.corePreferences.onPreferenceChanged(change => { + if (change.preferenceName === 'window.menuBarVisibility') { + this.showMenuBar(menuBar, change.newValue); + } + }), + this.keybindingRegistry.onKeybindingsChanged(() => { + this.showMenuBar(menuBar); + }), + this.menuProvider.onDidChange(() => { + this.showMenuBar(menuBar); + }) + ); + menuBar.disposed.connect(() => disposable.dispose()); return menuBar; } - protected showMenuBar(menuBar: DynamicMenuBarWidget, preference: string | undefined): void { + protected getMenuBarVisibility(): string { + return this.corePreferences.get('window.menuBarVisibility', 'classic'); + } + + protected showMenuBar(menuBar: DynamicMenuBarWidget, preference = this.getMenuBarVisibility()): void { if (preference && ['classic', 'visible'].includes(preference)) { menuBar.clearMenus(); this.fillMenuBar(menuBar); @@ -187,13 +192,13 @@ export class DynamicMenuBarWidget extends MenuBarWidget { this.openActiveMenu(); await waitForRevealed(menu); - const menuPath = [label]; + const menuPath = [label, ...labels]; let current = menu; for (const itemLabel of labels) { const item = current.items.find(i => i.label === itemLabel); if (!item || !item.submenu) { - throw new Error(`could not find '${label}' submenu in ${menuPath.map(l => "'" + l + "'").join(' -> ')} menu`); + throw new Error(`could not find '${itemLabel}' submenu in ${menuPath.map(l => "'" + l + "'").join(' -> ')} menu`); } current.activeItem = item; current.triggerActiveItem(); @@ -211,7 +216,7 @@ export class DynamicMenuBarWidget extends MenuBarWidget { const menu = await this.activateMenu(menuPath[0], ...menuPath.slice(1)); const item = menu.items.find(i => i.label === labels[labels.length - 1]); if (!item) { - throw new Error(`could not find '${label}' item in ${menuPath.map(l => "'" + l + "'").join(' -> ')} menu`); + throw new Error(`could not find '${labels[labels.length - 1]}' item in ${menuPath.map(l => "'" + l + "'").join(' -> ')} menu`); } menu.activeItem = item; menu.triggerActiveItem(); diff --git a/packages/core/src/common/menu/menu-model-registry.ts b/packages/core/src/common/menu/menu-model-registry.ts index 42b4a93ac9b96..e321440c45170 100644 --- a/packages/core/src/common/menu/menu-model-registry.ts +++ b/packages/core/src/common/menu/menu-model-registry.ts @@ -18,6 +18,7 @@ import { inject, injectable, named } from 'inversify'; import { Command, CommandRegistry } from '../command'; import { ContributionProvider } from '../contribution-provider'; import { Disposable } from '../disposable'; +import { Emitter, Event } from '../event'; import { ActionMenuNode } from './action-menu-node'; import { CompositeMenuNode, CompositeMenuNodeWrapper } from './composite-menu-node'; import { CompoundMenuNode, MenuAction, MenuNode, MenuNodeMetadata, MenuPath, MutableCompoundMenuNode, SubMenuOptions } from './menu-types'; @@ -68,6 +69,14 @@ export class MenuModelRegistry { protected readonly root = new CompositeMenuNode(''); protected readonly independentSubmenus = new Map(); + protected readonly onDidChangeEmitter = new Emitter(); + + get onDidChange(): Event { + return this.onDidChangeEmitter.event; + } + + protected isReady = false; + constructor( @inject(ContributionProvider) @named(MenuContribution) protected readonly contributions: ContributionProvider, @@ -78,6 +87,7 @@ export class MenuModelRegistry { for (const contrib of this.contributions.getContributions()) { contrib.registerMenus(this); } + this.isReady = true; } /** @@ -97,7 +107,9 @@ export class MenuModelRegistry { */ registerMenuNode(menuPath: MenuPath | string, menuNode: MenuNode, group?: string): Disposable { const parent = this.getMenuNode(menuPath, group); - return parent.addNode(menuNode); + const disposable = parent.addNode(menuNode); + this.fireChangeEvent(); + return this.changeEventOnDispose(disposable); } getMenuNode(menuPath: MenuPath | string, group?: string): MutableCompoundMenuNode { @@ -137,13 +149,15 @@ export class MenuModelRegistry { const groupPath = index === 0 ? [] : menuPath.slice(0, index); const parent = this.findGroup(groupPath, options); let groupNode = this.findSubMenu(parent, menuId, options); + let disposable = Disposable.NULL; if (!groupNode) { groupNode = new CompositeMenuNode(menuId, label, options, parent); - return parent.addNode(groupNode); + disposable = this.changeEventOnDispose(parent.addNode(groupNode)); } else { groupNode.updateOptions({ ...options, label }); - return Disposable.NULL; } + this.fireChangeEvent(); + return disposable; } registerIndependentSubmenu(id: string, label: string, options?: SubMenuOptions): Disposable { @@ -151,7 +165,7 @@ export class MenuModelRegistry { console.debug(`Independent submenu with path ${id} registered, but given ID already exists.`); } this.independentSubmenus.set(id, new CompositeMenuNode(id, label, options)); - return { dispose: () => this.independentSubmenus.delete(id) }; + return this.changeEventOnDispose(Disposable.create(() => this.independentSubmenus.delete(id))); } linkSubmenu(parentPath: MenuPath | string, childId: string | MenuPath, options?: SubMenuOptions, group?: string): Disposable { @@ -175,7 +189,9 @@ export class MenuModelRegistry { } const wrapper = new CompositeMenuNodeWrapper(child, parent, options); - return parent.addNode(wrapper); + const disposable = parent.addNode(wrapper); + this.fireChangeEvent(); + return this.changeEventOnDispose(disposable); } /** @@ -207,6 +223,7 @@ export class MenuModelRegistry { if (menuPath) { const parent = this.findGroup(menuPath); parent.removeNode(id); + this.fireChangeEvent(); return; } @@ -228,6 +245,7 @@ export class MenuModelRegistry { }); }; recurse(this.root); + this.fireChangeEvent(); } /** @@ -321,6 +339,19 @@ export class MenuModelRegistry { return true; } + protected changeEventOnDispose(disposable: Disposable): Disposable { + return Disposable.create(() => { + disposable.dispose(); + this.fireChangeEvent(); + }); + } + + protected fireChangeEvent(): void { + if (this.isReady) { + this.onDidChangeEmitter.fire(); + } + } + /** * Returns the {@link MenuPath path} at which a given menu node can be accessed from this registry, if it can be determined. * Returns `undefined` if the `parent` of any node in the chain is unknown. diff --git a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts index 162b4dca9235c..68a305c29b094 100644 --- a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts +++ b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts @@ -101,6 +101,9 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { this.keybindingRegistry.onKeybindingsChanged(() => { this.setMenuBar(); }); + this.menuProvider.onDidChange(() => { + this.setMenuBar(); + }); } async setMenuBar(): Promise { From 1a5d8d80bc16386094a78f55d1ba7228c7349a42 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 25 Jun 2024 11:39:50 +0200 Subject: [PATCH 279/441] Use `targetPlatform` when installing plugin from open-vsx (#13825) --- .vscode/launch.json | 3 +- dev-packages/cli/src/download-plugins.ts | 15 ++++-- dev-packages/ovsx-client/src/index.ts | 2 +- .../ovsx-client/src/ovsx-api-filter.ts | 47 ++++++++++++++++- .../ovsx-client/src/ovsx-http-client.ts | 5 +- .../ovsx-client/src/ovsx-mock-client.ts | 21 +++++--- .../ovsx-client/src/ovsx-router-client.ts | 7 ++- dev-packages/ovsx-client/src/ovsx-types.ts | 52 +++++++++++++++++-- .../src/node/sample-mock-open-vsx-server.ts | 4 +- .../frontend-only-application-module.ts | 1 + .../core/src/common/application-protocol.ts | 1 + packages/core/src/node/application-server.ts | 4 ++ .../browser/vsx-extensions-contribution.ts | 14 +++-- .../src/browser/vsx-extensions-model.ts | 42 +++++++++------ .../src/common/vsx-registry-common-module.ts | 19 ++++++- .../src/node/vsx-extension-resolver.ts | 22 +++++--- 16 files changed, 206 insertions(+), 53 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 00e3862ace5e0..3539be4173f25 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -58,12 +58,13 @@ "request": "launch", "name": "Launch Browser Backend", "program": "${workspaceFolder}/examples/browser/lib/backend/main.js", + "cwd": "${workspaceFolder}/examples/browser", "args": [ "--hostname=0.0.0.0", "--port=3000", "--no-cluster", "--app-project-path=${workspaceFolder}/examples/browser", - "--plugins=local-dir:plugins", + "--plugins=local-dir:../../plugins", "--hosted-plugin-inspect=9339", "--ovsx-router-config=${workspaceFolder}/examples/ovsx-router-config.json" ], diff --git a/dev-packages/cli/src/download-plugins.ts b/dev-packages/cli/src/download-plugins.ts index 1f4e3208a0900..7aa0abdc5d640 100644 --- a/dev-packages/cli/src/download-plugins.ts +++ b/dev-packages/cli/src/download-plugins.ts @@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { OVSXApiFilterImpl, OVSXClient } from '@theia/ovsx-client'; +import { OVSXApiFilterImpl, OVSXClient, VSXTargetPlatform } from '@theia/ovsx-client'; import * as chalk from 'chalk'; import * as decompress from 'decompress'; import { promises as fs } from 'fs'; @@ -75,7 +75,7 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer } = options; const rateLimiter = new RateLimiter({ tokensPerInterval: rateLimit, interval: 'second' }); - const apiFilter = new OVSXApiFilterImpl(apiVersion); + const apiFilter = new OVSXApiFilterImpl(ovsxClient, apiVersion); // Collect the list of failures to be appended at the end of the script. const failures: string[] = []; @@ -129,8 +129,11 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer await parallelOrSequence(Array.from(ids, id => async () => { try { await rateLimiter.removeTokens(1); - const { extensions } = await ovsxClient.query({ extensionId: id, includeAllVersions: true }); - const extension = apiFilter.getLatestCompatibleExtension(extensions); + const extension = await apiFilter.findLatestCompatibleExtension({ + extensionId: id, + includeAllVersions: true, + targetPlatform + }); const version = extension?.version; const downloadUrl = extension?.files.download; if (downloadUrl) { @@ -170,8 +173,10 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer } } +const targetPlatform = `${process.platform}-${process.arch}` as VSXTargetPlatform; + const placeholders: Record = { - targetPlatform: `${process.platform}-${process.arch}` + targetPlatform }; function resolveDownloadUrlPlaceholders(url: string): string { for (const [name, value] of Object.entries(placeholders)) { diff --git a/dev-packages/ovsx-client/src/index.ts b/dev-packages/ovsx-client/src/index.ts index 8d0d7318fe398..42455e7897de3 100644 --- a/dev-packages/ovsx-client/src/index.ts +++ b/dev-packages/ovsx-client/src/index.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -export { OVSXApiFilter, OVSXApiFilterImpl } from './ovsx-api-filter'; +export { OVSXApiFilter, OVSXApiFilterImpl, OVSXApiFilterProvider } from './ovsx-api-filter'; export { OVSXHttpClient } from './ovsx-http-client'; export { OVSXMockClient } from './ovsx-mock-client'; export { OVSXRouterClient, OVSXRouterConfig, OVSXRouterFilterFactory as FilterFactory } from './ovsx-router-client'; diff --git a/dev-packages/ovsx-client/src/ovsx-api-filter.ts b/dev-packages/ovsx-client/src/ovsx-api-filter.ts index 0c5c505491713..7b5e0bed3190a 100644 --- a/dev-packages/ovsx-client/src/ovsx-api-filter.ts +++ b/dev-packages/ovsx-client/src/ovsx-api-filter.ts @@ -15,7 +15,11 @@ // ***************************************************************************** import * as semver from 'semver'; -import { VSXAllVersions, VSXBuiltinNamespaces, VSXExtensionRaw, VSXSearchEntry } from './ovsx-types'; +import { OVSXClient, VSXAllVersions, VSXBuiltinNamespaces, VSXExtensionRaw, VSXQueryOptions, VSXSearchEntry } from './ovsx-types'; + +export const OVSXApiFilterProvider = Symbol('OVSXApiFilterProvider'); + +export type OVSXApiFilterProvider = () => Promise; export const OVSXApiFilter = Symbol('OVSXApiFilter'); /** @@ -23,6 +27,7 @@ export const OVSXApiFilter = Symbol('OVSXApiFilter'); */ export interface OVSXApiFilter { supportedApiVersion: string; + findLatestCompatibleExtension(query: VSXQueryOptions): Promise; /** * Get the latest compatible extension version: * - A builtin extension is fetched based on the extension version which matches the API. @@ -38,9 +43,49 @@ export interface OVSXApiFilter { export class OVSXApiFilterImpl implements OVSXApiFilter { constructor( + public client: OVSXClient, public supportedApiVersion: string ) { } + async findLatestCompatibleExtension(query: VSXQueryOptions): Promise { + const targetPlatform = query.targetPlatform; + if (!targetPlatform) { + return this.queryLatestCompatibleExtension(query); + } + const latestWithTargetPlatform = await this.queryLatestCompatibleExtension(query); + let latestUniversal: VSXExtensionRaw | undefined; + if (targetPlatform !== 'universal' && targetPlatform !== 'web') { + // Additionally query the universal version, as there might be a newer one available + latestUniversal = await this.queryLatestCompatibleExtension({ ...query, targetPlatform: 'universal' }); + } + if (latestWithTargetPlatform && latestUniversal) { + // Prefer the version with the target platform if it's greater or equal to the universal version + return this.versionGreaterThanOrEqualTo(latestWithTargetPlatform.version, latestUniversal.version) ? latestWithTargetPlatform : latestUniversal; + } + return latestWithTargetPlatform ?? latestUniversal; + } + + protected async queryLatestCompatibleExtension(query: VSXQueryOptions): Promise { + let offset = 0; + let loop = true; + while (loop) { + const queryOptions = { + ...query, + offset + }; + const results = await this.client.query(queryOptions); + const compatibleExtension = this.getLatestCompatibleExtension(results.extensions); + if (compatibleExtension) { + return compatibleExtension; + } + // Adjust offset by the amount of returned extensions + offset += results.extensions.length; + // Continue querying if there are more extensions available + loop = results.totalSize > offset; + } + return undefined; + } + getLatestCompatibleExtension(extensions: VSXExtensionRaw[]): VSXExtensionRaw | undefined { if (extensions.length === 0) { return; diff --git a/dev-packages/ovsx-client/src/ovsx-http-client.ts b/dev-packages/ovsx-client/src/ovsx-http-client.ts index 84629e61f02df..f59f9615d7ff6 100644 --- a/dev-packages/ovsx-client/src/ovsx-http-client.ts +++ b/dev-packages/ovsx-client/src/ovsx-http-client.ts @@ -48,10 +48,11 @@ export class OVSXHttpClient implements OVSXClient { async query(queryOptions?: VSXQueryOptions): Promise { try { - return await this.requestJson(this.buildUrl('api/-/query', queryOptions)); + return await this.requestJson(this.buildUrl('api/v2/-/query', queryOptions)); } catch (error) { - console.warn(error); return { + offset: 0, + totalSize: 0, extensions: [] }; } diff --git a/dev-packages/ovsx-client/src/ovsx-mock-client.ts b/dev-packages/ovsx-client/src/ovsx-mock-client.ts index f7366f841b62d..05388cce9e24c 100644 --- a/dev-packages/ovsx-client/src/ovsx-mock-client.ts +++ b/dev-packages/ovsx-client/src/ovsx-mock-client.ts @@ -61,21 +61,26 @@ export class OVSXMockClient implements OVSXClient { reviewsUrl: url.extensionReviewsUrl(namespace, name), timestamp: new Date(now - ids.length + i + 1).toISOString(), version, - description: `Mock VS Code Extension for ${id}` + description: `Mock VS Code Extension for ${id}`, + namespaceDisplayName: name, + preRelease: false }; }); return this; } async query(queryOptions?: VSXQueryOptions): Promise { + const extensions = this.extensions + .filter(extension => typeof queryOptions === 'object' && ( + this.compare(queryOptions.extensionId, this.id(extension)) && + this.compare(queryOptions.extensionName, extension.name) && + this.compare(queryOptions.extensionVersion, extension.version) && + this.compare(queryOptions.namespaceName, extension.namespace) + )); return { - extensions: this.extensions - .filter(extension => typeof queryOptions === 'object' && ( - this.compare(queryOptions.extensionId, this.id(extension)) && - this.compare(queryOptions.extensionName, extension.name) && - this.compare(queryOptions.extensionVersion, extension.version) && - this.compare(queryOptions.namespaceName, extension.namespace) - )) + offset: 0, + totalSize: extensions.length, + extensions }; } diff --git a/dev-packages/ovsx-client/src/ovsx-router-client.ts b/dev-packages/ovsx-client/src/ovsx-router-client.ts index f5121b3e18b09..6b524b9491b62 100644 --- a/dev-packages/ovsx-client/src/ovsx-router-client.ts +++ b/dev-packages/ovsx-client/src/ovsx-router-client.ts @@ -152,6 +152,8 @@ export class OVSXRouterClient implements OVSXClient { protected emptyQueryResult(queryOptions?: VSXQueryOptions): VSXQueryResult { return { + offset: 0, + totalSize: 0, extensions: [] }; } @@ -183,8 +185,11 @@ export class OVSXRouterClient implements OVSXClient { results.forEach((result, sourceUri) => { result.extensions.forEach(extension => filtering.push(this.filterExtension(sourceUri, extension))); }); + const extensions = removeNullValues(await Promise.all(filtering)); return { - extensions: removeNullValues(await Promise.all(filtering)) + offset: 0, + totalSize: extensions.length, + extensions }; } diff --git a/dev-packages/ovsx-client/src/ovsx-types.ts b/dev-packages/ovsx-client/src/ovsx-types.ts index 21327fe9aa36b..223e3ef5bea6f 100644 --- a/dev-packages/ovsx-client/src/ovsx-types.ts +++ b/dev-packages/ovsx-client/src/ovsx-types.ts @@ -49,7 +49,7 @@ export interface OVSXClient { */ search(searchOptions?: VSXSearchOptions): Promise; /** - * GET https://openvsx.org/api/-/query + * GET https://openvsx.org/api/v2/-/query * * Fetch one or all versions of an extension. */ @@ -110,8 +110,6 @@ export interface VSXSearchResult { extensions: VSXSearchEntry[]; } -/** @deprecated since 1.31.0 use {@link VSXQueryOptions} instead */ -export type VSXQueryParam = VSXQueryOptions; /** * The possible options when performing a search. * @@ -126,10 +124,25 @@ export interface VSXQueryOptions { extensionId?: string; extensionUuid?: string; namespaceUuid?: string; - includeAllVersions?: boolean; + includeAllVersions?: boolean | 'links'; + targetPlatform?: VSXTargetPlatform; + size?: number; + offset?: number; } +export type VSXTargetPlatform = + 'universal' | 'web' | + 'win32-x64' | 'win32-ia32' | 'win32-arm64' | + 'darwin-x64' | 'darwin-arm64' | + 'linux-x64' | 'linux-arm64' | 'linux-armhf' | + 'alpine-x64' | 'alpine-arm64' | (string & {}); + export interface VSXQueryResult { + success?: string; + warning?: string; + error?: string; + offset: number; + totalSize: number; extensions: VSXExtensionRaw[]; } @@ -199,12 +212,15 @@ export interface VSXExtensionRaw { reviewsUrl: string; name: string; namespace: string; - publishedBy: VSXUser + targetPlatform?: VSXTargetPlatform; + publishedBy: VSXUser; + preRelease: boolean; namespaceAccess: VSXExtensionNamespaceAccess; files: VSXExtensionRawFiles; allVersions: { [version: string]: string; }; + allVersionsUrl?: string; averageRating?: number; downloadCount: number; reviewCount: number; @@ -213,22 +229,48 @@ export interface VSXExtensionRaw { preview?: boolean; verified?: boolean; displayName?: string; + namespaceDisplayName: string; description?: string; categories?: string[]; + extensionKind?: string[]; tags?: string[]; license?: string; homepage?: string; repository?: string; + sponsorLink?: string; bugs?: string; markdown?: string; galleryColor?: string; galleryTheme?: string; + localizedLanguages?: string[]; qna?: string; + badges?: VSXBadge[]; + dependencies?: VSXExtensionReference[]; + bundledExtensions?: VSXExtensionReference[]; + allTargetPlatformVersions?: VSXTargetPlatforms[]; + url?: string; engines?: { [engine: string]: string; }; } +export interface VSXBadge { + url?: string; + href?: string; + description?: string; +} + +export interface VSXExtensionReference { + url: string; + namespace: string; + extension: string; +} + +export interface VSXTargetPlatforms { + version: string; + targetPlatforms: VSXTargetPlatform[]; +} + export interface VSXResponseError extends Error { statusCode: number; } diff --git a/examples/api-samples/src/node/sample-mock-open-vsx-server.ts b/examples/api-samples/src/node/sample-mock-open-vsx-server.ts index 4a60f8cbf47eb..4cb4af8616a4c 100644 --- a/examples/api-samples/src/node/sample-mock-open-vsx-server.ts +++ b/examples/api-samples/src/node/sample-mock-open-vsx-server.ts @@ -69,7 +69,7 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution { app.use( this.mockServerPath + '/api', express.Router() - .get('/-/query', async (req, res) => { + .get('/v2/-/query', async (req, res) => { await this.ready; res.json(await this.mockClient.query(this.sanitizeQuery(req.query))); }) @@ -170,6 +170,8 @@ export class SampleMockOpenVsxServer implements BackendApplicationContribution { reviewsUrl: url.extensionReviewsUrl(namespace, name), timestamp: new Date().toISOString(), version, + namespaceDisplayName: name, + preRelease: false } }); })); diff --git a/packages/core/src/browser-only/frontend-only-application-module.ts b/packages/core/src/browser-only/frontend-only-application-module.ts index 1820adde62504..407f29297fc17 100644 --- a/packages/core/src/browser-only/frontend-only-application-module.ts +++ b/packages/core/src/browser-only/frontend-only-application-module.ts @@ -56,6 +56,7 @@ export const frontendOnlyApplicationModule = new ContainerModule((bind, unbind, getExtensionsInfos: async (): Promise => [], getApplicationInfo: async (): Promise => undefined, getApplicationRoot: async (): Promise => '', + getApplicationPlatform: () => Promise.resolve('web'), getBackendOS: async (): Promise => OS.Type.Linux }; if (isBound(ApplicationServer)) { diff --git a/packages/core/src/common/application-protocol.ts b/packages/core/src/common/application-protocol.ts index d4b4a848d961f..a96361a71bb05 100644 --- a/packages/core/src/common/application-protocol.ts +++ b/packages/core/src/common/application-protocol.ts @@ -24,6 +24,7 @@ export interface ApplicationServer { getExtensionsInfos(): Promise; getApplicationInfo(): Promise; getApplicationRoot(): Promise; + getApplicationPlatform(): Promise; /** * @deprecated since 1.25.0. Use `OS.backend.type()` instead. */ diff --git a/packages/core/src/node/application-server.ts b/packages/core/src/node/application-server.ts index df3996b6bb1b0..80f6bd7055596 100644 --- a/packages/core/src/node/application-server.ts +++ b/packages/core/src/node/application-server.ts @@ -49,6 +49,10 @@ export class ApplicationServerImpl implements ApplicationServer { return Promise.resolve(this.applicationPackage.projectPath); } + getApplicationPlatform(): Promise { + return Promise.resolve(`${process.platform}-${process.arch}`); + } + async getBackendOS(): Promise { return OS.type(); } diff --git a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts index bead5c875a3d9..f29d92a566a19 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts +++ b/packages/vsx-registry/src/browser/vsx-extensions-contribution.ts @@ -29,7 +29,7 @@ import { UriAwareCommandHandler } from '@theia/core/lib/common/uri-command-handl import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { FileDialogService, OpenFileDialogProps } from '@theia/filesystem/lib/browser'; import { NAVIGATOR_CONTEXT_MENU } from '@theia/navigator/lib/browser/navigator-contribution'; -import { OVSXApiFilter, VSXExtensionRaw } from '@theia/ovsx-client'; +import { OVSXApiFilterProvider, VSXExtensionRaw } from '@theia/ovsx-client'; import { VscodeCommands } from '@theia/plugin-ext-vscode/lib/browser/plugin-vscode-commands-contribution'; import { DateTime } from 'luxon'; import { OVSXClientProvider } from '../common/ovsx-client-provider'; @@ -39,6 +39,7 @@ import { VSXExtensionsCommands } from './vsx-extension-commands'; import { VSXExtensionsModel } from './vsx-extensions-model'; import { BUILTIN_QUERY, INSTALLED_QUERY, RECOMMENDED_QUERY } from './vsx-extensions-search-model'; import { VSXExtensionsViewContainer } from './vsx-extensions-view-container'; +import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import debounce = require('@theia/core/shared/lodash.debounce'); export namespace VSXCommands { @@ -58,7 +59,8 @@ export class VSXExtensionsContribution extends AbstractViewContribution { const res = await client.query({ extensionId: id, extensionVersion: allVersions.version, includeAllVersions: true }); - let verified = res.extensions?.[0].verified; - if (!verified && res.extensions?.[0].publishedBy.loginName === 'open-vsx') { + const extension = res.extensions?.[0]; + let verified = extension?.verified; + if (!verified && extension?.publishedBy.loginName === 'open-vsx') { verified = true; } return verified; @@ -353,18 +359,22 @@ export class VSXExtensionsModel { if (!this.shouldRefresh(extension)) { return extension; } - const client = await this.clientProvider(); + const filter = await this.vsxApiFilter(); + const targetPlatform = await this.applicationServer.getApplicationPlatform() as VSXTargetPlatform; let data: VSXExtensionRaw | undefined; if (version === undefined) { - const { extensions } = await client.query({ extensionId: id, includeAllVersions: true }); - if (extensions?.length) { - data = this.vsxApiFilter.getLatestCompatibleExtension(extensions); - } + data = await filter.findLatestCompatibleExtension({ + extensionId: id, + includeAllVersions: true, + targetPlatform + }); } else { - const { extensions } = await client.query({ extensionId: id, extensionVersion: version, includeAllVersions: true }); - if (extensions?.length) { - data = extensions?.[0]; - } + data = await filter.findLatestCompatibleExtension({ + extensionId: id, + extensionVersion: version, + includeAllVersions: true, + targetPlatform + }); } if (!data) { return; diff --git a/packages/vsx-registry/src/common/vsx-registry-common-module.ts b/packages/vsx-registry/src/common/vsx-registry-common-module.ts index 0fd32471a58f7..fc7fd7de4f3db 100644 --- a/packages/vsx-registry/src/common/vsx-registry-common-module.ts +++ b/packages/vsx-registry/src/common/vsx-registry-common-module.ts @@ -17,7 +17,9 @@ import { ContainerModule } from '@theia/core/shared/inversify'; import { OVSXClientProvider, OVSXUrlResolver } from '../common'; import { RequestService } from '@theia/core/shared/@theia/request'; -import { ExtensionIdMatchesFilterFactory, OVSXApiFilter, OVSXApiFilterImpl, OVSXClient, OVSXHttpClient, OVSXRouterClient, RequestContainsFilterFactory } from '@theia/ovsx-client'; +import { + ExtensionIdMatchesFilterFactory, OVSXApiFilter, OVSXApiFilterImpl, OVSXApiFilterProvider, OVSXClient, OVSXHttpClient, OVSXRouterClient, RequestContainsFilterFactory +} from '@theia/ovsx-client'; import { VSXEnvironment } from './vsx-environment'; export default new ContainerModule(bind => { @@ -54,10 +56,23 @@ export default new ContainerModule(bind => { bind(OVSXApiFilter) .toDynamicValue(ctx => { const vsxEnvironment = ctx.container.get(VSXEnvironment); - const apiFilter = new OVSXApiFilterImpl('-- temporary invalid version value --'); + const apiFilter = new OVSXApiFilterImpl(undefined!, '-- temporary invalid version value --'); vsxEnvironment.getVscodeApiVersion() .then(apiVersion => apiFilter.supportedApiVersion = apiVersion); + const clientProvider = ctx.container.get(OVSXClientProvider); + Promise.resolve(clientProvider()).then(client => { + apiFilter.client = client; + }); return apiFilter; }) .inSingletonScope(); + bind(OVSXApiFilterProvider) + .toProvider(ctx => async () => { + const vsxEnvironment = ctx.container.get(VSXEnvironment); + const clientProvider = ctx.container.get(OVSXClientProvider); + const client = await clientProvider(); + const apiVersion = await vsxEnvironment.getVscodeApiVersion(); + const apiFilter = new OVSXApiFilterImpl(client, apiVersion); + return apiFilter; + }); }); diff --git a/packages/vsx-registry/src/node/vsx-extension-resolver.ts b/packages/vsx-registry/src/node/vsx-extension-resolver.ts index cae0e48820850..01f6b5f2173d3 100644 --- a/packages/vsx-registry/src/node/vsx-extension-resolver.ts +++ b/packages/vsx-registry/src/node/vsx-extension-resolver.ts @@ -23,7 +23,7 @@ import { PluginDeployerHandler, PluginDeployerResolver, PluginDeployerResolverCo import { FileUri } from '@theia/core/lib/node'; import { VSCodeExtensionUri } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-uri'; import { OVSXClientProvider } from '../common/ovsx-client-provider'; -import { OVSXApiFilter, VSXExtensionRaw } from '@theia/ovsx-client'; +import { OVSXApiFilterProvider, VSXExtensionRaw, VSXTargetPlatform } from '@theia/ovsx-client'; import { RequestService } from '@theia/core/shared/@theia/request'; import { PluginVSCodeEnvironment } from '@theia/plugin-ext-vscode/lib/common/plugin-vscode-environment'; import { PluginUninstallationManager } from '@theia/plugin-ext/lib/main/node/plugin-uninstallation-manager'; @@ -36,13 +36,14 @@ export class VSXExtensionResolver implements PluginDeployerResolver { @inject(RequestService) protected requestService: RequestService; @inject(PluginVSCodeEnvironment) protected readonly environment: PluginVSCodeEnvironment; @inject(PluginUninstallationManager) protected readonly uninstallationManager: PluginUninstallationManager; - @inject(OVSXApiFilter) protected vsxApiFilter: OVSXApiFilter; + @inject(OVSXApiFilterProvider) protected vsxApiFilter: OVSXApiFilterProvider; accept(pluginId: string): boolean { return !!VSCodeExtensionUri.toId(new URI(pluginId)); } static readonly TEMP_DIR_PREFIX = 'vscode-download'; + static readonly TARGET_PLATFORM = `${process.platform}-${process.arch}` as VSXTargetPlatform; async resolve(context: PluginDeployerResolverContext, options?: PluginDeployOptions): Promise { const id = VSCodeExtensionUri.toId(new URI(context.getOriginId())); @@ -50,16 +51,23 @@ export class VSXExtensionResolver implements PluginDeployerResolver { return; } let extension: VSXExtensionRaw | undefined; - const client = await this.clientProvider(); + const filter = await this.vsxApiFilter(); const version = options?.version || id.version; if (version) { console.log(`[${id}]: trying to resolve version ${version}...`); - const { extensions } = await client.query({ extensionId: id.id, extensionVersion: version, includeAllVersions: true }); - extension = extensions[0]; + extension = await filter.findLatestCompatibleExtension({ + extensionId: id.id, + extensionVersion: version, + includeAllVersions: true, + targetPlatform: VSXExtensionResolver.TARGET_PLATFORM + }); } else { console.log(`[${id}]: trying to resolve latest version...`); - const { extensions } = await client.query({ extensionId: id.id, includeAllVersions: true }); - extension = this.vsxApiFilter.getLatestCompatibleExtension(extensions); + extension = await filter.findLatestCompatibleExtension({ + extensionId: id.id, + includeAllVersions: true, + targetPlatform: VSXExtensionResolver.TARGET_PLATFORM + }); } if (!extension) { return; From b16f49b18976eb5c3d2269eb509b3995e468d82c Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 25 Jun 2024 12:29:48 +0200 Subject: [PATCH 280/441] [vscode] Stub Chat and Language Model API (#13778) Stub Chat and Language Model API fixes #13756 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- .../plugin-ext/src/plugin/plugin-context.ts | 49 +- .../plugin-ext/src/plugin/plugin-manager.ts | 11 +- packages/plugin-ext/src/plugin/types-impl.ts | 112 +++ packages/plugin/src/theia-extra.d.ts | 7 + packages/plugin/src/theia.d.ts | 859 ++++++++++++++++++ 5 files changed, 1033 insertions(+), 5 deletions(-) diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index fa37acbbb43bf..4aa0d22c6cb15 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -19,7 +19,7 @@ import type * as theia from '@theia/plugin'; import { CommandRegistryImpl } from './command-registry'; -import { Emitter } from '@theia/core/lib/common/event'; +import { Emitter, Event } from '@theia/core/lib/common/event'; import { CancellationError, CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation'; import { QuickOpenExtImpl } from './quick-open'; import { @@ -211,7 +211,19 @@ import { DeclarationCoverage, FileCoverage, StatementCoverage, - TestCoverageCount + TestCoverageCount, + ChatRequestTurn, + ChatResponseTurn, + ChatResponseAnchorPart, + ChatResponseCommandButtonPart, + ChatResponseFileTreePart, + ChatResponseMarkdownPart, + ChatResponseProgressPart, + ChatResponseReferencePart, + ChatResultFeedbackKind, + LanguageModelChatMessage, + LanguageModelChatMessageRole, + LanguageModelError } from './types-impl'; import { AuthenticationExtImpl } from './authentication-ext'; import { SymbolKind } from '../common/plugin-api-rpc-model'; @@ -1223,9 +1235,27 @@ export function createAPIFactory( /** @stubbed MappedEditsProvider */ registerMappedEditsProvider(documentSelector: theia.DocumentSelector, provider: theia.MappedEditsProvider): Disposable { return Disposable.NULL; + }, + /** @stubbed ChatRequestHandler */ + createChatParticipant(id: string, handler: theia.ChatRequestHandler): theia.ChatParticipant { + return { + id, + requestHandler: handler, + dispose() {}, + onDidReceiveFeedback: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables) + }; } }; + const lm: typeof theia.lm = { + /** @stubbed LanguageModelChat */ + selectChatModels(selector?: theia.LanguageModelChatSelector): Thenable { + return Promise.resolve([]); + }, + /** @stubbed LanguageModelChat */ + onDidChangeChatModels: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables) + }; + return { version: require('../../package.json').version, authentication, @@ -1244,6 +1274,7 @@ export function createAPIFactory( notebooks, l10n, tests, + lm, // Types StatusBarAlignment: StatusBarAlignment, Disposable: Disposable, @@ -1426,7 +1457,19 @@ export function createAPIFactory( DeclarationCoverage, FileCoverage, StatementCoverage, - TestCoverageCount + TestCoverageCount, + ChatRequestTurn, + ChatResponseTurn, + ChatResponseAnchorPart, + ChatResponseCommandButtonPart, + ChatResponseFileTreePart, + ChatResponseMarkdownPart, + ChatResponseProgressPart, + ChatResponseReferencePart, + ChatResultFeedbackKind, + LanguageModelChatMessage, + LanguageModelChatMessageRole, + LanguageModelError }; }; } diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index bc84aee65e84e..47ced24b00a76 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -41,7 +41,7 @@ import { PreferenceRegistryExtImpl } from './preference-registry'; import { InternalStorageExt, Memento, GlobalState } from './plugin-storage'; import { ExtPluginApi } from '../common/plugin-ext-api-contribution'; import { RPCProtocol } from '../common/rpc-protocol'; -import { Emitter } from '@theia/core/lib/common/event'; +import { Emitter, Event } from '@theia/core/lib/common/event'; import { WebviewsExtImpl } from './webviews'; import { URI as Uri } from './types-impl'; import { InternalSecretsExt, SecretStorageExt } from '../plugin/secrets-ext'; @@ -391,7 +391,14 @@ export abstract class AbstractPluginManagerExtImpl

    environmentVariableCollection: this.terminalService.getEnvironmentVariableCollection(plugin.model.id), extensionMode: extensionModeValue, extension, - logUri: Uri.file(logPath) + logUri: Uri.file(logPath), + languageModelAccessInformation: { + /** @stubbed LanguageModelChat */ + onDidChange: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables), + canSendRequest(chat: theia.LanguageModelChat): boolean | undefined { + return undefined; + } + } }; this.pluginContextsMap.set(plugin.model.id, pluginContext); diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 4dabfbe702e63..4209dd20d62d8 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3846,3 +3846,115 @@ export class TerminalQuickFixOpener { constructor(uri: theia.Uri) { } } +// #region Chat +export class ChatRequestTurn { + readonly prompt: string; + readonly participant: string; + readonly command?: string; + readonly references: theia.ChatPromptReference[]; + private constructor(prompt: string, command: string | undefined, references: theia.ChatPromptReference[], participant: string) { + this.prompt = prompt; + this.command = command; + this.participant = participant; + this.references = references; + }; +} + +export class ChatResponseTurn { + readonly command?: string; + + private constructor(readonly response: ReadonlyArray, readonly result: theia.ChatResult, readonly participant: string) { } +} + +export class ChatResponseAnchorPart { + value: URI | Location; + title?: string; + + constructor(value: URI | Location, title?: string) { } +} + +export class ChatResponseProgressPart { + value: string; + + constructor(value: string) { } +} + +export class ChatResponseReferencePart { + value: URI | Location; + iconPath?: URI | ThemeIcon | { light: URI; dark: URI; }; + + constructor(value: URI | theia.Location, iconPath?: URI | ThemeIcon | { + light: URI; + dark: URI; + }) { } +} +export class ChatResponseCommandButtonPart { + value: theia.Command; + + constructor(value: theia.Command) { } +} + +export class ChatResponseMarkdownPart { + value: theia.MarkdownString; + + constructor(value: string | theia.MarkdownString) { + } +} + +export class ChatResponseFileTreePart { + value: theia.ChatResponseFileTree[]; + baseUri: URI; + + constructor(value: theia.ChatResponseFileTree[], baseUri: URI) { } +} + +export type ChatResponsePart = ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart +| ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; + +export enum ChatResultFeedbackKind { + Unhelpful = 0, + Helpful = 1, +} + +export enum LanguageModelChatMessageRole { + User = 1, + Assistant = 2 +} + +export class LanguageModelChatMessage { + + static User(content: string, name?: string): LanguageModelChatMessage { + return new LanguageModelChatMessage(LanguageModelChatMessageRole.User, content, name); + } + + static Assistant(content: string, name?: string): LanguageModelChatMessage { + return new LanguageModelChatMessage(LanguageModelChatMessageRole.Assistant, content, name); + } + + constructor(public role: LanguageModelChatMessageRole, public content: string, public name?: string) { } +} + +export class LanguageModelError extends Error { + + static NoPermissions(message?: string): LanguageModelError { + return new LanguageModelError(message, LanguageModelError.NoPermissions.name); + } + + static Blocked(message?: string): LanguageModelError { + return new LanguageModelError(message, LanguageModelError.Blocked.name); + } + + static NotFound(message?: string): LanguageModelError { + return new LanguageModelError(message, LanguageModelError.NotFound.name); + } + + readonly code: string; + + constructor(message?: string, code?: string) { + super(message); + this.name = 'LanguageModelError'; + this.code = code ?? ''; + } +} +// #endregion diff --git a/packages/plugin/src/theia-extra.d.ts b/packages/plugin/src/theia-extra.d.ts index 66a30583d9428..54bbd49413dfd 100644 --- a/packages/plugin/src/theia-extra.d.ts +++ b/packages/plugin/src/theia-extra.d.ts @@ -291,6 +291,13 @@ export module '@theia/plugin' { * see - workspace.fs for how to read and write files and folders from an uri. */ readonly logUri: Uri; + + /** + * An object that keeps information about how this extension can use language models. + * + * @see {@link LanguageModelChat.sendRequest} + */ + readonly languageModelAccessInformation: LanguageModelAccessInformation; } export namespace commands { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 93093b6ab6a76..07913feef680d 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -4028,6 +4028,14 @@ export module '@theia/plugin' { * The current `Extension` instance. */ readonly extension: Extension; + + /** + * An object that keeps information about how this extension can use language models. + * + * @see {@link LanguageModelChat.sendRequest} + * @stubbed + */ + readonly languageModelAccessInformation: LanguageModelAccessInformation; } /** @@ -16732,6 +16740,857 @@ export module '@theia/plugin' { */ export type FileCoverageDetail = StatementCoverage | DeclarationCoverage; + /** + * Represents a user request in chat history. + */ + export class ChatRequestTurn { + /** + * The prompt as entered by the user. + * + * Information about references used in this request is stored in {@link ChatRequestTurn.references}. + * + * *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command} + * are not part of the prompt. + */ + readonly prompt: string; + + /** + * The id of the chat participant to which this request was directed. + */ + readonly participant: string; + + /** + * The name of the {@link ChatCommand command} that was selected for this request. + */ + readonly command?: string; + + /** + * The references that were used in this message. + */ + readonly references: ChatPromptReference[]; + + /** + * @hidden + */ + private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string); + } + + /** + * Represents a chat participant's response in chat history. + */ + export class ChatResponseTurn { + /** + * The content that was received from the chat participant. Only the stream parts that represent actual content (not metadata) are represented. + */ + readonly response: ReadonlyArray; + + /** + * The result that was received from the chat participant. + */ + readonly result: ChatResult; + + /** + * The id of the chat participant that this response came from. + */ + readonly participant: string; + + /** + * The name of the command that this response came from. + */ + readonly command?: string; + + /** + * @hidden + */ + private constructor(response: ReadonlyArray, result: ChatResult, participant: string); + } + + /** + * Extra context passed to a participant. + */ + export interface ChatContext { + /** + * All of the chat messages so far in the current chat session. Currently, only chat messages for the current participant are included. + */ + readonly history: ReadonlyArray; + } + + /** + * Represents an error result from a chat request. + */ + export interface ChatErrorDetails { + /** + * An error message that is shown to the user. + */ + message: string; + + /** + * If set to true, the response will be partly blurred out. + */ + responseIsFiltered?: boolean; + } + + /** + * The result of a chat request. + */ + export interface ChatResult { + /** + * If the request resulted in an error, this property defines the error details. + */ + errorDetails?: ChatErrorDetails; + + /** + * Arbitrary metadata for this result. Can be anything, but must be JSON-stringifyable. + */ + readonly metadata?: { readonly [key: string]: any }; + } + + /** + * Represents the type of user feedback received. + */ + export enum ChatResultFeedbackKind { + /** + * The user marked the result as unhelpful. + */ + Unhelpful = 0, + + /** + * The user marked the result as helpful. + */ + Helpful = 1, + } + + /** + * Represents user feedback for a result. + */ + export interface ChatResultFeedback { + /** + * The ChatResult for which the user is providing feedback. + * This object has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance. + */ + readonly result: ChatResult; + + /** + * The kind of feedback that was received. + */ + readonly kind: ChatResultFeedbackKind; + } + + /** + * A followup question suggested by the participant. + */ + export interface ChatFollowup { + /** + * The message to send to the chat. + */ + prompt: string; + + /** + * A title to show the user. The prompt will be shown by default, when this is unspecified. + */ + label?: string; + + /** + * By default, the followup goes to the same participant/command. But this property can be set to invoke a different participant by ID. + * Followups can only invoke a participant that was contributed by the same extension. + */ + participant?: string; + + /** + * By default, the followup goes to the same participant/command. But this property can be set to invoke a different command. + */ + command?: string; + } + + /** + * Will be invoked once after each request to get suggested followup questions to show the user. The user can click the followup to send it to the chat. + */ + export interface ChatFollowupProvider { + /** + * Provide followups for the given result. + * @param result This object has the same properties as the result returned from the participant callback, including `metadata`, but is not the same instance. + * @param token A cancellation token. + */ + provideFollowups(result: ChatResult, context: ChatContext, token: CancellationToken): ProviderResult; + } + + /** + * A chat request handler is a callback that will be invoked when a request is made to a chat participant. + */ + export type ChatRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult; + + /** + * A chat participant can be invoked by the user in a chat session, using the `@` prefix. When it is invoked, it handles the chat request and is solely + * responsible for providing a response to the user. A ChatParticipant is created using {@link chat.createChatParticipant}. + */ + export interface ChatParticipant { + /** + * A unique ID for this participant. + */ + readonly id: string; + + /** + * An icon for the participant shown in UI. + */ + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; + + /** + * The handler for requests to this participant. + */ + requestHandler: ChatRequestHandler; + + /** + * This provider will be called once after each request to retrieve suggested followup questions. + */ + followupProvider?: ChatFollowupProvider; + + /** + * An event that fires whenever feedback for a result is received, e.g. when a user up- or down-votes + * a result. + * + * The passed {@link ChatResultFeedback.result result} is guaranteed to be the same instance that was + * previously returned from this chat participant. + */ + onDidReceiveFeedback: Event; + + /** + * Dispose this participant and free resources. + */ + dispose(): void; + } + + /** + * A reference to a value that the user added to their chat request. + */ + export interface ChatPromptReference { + /** + * A unique identifier for this kind of reference. + */ + readonly id: string; + + /** + * The start and end index of the reference in the {@link ChatRequest.prompt prompt}. When undefined, the reference was not part of the prompt text. + * + * *Note* that the indices take the leading `#`-character into account which means they can + * used to modify the prompt as-is. + */ + readonly range?: [start: number, end: number]; + + /** + * A description of this value that could be used in an LLM prompt. + */ + readonly modelDescription?: string; + + /** + * The value of this reference. The `string | Uri | Location` types are used today, but this could expand in the future. + */ + readonly value: string | Uri | Location | unknown; + } + + /** + * A request to a chat participant. + */ + export interface ChatRequest { + /** + * The prompt as entered by the user. + * + * Information about references used in this request is stored in {@link ChatRequest.references}. + * + * *Note* that the {@link ChatParticipant.name name} of the participant and the {@link ChatCommand.name command} + * are not part of the prompt. + */ + readonly prompt: string; + + /** + * The name of the {@link ChatCommand command} that was selected for this request. + */ + readonly command: string | undefined; + + /** + * The list of references and their values that are referenced in the prompt. + * + * *Note* that the prompt contains references as authored and that it is up to the participant + * to further modify the prompt, for instance by inlining reference values or creating links to + * headings which contain the resolved values. References are sorted in reverse by their range + * in the prompt. That means the last reference in the prompt is the first in this list. This simplifies + * string-manipulation of the prompt. + */ + readonly references: readonly ChatPromptReference[]; + } + + /** + * The ChatResponseStream is how a participant is able to return content to the chat view. It provides several methods for streaming different types of content + * which will be rendered in an appropriate way in the chat view. A participant can use the helper method for the type of content it wants to return, or it + * can instantiate a {@link ChatResponsePart} and use the generic {@link ChatResponseStream.push} method to return it. + */ + export interface ChatResponseStream { + /** + * Push a markdown part to this stream. Short-hand for + * `push(new ChatResponseMarkdownPart(value))`. + * + * @see {@link ChatResponseStream.push} + * @param value A markdown string or a string that should be interpreted as markdown. The boolean form of {@link MarkdownString.isTrusted} is NOT supported. + */ + markdown(value: string | MarkdownString): void; + + /** + * Push an anchor part to this stream. Short-hand for + * `push(new ChatResponseAnchorPart(value, title))`. + * An anchor is an inline reference to some type of resource. + * + * @param value A uri, location, or symbol information. + * @param title An optional title that is rendered with value. + */ + anchor(value: Uri | Location, title?: string): void; + + /** + * Push a command button part to this stream. Short-hand for + * `push(new ChatResponseCommandButtonPart(value, title))`. + * + * @param command A Command that will be executed when the button is clicked. + */ + button(command: Command): void; + + /** + * Push a filetree part to this stream. Short-hand for + * `push(new ChatResponseFileTreePart(value))`. + * + * @param value File tree data. + * @param baseUri The base uri to which this file tree is relative. + */ + filetree(value: ChatResponseFileTree[], baseUri: Uri): void; + + /** + * Push a progress part to this stream. Short-hand for + * `push(new ChatResponseProgressPart(value))`. + * + * @param value A progress message + */ + progress(value: string): void; + + /** + * Push a reference to this stream. Short-hand for + * `push(new ChatResponseReferencePart(value))`. + * + * *Note* that the reference is not rendered inline with the response. + * + * @param value A uri or location + * @param iconPath Icon for the reference shown in UI + */ + reference(value: Uri | Location, iconPath?: Uri | ThemeIcon | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + }): void; + + /** + * Pushes a part to this stream. + * + * @param part A response part, rendered or metadata + */ + push(part: ChatResponsePart): void; + } + + /** + * Represents a part of a chat response that is formatted as Markdown. + */ + export class ChatResponseMarkdownPart { + /** + * A markdown string or a string that should be interpreted as markdown. + */ + value: MarkdownString; + + /** + * Create a new ChatResponseMarkdownPart. + * + * @param value A markdown string or a string that should be interpreted as markdown. The boolean form of {@link MarkdownString.isTrusted} is NOT supported. + */ + constructor(value: string | MarkdownString); + } + + /** + * Represents a file tree structure in a chat response. + */ + export interface ChatResponseFileTree { + /** + * The name of the file or directory. + */ + name: string; + + /** + * An array of child file trees, if the current file tree is a directory. + */ + children?: ChatResponseFileTree[]; + } + + /** + * Represents a part of a chat response that is a file tree. + */ + export class ChatResponseFileTreePart { + /** + * File tree data. + */ + value: ChatResponseFileTree[]; + + /** + * The base uri to which this file tree is relative + */ + baseUri: Uri; + + /** + * Create a new ChatResponseFileTreePart. + * @param value File tree data. + * @param baseUri The base uri to which this file tree is relative. + */ + constructor(value: ChatResponseFileTree[], baseUri: Uri); + } + + /** + * Represents a part of a chat response that is an anchor, that is rendered as a link to a target. + */ + export class ChatResponseAnchorPart { + /** + * The target of this anchor. + */ + value: Uri | Location; + + /** + * An optional title that is rendered with value. + */ + title?: string; + + /** + * Create a new ChatResponseAnchorPart. + * @param value A uri or location. + * @param title An optional title that is rendered with value. + */ + constructor(value: Uri | Location, title?: string); + } + + /** + * Represents a part of a chat response that is a progress message. + */ + export class ChatResponseProgressPart { + /** + * The progress message + */ + value: string; + + /** + * Create a new ChatResponseProgressPart. + * @param value A progress message + */ + constructor(value: string); + } + + /** + * Represents a part of a chat response that is a reference, rendered separately from the content. + */ + export class ChatResponseReferencePart { + /** + * The reference target. + */ + value: Uri | Location; + + /** + * The icon for the reference. + */ + iconPath?: Uri | ThemeIcon | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + }; + + /** + * Create a new ChatResponseReferencePart. + * @param value A uri or location + * @param iconPath Icon for the reference shown in UI + */ + constructor(value: Uri | Location, iconPath?: Uri | ThemeIcon | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + }); + } + + /** + * Represents a part of a chat response that is a button that executes a command. + */ + export class ChatResponseCommandButtonPart { + /** + * The command that will be executed when the button is clicked. + */ + value: Command; + + /** + * Create a new ChatResponseCommandButtonPart. + * @param value A Command that will be executed when the button is clicked. + */ + constructor(value: Command); + } + + /** + * Represents the different chat response types. + */ + export type ChatResponsePart = ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart + | ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; + + /** + * Namespace for chat functionality. Users interact with chat participants by sending messages + * to them in the chat view. Chat participants can respond with markdown or other types of content + * via the {@link ChatResponseStream}. + */ + export namespace chat { + /** + * Create a new {@link ChatParticipant chat participant} instance. + * + * @param id A unique identifier for the participant. + * @param handler A request handler for the participant. + * @returns A new chat participant + * @stubbed + */ + export function createChatParticipant(id: string, handler: ChatRequestHandler): ChatParticipant; + } + + /** + * Represents the role of a chat message. This is either the user or the assistant. + */ + export enum LanguageModelChatMessageRole { + /** + * The user role, e.g the human interacting with a language model. + */ + User = 1, + + /** + * The assistant role, e.g. the language model generating responses. + */ + Assistant = 2 + } + + /** + * Represents a message in a chat. Can assume different roles, like user or assistant. + */ + export class LanguageModelChatMessage { + + /** + * Utility to create a new user message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + static User(content: string, name?: string): LanguageModelChatMessage; + + /** + * Utility to create a new assistant message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + static Assistant(content: string, name?: string): LanguageModelChatMessage; + + /** + * The role of this message. + */ + role: LanguageModelChatMessageRole; + + /** + * The content of this message. + */ + content: string; + + /** + * The optional name of a user for this message. + */ + name: string | undefined; + + /** + * Create a new user message. + * + * @param role The role of the message. + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + constructor(role: LanguageModelChatMessageRole, content: string, name?: string); + } + + /** + * Represents a language model response. + * + * @see {@link LanguageModelAccess.chatRequest} + */ + export interface LanguageModelChatResponse { + + /** + * An async iterable that is a stream of text chunks forming the overall response. + * + * *Note* that this stream will error when during data receiving an error occurs. Consumers of + * the stream should handle the errors accordingly. + * + * To cancel the stream, the consumer can {@link CancellationTokenSource.cancel cancel} the token that was used to make the request + * or break from the for-loop. + * + * @example + * ```ts + * try { + * // consume stream + * for await (const chunk of response.text) { + * console.log(chunk); + * } + * + * } catch(e) { + * // stream ended with an error + * console.error(e); + * } + * ``` + */ + text: AsyncIterable; + } + + /** + * Represents a language model for making chat requests. + * + * @see {@link lm.selectChatModels} + */ + export interface LanguageModelChat { + + /** + * Human-readable name of the language model. + */ + readonly name: string; + + /** + * Opaque identifier of the language model. + */ + readonly id: string; + + /** + * A well-known identifier of the vendor of the language model. An example is `copilot`, but + * values are defined by extensions contributing chat models and need to be looked up with them. + */ + readonly vendor: string; + + /** + * Opaque family-name of the language model. Values might be `gpt-3.5-turbo`, `gpt4`, `phi2`, or `llama` + * but they are defined by extensions contributing languages and subject to change. + */ + readonly family: string; + + /** + * Opaque version string of the model. This is defined by the extension contributing the language model + * and subject to change. + */ + readonly version: string; + + /** + * The maximum number of tokens that can be sent to the model in a single request. + */ + readonly maxInputTokens: number; + + /** + * Make a chat request using a language model. + * + * *Note* that language model use may be subject to access restrictions and user consent. Calling this function + * for the first time (for a extension) will show a consent dialog to the user and because of that this function + * must _only be called in response to a user action!_ Extension can use {@link LanguageModelAccessInformation.canSendRequest} + * to check if they have the necessary permissions to make a request. + * + * This function will return a rejected promise if making a request to the language model is not + * possible. Reasons for this can be: + * + * - user consent not given, see {@link LanguageModelError.NoPermissions `NoPermissions`} + * - model does not exist anymore, see {@link LanguageModelError.NotFound `NotFound`} + * - quota limits exceeded, see {@link LanguageModelError.Blocked `Blocked`} + * - other issues in which case extension must check {@link LanguageModelError.cause `LanguageModelError.cause`} + * + * @param messages An array of message instances. + * @param options Options that control the request. + * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when the request couldn't be made. + */ + sendRequest(messages: LanguageModelChatMessage[], options?: LanguageModelChatRequestOptions, token?: CancellationToken): Thenable; + + /** + * Count the number of tokens in a message using the model specific tokenizer-logic. + * @param text A string or a message instance. + * @param token Optional cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns A thenable that resolves to the number of tokens. + */ + countTokens(text: string | LanguageModelChatMessage, token?: CancellationToken): Thenable; + } + + /** + * Describes how to select language models for chat requests. + * + * @see {@link lm.selectChatModels} + */ + export interface LanguageModelChatSelector { + + /** + * A vendor of language models. + * @see {@link LanguageModelChat.vendor} + */ + vendor?: string; + + /** + * A family of language models. + * @see {@link LanguageModelChat.family} + */ + family?: string; + + /** + * The version of a language model. + * @see {@link LanguageModelChat.version} + */ + version?: string; + + /** + * The identifier of a language model. + * @see {@link LanguageModelChat.id} + */ + id?: string; + } + + /** + * An error type for language model specific errors. + * + * Consumers of language models should check the code property to determine specific + * failure causes, like `if(someError.code === vscode.LanguageModelError.NotFound.name) {...}` + * for the case of referring to an unknown language model. For unspecified errors the `cause`-property + * will contain the actual error. + */ + export class LanguageModelError extends Error { + + /** + * The requestor does not have permissions to use this + * language model + */ + static NoPermissions(message?: string): LanguageModelError; + + /** + * The requestor is blocked from using this language model. + */ + static Blocked(message?: string): LanguageModelError; + + /** + * The language model does not exist. + */ + static NotFound(message?: string): LanguageModelError; + + /** + * A code that identifies this error. + * + * Possible values are names of errors, like {@linkcode LanguageModelError.NotFound NotFound}, + * or `Unknown` for unspecified errors from the language model itself. In the latter case the + * `cause`-property will contain the actual error. + */ + readonly code: string; + } + + /** + * Options for making a chat request using a language model. + * + * @see {@link LanguageModelChat.sendRequest} + */ + export interface LanguageModelChatRequestOptions { + + /** + * A human-readable message that explains why access to a language model is needed and what feature is enabled by it. + */ + justification?: string; + + /** + * A set of options that control the behavior of the language model. These options are specific to the language model + * and need to be lookup in the respective documentation. + */ + modelOptions?: { [name: string]: any }; + } + + /** + * Namespace for language model related functionality. + */ + export namespace lm { + + /** + * An event that is fired when the set of available chat models changes. + * @stubbed + */ + export const onDidChangeChatModels: Event; + + /** + * Select chat models by a {@link LanguageModelChatSelector selector}. This can yield multiple or no chat models and + * extensions must handle these cases, esp. when no chat model exists, gracefully. + * + * ```ts + * const models = await vscode.lm.selectChatModels({ family: 'gpt-3.5-turbo' }); + * if (models.length > 0) { + * const [first] = models; + * const response = await first.sendRequest(...) + * // ... + * } else { + * // NO chat models available + * } + * ``` + * + * A selector can be written to broadly match all models of a given vendor or family, or it can narrowly select one model by ID. + * Keep in mind that the available set of models will change over time, but also that prompts may perform differently in + * different models. + * + * *Note* that extensions can hold on to the results returned by this function and use them later. However, when the + * {@link onDidChangeChatModels}-event is fired the list of chat models might have changed and extensions should re-query. + * + * @param selector A chat model selector. When omitted all chat models are returned. + * @returns An array of chat models, can be empty! + * @stubbed + */ + export function selectChatModels(selector?: LanguageModelChatSelector): Thenable; + } + + /** + * Represents extension specific information about the access to language models. + */ + export interface LanguageModelAccessInformation { + + /** + * An event that fires when access information changes. + */ + onDidChange: Event; + + /** + * Checks if a request can be made to a language model. + * + * *Note* that calling this function will not trigger a consent UI but just checks for a persisted state. + * + * @param chat A language model chat object. + * @return `true` if a request can be made, `false` if not, `undefined` if the language + * model does not exist or consent hasn't been asked for. + */ + canSendRequest(chat: LanguageModelChat): boolean | undefined; + } + /** * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, * and others. This API makes no assumption about what promise library is being used which From 377de2689062855dd3cd029b8bdf0c4509d48984 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 25 Jun 2024 17:08:43 +0200 Subject: [PATCH 281/441] Fix data race in problem view tree (#13841) --- .../problem/problem-composite-tree-node.ts | 7 ++++++- .../src/browser/problem/problem-tree-model.ts | 19 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/markers/src/browser/problem/problem-composite-tree-node.ts b/packages/markers/src/browser/problem/problem-composite-tree-node.ts index a5b20be14516b..5cdd426d05bde 100644 --- a/packages/markers/src/browser/problem/problem-composite-tree-node.ts +++ b/packages/markers/src/browser/problem/problem-composite-tree-node.ts @@ -23,6 +23,11 @@ import { ProblemUtils } from './problem-utils'; export namespace ProblemCompositeTreeNode { + export interface Child { + node: MarkerInfoNode; + markers: Marker[]; + } + export function setSeverity(parent: MarkerInfoNode, markers: Marker[]): void { let maxSeverity: DiagnosticSeverity | undefined; markers.forEach(marker => { @@ -33,7 +38,7 @@ export namespace ProblemCompositeTreeNode { parent.severity = maxSeverity; }; - export function addChildren(parent: CompositeTreeNode, insertChildren: { node: MarkerInfoNode, markers: Marker[] }[]): void { + export function addChildren(parent: CompositeTreeNode, insertChildren: ProblemCompositeTreeNode.Child[]): void { for (const { node, markers } of insertChildren) { ProblemCompositeTreeNode.setSeverity(node, markers); } diff --git a/packages/markers/src/browser/problem/problem-tree-model.ts b/packages/markers/src/browser/problem/problem-tree-model.ts index 152feb7a33408..d4eab7184bd83 100644 --- a/packages/markers/src/browser/problem/problem-tree-model.ts +++ b/packages/markers/src/browser/problem/problem-tree-model.ts @@ -28,7 +28,8 @@ import debounce = require('@theia/core/shared/lodash.debounce'); @injectable() export class ProblemTree extends MarkerTree { - protected markers: { node: MarkerInfoNode, markers: Marker[] }[] = []; + + protected queuedMarkers = new Map(); constructor( @inject(ProblemManager) markerManager: ProblemManager, @@ -79,20 +80,28 @@ export class ProblemTree extends MarkerTree { } protected override insertNodeWithMarkers(node: MarkerInfoNode, markers: Marker[]): void { - this.markers.push({ node, markers }); + // Add the element to the queue. + // In case a diagnostics collection for the same file already exists, it will be replaced. + this.queuedMarkers.set(node.id, { node, markers }); this.doInsertNodesWithMarkers(); } protected doInsertNodesWithMarkers = debounce(() => { - ProblemCompositeTreeNode.addChildren(this.root as MarkerRootNode, this.markers); + const root = this.root; + // Sanity check; This should always be of type `MarkerRootNode` + if (!MarkerRootNode.is(root)) { + return; + } + const queuedItems = Array.from(this.queuedMarkers.values()); + ProblemCompositeTreeNode.addChildren(root, queuedItems); - for (const { node, markers } of this.markers) { + for (const { node, markers } of queuedItems) { const children = this.getMarkerNodes(node, markers); node.numberOfMarkers = markers.length; this.setChildren(node, children); } - this.markers.length = 0; + this.queuedMarkers.clear(); }, 50); } From 1c9e6bda090a8524308ff322cbe8369ec5079d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 26 Jun 2024 08:24:31 +0200 Subject: [PATCH 282/441] Switch single instance on per default. (#13831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #10890 Also implements VS Code-like behavior when opening second instances Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 5 ++--- .../src/application-props.ts | 2 +- .../electron-main-application.ts | 21 ++++++++++++------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4cd7eff0935..1a29f079c913c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,8 @@ [Breaking Changes:](#breaking_changes_not_yet_released) - [filesystem] Adjusted the "Save As" mechanism. It now assumes that `Saveable.getSnapshot()` returns a full snapshot of the editor model [#13689](https://github.com/eclipse-theia/theia/pull/13689). - ---> - +- [electron] Switch single instance on per default [#13831](https://github.com/eclipse-theia/theia/pull/13831) - contributed on behalf of STMicroelectronics + --> ## 1.50.0 - 06/03/2024 - [application-package] bumped the default supported API from `1.88.1` to `1.89.1` [#13738](https://github.com/eclipse-theia/theia/pull/13738) - contributed on behalf of STMicroelectronics diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index 91de09fc319b7..cf9320e7120fd 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -183,7 +183,7 @@ export namespace FrontendApplicationConfig { export type BackendApplicationConfig = RequiredRecursive; export namespace BackendApplicationConfig { export const DEFAULT: BackendApplicationConfig = { - singleInstance: false, + singleInstance: true, frontendConnectionTimeout: 0 }; export interface Partial extends ApplicationConfig { diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 8d59807252593..2da08aa9b4031 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -721,14 +721,19 @@ export class ElectronMainApplication { } protected async onSecondInstance(event: ElectronEvent, argv: string[], cwd: string): Promise { - const electronWindows = BrowserWindow.getAllWindows(); - if (electronWindows.length > 0) { - const electronWindow = electronWindows[0]; - if (electronWindow.isMinimized()) { - electronWindow.restore(); - } - electronWindow.focus(); - } + createYargs(this.processArgv.getProcessArgvWithoutBin(argv), process.cwd()) + .help(false) + .command('$0 [file]', false, + cmd => cmd + .positional('file', { type: 'string' }), + async args => { + this.handleMainCommand({ + file: args.file, + cwd: process.cwd(), + secondInstance: true + }); + }, + ).parse(); } protected onWindowAllClosed(event: ElectronEvent): void { From 37e1f2c5c130988ff29bfbfa2415eada018732d6 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 26 Jun 2024 11:34:10 +0200 Subject: [PATCH 283/441] Add an indicator for loading notebooks (#13843) --- packages/notebook/src/browser/notebook-editor-widget.tsx | 4 +++- packages/notebook/src/browser/style/index.css | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index c068ab924add6..de32c093ac735 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -233,7 +233,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa

    ; } else { - return
    ; + return
    +
    +
    ; } } diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 1ac9a9db976b6..9d0e8edcbdb3b 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -187,6 +187,14 @@ overflow: hidden; } +.theia-notebook-main-container .theia-notebook-main-loading-indicator { + /* `progress-animation` is defined in `packages/core/src/browser/style/progress-bar.css` */ + animation: progress-animation 1.8s 0s infinite + cubic-bezier(0.645, 0.045, 0.355, 1); + background-color: var(--theia-progressBar-background); + height: 2px; +} + .theia-notebook-viewport { display: flex; overflow: hidden; From a2d925190c92bfa94400f9ec13d7a16d2c78a7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 26 Jun 2024 21:01:27 +0200 Subject: [PATCH 284/441] Update doc comments on service-connection-provider.ts (#13805) Add some documentation to make the usage of the local/remote service id's more understandable. contributed on behalf of STMicroelectronics --- .../messaging/service-connection-provider.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/core/src/browser/messaging/service-connection-provider.ts b/packages/core/src/browser/messaging/service-connection-provider.ts index d946b0b289c47..aee98b88e6f4c 100644 --- a/packages/core/src/browser/messaging/service-connection-provider.ts +++ b/packages/core/src/browser/messaging/service-connection-provider.ts @@ -20,7 +20,13 @@ import { ChannelMultiplexer } from '../../common/message-rpc/channel'; import { Deferred } from '../../common/promise-util'; import { ConnectionSource } from './connection-source'; +/** + * Service id for the local connection provider + */ export const LocalConnectionProvider = Symbol('LocalConnectionProvider'); +/** + * Service id for the remote connection provider + */ export const RemoteConnectionProvider = Symbol('RemoteConnectionProvider'); export namespace ServiceConnectionProvider { @@ -28,7 +34,15 @@ export namespace ServiceConnectionProvider { } /** - * This class manages the channels for remote services in the back end + * This class manages the channels for remote services in the back end. + * + * Since we have the ability to use a remote back end via SSH, we need to distinguish + * between two types of services: those that will be redirected to the remote back end + * and those which must remain in the local back end. For example the service that manages + * the remote ssh connections and port forwarding to the remote instance must remain local + * while e.g. the file system service will run in the remote back end. For each set + * of services, we will bind an instance of this class to {@linkcode LocalConnectionProvider} + * and {@linkcode RemoteConnectionProvider} respectively. */ @injectable() export class ServiceConnectionProvider { From 735aab8c7981f4fe19d09103ed63ec7087575d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 27 Jun 2024 10:04:23 +0200 Subject: [PATCH 285/441] Block local navigation and open new windows externally in electron (#13782) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13592 Allow to open https/http links externally and ask the user for all other protocols. Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../src/generator/backend-generator.ts | 1 + .../electron-main-application.ts | 86 +++++++++++++------ .../electron-main/electron-main-constants.ts | 7 +- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/dev-packages/application-manager/src/generator/backend-generator.ts b/dev-packages/application-manager/src/generator/backend-generator.ts index 1ad8680f9241e..afa538291a6a6 100644 --- a/dev-packages/application-manager/src/generator/backend-generator.ts +++ b/dev-packages/application-manager/src/generator/backend-generator.ts @@ -76,6 +76,7 @@ const isSingleInstance = ${this.pck.props.backend.config.singleInstance === true THEIA_APP_PROJECT_PATH: theiaAppProjectPath, THEIA_BACKEND_MAIN_PATH: resolve(__dirname, 'main.js'), THEIA_FRONTEND_HTML_PATH: resolve(__dirname, '..', '..', 'lib', 'frontend', 'index.html'), + THEIA_SECONDARY_WINDOW_HTML_PATH: resolve(__dirname, '..', '..', 'lib', 'frontend', 'secondary-window.html') }); function load(raw) { diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 2da08aa9b4031..b004fd444d07a 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -15,7 +15,8 @@ // ***************************************************************************** import { inject, injectable, named } from 'inversify'; -import { screen, app, BrowserWindow, WebContents, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage, nativeTheme } from '../../electron-shared/electron'; +import { screen, app, BrowserWindow, WebContents, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage, + nativeTheme, shell, dialog } from '../../electron-shared/electron'; import * as path from 'path'; import { Argv } from 'yargs'; import { AddressInfo } from 'net'; @@ -31,7 +32,7 @@ import { ContributionProvider } from '../common/contribution-provider'; import { ElectronSecurityTokenService } from './electron-security-token-service'; import { ElectronSecurityToken } from '../electron-common/electron-token'; import Storage = require('electron-store'); -import { CancellationTokenSource, Disposable, DisposableCollection, isOSX, isWindows } from '../common'; +import { CancellationTokenSource, Disposable, DisposableCollection, Path, isOSX, isWindows } from '../common'; import { DEFAULT_WINDOW_HASH, WindowSearchParams } from '../common/window'; import { TheiaBrowserWindowOptions, TheiaElectronWindow, TheiaElectronWindowFactory } from './theia-electron-window'; import { ElectronMainApplicationGlobals } from './electron-main-constants'; @@ -410,7 +411,6 @@ export class ElectronMainApplication { electronWindow.window.on('unmaximize', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'unmaximize')); electronWindow.window.on('focus', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'focus')); this.attachSaveWindowState(electronWindow.window); - this.configureNativeSecondaryWindowCreation(electronWindow.window); return electronWindow.window; } @@ -488,31 +488,6 @@ export class ElectronMainApplication { return window; } - /** Configures native window creation, i.e. using window.open or links with target "_blank" in the frontend. */ - protected configureNativeSecondaryWindowCreation(electronWindow: BrowserWindow): void { - electronWindow.webContents.setWindowOpenHandler(() => { - const { minWidth, minHeight } = this.getDefaultOptions(); - const options: BrowserWindowConstructorOptions = { - ...this.getDefaultTheiaSecondaryWindowBounds(), - // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. - // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) - // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. - frame: true, - minWidth, - minHeight - }; - if (!this.useNativeWindowFrame) { - // If the main window does not have a native window frame, do not show an icon in the secondary window's native title bar. - // The data url is a 1x1 transparent png - options.icon = nativeImage.createFromDataURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12P4DwQACfsD/WMmxY8AAAAASUVORK5CYII='); - } - return { - action: 'allow', - overrideBrowserWindowOptions: options, - }; - }); - } - /** * "Gently" close all windows, application will not stop if a `beforeunload` handler returns `false`. */ @@ -714,6 +689,7 @@ export class ElectronMainApplication { app.on('will-quit', this.onWillQuit.bind(this)); app.on('second-instance', this.onSecondInstance.bind(this)); app.on('window-all-closed', this.onWindowAllClosed.bind(this)); + app.on('web-contents-created', this.onWebContentsCreated.bind(this)); } protected onWillQuit(event: ElectronEvent): void { @@ -736,6 +712,60 @@ export class ElectronMainApplication { ).parse(); } + protected onWebContentsCreated(event: ElectronEvent, webContents: WebContents): void { + // Block any in-page navigation except loading the secondary window contents + webContents.on('will-navigate', evt => { + if (new URI(evt.url).path.fsPath() !== new Path(this.globals.THEIA_SECONDARY_WINDOW_HTML_PATH).fsPath()) { + evt.preventDefault(); + } + }); + + webContents.setWindowOpenHandler(details => { + // if it's a secondary window, allow it to open + if (new URI(details.url).path.fsPath() === new Path(this.globals.THEIA_SECONDARY_WINDOW_HTML_PATH).fsPath()) { + const { minWidth, minHeight } = this.getDefaultOptions(); + const options: BrowserWindowConstructorOptions = { + ...this.getDefaultTheiaSecondaryWindowBounds(), + // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. + // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) + // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. + frame: true, + minWidth, + minHeight + }; + if (!this.useNativeWindowFrame) { + // If the main window does not have a native window frame, do not show an icon in the secondary window's native title bar. + // The data url is a 1x1 transparent png + options.icon = nativeImage.createFromDataURL( + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQI12P4DwQACfsD/WMmxY8AAAAASUVORK5CYII='); + } + return { + action: 'allow', + overrideBrowserWindowOptions: options, + }; + } else { + const uri: URI = new URI(details.url); + let okToOpen = uri.scheme === 'https' || uri.scheme === 'http'; + if (!okToOpen) { + const button = dialog.showMessageBoxSync(BrowserWindow.fromWebContents(webContents)!, { + message: `Open link\n\n${details.url}\n\nin the system handler?`, + type: 'question', + title: 'Open Link', + buttons: ['OK', 'Cancel'], + defaultId: 1, + cancelId: 1 + }); + okToOpen = button === 0; + } + if (okToOpen) { + shell.openExternal(details.url, {}); + } + + return { action: 'deny' }; + } + }); + } + protected onWindowAllClosed(event: ElectronEvent): void { if (!this.restarting) { this.requestStop(); diff --git a/packages/core/src/electron-main/electron-main-constants.ts b/packages/core/src/electron-main/electron-main-constants.ts index e235ee354a50a..b63c6ec4ff20e 100644 --- a/packages/core/src/electron-main/electron-main-constants.ts +++ b/packages/core/src/electron-main/electron-main-constants.ts @@ -16,7 +16,8 @@ export const ElectronMainApplicationGlobals = Symbol('ElectronMainApplicationGlobals'); export interface ElectronMainApplicationGlobals { - readonly THEIA_APP_PROJECT_PATH: string - readonly THEIA_BACKEND_MAIN_PATH: string - readonly THEIA_FRONTEND_HTML_PATH: string + readonly THEIA_APP_PROJECT_PATH: string; + readonly THEIA_BACKEND_MAIN_PATH: string; + readonly THEIA_FRONTEND_HTML_PATH: string; + readonly THEIA_SECONDARY_WINDOW_HTML_PATH: string } From fadba9c2ce032341e789b8c515e5e33b67ba9b22 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 27 Jun 2024 10:08:40 +0200 Subject: [PATCH 286/441] Avoid changing context key view when changing selection (#13768) fixes #13375 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 4 +++- .../plugin-ext/src/main/browser/view/tree-views-main.ts | 4 ---- .../src/main/browser/view/view-context-key-service.ts | 6 ------ 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a29f079c913c..d1fa54a24e354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) - + +## 1.50.0 - 06/27/2024 + +- [application-manager] updated logic to load correct messaging module in browser-only mode [#13827](https://github.com/eclipse-theia/theia/pull/13827) +- [application-package] bumped the default supported API from `1.89.1` to `1.90.2` [#13849](https://github.com/eclipse-theia/theia/pull/13849) - Contributed on behalf of STMicroelectronics +- [core] added support for dynamic menu contributions [#13720](https://github.com/eclipse-theia/theia/pull/13720) +- [core] fixed account menu order, icon and badge [#13771](https://github.com/eclipse-theia/theia/pull/13771) +- [core] fixed overflow behavior of sidebars [#13483](https://github.com/eclipse-theia/theia/pull/13483) - Contributed on behalf of STMicroelectronics +- [core] improved shown keybindings in context menu [#13830](https://github.com/eclipse-theia/theia/pull/13830) +- [core] introduced optional serialize method in Saveable [#13833](https://github.com/eclipse-theia/theia/pull/13833) +- [core] updated doc comments on service-connection-provider.ts [#13805](https://github.com/eclipse-theia/theia/pull/13805) - Contributed on behalf of STMicroelectronics +- [core] updated logic of links to block local navigation and open new windows externally in electron [#13782](https://github.com/eclipse-theia/theia/pull/13782) - Contributed on behalf of STMicroelectronics +- [core] updated logic to propagate "Save As" operation to plugin host [#13689](https://github.com/eclipse-theia/theia/pull/13689) +- [core] updated logic to use 'openWithSystemApp' to open uri when 'env.openExternal' requested [#13676](https://github.com/eclipse-theia/theia/pull/13676) +- [electron] switched single instance on per default. [#13831](https://github.com/eclipse-theia/theia/pull/13831) - Contributed on behalf of STMicroelectronics +- [filesystem] improved Upload Command [#13775](https://github.com/eclipse-theia/theia/pull/13775) +- [markers] fixed data race in problem view tree [#13841](https://github.com/eclipse-theia/theia/pull/13841) +- [messages] updated logic to always resolve existing before showing new notification [#13668](https://github.com/eclipse-theia/theia/pull/13668) +- [monaco] fixed editors theme change and widget not attached error [#13757](https://github.com/eclipse-theia/theia/pull/13757) +- [notebook] added an indicator for loading notebooks [#13843](https://github.com/eclipse-theia/theia/pull/13843) +- [notebook] added notebook output options and tag preference search [#13773](https://github.com/eclipse-theia/theia/pull/13773) +- [notebook] disabled cell editor search widget [#13836](https://github.com/eclipse-theia/theia/pull/13836) +- [notebook] improved ability to overwrite notebook services [#13776](https://github.com/eclipse-theia/theia/pull/13776) +- [notebook] improved notebook cell drag images [#13791](https://github.com/eclipse-theia/theia/pull/13791) +- [notebook] improved support for creating new notebooks [#13696](https://github.com/eclipse-theia/theia/pull/13696) +- [notebook] updated logic to set notebook editor as active when opening in foreground [#13828](https://github.com/eclipse-theia/theia/pull/13828) +- [notebook] updated logic to stop moving to next cell when suggestion widget is visible [#13774](https://github.com/eclipse-theia/theia/pull/13774) +- [playwright] fixed type definition of TheiaAppFactory [#13799](https://github.com/eclipse-theia/theia/pull/13799) - Contributed on behalf of STMicroelectronics +- [plugin] added stub for `registerMappedEditProvider` [#13681](https://github.com/eclipse-theia/theia/pull/13681) - Contributed on behalf of STMicroelectronics +- [plugin] added support for PluginExt#extensionKind [#13763](https://github.com/eclipse-theia/theia/pull/13763) +- [plugin] added support for TestRunRequest preserveFocus API [#13839](https://github.com/eclipse-theia/theia/pull/13839) - Contributed on behalf of STMicroelectronics +- [plugin] fixed RPC proxy handler notifications and requests order [#13810](https://github.com/eclipse-theia/theia/pull/13810) +- [plugin] fixed programmatic save for custom text editors [#13684](https://github.com/eclipse-theia/theia/pull/13684) +- [plugin] fixed tab group API event order [#13812](https://github.com/eclipse-theia/theia/pull/13812) +- [plugin] stubbed Chat and Language Model API [#13778](https://github.com/eclipse-theia/theia/pull/13778) +- [plugin] stubbed activeStackItem and related change event in debug namespace [#13847](https://github.com/eclipse-theia/theia/pull/13847) - Contributed on behalf of STMicroelectronics +- [plugin] updated logic to avoid pollution of all toolbars by actions contributed by tree views in extensions [#13768](https://github.com/eclipse-theia/theia/pull/13768) - Contributed on behalf of STMicroelectronics +- [plugin] updated logic to return empty appRoot in web plugin host [#13762](https://github.com/eclipse-theia/theia/pull/13762) +- [scm] updated jsdiff and simplify diff computation [#13787](https://github.com/eclipse-theia/theia/pull/13787) - Contributed on behalf of STMicroelectronics +- [vsx-registry] updated logic to use targetPlatform when installing plugin from open-vsx [#13825](https://github.com/eclipse-theia/theia/pull/13825) + +[Breaking Changes:](#breaking_changes_1.51.0) + +- [electron] switched single instance on per default. [#13831](https://github.com/eclipse-theia/theia/pull/13831) - Contributed on behalf of STMicroelectronics +- [filesystem] adjusted the "Save As" mechanism to assume that `Saveable.getSnapshot()` returns a full snapshot of the editor model [#13689](https://github.com/eclipse-theia/theia/pull/13689). + ## 1.50.0 - 06/03/2024 - [application-package] bumped the default supported API from `1.88.1` to `1.89.1` [#13738](https://github.com/eclipse-theia/theia/pull/13738) - contributed on behalf of STMicroelectronics From 9ae76400b5c660fc54684da31f1dfd275f8bf497 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Thu, 27 Jun 2024 16:13:45 +0200 Subject: [PATCH 295/441] Codicon color and URI support for TerminalOptions (#13413) Co-authored-by: FernandoAscencio Co-authored-by: Mark Sujew --- packages/core/src/browser/style/tabs.css | 11 +++ .../core/src/common/quick-pick-service.ts | 3 - packages/monaco/src/browser/style/index.css | 4 ++ .../plugin-ext/src/common/plugin-api-rpc.ts | 22 +++--- .../src/main/browser/quick-open-main.ts | 68 +++++++------------ .../src/main/browser/terminal-main.ts | 32 +++++++-- .../plugin-ext/src/plugin/plugin-context.ts | 4 +- .../plugin-ext/src/plugin/plugin-icon-path.ts | 7 +- packages/plugin-ext/src/plugin/quick-open.ts | 41 +++-------- .../plugin-ext/src/plugin/terminal-ext.ts | 60 ++++++++-------- .../plugin-ext/src/plugin/tree/tree-views.ts | 2 +- .../plugin-ext/src/plugin/type-converters.ts | 16 ++--- packages/plugin-ext/src/plugin/types-impl.ts | 3 + packages/plugin/src/theia.d.ts | 4 +- .../src/browser/base/terminal-widget.ts | 7 +- .../browser/terminal-frontend-contribution.ts | 3 +- .../src/browser/terminal-widget-impl.ts | 46 ++++++++++--- 17 files changed, 176 insertions(+), 157 deletions(-) diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css index 92c48350cd691..18db27f235de4 100644 --- a/packages/core/src/browser/style/tabs.css +++ b/packages/core/src/browser/style/tabs.css @@ -177,6 +177,17 @@ padding-right: 8px; } +.p-TabBar.theia-app-centers .p-TabBar-tabIcon[class*="plugin-icon-"], +.p-TabBar-tab.p-mod-drag-image .p-TabBar-tabIcon[class*="plugin-icon-"] { + background: none; + height: var(--theia-icon-size); +} + +.p-TabBar.theia-app-centers .p-TabBar-tabIcon[class*="plugin-icon-"]::before, +.p-TabBar-tab.p-mod-drag-image .p-TabBar-tabIcon[class*="plugin-icon-"]::before { + display: inline-block; +} + /* codicons */ .p-TabBar.theia-app-centers .p-TabBar-tabIcon.codicon, .p-TabBar-tab.p-mod-drag-image .p-TabBar-tabIcon.codicon { diff --git a/packages/core/src/common/quick-pick-service.ts b/packages/core/src/common/quick-pick-service.ts index d37481d76d658..dd3b07e1f8e1f 100644 --- a/packages/core/src/common/quick-pick-service.ts +++ b/packages/core/src/common/quick-pick-service.ts @@ -18,7 +18,6 @@ import * as fuzzy from 'fuzzy'; import { Event } from './event'; import { KeySequence } from './keys'; import { CancellationToken } from './cancellation'; -import { URI as Uri } from 'vscode-uri'; export const quickPickServicePath = '/services/quickPick'; export const QuickPickService = Symbol('QuickPickService'); @@ -53,7 +52,6 @@ export interface QuickPickItem { description?: string; detail?: string; keySequence?: KeySequence; - iconPath?: { light?: Uri; dark: Uri }; iconClasses?: string[]; alwaysShow?: boolean; highlights?: QuickPickItemHighlights; @@ -94,7 +92,6 @@ export interface QuickPickValue extends QuickPickItem { } export interface QuickInputButton { - iconPath?: { light?: Uri; dark: Uri }; iconClass?: string; tooltip?: string; /** diff --git a/packages/monaco/src/browser/style/index.css b/packages/monaco/src/browser/style/index.css index 7977874af744d..d03b5b4043382 100644 --- a/packages/monaco/src/browser/style/index.css +++ b/packages/monaco/src/browser/style/index.css @@ -176,6 +176,10 @@ text-align: left; } +.quick-input-list .monaco-icon-label::before { + height: 22px; +} + .codicon-file.default-file-icon.file-icon { padding-left: 2px; height: 22px; diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 83da0b1d101c1..0b081d3904639 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -339,7 +339,7 @@ export interface TerminalServiceMain { * Create new Terminal with Terminal options. * @param options - object with parameters to create new terminal. */ - $createTerminal(id: string, options: theia.TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise; + $createTerminal(id: string, options: TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise; /** * Send text to the terminal by id. @@ -469,6 +469,10 @@ export interface TerminalServiceMain { $unregisterTerminalObserver(id: string): unknown; } +export interface TerminalOptions extends theia.TerminalOptions { + iconUrl?: string | { light: string; dark: string } | ThemeIcon; +} + export interface AutoFocus { autoFocusFirstEntry?: boolean; // TODO @@ -524,10 +528,10 @@ export interface QuickOpenExt { $onDidChangeSelection(sessionId: number, handles: number[]): void; /* eslint-disable max-len */ - showQuickPick(itemsOrItemsPromise: Array | Promise>, options: theia.QuickPickOptions & { canPickMany: true; }, + showQuickPick(plugin: Plugin, itemsOrItemsPromise: Array | Promise>, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; - showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(plugin: Plugin, itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(plugin: Plugin, itemsOrItemsPromise: Array | Promise>, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; showInput(options?: theia.InputBoxOptions, token?: theia.CancellationToken): PromiseLike; // showWorkspaceFolderPick(options?: theia.WorkspaceFolderPickOptions, token?: theia.CancellationToken): Promise @@ -651,7 +655,7 @@ export interface TransferQuickPickItem { handle: number; kind: 'item' | 'separator', label: string; - iconPath?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon; + iconUrl?: string | { light: string; dark: string } | ThemeIcon; description?: string; detail?: string; picked?: boolean; @@ -675,7 +679,7 @@ export interface TransferQuickPickOptions { export interface TransferQuickInputButton { handle?: number; - readonly iconPath: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon; + readonly iconUrl?: string | { light: string; dark: string } | ThemeIcon; readonly tooltip?: string | undefined; } @@ -1514,12 +1518,10 @@ export interface WorkspaceEditEntryMetadataDto { needsConfirmation: boolean; label: string; description?: string; - iconPath?: { - id: string; - } | { + iconPath?: ThemeIcon | { light: UriComponents; dark: UriComponents; - } | ThemeIcon; + }; } export interface WorkspaceFileEditDto { diff --git a/packages/plugin-ext/src/main/browser/quick-open-main.ts b/packages/plugin-ext/src/main/browser/quick-open-main.ts index 39398d457438b..4c06ebdc8aa5f 100644 --- a/packages/plugin-ext/src/main/browser/quick-open-main.ts +++ b/packages/plugin-ext/src/main/browser/quick-open-main.ts @@ -42,11 +42,8 @@ import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposa import { CancellationToken } from '@theia/core/lib/common/cancellation'; import { MonacoQuickInputService } from '@theia/monaco/lib/browser/monaco-quick-input-service'; import { QuickInputButtons } from '../../plugin/types-impl'; -import * as monaco from '@theia/monaco-editor-core'; -import { UriComponents } from '../../common/uri-components'; -import { URI } from '@theia/core/shared/vscode-uri'; import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; -import { isUriComponents } from '@theia/monaco-editor-core/esm/vs/base/common/uri'; +import { PluginSharedStyle } from './plugin-shared-style'; import { QuickPickSeparator } from '@theia/core'; export interface QuickInputSession { @@ -54,16 +51,12 @@ export interface QuickInputSession { handlesToItems: Map; } -interface IconPath { - dark: URI, - light?: URI -}; - export class QuickOpenMainImpl implements QuickOpenMain, Disposable { private quickInputService: QuickInputService; private proxy: QuickOpenExt; private delegate: MonacoQuickInputService; + private sharedStyle: PluginSharedStyle; private readonly items: Record ({ - ...this.normalizeIconPath(button.iconPath), + iconClass: this.toIconClass(button.iconUrl), tooltip: button.tooltip, handle: button === QuickInputButtons.Back ? -1 : i, } as QuickInputButton)); diff --git a/packages/plugin-ext/src/main/browser/terminal-main.ts b/packages/plugin-ext/src/main/browser/terminal-main.ts index 059b4d9394be8..2ac7779f8cee5 100644 --- a/packages/plugin-ext/src/main/browser/terminal-main.ts +++ b/packages/plugin-ext/src/main/browser/terminal-main.ts @@ -15,21 +15,22 @@ // ***************************************************************************** import { interfaces } from '@theia/core/shared/inversify'; -import { ApplicationShell, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import { TerminalEditorLocationOptions, TerminalOptions } from '@theia/plugin'; +import { ApplicationShell, WidgetOpenerOptions, codicon } from '@theia/core/lib/browser'; +import { TerminalEditorLocationOptions } from '@theia/plugin'; import { TerminalLocation, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; import { TerminalProfileService } from '@theia/terminal/lib/browser/terminal-profile-service'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; -import { TerminalServiceMain, TerminalServiceExt, MAIN_RPC_CONTEXT } from '../../common/plugin-api-rpc'; +import { TerminalServiceMain, TerminalServiceExt, MAIN_RPC_CONTEXT, TerminalOptions } from '../../common/plugin-api-rpc'; import { RPCProtocol } from '../../common/rpc-protocol'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { SerializableEnvironmentVariableCollection, ShellTerminalServerProxy } from '@theia/terminal/lib/common/shell-terminal-protocol'; import { TerminalLink, TerminalLinkProvider } from '@theia/terminal/lib/browser/terminal-link-provider'; import { URI } from '@theia/core/lib/common/uri'; -import { getIconClass } from '../../plugin/terminal-ext'; import { PluginTerminalRegistry } from './plugin-terminal-registry'; -import { CancellationToken } from '@theia/core'; +import { CancellationToken, isObject } from '@theia/core'; import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin'; +import { PluginSharedStyle } from './plugin-shared-style'; +import { ThemeIcon } from '@theia/core/lib/common/theme'; import debounce = require('@theia/core/shared/lodash.debounce'); interface TerminalObserverData { @@ -49,6 +50,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin private readonly hostedPluginSupport: HostedPluginSupport; private readonly shell: ApplicationShell; private readonly extProxy: TerminalServiceExt; + private readonly sharedStyle: PluginSharedStyle; private readonly shellTerminalServer: ShellTerminalServerProxy; private readonly terminalLinkProviders: string[] = []; @@ -60,6 +62,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin this.terminalProfileService = container.get(TerminalProfileService); this.pluginTerminalRegistry = container.get(PluginTerminalRegistry); this.hostedPluginSupport = container.get(HostedPluginSupport); + this.sharedStyle = container.get(PluginSharedStyle); this.shell = container.get(ApplicationShell); this.shellTerminalServer = container.get(ShellTerminalServerProxy); this.extProxy = rpc.getProxy(MAIN_RPC_CONTEXT.TERMINAL_EXT); @@ -153,7 +156,7 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin const terminal = await this.terminals.newTerminal({ id, title: options.name, - iconClass: getIconClass(options), + iconClass: this.toIconClass(options), shellPath: options.shellPath, shellArgs: options.shellArgs, cwd: options.cwd ? new URI(options.cwd) : undefined, @@ -329,6 +332,23 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin })); } + protected toIconClass(options: TerminalOptions): string | ThemeIcon | undefined { + const iconColor = isObject<{ id: string }>(options.color) && typeof options.color.id === 'string' ? options.color.id : undefined; + let iconClass: string; + if (options.iconUrl) { + if (typeof options.iconUrl === 'object' && 'id' in options.iconUrl) { + iconClass = codicon(options.iconUrl.id); + } else { + const iconReference = this.sharedStyle.toIconClass(options.iconUrl); + this.toDispose.push(iconReference); + iconClass = iconReference.object.iconClass; + } + } else { + iconClass = codicon('terminal'); + } + return iconColor ? { id: iconClass, color: { id: iconColor } } : iconClass; + } + $unregisterTerminalObserver(id: string): void { const observer = this.observers.get(id); if (observer) { diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index c37330cbf90de..2dc28f72b501b 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -483,7 +483,7 @@ export function createAPIFactory( }, // eslint-disable-next-line @typescript-eslint/no-explicit-any showQuickPick(items: any, options?: theia.QuickPickOptions, token?: theia.CancellationToken): any { - return quickOpenExt.showQuickPick(items, options, token); + return quickOpenExt.showQuickPick(plugin, items, options, token); }, createQuickPick(): theia.QuickPick { return quickOpenExt.createQuickPick(plugin); @@ -564,7 +564,7 @@ export function createAPIFactory( createTerminal(nameOrOptions: theia.TerminalOptions | theia.ExtensionTerminalOptions | theia.ExtensionTerminalOptions | (string | undefined), shellPath?: string, shellArgs?: string[] | string): theia.Terminal { - return terminalExt.createTerminal(nameOrOptions, shellPath, shellArgs); + return terminalExt.createTerminal(plugin, nameOrOptions, shellPath, shellArgs); }, onDidChangeTerminalState, onDidCloseTerminal, diff --git a/packages/plugin-ext/src/plugin/plugin-icon-path.ts b/packages/plugin-ext/src/plugin/plugin-icon-path.ts index 610e7dcc1b3af..54b7b034a2aac 100644 --- a/packages/plugin-ext/src/plugin/plugin-icon-path.ts +++ b/packages/plugin-ext/src/plugin/plugin-icon-path.ts @@ -24,8 +24,8 @@ export type PluginIconPath = string | URI | { dark: string | URI }; export namespace PluginIconPath { - export function toUrl(iconPath: PluginIconPath | undefined, plugin: Plugin): IconUrl | undefined { - if (!iconPath) { + export function toUrl(iconPath: unknown, plugin: Plugin): IconUrl | undefined { + if (!is(iconPath)) { return undefined; } if (typeof iconPath === 'object' && 'light' in iconPath) { @@ -36,6 +36,9 @@ export namespace PluginIconPath { } return asString(iconPath, plugin); } + export function is(item: unknown): item is PluginIconPath { + return typeof item === 'string' || item instanceof URI || typeof item === 'object' && !!item && 'light' in item && 'dark' in item; + } export function asString(arg: string | URI, plugin: Plugin): string { arg = arg instanceof URI && arg.scheme === 'file' ? arg.fsPath : arg; if (typeof arg !== 'string') { diff --git a/packages/plugin-ext/src/plugin/quick-open.ts b/packages/plugin-ext/src/plugin/quick-open.ts index 7ed2773f56849..c9f51aa6c3f60 100644 --- a/packages/plugin-ext/src/plugin/quick-open.ts +++ b/packages/plugin-ext/src/plugin/quick-open.ts @@ -30,8 +30,8 @@ import { convertToTransferQuickPickItems } from './type-converters'; import { PluginPackage } from '../common/plugin-protocol'; import { QuickInputButtonHandle } from '@theia/core/lib/browser'; import { MaybePromise } from '@theia/core/lib/common/types'; -import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; import { Severity } from '@theia/core/lib/common/severity'; +import { PluginIconPath } from './plugin-icon-path'; const canceledName = 'Canceled'; /** @@ -42,27 +42,6 @@ export function isPromiseCanceledError(error: any): boolean { return error instanceof Error && error.name === canceledName && error.message === canceledName; } -export function getIconUris(iconPath: theia.QuickInputButton['iconPath']): { dark: URI, light: URI } | { id: string } { - if (ThemeIcon.is(iconPath)) { - return { id: iconPath.id }; - } - const dark = getDarkIconUri(iconPath as URI | { light: URI; dark: URI; }); - const light = getLightIconUri(iconPath as URI | { light: URI; dark: URI; }); - // Tolerate strings: https://github.com/microsoft/vscode/issues/110432#issuecomment-726144556 - return { - dark: typeof dark === 'string' ? URI.file(dark) : dark, - light: typeof light === 'string' ? URI.file(light) : light - }; -} - -export function getLightIconUri(iconPath: URI | { light: URI; dark: URI; }): URI { - return typeof iconPath === 'object' && 'light' in iconPath ? iconPath.light : iconPath; -} - -export function getDarkIconUri(iconPath: URI | { light: URI; dark: URI; }): URI { - return typeof iconPath === 'object' && 'dark' in iconPath ? iconPath.dark : iconPath; -} - type Item = theia.QuickPickItem | string; export class QuickOpenExtImpl implements QuickOpenExt { @@ -77,10 +56,10 @@ export class QuickOpenExtImpl implements QuickOpenExt { } /* eslint-disable max-len */ - showQuickPick(itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; - showQuickPick(itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; - showQuickPick(itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token: theia.CancellationToken = CancellationToken.None): Promise { + showQuickPick(plugin: Plugin, itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options: theia.QuickPickOptions & { canPickMany: true; }, token?: theia.CancellationToken): Promise | undefined>; + showQuickPick(plugin: Plugin, itemsOrItemsPromise: string[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(plugin: Plugin, itemsOrItemsPromise: theia.QuickPickItem[] | Promise, options?: theia.QuickPickOptions, token?: theia.CancellationToken): Promise; + showQuickPick(plugin: Plugin, itemsOrItemsPromise: Item[] | Promise, options?: theia.QuickPickOptions, token: theia.CancellationToken = CancellationToken.None): Promise { this.onDidSelectItem = undefined; const itemsPromise = Promise.resolve(itemsOrItemsPromise); @@ -104,7 +83,7 @@ export class QuickOpenExtImpl implements QuickOpenExt { return undefined; } return itemsPromise.then(async items => { - const pickItems = convertToTransferQuickPickItems(items); + const pickItems = convertToTransferQuickPickItems(plugin, items); if (options && typeof options.onDidSelectItem === 'function') { this.onDidSelectItem = handle => { @@ -398,8 +377,7 @@ export class QuickInputExt implements theia.QuickInput { }); this.update({ buttons: buttons.map((button, i) => ({ - iconPath: getIconUris(button.iconPath), - iconClass: ThemeIcon.is(button.iconPath) ? MonacoThemeIcon.asClassName(button.iconPath) : undefined, + iconUrl: PluginIconPath.toUrl(button.iconPath, this.plugin) ?? ThemeIcon.get(button.iconPath), tooltip: button.tooltip, handle: button === QuickInputButtons.Back ? -1 : i, })) @@ -640,15 +618,14 @@ export class QuickPickExt extends QuickInputExt i pickItems.push({ kind: 'item', label: item.label, - iconPath: item.iconPath ? getIconUris(item.iconPath) : undefined, + iconUrl: PluginIconPath.toUrl(item.iconPath, this.plugin) ?? ThemeIcon.get(item.iconPath), description: item.description, handle, detail: item.detail, picked: item.picked, alwaysShow: item.alwaysShow, buttons: item.buttons?.map((button, index) => ({ - iconPath: getIconUris(button.iconPath), - iconClass: ThemeIcon.is(button.iconPath) ? MonacoThemeIcon.asClassName(button.iconPath) : undefined, + iconUrl: PluginIconPath.toUrl(button.iconPath, this.plugin) ?? ThemeIcon.get(button.iconPath), tooltip: button.tooltip, handle: button === QuickInputButtons.Back ? -1 : index, })) diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index d23967b13ca77..d8e2a3f01bd70 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -13,10 +13,10 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** + import { UUID } from '@theia/core/shared/@phosphor/coreutils'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { Terminal, TerminalOptions, PseudoTerminalOptions, ExtensionTerminalOptions, TerminalState } from '@theia/plugin'; -import { TerminalServiceExt, TerminalServiceMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc'; +import { TerminalServiceExt, TerminalServiceMain, PLUGIN_RPC_CONTEXT, Plugin, TerminalOptions } from '../common/plugin-api-rpc'; import { RPCProtocol } from '../common/rpc-protocol'; import { Event, Emitter } from '@theia/core/lib/common/event'; import { MultiKeyMap } from '@theia/core/lib/common/collections'; @@ -26,28 +26,13 @@ import * as Converter from './type-converters'; import { Disposable, EnvironmentVariableMutatorType, TerminalExitReason, ThemeIcon } from './types-impl'; import { NO_ROOT_URI, SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/shell-terminal-protocol'; import { ProvidedTerminalLink } from '../common/plugin-api-rpc-model'; -import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; - -export function getIconUris(iconPath: theia.TerminalOptions['iconPath']): { id: string } | undefined { - if (ThemeIcon.is(iconPath)) { - return { id: iconPath.id }; - } - return undefined; -} - -export function getIconClass(options: theia.TerminalOptions | theia.ExtensionTerminalOptions): string | undefined { - const iconClass = getIconUris(options.iconPath); - if (iconClass) { - return MonacoThemeIcon.asClassName(iconClass); - } - return undefined; -} +import { PluginIconPath } from './plugin-icon-path'; /** * Provides high level terminal plugin api to use in the Theia plugins. * This service allow(with help proxy) create and use terminal emulator. */ - @injectable() +@injectable() export class TerminalServiceExtImpl implements TerminalServiceExt { private readonly proxy: TerminalServiceMain; @@ -59,17 +44,17 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { private readonly terminalLinkProviders = new Map(); private readonly terminalObservers = new Map(); private readonly terminalProfileProviders = new Map(); - private readonly onDidCloseTerminalEmitter = new Emitter(); - readonly onDidCloseTerminal: theia.Event = this.onDidCloseTerminalEmitter.event; + private readonly onDidCloseTerminalEmitter = new Emitter(); + readonly onDidCloseTerminal: theia.Event = this.onDidCloseTerminalEmitter.event; - private readonly onDidOpenTerminalEmitter = new Emitter(); - readonly onDidOpenTerminal: theia.Event = this.onDidOpenTerminalEmitter.event; + private readonly onDidOpenTerminalEmitter = new Emitter(); + readonly onDidOpenTerminal: theia.Event = this.onDidOpenTerminalEmitter.event; - private readonly onDidChangeActiveTerminalEmitter = new Emitter(); - readonly onDidChangeActiveTerminal: theia.Event = this.onDidChangeActiveTerminalEmitter.event; + private readonly onDidChangeActiveTerminalEmitter = new Emitter(); + readonly onDidChangeActiveTerminal: theia.Event = this.onDidChangeActiveTerminalEmitter.event; - private readonly onDidChangeTerminalStateEmitter = new Emitter(); - readonly onDidChangeTerminalState: theia.Event = this.onDidChangeTerminalStateEmitter.event; + private readonly onDidChangeTerminalStateEmitter = new Emitter(); + readonly onDidChangeTerminalState: theia.Event = this.onDidChangeTerminalStateEmitter.event; protected environmentVariableCollections: MultiKeyMap = new MultiKeyMap(2); @@ -97,9 +82,10 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { } createTerminal( - nameOrOptions: TerminalOptions | PseudoTerminalOptions | ExtensionTerminalOptions | (string | undefined), + plugin: Plugin, + nameOrOptions: theia.TerminalOptions | theia.PseudoTerminalOptions | theia.ExtensionTerminalOptions | string | undefined, shellPath?: string, shellArgs?: string[] | string - ): Terminal { + ): theia.Terminal { const id = `plugin-terminal-${UUID.uuid4()}`; let options: TerminalOptions; let pseudoTerminal: theia.Pseudoterminal | undefined = undefined; @@ -122,7 +108,6 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { } let parentId; - if (options.location && typeof options.location === 'object' && 'parentTerminal' in options.location) { const parentTerminal = options.location.parentTerminal; if (parentTerminal instanceof TerminalExtImpl) { @@ -135,6 +120,15 @@ export class TerminalServiceExtImpl implements TerminalServiceExt { } } + if (typeof nameOrOptions === 'object' && 'iconPath' in nameOrOptions) { + const iconPath = nameOrOptions.iconPath; + options.iconUrl = PluginIconPath.toUrl(iconPath, plugin) ?? ThemeIcon.get(iconPath); + } + + if (typeof nameOrOptions === 'object' && 'color' in nameOrOptions) { + options.color = nameOrOptions.color; + } + this.proxy.$createTerminal(id, options, parentId, !!pseudoTerminal); let creationOptions: theia.TerminalOptions | theia.ExtensionTerminalOptions = options; @@ -462,7 +456,7 @@ export class EnvironmentVariableCollectionImpl implements theia.GlobalEnvironmen } } -export class TerminalExtImpl implements Terminal { +export class TerminalExtImpl implements theia.Terminal { name: string; @@ -476,9 +470,9 @@ export class TerminalExtImpl implements Terminal { return this.deferredProcessId.promise; } - readonly creationOptions: Readonly; + readonly creationOptions: Readonly; - state: TerminalState = { isInteractedWith: false }; + state: theia.TerminalState = { isInteractedWith: false }; constructor(private readonly proxy: TerminalServiceMain, private readonly options: theia.TerminalOptions | theia.ExtensionTerminalOptions) { this.creationOptions = this.options; diff --git a/packages/plugin-ext/src/plugin/tree/tree-views.ts b/packages/plugin-ext/src/plugin/tree/tree-views.ts index d7a130c2a3273..5dec3ca816fdd 100644 --- a/packages/plugin-ext/src/plugin/tree/tree-views.ts +++ b/packages/plugin-ext/src/plugin/tree/tree-views.ts @@ -446,7 +446,7 @@ class TreeViewExtImpl implements Disposable { } else if (ThemeIcon.is(iconPath)) { themeIcon = iconPath; } else { - iconUrl = PluginIconPath.toUrl(iconPath, this.plugin); + iconUrl = PluginIconPath.toUrl(iconPath, this.plugin); } let checkboxInfo; diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 86e70d7258510..557b412ed4e43 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -16,7 +16,7 @@ import * as theia from '@theia/plugin'; import * as lstypes from '@theia/core/shared/vscode-languageserver-protocol'; -import { InlineValueEvaluatableExpression, InlineValueText, InlineValueVariableLookup, QuickPickItemKind, URI } from './types-impl'; +import { InlineValueEvaluatableExpression, InlineValueText, InlineValueVariableLookup, QuickPickItemKind, ThemeIcon, URI } from './types-impl'; import * as rpc from '../common/plugin-api-rpc'; import { DecorationOptions, EditorPosition, Plugin, Position, WorkspaceTextEditDto, WorkspaceFileEditDto, Selection, TaskDto, WorkspaceEditDto @@ -35,7 +35,7 @@ import { CellRange, isTextStreamMime } from '@theia/notebook/lib/common'; import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering'; import { TestItemDTO, TestMessageDTO } from '../common/test-types'; -import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables'; +import { PluginIconPath } from './plugin-icon-path'; const SIDE_GROUP = -2; const ACTIVE_GROUP = -1; @@ -1226,7 +1226,7 @@ export function convertIconPath(iconPath: types.URI | { light: types.URI; dark: dark: iconPath.dark.toJSON(), light: iconPath.light?.toJSON() }; - } else if (ThemeIcon.isThemeIcon(iconPath)) { + } else if (ThemeIcon.is(iconPath)) { return { id: iconPath.id, color: iconPath.color ? { id: iconPath.color.id } : undefined @@ -1236,19 +1236,19 @@ export function convertIconPath(iconPath: types.URI | { light: types.URI; dark: } } -export function convertQuickInputButton(button: theia.QuickInputButton, index: number): rpc.TransferQuickInputButton { +export function convertQuickInputButton(plugin: Plugin, button: theia.QuickInputButton, index: number): rpc.TransferQuickInputButton { const iconPath = convertIconPath(button.iconPath); if (!iconPath) { throw new Error(`Could not convert icon path: '${button.iconPath}'`); } return { handle: index, - iconPath: iconPath, + iconUrl: PluginIconPath.toUrl(iconPath, plugin) ?? ThemeIcon.get(iconPath), tooltip: button.tooltip }; } -export function convertToTransferQuickPickItems(items: (theia.QuickPickItem | string)[]): rpc.TransferQuickPickItem[] { +export function convertToTransferQuickPickItems(plugin: Plugin, items: (theia.QuickPickItem | string)[]): rpc.TransferQuickPickItem[] { return items.map((item, index) => { if (typeof item === 'string') { return { kind: 'item', label: item, handle: index }; @@ -1260,11 +1260,11 @@ export function convertToTransferQuickPickItems(items: (theia.QuickPickItem | st kind: 'item', label, description, - iconPath: convertIconPath(iconPath), + iconUrl: PluginIconPath.toUrl(iconPath, plugin) ?? ThemeIcon.get(iconPath), detail, picked, alwaysShow, - buttons: buttons ? buttons.map(convertQuickInputButton) : undefined, + buttons: buttons ? buttons.map((button, i) => convertQuickInputButton(plugin, button, i)) : undefined, handle: index, }; } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index 2d96f2b75bc39..b0c4f0a8cd1a2 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -744,6 +744,9 @@ export namespace ThemeIcon { export function is(item: unknown): item is ThemeIcon { return isObject(item) && 'id' in item; } + export function get(item: unknown): ThemeIcon | undefined { + return is(item) ? item : undefined; + } } export enum TextEditorRevealType { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 1767f570a9a8f..98a0f69b3ca0f 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -3172,7 +3172,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: ThemeIcon; + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; /** * The icon {@link ThemeColor} for the terminal. @@ -3293,7 +3293,7 @@ export module '@theia/plugin' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: ThemeIcon; + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; /** * The icon {@link ThemeColor} for the terminal. diff --git a/packages/terminal/src/browser/base/terminal-widget.ts b/packages/terminal/src/browser/base/terminal-widget.ts index 8ccb29477e2dc..7c9443f94de7f 100644 --- a/packages/terminal/src/browser/base/terminal-widget.ts +++ b/packages/terminal/src/browser/base/terminal-widget.ts @@ -16,11 +16,12 @@ import { Event, ViewColumn } from '@theia/core'; import { BaseWidget } from '@theia/core/lib/browser'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string'; +import { ThemeIcon } from '@theia/core/lib/common/theme'; import { CommandLineOptions } from '@theia/process/lib/common/shell-command-builder'; import { TerminalSearchWidget } from '../search/terminal-search-widget'; import { TerminalProcessInfo, TerminalExitReason } from '../../common/base-terminal-protocol'; import URI from '@theia/core/lib/common/uri'; -import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string'; export interface TerminalDimensions { cols: number; @@ -191,9 +192,9 @@ export interface TerminalWidgetOptions { readonly title?: string; /** - * icon class + * icon class with or without color modifier */ - readonly iconClass?: string; + readonly iconClass?: string | ThemeIcon; /** * Path to the executable shell. For example: `/bin/bash`, `bash`, `sh`. diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index caa5199954f1f..ccd9f9a7254da 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -312,7 +312,8 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu } else { this.contributedProfileStore.registerTerminalProfile('SHELL', new ShellTerminalProfile(this, { shellPath: await this.resolveShellPath('${SHELL}')!, - shellArgs: ['-l'] + shellArgs: ['-l'], + iconClass: 'codicon codicon-terminal' })); } diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 42924cfdefc93..5b923a278ce1c 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -17,9 +17,10 @@ import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; -import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core'; +import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS, generateUuid } from '@theia/core'; import { - Widget, Message, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer + Widget, Message, StatefulWidget, isFirefox, MessageLoop, KeyCode, ExtractableWidget, ContextMenuRenderer, + DecorationStyle } from '@theia/core/lib/browser'; import { isOSX } from '@theia/core/lib/common'; import { WorkspaceService } from '@theia/workspace/lib/browser'; @@ -48,6 +49,7 @@ import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markd import { EnhancedPreviewWidget } from '@theia/core/lib/browser/widgets/enhanced-preview-widget'; import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; +import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; export const TERMINAL_WIDGET_FACTORY_ID = 'terminal'; @@ -104,6 +106,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget protected isAttachedCloseListener: boolean = false; protected shown = false; protected enhancedPreviewNode: Node | undefined; + protected styleElement: HTMLStyleElement | undefined; override lastCwd = new URI(); @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -119,6 +122,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget @inject(TerminalSearchWidgetFactory) protected readonly terminalSearchBoxFactory: TerminalSearchWidgetFactory; @inject(TerminalCopyOnSelectionHandler) protected readonly copyOnSelectionHandler: TerminalCopyOnSelectionHandler; @inject(TerminalThemeService) protected readonly themeService: TerminalThemeService; + @inject(ColorRegistry) protected readonly colorRegistry: ColorRegistry; @inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder; @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; @inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory; @@ -163,12 +167,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget @postConstruct() protected init(): void { this.setTitle(this.options.title || TerminalWidgetImpl.LABEL); - - if (this.options.iconClass) { - this.title.iconClass = this.options.iconClass; - } else { - this.title.iconClass = codicon('terminal'); - } + this.setIconClass(); if (this.options.kind) { this.terminalKind = this.options.kind; @@ -213,7 +212,10 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.update(); })); - this.toDispose.push(this.themeService.onDidChange(() => this.term.options.theme = this.themeService.theme)); + this.toDispose.push(this.themeService.onDidChange(() => { + this.term.options.theme = this.themeService.theme; + this.setIconClass(); + })); this.attachCustomKeyEventHandler(); const titleChangeListenerDispose = this.term.onTitleChange((title: string) => { if (this.options.useServerTitle) { @@ -333,6 +335,31 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget this.term.options.fastScrollSensitivity = this.preferences.get('terminal.integrated.fastScrollSensitivity'); } + protected setIconClass(): void { + this.styleElement?.remove(); + if (this.options.iconClass) { + const iconClass = this.options.iconClass; + if (typeof iconClass === 'string') { + this.title.iconClass = iconClass; + } else { + const iconClasses: string[] = []; + iconClasses.push(iconClass.id); + if (iconClass.color) { + this.styleElement = DecorationStyle.createStyleElement(`${this.terminalId}-terminal-style`); + const classId = 'terminal-icon-' + generateUuid().replace(/-/g, ''); + const color = this.colorRegistry.getCurrentColor(iconClass.color.id); + this.styleElement.textContent = ` + .${classId}::before { + color: ${color}; + } + `; + iconClasses.push(classId); + } + this.title.iconClass = iconClasses.join(' '); + } + } + } + private setCursorBlink(blink: boolean): void { if (this.term.options.cursorBlink !== blink) { this.term.options.cursorBlink = blink; @@ -812,6 +839,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget // don't use preview node anymore. rendered markdown will be disposed on super call this.enhancedPreviewNode = undefined; } + this.styleElement?.remove(); super.dispose(); } From 04de7f84cd03126ddbaee9212402eaa40039a443 Mon Sep 17 00:00:00 2001 From: rifat87 <102798983+rifat87@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:21:00 +0600 Subject: [PATCH 296/441] Refactor trademark section for better readability (#13866) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index cd8f42e42bfc3..0227b52152c6e 100644 --- a/README.md +++ b/README.md @@ -119,5 +119,4 @@ Read below how to engage with Theia community: ## Trademark -"Theia" is a trademark of the Eclipse Foundation - +"Theia" is a **trademark of the Eclipse Foundation**. [Learn More](https://www.eclipse.org/theia) From 71a1235882c79395d8c3b0773657f2005158fcbe Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Wed, 3 Jul 2024 10:52:30 +0200 Subject: [PATCH 297/441] Add support for 256 truecolor (#13853) Beforehand the `terminfo[colors]` and `COLORTERM` were not set properly. The terminal already supports 256 truecolor, but cli tools might not use it, as the env variables were not set correctly. This is fixed with this PR, the color is not set to `256` and `COLORTERM` is set to `truecolor`. Fixes #13523. --- packages/terminal/src/node/shell-process.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/terminal/src/node/shell-process.ts b/packages/terminal/src/node/shell-process.ts index 4bf64080756c5..1efab68ed7c3e 100644 --- a/packages/terminal/src/node/shell-process.ts +++ b/packages/terminal/src/node/shell-process.ts @@ -61,15 +61,16 @@ export class ShellProcess extends TerminalProcess { @inject(ILogger) @named('terminal') logger: ILogger, @inject(EnvironmentUtils) environmentUtils: EnvironmentUtils, ) { + const env = { 'COLORTERM': 'truecolor' }; super({ command: options.shell || ShellProcess.getShellExecutablePath(), args: options.args || ShellProcess.getShellExecutableArgs(), options: { - name: 'xterm-color', + name: 'xterm-256color', cols: options.cols || ShellProcess.defaultCols, rows: options.rows || ShellProcess.defaultRows, cwd: getRootPath(options.rootURI), - env: options.strictEnv !== true ? environmentUtils.mergeProcessEnv(options.env) : options.env, + env: options.strictEnv !== true ? Object.assign(env, environmentUtils.mergeProcessEnv(options.env)) : Object.assign(env, options.env), }, isPseudo: options.isPseudo, }, processManager, ringBuffer, logger); From 5a8d3cf2e00f948a17398aab7bfbd03f2a228c25 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 3 Jul 2024 12:52:02 +0200 Subject: [PATCH 298/441] Refactor preference tree layouting (#13819) --- .../common/preferences/preference-schema.ts | 5 + .../src/hosted/node/scanners/scanner-theia.ts | 1 + .../src/browser/preference-frontend-module.ts | 2 + .../src/browser/preference-tree-model.ts | 3 +- .../src/browser/util/preference-layout.ts | 377 ++++++++++++++++++ .../browser/util/preference-tree-generator.ts | 91 ++--- .../preference-tree-label-provider.spec.ts | 2 + .../util/preference-tree-label-provider.ts | 22 +- .../src/browser/util/preference-types.ts | 10 +- .../browser/views/preference-editor-widget.ts | 2 +- 10 files changed, 443 insertions(+), 72 deletions(-) create mode 100644 packages/preferences/src/browser/util/preference-layout.ts diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts index b77c01a1891f9..436a9f82a1d38 100644 --- a/packages/core/src/common/preferences/preference-schema.ts +++ b/packages/core/src/common/preferences/preference-schema.ts @@ -25,6 +25,11 @@ export interface PreferenceSchema { [name: string]: any, scope?: 'application' | 'window' | 'resource' | PreferenceScope, overridable?: boolean; + /** + * The title of the preference schema. + * It is used in the preference UI to associate a localized group of preferences. + */ + title?: string; properties: PreferenceSchemaProperties } export namespace PreferenceSchema { diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 834aaee8bc35a..1d309445f0bfb 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -196,6 +196,7 @@ export class TheiaPluginScanner extends AbstractPluginScanner { for (const c of configurations) { const config = this.readConfiguration(c, rawPlugin.packagePath); if (config) { + Object.values(config.properties).forEach(property => property.title = config.title); contributions.configuration.push(config); } } diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index a66fbace9102e..ef7d553bcee84 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -32,12 +32,14 @@ import { PreferenceOpenHandler } from './preference-open-handler'; import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import { PreferenceFrontendContribution } from './preference-frontend-contribution'; +import { PreferenceLayoutProvider } from './util/preference-layout'; export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind): void { bindPreferenceProviders(bind, unbind); bindPreferencesWidgets(bind); bind(PreferenceTreeGenerator).toSelf().inSingletonScope(); + bind(PreferenceLayoutProvider).toSelf().inSingletonScope(); bindViewContribution(bind, PreferencesContribution); diff --git a/packages/preferences/src/browser/preference-tree-model.ts b/packages/preferences/src/browser/preference-tree-model.ts index 689e186641fba..c65e88166ea77 100644 --- a/packages/preferences/src/browser/preference-tree-model.ts +++ b/packages/preferences/src/browser/preference-tree-model.ts @@ -30,11 +30,12 @@ import { } from '@theia/core/lib/browser'; import { Emitter } from '@theia/core'; import { PreferencesSearchbarWidget } from './views/preference-searchbar-widget'; -import { PreferenceTreeGenerator, COMMONLY_USED_SECTION_PREFIX } from './util/preference-tree-generator'; +import { PreferenceTreeGenerator } from './util/preference-tree-generator'; import * as fuzzy from '@theia/core/shared/fuzzy'; import { PreferencesScopeTabBar } from './views/preference-scope-tabbar-widget'; import { Preference } from './util/preference-types'; import { Event } from '@theia/core/lib/common'; +import { COMMONLY_USED_SECTION_PREFIX } from './util/preference-layout'; export interface PreferenceTreeNodeProps extends NodeProps { visibleChildren: number; diff --git a/packages/preferences/src/browser/util/preference-layout.ts b/packages/preferences/src/browser/util/preference-layout.ts new file mode 100644 index 0000000000000..43ba032e0ca7f --- /dev/null +++ b/packages/preferences/src/browser/util/preference-layout.ts @@ -0,0 +1,377 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; + +export interface PreferenceLayout { + id: string; + label: string; + children?: PreferenceLayout[]; + settings?: string[]; +} + +export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used'; + +export const COMMONLY_USED_LAYOUT = { + id: COMMONLY_USED_SECTION_PREFIX, + label: nls.localizeByDefault('Commonly Used'), + settings: [ + 'files.autoSave', + 'editor.fontSize', + 'editor.fontFamily', + 'editor.tabSize', + 'editor.renderWhitespace', + 'editor.cursorStyle', + 'editor.multiCursorModifier', + 'editor.insertSpaces', + 'editor.wordWrap', + 'files.exclude', + 'files.associations' + ] +}; + +export const DEFAULT_LAYOUT: PreferenceLayout[] = [ + { + id: 'editor', + label: nls.localizeByDefault('Text Editor'), + settings: ['editor.*'], + children: [ + { + id: 'editor.cursor', + label: nls.localizeByDefault('Cursor'), + settings: ['editor.cursor*'] + }, + { + id: 'editor.find', + label: nls.localizeByDefault('Find'), + settings: ['editor.find.*'] + }, + { + id: 'editor.font', + label: nls.localizeByDefault('Font'), + settings: ['editor.font*'] + }, + { + id: 'editor.format', + label: nls.localizeByDefault('Formatting'), + settings: ['editor.format*'] + }, + { + id: 'editor.diffEditor', + label: nls.localizeByDefault('Diff Editor'), + settings: ['diffEditor.*'] + }, + { + id: 'editor.multiDiffEditor', + label: nls.localizeByDefault('Multi-File Diff Editor'), + settings: ['multiDiffEditor.*'] + }, + { + id: 'editor.minimap', + label: nls.localizeByDefault('Minimap'), + settings: ['editor.minimap.*'] + }, + { + id: 'editor.suggestions', + label: nls.localizeByDefault('Suggestions'), + settings: ['editor.*suggest*'] + }, + { + id: 'editor.files', + label: nls.localizeByDefault('Files'), + settings: ['files.*'] + } + ] + }, + { + id: 'workbench', + label: nls.localizeByDefault('Workbench'), + settings: ['workbench.*', 'workspace.*'], + children: [ + { + id: 'workbench.appearance', + label: nls.localizeByDefault('Appearance'), + settings: [ + 'workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', + 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*' + ] + }, + { + id: 'workbench.breadcrumbs', + label: nls.localizeByDefault('Breadcrumbs'), + settings: ['breadcrumbs.*'] + }, + { + id: 'workbench.editor', + label: nls.localizeByDefault('Editor Management'), + settings: ['workbench.editor.*'] + }, + { + id: 'workbench.settings', + label: nls.localizeByDefault('Settings Editor'), + settings: ['workbench.settings.*'] + }, + { + id: 'workbench.zenmode', + label: nls.localizeByDefault('Zen Mode'), + settings: ['zenmode.*'] + }, + { + id: 'workbench.screencastmode', + label: nls.localizeByDefault('Screencast Mode'), + settings: ['screencastMode.*'] + } + ] + }, + { + id: 'window', + label: nls.localizeByDefault('Window'), + settings: ['window.*'], + children: [ + { + id: 'window.newWindow', + label: nls.localizeByDefault('New Window'), + settings: ['window.*newwindow*'] + } + ] + }, + { + id: 'features', + label: nls.localizeByDefault('Features'), + children: [ + { + id: 'features.accessibilitySignals', + label: nls.localizeByDefault('Accessibility Signals'), + settings: ['accessibility.signal*'] + }, + { + id: 'features.accessibility', + label: nls.localizeByDefault('Accessibility'), + settings: ['accessibility.*'] + }, + { + id: 'features.explorer', + label: nls.localizeByDefault('Explorer'), + settings: ['explorer.*', 'outline.*'] + }, + { + id: 'features.search', + label: nls.localizeByDefault('Search'), + settings: ['search.*'] + }, + { + id: 'features.debug', + label: nls.localizeByDefault('Debug'), + settings: ['debug.*', 'launch'] + }, + { + id: 'features.testing', + label: nls.localizeByDefault('Testing'), + settings: ['testing.*'] + }, + { + id: 'features.scm', + label: nls.localizeByDefault('Source Control'), + settings: ['scm.*'] + }, + { + id: 'features.extensions', + label: nls.localizeByDefault('Extensions'), + settings: ['extensions.*'] + }, + { + id: 'features.terminal', + label: nls.localizeByDefault('Terminal'), + settings: ['terminal.*'] + }, + { + id: 'features.task', + label: nls.localizeByDefault('Task'), + settings: ['task.*'] + }, + { + id: 'features.problems', + label: nls.localizeByDefault('Problems'), + settings: ['problems.*'] + }, + { + id: 'features.output', + label: nls.localizeByDefault('Output'), + settings: ['output.*'] + }, + { + id: 'features.comments', + label: nls.localizeByDefault('Comments'), + settings: ['comments.*'] + }, + { + id: 'features.remote', + label: nls.localizeByDefault('Remote'), + settings: ['remote.*'] + }, + { + id: 'features.timeline', + label: nls.localizeByDefault('Timeline'), + settings: ['timeline.*'] + }, + { + id: 'features.toolbar', + label: nls.localize('theia/preferences/toolbar', 'Toolbar'), + settings: ['toolbar.*'] + }, + { + id: 'features.notebook', + label: nls.localizeByDefault('Notebook'), + settings: ['notebook.*', 'interactiveWindow.*'] + }, + { + id: 'features.mergeEditor', + label: nls.localizeByDefault('Merge Editor'), + settings: ['mergeEditor.*'] + }, + { + id: 'features.chat', + label: nls.localizeByDefault('Chat'), + settings: ['chat.*', 'inlineChat.*'] + } + ] + }, + { + id: 'application', + label: nls.localizeByDefault('Application'), + children: [ + { + id: 'application.http', + label: nls.localizeByDefault('HTTP'), + settings: ['http.*'] + }, + { + id: 'application.keyboard', + label: nls.localizeByDefault('Keyboard'), + settings: ['keyboard.*'] + }, + { + id: 'application.update', + label: nls.localizeByDefault('Update'), + settings: ['update.*'] + }, + { + id: 'application.telemetry', + label: nls.localizeByDefault('Telemetry'), + settings: ['telemetry.*'] + }, + { + id: 'application.settingsSync', + label: nls.localizeByDefault('Settings Sync'), + settings: ['settingsSync.*'] + }, + { + id: 'application.experimental', + label: nls.localizeByDefault('Experimental'), + settings: ['application.experimental.*'] + }, + { + id: 'application.other', + label: nls.localizeByDefault('Other'), + settings: ['application.*'] + } + ] + }, + { + id: 'security', + label: nls.localizeByDefault('Security'), + settings: ['security.*'], + children: [ + { + id: 'security.workspace', + label: nls.localizeByDefault('Workspace'), + settings: ['security.workspace.*'] + } + ] + }, + { + id: 'extensions', + label: nls.localizeByDefault('Extensions'), + children: [ + { + id: 'extensions.hosted-plugin', + label: nls.localize('theia/preferences/hostedPlugin', 'Hosted Plugin'), + settings: ['hosted-plugin.*'] + } + ] + } +]; + +@injectable() +export class PreferenceLayoutProvider { + + getLayout(): PreferenceLayout[] { + return DEFAULT_LAYOUT; + } + + getCommonlyUsedLayout(): PreferenceLayout { + return COMMONLY_USED_LAYOUT; + } + + hasCategory(id: string): boolean { + return [...this.getLayout(), this.getCommonlyUsedLayout()].some(e => e.id === id); + } + + getLayoutForPreference(preferenceId: string): PreferenceLayout | undefined { + const layout = this.getLayout(); + for (const section of layout) { + const item = this.findItemInSection(section, preferenceId); + if (item) { + return item; + } + } + return undefined; + } + + protected findItemInSection(section: PreferenceLayout, preferenceId: string): PreferenceLayout | undefined { + // First check whether any of its children match the preferenceId. + if (section.children) { + for (const child of section.children) { + const item = this.findItemInSection(child, preferenceId); + if (item) { + return item; + } + } + } + // Then check whether the section itself matches the preferenceId. + if (section.settings) { + for (const setting of section.settings) { + if (this.matchesSetting(preferenceId, setting)) { + return section; + } + } + } + return undefined; + } + + protected matchesSetting(preferenceId: string, setting: string): boolean { + if (setting.includes('*')) { + return this.createRegExp(setting).test(preferenceId); + } + return preferenceId === setting; + } + + protected createRegExp(setting: string): RegExp { + return new RegExp(`^${setting.replace(/\./g, '\\.').replace(/\*/g, '.*')}$`); + } + +} diff --git a/packages/preferences/src/browser/util/preference-tree-generator.ts b/packages/preferences/src/browser/util/preference-tree-generator.ts index 1c6e8b392767f..05648a634b5b2 100644 --- a/packages/preferences/src/browser/util/preference-tree-generator.ts +++ b/packages/preferences/src/browser/util/preference-tree-generator.ts @@ -20,58 +20,19 @@ import { PreferenceConfigurations } from '@theia/core/lib/browser/preferences/pr import { Emitter } from '@theia/core'; import debounce = require('@theia/core/shared/lodash.debounce'); import { Preference } from './preference-types'; +import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout'; -export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used'; @injectable() export class PreferenceTreeGenerator { @inject(PreferenceSchemaProvider) protected readonly schemaProvider: PreferenceSchemaProvider; @inject(PreferenceConfigurations) protected readonly preferenceConfigs: PreferenceConfigurations; + @inject(PreferenceLayoutProvider) protected readonly layoutProvider: PreferenceLayoutProvider; protected _root: CompositeTreeNode; protected readonly onSchemaChangedEmitter = new Emitter(); readonly onSchemaChanged = this.onSchemaChangedEmitter.event; - protected readonly commonlyUsedPreferences = [ - 'files.autoSave', 'files.autoSaveDelay', 'editor.fontSize', - 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', - 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', - 'editor.wordWrap', 'files.exclude', 'files.associations' - ]; - protected readonly topLevelCategories = new Map([ - [COMMONLY_USED_SECTION_PREFIX, 'Commonly Used'], - ['editor', 'Text Editor'], - ['workbench', 'Workbench'], - ['window', 'Window'], - ['features', 'Features'], - ['application', 'Application'], - ['security', 'Security'], - ['extensions', 'Extensions'] - ]); - protected readonly sectionAssignments = new Map([ - ['breadcrumbs', 'workbench'], - ['comments', 'features'], - ['debug', 'features'], - ['diffEditor', 'editor'], - ['explorer', 'features'], - ['extensions', 'features'], - ['files', 'editor'], - ['hosted-plugin', 'features'], - ['http', 'application'], - ['keyboard', 'application'], - ['notification', 'workbench'], - ['output', 'features'], - ['preview', 'features'], - ['problems', 'features'], - ['scm', 'features'], - ['search', 'features'], - ['task', 'features'], - ['terminal', 'features'], - ['testing', 'features'], - ['toolbar', 'features'], - ['webview', 'features'], - ['workspace', 'application'], - ]); protected readonly defaultTopLevelCategory = 'extensions'; get root(): CompositeTreeNode { @@ -95,11 +56,13 @@ export class PreferenceTreeGenerator { const groups = new Map(); const root = this.createRootNode(); - for (const id of this.topLevelCategories.keys()) { - this.getOrCreatePreferencesGroup(id, id, root, groups); + const commonlyUsedLayout = this.layoutProvider.getCommonlyUsedLayout(); + const commonlyUsed = this.getOrCreatePreferencesGroup(commonlyUsedLayout.id, commonlyUsedLayout.id, root, groups, commonlyUsedLayout.label); + + for (const layout of this.layoutProvider.getLayout()) { + this.getOrCreatePreferencesGroup(layout.id, layout.id, root, groups, layout.label); } - const commonlyUsed = this.getOrCreatePreferencesGroup(COMMONLY_USED_SECTION_PREFIX, COMMONLY_USED_SECTION_PREFIX, root, groups); - for (const preference of this.commonlyUsedPreferences) { + for (const preference of commonlyUsedLayout.settings ?? []) { if (preference in preferencesSchema.properties) { this.createLeafNode(preference, commonlyUsed, preferencesSchema.properties[preference]); } @@ -107,12 +70,15 @@ export class PreferenceTreeGenerator { for (const propertyName of propertyNames) { const property = preferencesSchema.properties[propertyName]; if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) { - const labels = propertyName.split('.'); - const groupID = this.getGroupName(labels); - const subgroupName = this.getSubgroupName(labels, groupID); + const layoutItem = this.layoutProvider.getLayoutForPreference(propertyName); + const labels = layoutItem ? layoutItem.id.split('.') : propertyName.split('.'); + // If a title is set, this property belongs to the 'extensions' category + const groupID = property.title ? this.defaultTopLevelCategory : this.getGroupName(labels); + // Automatically assign all properties with the same title to the same subgroup + const subgroupName = property.title ?? this.getSubgroupName(labels, groupID); const subgroupID = [groupID, subgroupName].join('.'); const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups); - const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups); + const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label); this.createLeafNode(propertyName, immediateParent || toplevelParent, property); } } @@ -145,21 +111,19 @@ export class PreferenceTreeGenerator { protected getGroupName(labels: string[]): string { const defaultGroup = labels[0]; - if (this.topLevelCategories.has(defaultGroup)) { + if (this.layoutProvider.hasCategory(defaultGroup)) { return defaultGroup; } - const assignedGroup = this.sectionAssignments.get(defaultGroup); - if (assignedGroup) { - return assignedGroup; - } return this.defaultTopLevelCategory; } protected getSubgroupName(labels: string[], computedGroupName: string): string | undefined { if (computedGroupName !== labels[0]) { return labels[0]; - } else if (labels.length > 2) { + } else if (labels.length > 1) { return labels[1]; + } else { + return undefined; } } @@ -182,7 +146,7 @@ export class PreferenceTreeGenerator { protected createLeafNode(property: string, preferencesGroup: Preference.CompositeTreeNode, data: PreferenceDataProperty): Preference.LeafNode { const { group } = Preference.TreeNode.getGroupAndIdFromNodeId(preferencesGroup.id); - const newNode = { + const newNode: Preference.LeafNode = { id: `${group}@${property}`, preferenceId: property, parent: preferencesGroup, @@ -193,8 +157,8 @@ export class PreferenceTreeGenerator { return newNode; } - protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode): Preference.CompositeTreeNode { - const newNode = { + protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode { + const newNode: Preference.CompositeTreeNode = { id: `${group}@${id}`, visible: true, parent: root, @@ -202,6 +166,7 @@ export class PreferenceTreeGenerator { expanded: false, selected: false, depth: 0, + label }; const isTopLevel = Preference.TreeNode.isTopLevel(newNode); if (!isTopLevel) { @@ -213,14 +178,12 @@ export class PreferenceTreeGenerator { return newNode; } - getCustomLabelFor(id: string): string | undefined { - return this.topLevelCategories.get(id); - } - - protected getOrCreatePreferencesGroup(id: string, group: string, root: CompositeTreeNode, groups: Map): Preference.CompositeTreeNode { + protected getOrCreatePreferencesGroup( + id: string, group: string, root: CompositeTreeNode, groups: Map, label?: string + ): Preference.CompositeTreeNode { const existingGroup = groups.get(id); if (existingGroup) { return existingGroup; } - const newNode = this.createPreferencesGroup(id, group, root); + const newNode = this.createPreferencesGroup(id, group, root, label); groups.set(id, newNode); return newNode; }; diff --git a/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts b/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts index 06b764580b449..d50b22baef6c3 100644 --- a/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts +++ b/packages/preferences/src/browser/util/preference-tree-label-provider.spec.ts @@ -28,6 +28,7 @@ import { PreferenceTreeGenerator } from './preference-tree-generator'; import { PreferenceTreeLabelProvider } from './preference-tree-label-provider'; import { Preference } from './preference-types'; import { SelectableTreeNode } from '@theia/core/lib/browser'; +import { PreferenceLayoutProvider } from './preference-layout'; disableJSDOM(); @@ -37,6 +38,7 @@ describe('preference-tree-label-provider', () => { beforeEach(() => { const container = new Container(); + container.bind(PreferenceLayoutProvider).toSelf().inSingletonScope(); container.bind(PreferenceTreeGenerator).toConstantValue({ getCustomLabelFor: () => { } }); preferenceTreeLabelProvider = container.resolve(PreferenceTreeLabelProvider); }); diff --git a/packages/preferences/src/browser/util/preference-tree-label-provider.ts b/packages/preferences/src/browser/util/preference-tree-label-provider.ts index 671bed7be7565..a40f1fbd272af 100644 --- a/packages/preferences/src/browser/util/preference-tree-label-provider.ts +++ b/packages/preferences/src/browser/util/preference-tree-label-provider.ts @@ -14,21 +14,35 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { LabelProviderContribution, TreeNode } from '@theia/core/lib/browser'; import { Preference } from './preference-types'; -import { PreferenceTreeGenerator } from './preference-tree-generator'; +import { PreferenceLayoutProvider } from './preference-layout'; + @injectable() export class PreferenceTreeLabelProvider implements LabelProviderContribution { - @inject(PreferenceTreeGenerator) protected readonly treeGenerator: PreferenceTreeGenerator; + + @inject(PreferenceLayoutProvider) + protected readonly layoutProvider: PreferenceLayoutProvider; canHandle(element: object): number { return TreeNode.is(element) && Preference.TreeNode.is(element) ? 150 : 0; } getName(node: Preference.TreeNode): string { + if (Preference.CompositeTreeNode.is(node) && node.label) { + return node.label; + } const { id } = Preference.TreeNode.getGroupAndIdFromNodeId(node.id); - return this.formatString(this.treeGenerator.getCustomLabelFor(id) ?? id.split('.').pop()!); + const layouts = this.layoutProvider.getLayout(); + const layout = layouts.find(e => e.id === id); + if (layout) { + return layout.label; + } else { + const labels = id.split('.'); + const groupName = labels[labels.length - 1]; + return this.formatString(groupName); + } } getPrefix(node: Preference.TreeNode, fullPath = false): string | undefined { diff --git a/packages/preferences/src/browser/util/preference-types.ts b/packages/preferences/src/browser/util/preference-types.ts index c901c87a3ae85..29ee2bf3f67c9 100644 --- a/packages/preferences/src/browser/util/preference-types.ts +++ b/packages/preferences/src/browser/util/preference-types.ts @@ -18,7 +18,8 @@ import { PreferenceDataProperty, PreferenceScope, TreeNode as BaseTreeNode, - CompositeTreeNode as BaseCompositeTreeNode, + ExpandableTreeNode, + SelectableTreeNode, PreferenceInspection, CommonCommands, } from '@theia/core/lib/browser'; @@ -58,8 +59,13 @@ export namespace Preference { }; } - export interface CompositeTreeNode extends BaseCompositeTreeNode { + export interface CompositeTreeNode extends ExpandableTreeNode, SelectableTreeNode { depth: number; + label?: string; + } + + export namespace CompositeTreeNode { + export const is = (node: TreeNode): node is CompositeTreeNode => !LeafNode.is(node); } export interface LeafNode extends BaseTreeNode { diff --git a/packages/preferences/src/browser/views/preference-editor-widget.ts b/packages/preferences/src/browser/views/preference-editor-widget.ts index 4d2e06ea70568..d8405759ad312 100644 --- a/packages/preferences/src/browser/views/preference-editor-widget.ts +++ b/packages/preferences/src/browser/views/preference-editor-widget.ts @@ -33,9 +33,9 @@ import { BaseWidget, DEFAULT_SCROLL_OPTIONS } from '@theia/core/lib/browser/widg import { PreferenceTreeModel, PreferenceFilterChangeEvent, PreferenceFilterChangeSource } from '../preference-tree-model'; import { PreferenceNodeRendererFactory, GeneralPreferenceNodeRenderer } from './components/preference-node-renderer'; import { Preference } from '../util/preference-types'; -import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-tree-generator'; import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget'; import { PreferenceNodeRendererCreatorRegistry } from './components/preference-node-renderer-creator'; +import { COMMONLY_USED_SECTION_PREFIX } from '../util/preference-layout'; export interface PreferencesEditorState { firstVisibleChildID: string, From 69b8877956f9c061ed01d127bf34f0b7d51f9085 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Wed, 3 Jul 2024 17:20:14 +0200 Subject: [PATCH 299/441] Use relative paths for ctx.importScripts() (#13854) Before we tried to load absolute paths e.g. `/context/`. This is a problem, when working in deployed use cases, where Theia might be hosted under a path (e.g. try.theia-cloud.io). Because then the loaded path will be from the root of the page and not, like wanted, from the current path. Simply changing it to relative paths (e.g. `./context/`) solves this issue. Fixes #13813 --- packages/plugin-ext/src/hosted/browser/worker/worker-main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts index 56045b5f839fe..3739987040f1a 100644 --- a/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts +++ b/packages/plugin-ext/src/hosted/browser/worker/worker-main.ts @@ -45,7 +45,7 @@ const pluginsModulesNames = new Map(); const scripts = new Set(); function initialize(contextPath: string, pluginMetadata: PluginMetadata): void { - const path = '/context/' + contextPath; + const path = './context/' + contextPath; if (!scripts.has(path)) { ctx.importScripts(path); @@ -70,7 +70,7 @@ pluginManager.setPluginHost({ ctx.frontendModuleName = plugin.lifecycle.frontendModuleName; } - ctx.importScripts('/hostedPlugin/' + getPluginId(plugin.model) + '/' + plugin.pluginPath); + ctx.importScripts('./hostedPlugin/' + getPluginId(plugin.model) + '/' + plugin.pluginPath); } } From 77c66e4b85a0fa38d5590dca4b9dc10e81595952 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 8 Jul 2024 13:01:39 +0200 Subject: [PATCH 300/441] Add breaking change entry for `OpenWithService` (#13893) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b318828784106..aa002cee07bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,7 +100,8 @@ [Breaking Changes:](#breaking_changes_1.50.0) - [core] Classes implementing the `Saveable` interface no longer need to implement the `autoSave` field. However, a new `onContentChanged` event has been added instead. - +- [navigator] The `Open With...` command now uses a dedicated `OpenWithHandler` to populate the quick pick. + Adopters contributing an open handler need to explicitly add the handler to the `OpenWithHandler` ([#13573](https://github.com/eclipse-theia/theia/pull/13573)). ## v1.49.0 - 04/29/2024 From 10b156c45156390228bf341381a4c675095fb812 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 9 Jul 2024 08:39:13 +0200 Subject: [PATCH 301/441] fixed reload for remote feature and added option to the electron handler to add change url (#13891) Signed-off-by: Jonah Iden --- packages/core/src/electron-browser/preload.ts | 2 +- .../electron-browser/window/electron-window-service.ts | 2 +- packages/core/src/electron-common/electron-api.ts | 2 +- .../core/src/electron-main/theia-electron-window.ts | 10 +++++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index 78d0d29a93f3c..43eabd8271cca 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -199,7 +199,7 @@ const api: TheiaCoreAPI = { ipcRenderer.send(CHANNEL_TOGGLE_FULL_SCREEN); }, - requestReload: () => ipcRenderer.send(CHANNEL_REQUEST_RELOAD), + requestReload: (newUrl?: string) => ipcRenderer.send(CHANNEL_REQUEST_RELOAD, newUrl), restart: () => ipcRenderer.send(CHANNEL_RESTART), applicationStateChanged: state => { diff --git a/packages/core/src/electron-browser/window/electron-window-service.ts b/packages/core/src/electron-browser/window/electron-window-service.ts index 518dd1127b2e1..699b8373b4db7 100644 --- a/packages/core/src/electron-browser/window/electron-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-window-service.ts @@ -100,7 +100,7 @@ export class ElectronWindowService extends DefaultWindowService { if (params.hash) { newLocation.hash = '#' + params.hash; } - location.assign(newLocation); + window.electronTheiaCore.requestReload(newLocation.toString()); } else { window.electronTheiaCore.requestReload(); } diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 833b44d6070e2..4edab9cb6f7b0 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -84,7 +84,7 @@ export interface TheiaCoreAPI { isFullScreen(): boolean; // TODO: this should really be async, since it blocks the renderer process toggleFullScreen(): void; - requestReload(): void; + requestReload(newUrl?: string): void; restart(): void; applicationStateChanged(state: FrontendApplicationState): void; diff --git a/packages/core/src/electron-main/theia-electron-window.ts b/packages/core/src/electron-main/theia-electron-window.ts index 985fc01220fbc..29569d4970593 100644 --- a/packages/core/src/electron-main/theia-electron-window.ts +++ b/packages/core/src/electron-main/theia-electron-window.ts @@ -147,10 +147,14 @@ export class TheiaElectronWindow { return this.handleStopRequest(() => this.doCloseWindow(), reason); } - protected reload(): void { + protected reload(newUrl?: string): void { this.handleStopRequest(async () => { this.applicationState = 'init'; - this._window.reload(); + if (newUrl) { + this._window.loadURL(newUrl); + } else { + this._window.reload(); + } }, StopReason.Reload); } @@ -195,7 +199,7 @@ export class TheiaElectronWindow { } protected attachReloadListener(): void { - this.toDispose.push(TheiaRendererAPI.onRequestReload(this.window.webContents, () => this.reload())); + this.toDispose.push(TheiaRendererAPI.onRequestReload(this.window.webContents, (newUrl?: string) => this.reload(newUrl))); } dispose(): void { From 48cb2b8262a916681707216706b0dbaf5d373849 Mon Sep 17 00:00:00 2001 From: Hanksha Date: Wed, 10 Jul 2024 09:07:22 +0200 Subject: [PATCH 302/441] Fix FileResource not adding event listener to the disposable collection (#13880) Signed-off-by: Vivien Jovet Co-authored-by: Vivien Jovet --- packages/filesystem/src/browser/file-resource.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/filesystem/src/browser/file-resource.ts b/packages/filesystem/src/browser/file-resource.ts index 5f6cd9c31a679..9cf675ce1cf64 100644 --- a/packages/filesystem/src/browser/file-resource.ts +++ b/packages/filesystem/src/browser/file-resource.ts @@ -97,11 +97,11 @@ export class FileResource implements Resource { this.updateReadOnly(); } })); - this.fileService.onDidChangeFileSystemProviderReadOnlyMessage(async e => { + this.toDispose.push(this.fileService.onDidChangeFileSystemProviderReadOnlyMessage(async e => { if (e.scheme === this.uri.scheme) { this.updateReadOnly(); } - }); + })); } protected async updateReadOnly(): Promise { From a59e0731650fa0553f786bb41a19ae4351a8f871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 10 Jul 2024 14:21:11 +0200 Subject: [PATCH 303/441] Update Mac OS version to 14 in CI (#13908) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder --- .github/workflows/ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 2bdf6bf21774a..06c97f26cbaa0 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2019, ubuntu-latest, macos-11] + os: [windows-2019, ubuntu-latest, macos-14] node: [16.x, 18.x, 20.x] runs-on: ${{ matrix.os }} From 62476a0f1e6ec8ddbfd95df87c925e3e028db33a Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 10 Jul 2024 17:04:05 +0200 Subject: [PATCH 304/441] Stub proposed APIs used by Python debugger (#13894) --- .../plugin-ext/src/plugin/plugin-context.ts | 28 ++- packages/plugin-ext/src/plugin/types-impl.ts | 35 +++- packages/plugin/src/theia.d.ts | 8 +- .../theia.proposed.debugVisualization.d.ts | 189 ++++++++++++++++++ .../src/theia.proposed.portsAttributes.d.ts | 115 +++++++++++ 5 files changed, 363 insertions(+), 12 deletions(-) create mode 100644 packages/plugin/src/theia.proposed.debugVisualization.d.ts create mode 100644 packages/plugin/src/theia.proposed.portsAttributes.d.ts diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 2dc28f72b501b..4367992efd925 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -225,7 +225,10 @@ import { ChatResultFeedbackKind, LanguageModelChatMessage, LanguageModelChatMessageRole, - LanguageModelError + LanguageModelError, + PortAutoForwardAction, + PortAttributes, + DebugVisualization } from './types-impl'; import { AuthenticationExtImpl } from './authentication-ext'; import { SymbolKind } from '../common/plugin-api-rpc-model'; @@ -814,7 +817,13 @@ export function createAPIFactory( }, getCanonicalUri(uri: theia.Uri, options: theia.CanonicalUriRequestOptions, token: CancellationToken): theia.ProviderResult { return workspaceExt.getCanonicalUri(uri, options, token); - } + }, + /** + * @stubbed + * This is a stub implementation, that should minimally satisfy vscode extensions + * that currently use this proposed API. + */ + registerPortAttributesProvider: () => Disposable.NULL }; const onDidChangeLogLevel = new Emitter(); @@ -1134,7 +1143,11 @@ export function createAPIFactory( }, asDebugSourceUri(source: theia.DebugProtocolSource, session?: theia.DebugSession): theia.Uri { return debugExt.asDebugSourceUri(source, session); - } + }, + /** @stubbed Due to proposed API */ + registerDebugVisualizationProvider: () => Disposable.NULL, + /** @stubbed Due to proposed API */ + registerDebugVisualizationTreeProvider: () => Disposable.NULL }; const tasks: typeof theia.tasks = { @@ -1241,7 +1254,7 @@ export function createAPIFactory( } }; - const chat: typeof theia.chat = { + const chat: typeof theia.chat = { /** @stubbed MappedEditsProvider */ registerMappedEditsProvider(documentSelector: theia.DocumentSelector, provider: theia.MappedEditsProvider): Disposable { return Disposable.NULL; @@ -1251,7 +1264,7 @@ export function createAPIFactory( return { id, requestHandler: handler, - dispose() {}, + dispose() { }, onDidReceiveFeedback: (listener, thisArgs?, disposables?) => Event.None(listener, thisArgs, disposables) }; } @@ -1481,7 +1494,10 @@ export function createAPIFactory( ChatResultFeedbackKind, LanguageModelChatMessage, LanguageModelChatMessageRole, - LanguageModelError + LanguageModelError, + PortAutoForwardAction, + PortAttributes, + DebugVisualization }; }; } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index b0c4f0a8cd1a2..3b9a28057432c 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3036,7 +3036,7 @@ export class DebugThread { } export class DebugStackFrame { - private constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { } + private constructor(readonly session: theia.DebugSession, readonly threadId: number, readonly frameId: number) { } } @es5ClassCompat @@ -3363,7 +3363,7 @@ export class TestMessage implements theia.TestMessage { @es5ClassCompat export class TestCoverageCount { - constructor( public covered: number, public total: number) { } + constructor(public covered: number, public total: number) { } } @es5ClassCompat @@ -3922,7 +3922,7 @@ export class ChatResponseFileTreePart { } export type ChatResponsePart = ChatResponseMarkdownPart | ChatResponseFileTreePart | ChatResponseAnchorPart -| ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; + | ChatResponseProgressPart | ChatResponseReferencePart | ChatResponseCommandButtonPart; export enum ChatResultFeedbackKind { Unhelpful = 0, @@ -3970,3 +3970,32 @@ export class LanguageModelError extends Error { } } // #endregion + +// #region Port Attributes + +export enum PortAutoForwardAction { + Notify = 1, + OpenBrowser = 2, + OpenPreview = 3, + Silent = 4, + Ignore = 5 +} + +export class PortAttributes { + constructor(public autoForwardAction: PortAutoForwardAction) { + } +} + +// #endregion + +// #region Debug Visualization + +export class DebugVisualization { + iconPath?: URI | { light: URI; dark: URI } | ThemeIcon; + visualization?: theia.Command | { treeId: string }; + + constructor(public name: string) { + } +} + +// #endregion diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 98a0f69b3ca0f..a712b59b05b16 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -24,18 +24,20 @@ import './theia-extra'; import './theia.proposed.canonicalUriProvider'; import './theia.proposed.customEditorMove'; +import './theia.proposed.debugVisualization'; import './theia.proposed.diffCommand'; import './theia.proposed.documentPaste'; import './theia.proposed.editSessionIdentityProvider'; import './theia.proposed.extensionsAny'; import './theia.proposed.externalUriOpener'; +import './theia.proposed.findTextInFiles'; +import './theia.proposed.fsChunks'; import './theia.proposed.mappedEditsProvider'; +import './theia.proposed.multiDocumentHighlightProvider'; import './theia.proposed.notebookCellExecutionState'; import './theia.proposed.notebookKernelSource'; import './theia.proposed.notebookMessaging'; -import './theia.proposed.findTextInFiles'; -import './theia.proposed.fsChunks'; -import './theia.proposed.multiDocumentHighlightProvider'; +import './theia.proposed.portsAttributes'; import './theia.proposed.profileContentHandlers'; import './theia.proposed.resolvers'; import './theia.proposed.scmValidation'; diff --git a/packages/plugin/src/theia.proposed.debugVisualization.d.ts b/packages/plugin/src/theia.proposed.debugVisualization.d.ts new file mode 100644 index 0000000000000..181fda82930bf --- /dev/null +++ b/packages/plugin/src/theia.proposed.debugVisualization.d.ts @@ -0,0 +1,189 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +declare module '@theia/plugin' { + export namespace debug { + /** + * Registers a custom data visualization for variables when debugging. + * + * @param id The corresponding ID in the package.json `debugVisualizers` contribution point. + * @param provider The {@link DebugVisualizationProvider} to register + * @stubbed + */ + export function registerDebugVisualizationProvider( + id: string, + provider: DebugVisualizationProvider + ): Disposable; + + /** + * Registers a tree that can be referenced by {@link DebugVisualization.visualization}. + * @param id + * @param provider + * @stubbed + */ + export function registerDebugVisualizationTreeProvider( + id: string, + provider: DebugVisualizationTree + ): Disposable; + } + + /** + * An item from the {@link DebugVisualizationTree} + */ + export interface DebugTreeItem { + /** + * A human-readable string describing this item. + */ + label: string; + + /** + * A human-readable string which is rendered less prominent. + */ + description?: string; + + /** + * {@link TreeItemCollapsibleState} of the tree item. + */ + collapsibleState?: TreeItemCollapsibleState; + + /** + * Context value of the tree item. This can be used to contribute item specific actions in the tree. + * For example, a tree item is given a context value as `folder`. When contributing actions to `view/item/context` + * using `menus` extension point, you can specify context value for key `viewItem` in `when` expression like `viewItem == folder`. + * ```json + * "contributes": { + * "menus": { + * "view/item/context": [ + * { + * "command": "extension.deleteFolder", + * "when": "viewItem == folder" + * } + * ] + * } + * } + * ``` + * This will show action `extension.deleteFolder` only for items with `contextValue` is `folder`. + */ + contextValue?: string; + + /** + * Whether this item can be edited by the user. + */ + canEdit?: boolean; + } + + /** + * Provides a tree that can be referenced in debug visualizations. + */ + export interface DebugVisualizationTree { + /** + * Gets the tree item for an element or the base context item. + */ + getTreeItem(context: DebugVisualizationContext): ProviderResult; + /** + * Gets children for the tree item or the best context item. + */ + getChildren(element: T): ProviderResult; + /** + * Handles the user editing an item. + */ + editItem?(item: T, value: string): ProviderResult; + } + + export class DebugVisualization { + /** + * The name of the visualization to show to the user. + */ + name: string; + + /** + * An icon for the view when it's show in inline actions. + */ + iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * Visualization to use for the variable. This may be either: + * - A command to run when the visualization is selected for a variable. + * - A reference to a previously-registered {@link DebugVisualizationTree} + */ + visualization?: Command | { treeId: string }; + + /** + * Creates a new debug visualization object. + * @param name Name of the visualization to show to the user. + */ + constructor(name: string); + } + + export interface DebugVisualizationProvider { + /** + * Called for each variable when the debug session stops. It should return + * any visualizations the extension wishes to show to the user. + * + * Note that this is only called when its `when` clause defined under the + * `debugVisualizers` contribution point in the `package.json` evaluates + * to true. + */ + provideDebugVisualization(context: DebugVisualizationContext, token: CancellationToken): ProviderResult; + + /** + * Invoked for a variable when a user picks the visualizer. + * + * It may return a {@link TreeView} that's shown in the Debug Console or + * inline in a hover. A visualizer may choose to return `undefined` from + * this function and instead trigger other actions in the UI, such as opening + * a custom {@link WebviewView}. + */ + resolveDebugVisualization?(visualization: T, token: CancellationToken): ProviderResult; + } + + export interface DebugVisualizationContext { + /** + * The Debug Adapter Protocol Variable to be visualized. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable + */ + variable: any; + /** + * The Debug Adapter Protocol variable reference the type (such as a scope + * or another variable) that contained this one. Empty for variables + * that came from user evaluations in the Debug Console. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable + */ + containerId?: number; + /** + * The ID of the Debug Adapter Protocol StackFrame in which the variable was found, + * for variables that came from scopes in a stack frame. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame + */ + frameId?: number; + /** + * The ID of the Debug Adapter Protocol Thread in which the variable was found. + * @see https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame + */ + threadId: number; + /** + * The debug session the variable belongs to. + */ + session: DebugSession; + } +} diff --git a/packages/plugin/src/theia.proposed.portsAttributes.d.ts b/packages/plugin/src/theia.proposed.portsAttributes.d.ts new file mode 100644 index 0000000000000..b67d15c900342 --- /dev/null +++ b/packages/plugin/src/theia.proposed.portsAttributes.d.ts @@ -0,0 +1,115 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module '@theia/plugin' { + + /** + * The action that should be taken when a port is discovered through automatic port forwarding discovery. + */ + export enum PortAutoForwardAction { + /** + * Notify the user that the port is being forwarded. This is the default action. + */ + Notify = 1, + /** + * Once the port is forwarded, open the user's web browser to the forwarded port. + */ + OpenBrowser = 2, + /** + * Once the port is forwarded, open the preview browser to the forwarded port. + */ + OpenPreview = 3, + /** + * Forward the port silently. + */ + Silent = 4, + /** + * Do not forward the port. + */ + Ignore = 5 + } + + /** + * The attributes that a forwarded port can have. + */ + export class PortAttributes { + /** + * The action to be taken when this port is detected for auto forwarding. + */ + autoForwardAction: PortAutoForwardAction; + + /** + * Creates a new PortAttributes object + * @param port the port number + * @param autoForwardAction the action to take when this port is detected + */ + constructor(autoForwardAction: PortAutoForwardAction); + } + + /** + * A provider of port attributes. Port attributes are used to determine what action should be taken when a port is discovered. + */ + export interface PortAttributesProvider { + /** + * Provides attributes for the given port. For ports that your extension doesn't know about, simply + * return undefined. For example, if `providePortAttributes` is called with ports 3000 but your + * extension doesn't know anything about 3000 you should return undefined. + * @param port The port number of the port that attributes are being requested for. + * @param pid The pid of the process that is listening on the port. If the pid is unknown, undefined will be passed. + * @param commandLine The command line of the process that is listening on the port. If the command line is unknown, undefined will be passed. + * @param token A cancellation token that indicates the result is no longer needed. + */ + providePortAttributes(attributes: { port: number; pid?: number; commandLine?: string }, token: CancellationToken): ProviderResult; + } + + /** + * A selector that will be used to filter which {@link PortAttributesProvider} should be called for each port. + */ + export interface PortAttributesSelector { + /** + * Specifying a port range will cause your provider to only be called for ports within the range. + * The start is inclusive and the end is exclusive. + */ + portRange?: [number, number] | number; + + /** + * Specifying a command pattern will cause your provider to only be called for processes whose command line matches the pattern. + */ + commandPattern?: RegExp; + } + + export namespace workspace { + /** + * If your extension listens on ports, consider registering a PortAttributesProvider to provide information + * about the ports. For example, a debug extension may know about debug ports in it's debuggee. By providing + * this information with a PortAttributesProvider the extension can tell the editor that these ports should be + * ignored, since they don't need to be user facing. + * + * The results of the PortAttributesProvider are merged with the user setting `remote.portsAttributes`. If the values conflict, the user setting takes precedence. + * + * @param portSelector It is best practice to specify a port selector to avoid unnecessary calls to your provider. + * If you don't specify a port selector your provider will be called for every port, which will result in slower port forwarding for the user. + * @param provider The {@link PortAttributesProvider PortAttributesProvider}. + * @stubbed + */ + export function registerPortAttributesProvider(portSelector: PortAttributesSelector, provider: PortAttributesProvider): Disposable; + } +} From f12a854ea73a2bd140c81816a41aee18f819941e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 11 Jul 2024 11:34:24 +0200 Subject: [PATCH 305/441] Fix plugin version comparison (#13907) --- dev-packages/ovsx-client/src/ovsx-api-filter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev-packages/ovsx-client/src/ovsx-api-filter.ts b/dev-packages/ovsx-client/src/ovsx-api-filter.ts index 7b5e0bed3190a..33b1f197e2b1c 100644 --- a/dev-packages/ovsx-client/src/ovsx-api-filter.ts +++ b/dev-packages/ovsx-client/src/ovsx-api-filter.ts @@ -90,7 +90,7 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { if (extensions.length === 0) { return; } else if (this.isBuiltinNamespace(extensions[0].namespace.toLowerCase())) { - return extensions.find(extension => this.versionGreaterThanOrEqualTo(extension.version, this.supportedApiVersion)); + return extensions.find(extension => this.versionGreaterThanOrEqualTo(this.supportedApiVersion, extension.version)); } else { return extensions.find(extension => this.supportedVscodeApiSatisfies(extension.engines?.vscode ?? '*')); } @@ -108,7 +108,7 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { } } if (this.isBuiltinNamespace(searchEntry.namespace)) { - return getLatestCompatibleVersion(allVersions => this.versionGreaterThanOrEqualTo(allVersions.version, this.supportedApiVersion)); + return getLatestCompatibleVersion(allVersions => this.versionGreaterThanOrEqualTo(this.supportedApiVersion, allVersions.version)); } else { return getLatestCompatibleVersion(allVersions => this.supportedVscodeApiSatisfies(allVersions.engines?.vscode ?? '*')); } @@ -127,7 +127,7 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { if (!versionA || !versionB) { return false; } - return semver.lte(versionA, versionB); + return semver.gte(versionA, versionB); } protected supportedVscodeApiSatisfies(vscodeApiRange: string): boolean { From 54c79272c1b1876823acaa13f0ee6a48ec501e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Fri, 12 Jul 2024 09:08:28 +0200 Subject: [PATCH 306/441] Add logging to `download:plugins` script (#13905) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempted mitigation for #13902 - do not eat exceptions and properly log errors - reduces request rate to 3/sec. Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .github/workflows/ci-cd.yml | 2 +- dev-packages/cli/src/download-plugins.ts | 1 + .../ovsx-client/src/ovsx-api-filter.ts | 5 +- .../ovsx-client/src/ovsx-http-client.ts | 24 +----- dev-packages/ovsx-client/src/ovsx-types.ts | 1 - .../request/src/node-request-service.ts | 14 +++- .../src/browser/vsx-extensions-model.ts | 76 +++++++++++-------- .../vsx-language-quick-pick-service.ts | 70 ++++++++--------- 8 files changed, 99 insertions(+), 94 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 06c97f26cbaa0..e563ca511eb20 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -86,7 +86,7 @@ jobs: if: runner.os == 'Linux' shell: bash run: | - yarn -s download:plugins + yarn -s download:plugins --rate-limit 3 - name: Build shell: bash diff --git a/dev-packages/cli/src/download-plugins.ts b/dev-packages/cli/src/download-plugins.ts index 7aa0abdc5d640..dee69530e72c8 100644 --- a/dev-packages/cli/src/download-plugins.ts +++ b/dev-packages/cli/src/download-plugins.ts @@ -143,6 +143,7 @@ export default async function downloadPlugins(ovsxClient: OVSXClient, requestSer failures.push(`No download url for extension pack ${id} (${version})`); } } catch (err) { + console.error(err); failures.push(err.message); } })); diff --git a/dev-packages/ovsx-client/src/ovsx-api-filter.ts b/dev-packages/ovsx-client/src/ovsx-api-filter.ts index 33b1f197e2b1c..a470a4ee3f989 100644 --- a/dev-packages/ovsx-client/src/ovsx-api-filter.ts +++ b/dev-packages/ovsx-client/src/ovsx-api-filter.ts @@ -69,9 +69,10 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { let offset = 0; let loop = true; while (loop) { - const queryOptions = { + const queryOptions: VSXQueryOptions = { ...query, - offset + offset, + size: 5 // there is a great chance that the newest version will work }; const results = await this.client.query(queryOptions); const compatibleExtension = this.getLatestCompatibleExtension(results.extensions); diff --git a/dev-packages/ovsx-client/src/ovsx-http-client.ts b/dev-packages/ovsx-client/src/ovsx-http-client.ts index f59f9615d7ff6..a810680711f30 100644 --- a/dev-packages/ovsx-client/src/ovsx-http-client.ts +++ b/dev-packages/ovsx-client/src/ovsx-http-client.ts @@ -34,28 +34,12 @@ export class OVSXHttpClient implements OVSXClient { protected requestService: RequestService ) { } - async search(searchOptions?: VSXSearchOptions): Promise { - try { - return await this.requestJson(this.buildUrl('api/-/search', searchOptions)); - } catch (err) { - return { - error: err?.message || String(err), - offset: -1, - extensions: [] - }; - } + search(searchOptions?: VSXSearchOptions): Promise { + return this.requestJson(this.buildUrl('api/-/search', searchOptions)); } - async query(queryOptions?: VSXQueryOptions): Promise { - try { - return await this.requestJson(this.buildUrl('api/v2/-/query', queryOptions)); - } catch (error) { - return { - offset: 0, - totalSize: 0, - extensions: [] - }; - } + query(queryOptions?: VSXQueryOptions): Promise { + return this.requestJson(this.buildUrl('api/v2/-/query', queryOptions)); } protected async requestJson(url: string): Promise { diff --git a/dev-packages/ovsx-client/src/ovsx-types.ts b/dev-packages/ovsx-client/src/ovsx-types.ts index 223e3ef5bea6f..ec747ebf91e8e 100644 --- a/dev-packages/ovsx-client/src/ovsx-types.ts +++ b/dev-packages/ovsx-client/src/ovsx-types.ts @@ -105,7 +105,6 @@ export interface VSXSearchOptions { * Should be aligned with https://github.com/eclipse/openvsx/blob/e8f64fe145fc05d2de1469735d50a7a90e400bc4/server/src/main/java/org/eclipse/openvsx/json/SearchResultJson.java */ export interface VSXSearchResult { - error?: string; offset: number; extensions: VSXSearchEntry[]; } diff --git a/dev-packages/request/src/node-request-service.ts b/dev-packages/request/src/node-request-service.ts index 4126647f6451a..e31a022bc1ef6 100644 --- a/dev-packages/request/src/node-request-service.ts +++ b/dev-packages/request/src/node-request-service.ts @@ -136,11 +136,19 @@ export class NodeRequestService implements RequestService { }); }); - stream.on('error', reject); + stream.on('error', err => { + reject(err); + }); } }); - req.on('error', reject); + req.on('error', err => { + reject(err); + }); + + req.on('timeout', () => { + reject('timeout'); + }); if (options.timeout) { req.setTimeout(options.timeout); @@ -153,7 +161,7 @@ export class NodeRequestService implements RequestService { req.end(); token?.onCancellationRequested(() => { - req.abort(); + req.destroy(); reject(); }); }); diff --git a/packages/vsx-registry/src/browser/vsx-extensions-model.ts b/packages/vsx-registry/src/browser/vsx-extensions-model.ts index 12649d8a5092e..b050cf0d54d76 100644 --- a/packages/vsx-registry/src/browser/vsx-extensions-model.ts +++ b/packages/vsx-registry/src/browser/vsx-extensions-model.ts @@ -225,49 +225,59 @@ export class VSXExtensionsModel { } const client = await this.clientProvider(); const filter = await this.vsxApiFilter(); - const result = await client.search(param); - this._searchError = result.error; - if (token.isCancellationRequested) { - return; - } - for (const data of result.extensions) { - const id = data.namespace.toLowerCase() + '.' + data.name.toLowerCase(); - const allVersions = filter.getLatestCompatibleVersion(data); - if (!allVersions) { - continue; + try { + const result = await client.search(param); + + if (token.isCancellationRequested) { + return; } - if (this.preferences.get('extensions.onlyShowVerifiedExtensions')) { - this.fetchVerifiedStatus(id, client, allVersions).then(verified => { - this.doChange(() => { - this.addExtensions(data, id, allVersions, !!verified); - return Promise.resolve(); + for (const data of result.extensions) { + const id = data.namespace.toLowerCase() + '.' + data.name.toLowerCase(); + const allVersions = filter.getLatestCompatibleVersion(data); + if (!allVersions) { + continue; + } + if (this.preferences.get('extensions.onlyShowVerifiedExtensions')) { + this.fetchVerifiedStatus(id, client, allVersions).then(verified => { + this.doChange(() => { + this.addExtensions(data, id, allVersions, !!verified); + return Promise.resolve(); + }); }); - }); - } else { - this.addExtensions(data, id, allVersions); - this.fetchVerifiedStatus(id, client, allVersions).then(verified => { - this.doChange(() => { - let extension = this.getExtension(id); - extension = this.setExtension(id); - extension.update(Object.assign({ - verified: verified - })); - return Promise.resolve(); + } else { + this.addExtensions(data, id, allVersions); + this.fetchVerifiedStatus(id, client, allVersions).then(verified => { + this.doChange(() => { + let extension = this.getExtension(id); + extension = this.setExtension(id); + extension.update(Object.assign({ + verified: verified + })); + return Promise.resolve(); + }); }); - }); + } } + } catch (error) { + this._searchError = error?.message || String(error); } + }, token); } protected async fetchVerifiedStatus(id: string, client: OVSXClient, allVersions: VSXAllVersions): Promise { - const res = await client.query({ extensionId: id, extensionVersion: allVersions.version, includeAllVersions: true }); - const extension = res.extensions?.[0]; - let verified = extension?.verified; - if (!verified && extension?.publishedBy.loginName === 'open-vsx') { - verified = true; + try { + const res = await client.query({ extensionId: id, extensionVersion: allVersions.version, includeAllVersions: true }); + const extension = res.extensions?.[0]; + let verified = extension?.verified; + if (!verified && extension?.publishedBy.loginName === 'open-vsx') { + verified = true; + } + return verified; + } catch (error) { + console.error(error); + return false; } - return verified; } protected addExtensions(data: VSXSearchEntry, id: string, allVersions: VSXAllVersions, verified?: boolean): void { diff --git a/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts b/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts index 6fc219c9247a2..717a35d1fe048 100644 --- a/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts +++ b/packages/vsx-registry/src/browser/vsx-language-quick-pick-service.ts @@ -42,47 +42,49 @@ export class VSXLanguageQuickPickService extends LanguageQuickPickService { protected override async getAvailableLanguages(): Promise { const client = await this.clientProvider(); - const searchResult = await client.search({ - category: 'Language Packs', - sortBy: 'downloadCount', - sortOrder: 'desc', - size: 20 - }); - if (searchResult.error) { - throw new Error('Error while loading available languages: ' + searchResult.error); - } + try { + const searchResult = await client.search({ + category: 'Language Packs', + sortBy: 'downloadCount', + sortOrder: 'desc', + size: 20 + }); - const extensionLanguages = await Promise.all( - searchResult.extensions.map(async extension => ({ - extension, - languages: await this.loadExtensionLanguages(extension) - })) - ); + const extensionLanguages = await Promise.all( + searchResult.extensions.map(async extension => ({ + extension, + languages: await this.loadExtensionLanguages(extension) + })) + ); - const languages = new Map(); + const languages = new Map(); - for (const extension of extensionLanguages) { - for (const localizationContribution of extension.languages) { - if (!languages.has(localizationContribution.languageId)) { - languages.set(localizationContribution.languageId, { - ...this.createLanguageQuickPickItem(localizationContribution), - execute: async () => { - const progress = await this.messageService.showProgress({ - text: nls.localizeByDefault('Installing {0} language support...', - localizationContribution.localizedLanguageName ?? localizationContribution.languageName ?? localizationContribution.languageId), - }); - try { - const extensionUri = VSCodeExtensionUri.fromId(`${extension.extension.namespace}.${extension.extension.name}`).toString(); - await this.pluginServer.deploy(extensionUri); - } finally { - progress.cancel(); + for (const extension of extensionLanguages) { + for (const localizationContribution of extension.languages) { + if (!languages.has(localizationContribution.languageId)) { + languages.set(localizationContribution.languageId, { + ...this.createLanguageQuickPickItem(localizationContribution), + execute: async () => { + const progress = await this.messageService.showProgress({ + text: nls.localizeByDefault('Installing {0} language support...', + localizationContribution.localizedLanguageName ?? localizationContribution.languageName ?? localizationContribution.languageId), + }); + try { + const extensionUri = VSCodeExtensionUri.fromId(`${extension.extension.namespace}.${extension.extension.name}`).toString(); + await this.pluginServer.deploy(extensionUri); + } finally { + progress.cancel(); + } } - } - }); + }); + } } } + return Array.from(languages.values()); + } catch (error) { + console.error(error); + return []; } - return Array.from(languages.values()); } protected async loadExtensionLanguages(extension: VSXSearchEntry): Promise { From a3c03bf5c61ba80aedcf273aeb1dbd9609c4bab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 15 Jul 2024 15:48:31 +0200 Subject: [PATCH 307/441] Make sure UI loads when minimized. (#13887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13798 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- examples/api-tests/src/views.spec.js | 12 ++++++++---- packages/core/src/browser/shell/split-panels.ts | 7 ++++--- .../src/electron-main/electron-main-application.ts | 10 ++++++++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/examples/api-tests/src/views.spec.js b/examples/api-tests/src/views.spec.js index cdec58bc2348f..61e57595308cd 100644 --- a/examples/api-tests/src/views.spec.js +++ b/examples/api-tests/src/views.spec.js @@ -14,6 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** +const { timeout } = require('@theia/core/lib/common/promise-util'); + // @ts-check describe('Views', function () { this.timeout(7500); @@ -52,24 +54,26 @@ describe('Views', function () { if (view) { assert.notEqual(shell.getAreaFor(view), contribution.defaultViewOptions.area); assert.isFalse(view.isVisible); - assert.notEqual(view, shell.activeWidget); + assert.isTrue(view !== shell.activeWidget, `${contribution.viewLabel} !== shell.activeWidget`); } view = await contribution.toggleView(); - assert.notEqual(view, undefined); + // we can't use "equals" here because Mocha chokes on the diff for certain widgets + assert.isTrue(view !== undefined, `${contribution.viewLabel} !== undefined`); assert.equal(shell.getAreaFor(view), contribution.defaultViewOptions.area); assert.isDefined(shell.getTabBarFor(view)); // @ts-ignore assert.equal(shell.getAreaFor(shell.getTabBarFor(view)), contribution.defaultViewOptions.area); assert.isTrue(view.isVisible); - assert.equal(view, shell.activeWidget); + assert.isTrue(view === shell.activeWidget, `${contribution.viewLabel} === shell.activeWidget`); view = await contribution.toggleView(); + await timeout(0); // seems that the "await" is not enought to guarantee that the panel is hidden assert.notEqual(view, undefined); assert.equal(shell.getAreaFor(view), contribution.defaultViewOptions.area); assert.isDefined(shell.getTabBarFor(view)); assert.isFalse(view.isVisible); - assert.notEqual(view, shell.activeWidget); + assert.isTrue(view !== shell.activeWidget, `${contribution.viewLabel} !== shell.activeWidget`); }); } diff --git a/packages/core/src/browser/shell/split-panels.ts b/packages/core/src/browser/shell/split-panels.ts index cb3de57c26dd7..d9f59bdf4a97a 100644 --- a/packages/core/src/browser/shell/split-panels.ts +++ b/packages/core/src/browser/shell/split-panels.ts @@ -91,13 +91,14 @@ export class SplitPositionHandler { move.resolve = resolve; move.reject = reject; if (this.splitMoves.length === 0) { - window.requestAnimationFrame(this.animationFrame.bind(this)); + setTimeout(this.animationFrame.bind(this), 10); } this.splitMoves.push(move); }); } - protected animationFrame(time: number): void { + protected animationFrame(): void { + const time = Date.now(); const move = this.splitMoves[this.currentMoveIndex]; let rejectedOrResolved = false; if (move.ended || move.referenceWidget && move.referenceWidget.isHidden) { @@ -133,7 +134,7 @@ export class SplitPositionHandler { this.currentMoveIndex = 0; } if (this.splitMoves.length > 0) { - window.requestAnimationFrame(this.animationFrame.bind(this)); + setTimeout(this.animationFrame.bind(this)); } } diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index b004fd444d07a..5a895f7ca2221 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -15,8 +15,10 @@ // ***************************************************************************** import { inject, injectable, named } from 'inversify'; -import { screen, app, BrowserWindow, WebContents, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage, - nativeTheme, shell, dialog } from '../../electron-shared/electron'; +import { + screen, app, BrowserWindow, WebContents, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage, + nativeTheme, shell, dialog +} from '../../electron-shared/electron'; import * as path from 'path'; import { Argv } from 'yargs'; import { AddressInfo } from 'net'; @@ -350,6 +352,9 @@ export class ElectronMainApplication { alwaysOnTop: true, show: false, transparent: true, + webPreferences: { + backgroundThrottling: false + } }); if (this.isShowWindowEarly()) { @@ -458,6 +463,7 @@ export class ElectronMainApplication { // Setting the following option to `true` causes some features to break, somehow. // Issue: https://github.com/eclipse-theia/theia/issues/8577 nodeIntegrationInWorker: false, + backgroundThrottling: false, preload: path.resolve(this.globals.THEIA_APP_PROJECT_PATH, 'lib', 'frontend', 'preload.js').toString() }, ...this.config.electron?.windowOptions || {}, From 8ad18ea80cc7975209e299468fbe29d10ad0e227 Mon Sep 17 00:00:00 2001 From: Dani De Leo Date: Tue, 16 Jul 2024 08:39:19 -0400 Subject: [PATCH 308/441] Update dead/broken README links (#13931) Signed-off-by: Danielle De Leo --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0227b52152c6e..7787064ed858a 100644 --- a/README.md +++ b/README.md @@ -68,10 +68,10 @@ See [our roadmap](https://github.com/eclipse-theia/theia/wiki/Eclipse-Theia-Road Here you can find guides and examples for common scenarios to adopt Theia: - [Get an overview of how to get started](https://theia-ide.org/#gettingstarted) on the Theia website -- [Develop a Theia application - your own IDE/Tool](https://www.theia-ide.org/doc/Composing_Applications.html) +- [Develop a Theia application - your own IDE/Tool](https://theia-ide.org/docs/composing_applications/) - [Learn about Theia's extension mechanisms](https://theia-ide.org/docs/extensions/) - [Develop a VS Code like extension](https://theia-ide.org/docs/authoring_vscode_extensions/) -- [Develop a Theia extension](http://www.theia-ide.org/doc/Authoring_Extensions.html) +- [Develop a Theia extension](https://theia-ide.org/docs/authoring_extensions/) - [Test a VS Code extension in Theia](https://github.com/eclipse-theia/theia/wiki/Testing-VS-Code-extensions) - [Package a desktop Theia application with Electron](https://theia-ide.org/docs/blueprint_documentation/) @@ -84,7 +84,7 @@ Read below to learn how to take part in improving Theia: - Find an issue to work on and submit a pull request - First time contributing to open source? Pick a [good first issue](https://github.com/eclipse-theia/theia/labels/good%20first%20issue) to get you familiar with GitHub contributing process. - First time contributing to Theia? Pick a [beginner friendly issue](https://github.com/eclipse-theia/theia/labels/beginners) to get you familiar with codebase and our contributing process. - - Want to become a Committer? Solve an issue showing that you understand Theia objectives and architecture. [Here](https://github.com/eclipse-theia/theia/labels/help%20wanted) is a good list to start. Further, have a look at our [roadmap](https://github.com/eclipse-theia/theia/wiki/Roadmap) to align your contributions with the current project goals. + - Want to become a Committer? Solve an issue showing that you understand Theia objectives and architecture. [Here](https://github.com/eclipse-theia/theia/labels/help%20wanted) is a good list to start. Further, have a look at our [roadmap](https://github.com/eclipse-theia/theia/wiki/Eclipse-Theia-Roadmap) to align your contributions with the current project goals. - Could not find an issue? Look for bugs, typos, and missing features. ## Feedback @@ -115,7 +115,7 @@ Read below how to engage with Theia community: ## License - [Eclipse Public License 2.0](LICENSE-EPL) -- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](LICENSE-GPL-W-CLASSPATH-EXCEPTION) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](LICENSE-GPL-2.0-ONLY-CLASSPATH-EXCEPTION) ## Trademark From 7f928f27ad2d626362dcfed2e8d41a0e934ed49a Mon Sep 17 00:00:00 2001 From: "Christian W. Damus" Date: Tue, 16 Jul 2024 08:41:11 -0400 Subject: [PATCH 309/441] Make Notebook preferences registration substitutable (#13926) Implement the commonly employed pattern for preference contribution registration to enable substitution of the Notebook preference schema. Fixes #13913 Signed-off-by: Christian W. Damus --- .../src/browser/contributions/notebook-preferences.ts | 11 ++++++++++- .../notebook/src/browser/notebook-frontend-module.ts | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-preferences.ts b/packages/notebook/src/browser/contributions/notebook-preferences.ts index bb947fb756dc3..4187d6d914d80 100644 --- a/packages/notebook/src/browser/contributions/notebook-preferences.ts +++ b/packages/notebook/src/browser/contributions/notebook-preferences.ts @@ -19,7 +19,8 @@ *--------------------------------------------------------------------------------------------*/ import { nls } from '@theia/core'; -import { PreferenceSchema } from '@theia/core/lib/browser'; +import { interfaces } from '@theia/core/shared/inversify'; +import { PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser'; export namespace NotebookPreferences { export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers'; @@ -81,3 +82,11 @@ export const notebookPreferenceSchema: PreferenceSchema = { } }; + +export const NotebookPreferenceContribution = Symbol('NotebookPreferenceContribution'); + +export function bindNotebookPreferences(bind: interfaces.Bind): void { + // We don't need a NotebookPreferenceConfiguration class, so there's no preference proxy to bind + bind(NotebookPreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema }); + bind(PreferenceContribution).toService(NotebookPreferenceContribution); +} diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 681780f512655..fda9e983c0d8b 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, PreferenceContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -44,7 +44,7 @@ import { NotebookOutlineContribution } from './contributions/notebook-outline-co import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution'; import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution'; import { NotebookClipboardService } from './service/notebook-clipboard-service'; -import { notebookPreferenceSchema } from './contributions/notebook-preferences'; +import { bindNotebookPreferences } from './contributions/notebook-preferences'; import { NotebookOptionsService } from './service/notebook-options'; export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -106,6 +106,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookLabelProviderContribution).toSelf().inSingletonScope(); bind(LabelProviderContribution).toService(NotebookLabelProviderContribution); - bind(PreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema }); + bindNotebookPreferences(bind); bind(NotebookOptionsService).toSelf().inSingletonScope(); }); From 9e9a9f11d6f4654eb36294aed64d20a4a776da52 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 17 Jul 2024 13:13:44 +0200 Subject: [PATCH 310/441] change cell type when selecting markdown as a code cells language (#13933) Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index e3522f4112f45..4b4242df805cb 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -365,15 +365,22 @@ export class NotebookCellActionContribution implements MenuContribution, Command execute: async (notebook?: NotebookModel, cell?: NotebookCellModel) => { const selectedCell = cell ?? this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell; const activeNotebook = notebook ?? this.notebookEditorWidgetService.focusedEditor?.model; - if (selectedCell && activeNotebook) { - const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); - if (language?.value && language.value !== 'autoDetect') { - this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ - editType: CellEditType.CellLanguage, - index: activeNotebook.cells.indexOf(selectedCell), - language: language.value.id - }], true); - } + if (!selectedCell || !activeNotebook) { + return; + } + const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); + if (!language?.value || language.value === 'autoDetect') { + return; + } + if (language.value.id === 'markdown') { + selectedCell.language = 'markdown'; + changeCellType(activeNotebook, selectedCell, CellKind.Markup); + } else { + this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ + editType: CellEditType.CellLanguage, + index: activeNotebook.cells.indexOf(selectedCell), + language: language.value.id + }], true); } } }); From feeb791f39c10103e093d1d0769688465a80b07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 17 Jul 2024 14:37:47 +0200 Subject: [PATCH 311/441] Fix python version doc (#13928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Mäder Co-authored-by: Johannes Faltermeier --- doc/Developing.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/Developing.md b/doc/Developing.md index bce52b7b21bbd..2f8e242dfc811 100644 --- a/doc/Developing.md +++ b/doc/Developing.md @@ -512,11 +512,10 @@ etc.) by opening `packages//coverage/index.html`. - Install `yarn`: `scoop install yarn`. - If you need to install `windows-build-tools`, see [`Installing Windows Build Tools`](#installing-windows-build-tools). - If you run into problems with installing the required build tools, the `node-gyp` documentation offers a useful [guide](https://github.com/nodejs/node-gyp#on-windows) how to install the dependencies manually. The versions required for building Theia are: - - Python 3.6 or higher + - Python 3.6 to 3.11 - Visual Studio [build tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) 17 - - If you have multiple versions of either python or Visual Studio installed or if the tool is not found, you may adjust the used version via the npm config: - - `npm config set python /path/to/executable/python --global` - - `npm config set msvs_version 2017 --global` + - If you have multiple versions of either python or Visual Studio installed, or if the tool is not found, you may adjust the version used as described + [here](https://github.com/nodejs/node-gyp?tab=readme-ov-file#configuring-python-dependency) Clone, build and run Theia. Using Git Bash as administrator: From 9e307dd265c82ac828d6d4340dd12ccf51df245a Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 18 Jul 2024 09:26:33 +0200 Subject: [PATCH 312/441] Introduce environment variable to override connexion settings (#13936) fixes #13800 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger Co-authored-by: Philip Langer --- CHANGELOG.md | 2 ++ packages/core/README.md | 4 ++++ packages/core/README_TEMPLATE.md | 4 ++++ .../websocket-frontend-connection-service.ts | 12 +++++++++++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa002cee07bb0..5998da27c3378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ diff --git a/packages/core/README.md b/packages/core/README.md index 20dcb0ee6ae26..d1bcd9e2030c6 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -152,6 +152,10 @@ Where `root` is the name of the logger and `INFO` is the log level. These are op - If possible, you should set this environment variable: - When not set, Theia will allow any origin to access the WebSocket services. - When set, Theia will only allow the origins defined in this environment variable. +- `FRONTEND_CONNECTION_TIMEOUT` + - The duration in milliseconds during which the backend keeps the connection contexts for the frontend to reconnect. + - This duration defaults to '0' if not set. + - If set to negative number, the backend will never close the connection. ## Additional Information diff --git a/packages/core/README_TEMPLATE.md b/packages/core/README_TEMPLATE.md index 5b59b54820f49..fc9f696af0a92 100644 --- a/packages/core/README_TEMPLATE.md +++ b/packages/core/README_TEMPLATE.md @@ -121,6 +121,10 @@ Where `root` is the name of the logger and `INFO` is the log level. These are op - If possible, you should set this environment variable: - When not set, Theia will allow any origin to access the WebSocket services. - When set, Theia will only allow the origins defined in this environment variable. +- `FRONTEND_CONNECTION_TIMEOUT` + - The duration in milliseconds during which the backend keeps the connection contexts for the frontend to reconnect. + - This duration defaults to '0' if not set. + - If set to negative number, the backend will never close the connection. ## Additional Information diff --git a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts index 633dc1d410aff..22349d08c6689 100644 --- a/packages/core/src/node/messaging/websocket-frontend-connection-service.ts +++ b/packages/core/src/node/messaging/websocket-frontend-connection-service.ts @@ -105,7 +105,8 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer socket.on('disconnect', evt => { console.info('socked closed'); channel.disconnect(); - const timeout = BackendApplicationConfigProvider.get().frontendConnectionTimeout; + + const timeout = this.frontendConnectionTimeout(); const isMarkedForClose = this.channelsMarkedForClose.delete(frontEndId); if (timeout === 0 || isMarkedForClose) { this.closeConnection(frontEndId, evt); @@ -124,6 +125,15 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer markForClose(channelId: string): void { this.channelsMarkedForClose.add(channelId); } + + private frontendConnectionTimeout(): number { + const envValue = Number(process.env['FRONTEND_CONNECTION_TIMEOUT']); + if (!isNaN(envValue)) { + return envValue; + } + + return BackendApplicationConfigProvider.get().frontendConnectionTimeout; + } } class ReconnectableSocketChannel extends AbstractChannel { From a4bf86f40846387b70976d402e2293b495b484de Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 18 Jul 2024 09:57:55 +0200 Subject: [PATCH 313/441] tab selected should be adjacent when closing last one. (#13912) fixes #13886 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + packages/core/src/browser/shell/application-shell.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5998da27c3378..e26928086168f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - ## 1.50.0 - 06/27/2024 - [application-manager] updated logic to load correct messaging module in browser-only mode [#13827](https://github.com/eclipse-theia/theia/pull/13827) diff --git a/packages/debug/package.json b/packages/debug/package.json index d3b471366b2b4..16feb45ee2f0c 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -13,6 +13,7 @@ "@theia/output": "1.51.0", "@theia/process": "1.51.0", "@theia/task": "1.51.0", + "@theia/test": "1.51.0", "@theia/terminal": "1.51.0", "@theia/variable-resolver": "1.51.0", "@theia/workspace": "1.51.0", diff --git a/packages/debug/src/browser/debug-session-contribution.ts b/packages/debug/src/browser/debug-session-contribution.ts index 907e64e531aaf..7639afb6bfa1b 100644 --- a/packages/debug/src/browser/debug-session-contribution.ts +++ b/packages/debug/src/browser/debug-session-contribution.ts @@ -31,6 +31,8 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { DebugContribution } from './debug-contribution'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; +import { TestService } from '@theia/test/lib/browser/test-service'; +import { DebugSessionManager } from './debug-session-manager'; /** * DebugSessionContribution symbol for DI. @@ -90,7 +92,7 @@ export const DebugSessionFactory = Symbol('DebugSessionFactory'); * The [debug session](#DebugSession) factory. */ export interface DebugSessionFactory { - get(sessionId: string, options: DebugSessionOptions, parentSession?: DebugSession): DebugSession; + get(manager: DebugSessionManager, sessionId: string, options: DebugSessionOptions, parentSession?: DebugSession): DebugSession; } @injectable() @@ -115,10 +117,12 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory { protected readonly fileService: FileService; @inject(ContributionProvider) @named(DebugContribution) protected readonly debugContributionProvider: ContributionProvider; + @inject(TestService) + protected readonly testService: TestService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - get(sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession { + get(manager: DebugSessionManager, sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession { const connection = new DebugSessionConnection( sessionId, () => new Promise(resolve => @@ -131,6 +135,9 @@ export class DefaultDebugSessionFactory implements DebugSessionFactory { sessionId, options, parentSession, + this.testService, + options.testRun, + manager, connection, this.terminalService, this.editorManager, diff --git a/packages/debug/src/browser/debug-session-manager.ts b/packages/debug/src/browser/debug-session-manager.ts index b462ecba88bb7..a01339d5cf01e 100644 --- a/packages/debug/src/browser/debug-session-manager.ts +++ b/packages/debug/src/browser/debug-session-manager.ts @@ -386,7 +386,7 @@ export class DebugSessionManager { const parentSession = options.configuration.parentSessionId ? this._sessions.get(options.configuration.parentSessionId) : undefined; const contrib = this.sessionContributionRegistry.get(options.configuration.type); const sessionFactory = contrib ? contrib.debugSessionFactory() : this.debugSessionFactory; - const session = sessionFactory.get(sessionId, options, parentSession); + const session = sessionFactory.get(this, sessionId, options, parentSession); this._sessions.set(sessionId, session); this.debugTypeKey.set(session.configuration.type); diff --git a/packages/debug/src/browser/debug-session-options.ts b/packages/debug/src/browser/debug-session-options.ts index 3e61d0ec4dfaa..8a94ade9d8636 100644 --- a/packages/debug/src/browser/debug-session-options.ts +++ b/packages/debug/src/browser/debug-session-options.ts @@ -31,8 +31,14 @@ export class DebugCompoundRoot { } } +export interface TestRunReference { + controllerId: string, + runId: string +} + export interface DebugSessionOptionsBase { workspaceFolderUri?: string, + testRun?: TestRunReference } export interface DebugConfigurationSessionOptions extends DebugSessionOptionsBase { diff --git a/packages/debug/src/browser/debug-session.tsx b/packages/debug/src/browser/debug-session.tsx index 3a80f0efa8905..06f7f0140b9c2 100644 --- a/packages/debug/src/browser/debug-session.tsx +++ b/packages/debug/src/browser/debug-session.tsx @@ -33,7 +33,7 @@ import { DebugSourceBreakpoint } from './model/debug-source-breakpoint'; import debounce = require('p-debounce'); import URI from '@theia/core/lib/common/uri'; import { BreakpointManager } from './breakpoint/breakpoint-manager'; -import { DebugConfigurationSessionOptions, InternalDebugSessionOptions } from './debug-session-options'; +import { DebugConfigurationSessionOptions, InternalDebugSessionOptions, TestRunReference } from './debug-session-options'; import { DebugConfiguration, DebugConsoleMode } from '../common/debug-common'; import { SourceBreakpoint, ExceptionBreakpoint } from './breakpoint/breakpoint-marker'; import { TerminalWidgetOptions, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; @@ -44,6 +44,8 @@ import { Deferred, waitForEvent } from '@theia/core/lib/common/promise-util'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { DebugInstructionBreakpoint } from './model/debug-instruction-breakpoint'; import { nls } from '@theia/core'; +import { TestService, TestServices } from '@theia/test/lib/browser/test-service'; +import { DebugSessionManager } from './debug-session-manager'; export enum DebugState { Inactive, @@ -98,6 +100,9 @@ export class DebugSession implements CompositeTreeElement { readonly id: string, readonly options: DebugConfigurationSessionOptions, readonly parentSession: DebugSession | undefined, + testService: TestService, + testRun: TestRunReference | undefined, + sessionManager: DebugSessionManager, protected readonly connection: DebugSessionConnection, protected readonly terminalServer: TerminalService, protected readonly editorManager: EditorManager, @@ -124,6 +129,19 @@ export class DebugSession implements CompositeTreeElement { this.parentSession?.childSessions?.delete(id); })); } + if (testRun) { + try { + const run = TestServices.withTestRun(testService, testRun.controllerId, testRun.runId); + run.onDidChangeProperty(evt => { + if (evt.isRunning === false) { + sessionManager.terminateSession(this); + } + }); + } catch (err) { + console.error(err); + } + } + this.connection.onDidClose(() => this.toDispose.dispose()); this.toDispose.pushAll([ this.onDidChangeEmitter, diff --git a/packages/debug/src/common/debug-configuration.ts b/packages/debug/src/common/debug-configuration.ts index b441a39416abe..63efae4a8a7f3 100644 --- a/packages/debug/src/common/debug-configuration.ts +++ b/packages/debug/src/common/debug-configuration.ts @@ -100,6 +100,10 @@ export interface DebugSessionOptions { suppressSaveBeforeStart?: boolean; suppressDebugStatusbar?: boolean; suppressDebugView?: boolean; + testRun?: { + controllerId: string, + runId: string + } } export enum DebugConsoleMode { diff --git a/packages/debug/tsconfig.json b/packages/debug/tsconfig.json index aec95d2fcfb8e..a87f1347f8519 100644 --- a/packages/debug/tsconfig.json +++ b/packages/debug/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../terminal" }, + { + "path": "../test" + }, { "path": "../variable-resolver" }, diff --git a/packages/plugin-ext/src/main/browser/debug/debug-main.ts b/packages/plugin-ext/src/main/browser/debug/debug-main.ts index 703912d10d6fb..471310a0b44a6 100644 --- a/packages/plugin-ext/src/main/browser/debug/debug-main.ts +++ b/packages/plugin-ext/src/main/browser/debug/debug-main.ts @@ -58,6 +58,7 @@ import { WorkspaceService } from '@theia/workspace/lib/browser'; import { DebugSessionOptions as TheiaDebugSessionOptions } from '@theia/debug/lib/browser/debug-session-options'; import { DebugStackFrame } from '@theia/debug/lib/browser/model/debug-stack-frame'; import { DebugThread } from '@theia/debug/lib/browser/model/debug-thread'; +import { TestService } from '@theia/test/lib/browser/test-service'; export class DebugMainImpl implements DebugMain, Disposable { private readonly debugExt: DebugExt; @@ -77,6 +78,7 @@ export class DebugMainImpl implements DebugMain, Disposable { private readonly fileService: FileService; private readonly pluginService: HostedPluginSupport; private readonly debugContributionProvider: ContributionProvider; + private readonly testService: TestService; private readonly workspaceService: WorkspaceService; private readonly debuggerContributions = new Map(); @@ -100,6 +102,7 @@ export class DebugMainImpl implements DebugMain, Disposable { this.debugContributionProvider = container.getNamed(ContributionProvider, DebugContribution); this.fileService = container.get(FileService); this.pluginService = container.get(HostedPluginSupport); + this.testService = container.get(TestService); this.workspaceService = container.get(WorkspaceService); const fireDidChangeBreakpoints = ({ added, removed, changed }: BreakpointsChangeEvent) => { @@ -165,6 +168,7 @@ export class DebugMainImpl implements DebugMain, Disposable { this.fileService, terminalOptionsExt, this.debugContributionProvider, + this.testService, this.workspaceService, ); @@ -327,6 +331,7 @@ export class DebugMainImpl implements DebugMain, Disposable { } else { sessionOptions = { ...sessionOptions, ...options, workspaceFolderUri }; } + sessionOptions.testRun = options.testRun; // start options const session = await this.sessionManager.start(sessionOptions); @@ -345,7 +350,7 @@ export class DebugMainImpl implements DebugMain, Disposable { } private toDebugStackFrameDTO(stackFrame: DebugStackFrame | undefined): DebugStackFrameDTO | undefined { - return stackFrame ? { + return stackFrame ? { sessionId: stackFrame.session.id, frameId: stackFrame.frameId, threadId: stackFrame.thread.threadId diff --git a/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts b/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts index 02f88e0dc9b71..2cd49a625ec7b 100644 --- a/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts +++ b/packages/plugin-ext/src/main/browser/debug/plugin-debug-session-factory.ts @@ -22,7 +22,7 @@ import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import { MessageClient } from '@theia/core/lib/common/message-service-protocol'; import { OutputChannelManager } from '@theia/output/lib/browser/output-channel'; import { DebugPreferences } from '@theia/debug/lib/browser/debug-preferences'; -import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options'; +import { DebugConfigurationSessionOptions, TestRunReference } from '@theia/debug/lib/browser/debug-session-options'; import { DebugSession } from '@theia/debug/lib/browser/debug-session'; import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection'; import { TerminalWidgetOptions, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget'; @@ -32,12 +32,17 @@ import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution'; import { ContributionProvider } from '@theia/core/lib/common/contribution-provider'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { PluginChannel } from '../../../common/connection'; +import { TestService } from '@theia/test/lib/browser/test-service'; +import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager'; export class PluginDebugSession extends DebugSession { constructor( override readonly id: string, override readonly options: DebugConfigurationSessionOptions, override readonly parentSession: DebugSession | undefined, + testService: TestService, + testRun: TestRunReference | undefined, + sessionManager: DebugSessionManager, protected override readonly connection: DebugSessionConnection, protected override readonly terminalServer: TerminalService, protected override readonly editorManager: EditorManager, @@ -48,7 +53,8 @@ export class PluginDebugSession extends DebugSession { protected readonly terminalOptionsExt: TerminalOptionsExt | undefined, protected override readonly debugContributionProvider: ContributionProvider, protected override readonly workspaceService: WorkspaceService) { - super(id, options, parentSession, connection, terminalServer, editorManager, breakpoints, labelProvider, messages, fileService, debugContributionProvider, + super(id, options, parentSession, testService, testRun, sessionManager, connection, terminalServer, editorManager, breakpoints, + labelProvider, messages, fileService, debugContributionProvider, workspaceService); } @@ -75,12 +81,13 @@ export class PluginDebugSessionFactory extends DefaultDebugSessionFactory { protected override readonly fileService: FileService, protected readonly terminalOptionsExt: TerminalOptionsExt | undefined, protected override readonly debugContributionProvider: ContributionProvider, + protected override readonly testService: TestService, protected override readonly workspaceService: WorkspaceService, ) { super(); } - override get(sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession { + override get(manager: DebugSessionManager, sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession { const connection = new DebugSessionConnection( sessionId, this.connectionFactory, @@ -90,6 +97,9 @@ export class PluginDebugSessionFactory extends DefaultDebugSessionFactory { sessionId, options, parentSession, + this.testService, + options.testRun, + manager, connection, this.terminalService, this.editorManager, diff --git a/packages/plugin-ext/src/plugin/debug/debug-ext.ts b/packages/plugin-ext/src/plugin/debug/debug-ext.ts index 48c13a6ec9bee..790a16583eaa9 100644 --- a/packages/plugin-ext/src/plugin/debug/debug-ext.ts +++ b/packages/plugin-ext/src/plugin/debug/debug-ext.ts @@ -33,7 +33,8 @@ import { DebugAdapter } from '@theia/debug/lib/common/debug-model'; import { PluginDebugAdapterCreator } from './plugin-debug-adapter-creator'; import { NodeDebugAdapterCreator } from '../node/debug/plugin-node-debug-adapter-creator'; import { DebugProtocol } from '@vscode/debugprotocol'; -import { DebugConfiguration } from '@theia/debug/lib/common/debug-configuration'; +import { DebugConfiguration, DebugSessionOptions } from '@theia/debug/lib/common/debug-configuration'; +import { checkTestRunInstance } from '../tests'; interface ConfigurationProviderRecord { handle: number; @@ -193,7 +194,7 @@ export class DebugExtImpl implements DebugExt { } startDebugging(folder: theia.WorkspaceFolder | undefined, nameOrConfiguration: string | theia.DebugConfiguration, options: theia.DebugSessionOptions): PromiseLike { - return this.proxy.$startDebugging(folder, nameOrConfiguration, { + const optionsDto: DebugSessionOptions = { parentSessionId: options.parentSession?.id, compact: options.compact, consoleMode: options.consoleMode, @@ -201,8 +202,16 @@ export class DebugExtImpl implements DebugExt { suppressDebugStatusbar: options.suppressDebugStatusbar, suppressDebugView: options.suppressDebugView, lifecycleManagedByParent: options.lifecycleManagedByParent, - noDebug: options.noDebug - }); + noDebug: options.noDebug, + }; + if (options.testRun) { + const run = checkTestRunInstance(options.testRun); + optionsDto.testRun = { + controllerId: run.controller.id, + runId: run.id + }; + } + return this.proxy.$startDebugging(folder, nameOrConfiguration, optionsDto); } stopDebugging(session?: theia.DebugSession): PromiseLike { diff --git a/packages/plugin-ext/src/plugin/tests.ts b/packages/plugin-ext/src/plugin/tests.ts index 7390fc82bdb35..101532e86b2ed 100644 --- a/packages/plugin-ext/src/plugin/tests.ts +++ b/packages/plugin-ext/src/plugin/tests.ts @@ -135,21 +135,7 @@ export class TestControllerImpl implements theia.TestController { } createTestRun(request: theia.TestRunRequest, name?: string, persist: boolean = true): theia.TestRun { - return this.testRunStarted(request, name || '', persist, true); - } - - dispose() { - this.proxy.$unregisterTestController(this.id); - this.onDispose(); - } - - protected testRunStarted(request: theia.TestRunRequest, name: string, persist: boolean, isRunning: boolean): TestRun { - const existing = this.activeRuns.get(request); - if (existing) { - return existing; - } - - const run = new TestRun(this, this.proxy, name, persist, isRunning, request.preserveFocus); + const run = new TestRun(this, this.proxy, name || '', persist, true, request.preserveFocus); const endListener = run.onWillFlush(() => { // make sure we notify the front end of test item changes before test run state is sent this.deltaBuilder.flush(); @@ -162,6 +148,11 @@ export class TestControllerImpl implements theia.TestController { return run; } + dispose() { + this.proxy.$unregisterTestController(this.id); + this.onDispose(); + } + runTestsForUI(profileId: string, name: string, includedTests: string[][], excludedTests: string[][], preserveFocus: boolean): void { const profile = this.getProfile(profileId); if (!profile) { @@ -200,8 +191,8 @@ export class TestControllerImpl implements theia.TestController { includeTests, excludeTests, profile, false /* don't support continuous run yet */, preserveFocus ); - const run = this.testRunStarted(request, name, false, false); - profile.runHandler(request, run.token); + // we do not cancel test runs via a cancellation token, but instead invoke "cancel" on the test runs + profile.runHandler(request, CancellationToken.None); } cancelRun(runId?: string): void { @@ -235,7 +226,18 @@ function checkTestInstance(item?: theia.TestItem): TestItemImpl | undefined { return undefined; } -class TestRun implements theia.TestRun { +export function checkTestRunInstance(item: theia.TestRun): TestRun; +export function checkTestRunInstance(item?: theia.TestRun): TestRun | undefined; +export function checkTestRunInstance(item?: theia.TestRun): TestRun | undefined { + if (item instanceof TestRun) { + return item; + } else if (item) { + throw new Error('Not a TestRun instance'); + } + return undefined; +} + +export class TestRun implements theia.TestRun { private onDidEndEmitter = new Emitter(); onDidEnd: Event = this.onDidEndEmitter.event; private onWillFlushEmitter = new Emitter(); @@ -251,10 +253,9 @@ class TestRun implements theia.TestRun { }, 200); private ended: boolean; private tokenSource: CancellationTokenSource; - readonly token: CancellationToken; constructor( - private readonly controller: TestControllerImpl, + readonly controller: TestControllerImpl, private readonly proxy: TestingMain, readonly name: string, readonly isPersisted: boolean, @@ -263,10 +264,14 @@ class TestRun implements theia.TestRun { this.id = generateUuid(); this.tokenSource = new CancellationTokenSource(); - this.token = this.tokenSource.token; this.proxy.$notifyTestRunCreated(this.controller.id, { id: this.id, name: this.name, isRunning }, preserveFocus); } + + get token(): CancellationToken { + return this.tokenSource.token; + } + enqueued(test: theia.TestItem): void { this.updateTestState(test, { itemPath: checkTestInstance(test).path, state: TestExecutionState.Queued }); } @@ -509,7 +514,7 @@ export class TestRunProfile implements theia.TestRunProfile { } doSetDefault(isDefault: boolean): boolean { - if (this._isDefault !== isDefault) { + if (this._isDefault !== isDefault) { this._isDefault = isDefault; this.onDidChangeDefaultEmitter.fire(isDefault); return true; diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index a1a07acfb420f..7134d063da377 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -12087,6 +12087,12 @@ export module '@theia/plugin' { * When true, the debug viewlet will not be automatically revealed for this session. */ suppressDebugView?: boolean; + /** + * Signals to the editor that the debug session was started from a test run + * request. This is used to link the lifecycle of the debug session and + * test run in UI actions. + */ + testRun?: TestRun; } /** diff --git a/packages/test/src/browser/test-service.ts b/packages/test/src/browser/test-service.ts index 130e0c7623754..210ca558c58c7 100644 --- a/packages/test/src/browser/test-service.ts +++ b/packages/test/src/browser/test-service.ts @@ -96,6 +96,7 @@ export interface TestStateChangedEvent { export interface TestRun { cancel(): void; + readonly id: string; readonly name: string; readonly isRunning: boolean; readonly controller: TestController; @@ -191,6 +192,20 @@ export interface TestService { onDidChangeIsRefreshing: Event; } +export namespace TestServices { + export function withTestRun(service: TestService, controllerId: string, runId: string): TestRun { + const controller = service.getControllers().find(c => c.id === controllerId); + if (!controller) { + throw new Error(`No test controller with id '${controllerId}' found`); + } + const run = controller.testRuns.find(r => r.id === runId); + if (!run) { + throw new Error(`No test run with id '${runId}' found`); + } + return run; + } +} + export const TestContribution = Symbol('TestContribution'); export interface TestContribution { From fe4cd14d02804a5dcbb9bc5edde1383c5bd39d4e Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 25 Jul 2024 11:43:43 +0200 Subject: [PATCH 321/441] [chore] downgrade jsdom to 22.1.0 (#13944) fixes #13943 --- package.json | 2 +- yarn.lock | 172 +++++++++++++++++++++------------------------------ 2 files changed, 71 insertions(+), 103 deletions(-) diff --git a/package.json b/package.json index dfe25497fbf51..5184f195a7017 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "if-env": "^1.0.4", "ignore-styles": "^5.0.1", "improved-yarn-audit": "^3.0.0", - "jsdom": "^24.1.0", + "jsdom": "^22.1.0", "lerna": "^7.1.1", "mkdirp": "^0.5.0", "node-gyp": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index b4e929306ce44..874de58e34b6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2764,7 +2764,7 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.5: +abab@^2.0.5, abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== @@ -2821,13 +2821,6 @@ agent-base@^7.0.2: dependencies: debug "^4.3.4" -agent-base@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== - dependencies: - debug "^4.3.4" - agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -4446,10 +4439,10 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssstyle@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.0.1.tgz#ef29c598a1e90125c870525490ea4f354db0660a" - integrity sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== dependencies: rrweb-cssom "^0.6.0" @@ -4463,13 +4456,14 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== -data-urls@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" - integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" date-fns@^1.23.0: version "1.30.1" @@ -4804,6 +4798,13 @@ domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -6456,13 +6457,6 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" -html-encoding-sniffer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" - integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== - dependencies: - whatwg-encoding "^3.1.1" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -6512,14 +6506,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http-proxy-agent@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" - integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -6561,7 +6547,7 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0: +https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -6577,14 +6563,6 @@ https-proxy-agent@^7.0.2: agent-base "^7.0.2" debug "4" -https-proxy-agent@^7.0.4: - version "7.0.5" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" - integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== - dependencies: - agent-base "^7.0.2" - debug "4" - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -7283,32 +7261,34 @@ jschardet@^2.1.1: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.3.0.tgz#06e2636e16c8ada36feebbdc08aa34e6a9b3ff75" integrity sha512-6I6xT7XN/7sBB7q8ObzKbmv5vN+blzLcboDE1BNEsEfmRXJValMxO6OIRT69ylPBRemS3rw6US+CMCar0OBc9g== -jsdom@^24.1.0: - version "24.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-24.1.0.tgz#0cffdabd42c506788bfecd160e8ac22d4387f971" - integrity sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA== +jsdom@^22.1.0: + version "22.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" + integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== dependencies: - cssstyle "^4.0.1" - data-urls "^5.0.0" + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" decimal.js "^10.4.3" + domexception "^4.0.0" form-data "^4.0.0" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.4" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.10" + nwsapi "^2.2.4" parse5 "^7.1.2" - rrweb-cssom "^0.7.0" + rrweb-cssom "^0.6.0" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.1.4" - w3c-xmlserializer "^5.0.0" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" webidl-conversions "^7.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" - ws "^8.17.0" - xml-name-validator "^5.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" jsesc@^2.5.1: version "2.5.2" @@ -8841,10 +8821,10 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== -nwsapi@^2.2.10: - version "2.2.10" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.10.tgz#0b77a68e21a0b483db70b11fad055906e867cda8" - integrity sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ== +nwsapi@^2.2.4: + version "2.2.12" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.12.tgz#fb6af5c0ec35b27b4581eb3bbad34ec9e5c696f8" + integrity sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w== nx@16.10.0, "nx@>=16.5.1 < 17": version "16.10.0" @@ -9785,7 +9765,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== @@ -10331,11 +10311,6 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== -rrweb-cssom@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" - integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -11429,7 +11404,7 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tough-cookie@^4.1.4: +tough-cookie@^4.1.2: version "4.1.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== @@ -11439,12 +11414,12 @@ tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" -tr46@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" - integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== dependencies: - punycode "^2.3.1" + punycode "^2.3.0" tr46@~0.0.3: version "0.0.3" @@ -12053,12 +12028,12 @@ vscode-uri@^2.1.1: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== -w3c-xmlserializer@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" - integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: - xml-name-validator "^5.0.0" + xml-name-validator "^4.0.0" watchpack@^2.4.0: version "2.4.0" @@ -12155,24 +12130,17 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" -whatwg-encoding@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" - integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== - dependencies: - iconv-lite "0.6.3" - -whatwg-mimetype@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" - integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-url@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" - integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== dependencies: - tr46 "^5.0.0" + tr46 "^4.1.1" webidl-conversions "^7.0.0" whatwg-url@^5.0.0: @@ -12407,7 +12375,7 @@ ws@8.11.0, ws@~8.11.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== -ws@^8.17.0, ws@^8.17.1: +ws@^8.13.0, ws@^8.17.1: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== @@ -12427,10 +12395,10 @@ xdg-trashdir@^3.1.0: user-home "^2.0.0" xdg-basedir "^4.0.0" -xml-name-validator@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" - integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xml2js@^0.5.0: version "0.5.0" From cad1b1b6601199e75a313a27342ab32c20b1d974 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Thu, 25 Jul 2024 11:46:07 +0200 Subject: [PATCH 322/441] Bump API version to 1.91.1 (#13955) fixes #13954 Contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 1 + dev-packages/application-package/src/api.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8145bc5a00391..ab37679b63df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## not yet released - [test] Added `DebugSessionOptions.testRun` [#13939](https://github.com/eclipse-theia/theia/pull/13939) - Contributed on behalf of STMicroelectronics +- [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#]() - Contributed on behalf of STMicroelectronics - [core] introduce `FRONTEND_CONNECTION_TIMEOUT` environment variable to override application connexion settings. [#13936](https://github.com/eclipse-theia/theia/pull/13936) - Contributed on behalf of STMicroelectronics - [core] tab selected should be adjacent when closing last one [#13912](https://github.com/eclipse-theia/theia/pull/13912) - contributed on behalf of STMicroelectronics - [core] upgraded ws to 8.18.0 [#13903](https://github.com/eclipse-theia/theia/pull/13903) - contributed on behalf of STMicroelectronics diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index e64c5ac4b140e..9df0ceecc069b 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.90.2'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.91.1'; From 71c839eb6013915e115a6948a7232c17035af46f Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 25 Jul 2024 11:56:21 +0200 Subject: [PATCH 323/441] docs: updated changelog for 1.52.0 --- CHANGELOG.md | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab37679b63df6..a96f61eb0ca6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,18 +4,32 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## not yet released -- [test] Added `DebugSessionOptions.testRun` [#13939](https://github.com/eclipse-theia/theia/pull/13939) - Contributed on behalf of STMicroelectronics - -- [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#]() - Contributed on behalf of STMicroelectronics -- [core] introduce `FRONTEND_CONNECTION_TIMEOUT` environment variable to override application connexion settings. [#13936](https://github.com/eclipse-theia/theia/pull/13936) - Contributed on behalf of STMicroelectronics -- [core] tab selected should be adjacent when closing last one [#13912](https://github.com/eclipse-theia/theia/pull/13912) - contributed on behalf of STMicroelectronics -- [core] upgraded ws to 8.18.0 [#13903](https://github.com/eclipse-theia/theia/pull/13903) - contributed on behalf of STMicroelectronics -- [debug] implement activeStackItem and related change event in debug namespace [#13900](https://github.com/eclipse-theia/theia/pull/13900) - contributed on behalf of STMicroelectronics - -[Breaking Changes:](#breaking_changes_not_yet_released) - -## 1.50.0 - 06/27/2024 +## 1.52.0 - 07/25/2024 + +- [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#13955](https://github.com/eclipse-theia/theia/pull/13955) - Contributed on behalf of STMicroelectronics +- [cli] added logging to download:plugins script [#13905](https://github.com/eclipse-theia/theia/pull/13905) - Contributed on behalf of STMicroelectronics +- [core] bug fix: "core.saveAll" command only saved dirty widgets [#13942](https://github.com/eclipse-theia/theia/pull/13942) +- [core] downgrade jsdom to 22.1.0 [#13944](https://github.com/eclipse-theia/theia/pull/13944) +- [core] fixed reload for remote feature and added option to the electron window to change URL on reload [#13891](https://github.com/eclipse-theia/theia/pull/13891) +- [core] improved implementation around widget management [#13818](https://github.com/eclipse-theia/theia/pull/13818) +- [core] introduced `FRONTEND_CONNECTION_TIMEOUT` environment variable to override application connection settings [#13936](https://github.com/eclipse-theia/theia/pull/13936) - Contributed on behalf of STMicroelectronics +- [core] made sure UI loaded when minimized [#13887](https://github.com/eclipse-theia/theia/pull/13887) - Contributed on behalf of STMicroelectronics +- [core] prevented the rendering of the tab bar tooltip if no caption was provided [#13945](https://github.com/eclipse-theia/theia/pull/13945) +- [core] tab selected should be adjacent when closing the last one [#13912](https://github.com/eclipse-theia/theia/pull/13912) - Contributed on behalf of STMicroelectronics +- [core] upgraded ws to 8.18.0 [#13903](https://github.com/eclipse-theia/theia/pull/13903) +- [debug] added DebugSessionOptions.testRun [#13939](https://github.com/eclipse-theia/theia/pull/13939) - Contributed on behalf of STMicroelectronics +- [debug] implemented activeStackItem and related change event in debug namespace [#13900](https://github.com/eclipse-theia/theia/pull/13900) - Contributed on behalf of STMicroelectronics +- [filesystem] fixed FileResource not adding event listener to the disposable collection [#13880](https://github.com/eclipse-theia/theia/pull/13880) +- [notebook] changed cell type when selecting markdown as a code cell's language [#13933](https://github.com/eclipse-theia/theia/pull/13933) +- [notebook] made Notebook preferences registration substitutable [#13926](https://github.com/eclipse-theia/theia/pull/13926) +- [ovsx-client] fixed plugin version comparison [#13907](https://github.com/eclipse-theia/theia/pull/13907) +- [plugin-ext] codicon color and URI support to TerminalOptions [#13413](https://github.com/eclipse-theia/theia/pull/13413) +- [plugin-ext] used relative paths for ctx.importScripts() [#13854](https://github.com/eclipse-theia/theia/pull/13854) +- [preferences] refactored preference tree layouting [#13819](https://github.com/eclipse-theia/theia/pull/13819) +- [terminal] added support for 256 truecolor [#13853](https://github.com/eclipse-theia/theia/pull/13853) +- [workflows] updated Mac OS version to 14 in CI [#13908](https://github.com/eclipse-theia/theia/pull/13908) + +## 1.51.0 - 06/27/2024 - [application-manager] updated logic to load correct messaging module in browser-only mode [#13827](https://github.com/eclipse-theia/theia/pull/13827) - [application-package] bumped the default supported API from `1.89.1` to `1.90.2` [#13849](https://github.com/eclipse-theia/theia/pull/13849) - Contributed on behalf of STMicroelectronics From 14c291198ec0f5398de9c10c6e7d11ba60ce9f9b Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 25 Jul 2024 12:09:55 +0200 Subject: [PATCH 324/441] core: update re-exports for 1.52.0 --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index d6d7eba3d00c3..56a6688237adc 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.51.0`](https://www.npmjs.com/package/@theia/application-package/v/1.51.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.51.0`](https://www.npmjs.com/package/@theia/application-package/v/1.51.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.51.0`](https://www.npmjs.com/package/@theia/application-package/v/1.51.0)) - - `@theia/request` (from [`@theia/request@1.51.0`](https://www.npmjs.com/package/@theia/request/v/1.51.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.51.0`](https://www.npmjs.com/package/@theia/request/v/1.51.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.51.0`](https://www.npmjs.com/package/@theia/request/v/1.51.0)) + - `@theia/application-package` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) + - `@theia/request` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From 266fa0b2a9cf2649ed9b34c8b71b786806e787b4 Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Thu, 25 Jul 2024 12:12:24 +0200 Subject: [PATCH 325/441] v1.52.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 90 ++++++++-------- examples/browser/package.json | 100 +++++++++--------- examples/electron/package.json | 98 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 30 +++--- packages/dev-container/package.json | 12 +-- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 14 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 +++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 12 +-- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 18 ++-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 72 files changed, 515 insertions(+), 515 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index f666b2faf1646..10c58467c38f5 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.51.0", - "@theia/ffmpeg": "1.51.0", - "@theia/native-webpack-plugin": "1.51.0", + "@theia/application-package": "1.52.0", + "@theia/ffmpeg": "1.52.0", + "@theia/native-webpack-plugin": "1.52.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index 1ddac998afd26..c448d1ec3df4d 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.51.0", + "@theia/request": "1.52.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 29a36790431c3..0fb56c018e2bb 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,12 +32,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.51.0", - "@theia/application-package": "1.51.0", - "@theia/ffmpeg": "1.51.0", - "@theia/localization-manager": "1.51.0", - "@theia/ovsx-client": "1.51.0", - "@theia/request": "1.51.0", + "@theia/application-manager": "1.52.0", + "@theia/application-package": "1.52.0", + "@theia/ffmpeg": "1.52.0", + "@theia/localization-manager": "1.52.0", + "@theia/ovsx-client": "1.52.0", + "@theia/request": "1.52.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index 706c2719be385..6d04dec267ac9 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index cd38e4719f912..c316b4d2c0344 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~5.4.5" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index d68302c454f0d..face0ca0563de 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.51.0", + "version": "1.52.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index c5ce302d8bf95..edbc5bb930921 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.51.0", + "@theia/request": "1.52.0", "semver": "^7.5.4", "tslib": "^2.6.2" } diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index 035b6116001c1..99d5704dde653 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.51.0", + "version": "1.52.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.51.0", - "@theia/ext-scripts": "1.51.0", - "@theia/re-exports": "1.51.0", + "@theia/core": "1.52.0", + "@theia/ext-scripts": "1.52.0", + "@theia/re-exports": "1.52.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index 74c6076f34421..3bcf0da0a0565 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.51.0", + "version": "1.52.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index 778eabafcbabd..aec9cadd287e2 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index 818773c0a5858..9011b84a4dce2 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index 6e7acec85da61..c8553f9c0caf6 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/plugin-ext-headless": "1.51.0" + "@theia/core": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/plugin-ext-headless": "1.52.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index 8ed379c86eead..f5a4b84142086 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.51.0", - "@theia/ovsx-client": "1.51.0", - "@theia/search-in-workspace": "1.51.0", - "@theia/test": "1.51.0", - "@theia/toolbar": "1.51.0", - "@theia/vsx-registry": "1.51.0", - "@theia/workspace": "1.51.0" + "@theia/output": "1.52.0", + "@theia/ovsx-client": "1.52.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/test": "1.52.0", + "@theia/toolbar": "1.52.0", + "@theia/vsx-registry": "1.52.0", + "@theia/workspace": "1.52.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 7dd1167a8f29d..947cd74ef3332 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.51.0" + "@theia/core": "1.52.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 5605a2594f42b..087fd5c6548a3 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.51.0", + "version": "1.52.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,49 +15,49 @@ } }, "dependencies": { - "@theia/api-samples": "1.51.0", - "@theia/bulk-edit": "1.51.0", - "@theia/callhierarchy": "1.51.0", - "@theia/console": "1.51.0", - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/editor-preview": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/getting-started": "1.51.0", - "@theia/git": "1.51.0", - "@theia/keymaps": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/memory-inspector": "1.51.0", - "@theia/messages": "1.51.0", - "@theia/metrics": "1.51.0", - "@theia/mini-browser": "1.51.0", - "@theia/monaco": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/outline-view": "1.51.0", - "@theia/output": "1.51.0", - "@theia/plugin-dev": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/plugin-ext-vscode": "1.51.0", - "@theia/plugin-metrics": "1.51.0", - "@theia/preferences": "1.51.0", - "@theia/preview": "1.51.0", - "@theia/process": "1.51.0", - "@theia/property-view": "1.51.0", - "@theia/scm": "1.51.0", - "@theia/scm-extra": "1.51.0", - "@theia/search-in-workspace": "1.51.0", - "@theia/secondary-window": "1.51.0", - "@theia/task": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/timeline": "1.51.0", - "@theia/toolbar": "1.51.0", - "@theia/typehierarchy": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/vsx-registry": "1.51.0", - "@theia/workspace": "1.51.0" + "@theia/api-samples": "1.52.0", + "@theia/bulk-edit": "1.52.0", + "@theia/callhierarchy": "1.52.0", + "@theia/console": "1.52.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/editor-preview": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/getting-started": "1.52.0", + "@theia/git": "1.52.0", + "@theia/keymaps": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/memory-inspector": "1.52.0", + "@theia/messages": "1.52.0", + "@theia/metrics": "1.52.0", + "@theia/mini-browser": "1.52.0", + "@theia/monaco": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/outline-view": "1.52.0", + "@theia/output": "1.52.0", + "@theia/plugin-dev": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/plugin-ext-vscode": "1.52.0", + "@theia/plugin-metrics": "1.52.0", + "@theia/preferences": "1.52.0", + "@theia/preview": "1.52.0", + "@theia/process": "1.52.0", + "@theia/property-view": "1.52.0", + "@theia/scm": "1.52.0", + "@theia/scm-extra": "1.52.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/secondary-window": "1.52.0", + "@theia/task": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/timeline": "1.52.0", + "@theia/toolbar": "1.52.0", + "@theia/typehierarchy": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/vsx-registry": "1.52.0", + "@theia/workspace": "1.52.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -73,6 +73,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.51.0" + "@theia/cli": "1.52.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index 3a2cf7d6d79c3..5a938e845cb07 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.51.0", + "version": "1.52.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,54 +20,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.51.0", - "@theia/api-samples": "1.51.0", - "@theia/bulk-edit": "1.51.0", - "@theia/callhierarchy": "1.51.0", - "@theia/console": "1.51.0", - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", - "@theia/dev-container": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/editor-preview": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/getting-started": "1.51.0", - "@theia/keymaps": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/memory-inspector": "1.51.0", - "@theia/messages": "1.51.0", - "@theia/metrics": "1.51.0", - "@theia/mini-browser": "1.51.0", - "@theia/monaco": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/notebook": "1.51.0", - "@theia/outline-view": "1.51.0", - "@theia/output": "1.51.0", - "@theia/plugin-dev": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/plugin-ext-headless": "1.51.0", - "@theia/plugin-ext-vscode": "1.51.0", - "@theia/plugin-metrics": "1.51.0", - "@theia/preferences": "1.51.0", - "@theia/preview": "1.51.0", - "@theia/process": "1.51.0", - "@theia/property-view": "1.51.0", - "@theia/remote": "1.51.0", - "@theia/scm": "1.51.0", - "@theia/scm-extra": "1.51.0", - "@theia/search-in-workspace": "1.51.0", - "@theia/secondary-window": "1.51.0", - "@theia/task": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/test": "1.51.0", - "@theia/timeline": "1.51.0", - "@theia/toolbar": "1.51.0", - "@theia/typehierarchy": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/vsx-registry": "1.51.0", - "@theia/workspace": "1.51.0" + "@theia/api-provider-sample": "1.52.0", + "@theia/api-samples": "1.52.0", + "@theia/bulk-edit": "1.52.0", + "@theia/callhierarchy": "1.52.0", + "@theia/console": "1.52.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", + "@theia/dev-container": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/editor-preview": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/getting-started": "1.52.0", + "@theia/keymaps": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/memory-inspector": "1.52.0", + "@theia/messages": "1.52.0", + "@theia/metrics": "1.52.0", + "@theia/mini-browser": "1.52.0", + "@theia/monaco": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/notebook": "1.52.0", + "@theia/outline-view": "1.52.0", + "@theia/output": "1.52.0", + "@theia/plugin-dev": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/plugin-ext-headless": "1.52.0", + "@theia/plugin-ext-vscode": "1.52.0", + "@theia/plugin-metrics": "1.52.0", + "@theia/preferences": "1.52.0", + "@theia/preview": "1.52.0", + "@theia/process": "1.52.0", + "@theia/property-view": "1.52.0", + "@theia/remote": "1.52.0", + "@theia/scm": "1.52.0", + "@theia/scm-extra": "1.52.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/secondary-window": "1.52.0", + "@theia/task": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/test": "1.52.0", + "@theia/timeline": "1.52.0", + "@theia/toolbar": "1.52.0", + "@theia/typehierarchy": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/vsx-registry": "1.52.0", + "@theia/workspace": "1.52.0" }, "scripts": { "clean": "theia clean", @@ -90,6 +90,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.51.0" + "@theia/cli": "1.52.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 8fe7d066678ba..44c941c225780 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.51.0", + "version": "1.52.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -26,53 +26,53 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.51.0", - "@theia/api-samples": "1.51.0", - "@theia/bulk-edit": "1.51.0", - "@theia/callhierarchy": "1.51.0", - "@theia/console": "1.51.0", - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", - "@theia/dev-container": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/editor-preview": "1.51.0", - "@theia/electron": "1.51.0", - "@theia/external-terminal": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/getting-started": "1.51.0", - "@theia/keymaps": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/memory-inspector": "1.51.0", - "@theia/messages": "1.51.0", - "@theia/metrics": "1.51.0", - "@theia/mini-browser": "1.51.0", - "@theia/monaco": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/outline-view": "1.51.0", - "@theia/output": "1.51.0", - "@theia/plugin-dev": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/plugin-ext-headless": "1.51.0", - "@theia/plugin-ext-vscode": "1.51.0", - "@theia/preferences": "1.51.0", - "@theia/preview": "1.51.0", - "@theia/process": "1.51.0", - "@theia/property-view": "1.51.0", - "@theia/remote": "1.51.0", - "@theia/scm": "1.51.0", - "@theia/scm-extra": "1.51.0", - "@theia/search-in-workspace": "1.51.0", - "@theia/secondary-window": "1.51.0", - "@theia/task": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/timeline": "1.51.0", - "@theia/toolbar": "1.51.0", - "@theia/typehierarchy": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/vsx-registry": "1.51.0", - "@theia/workspace": "1.51.0" + "@theia/api-provider-sample": "1.52.0", + "@theia/api-samples": "1.52.0", + "@theia/bulk-edit": "1.52.0", + "@theia/callhierarchy": "1.52.0", + "@theia/console": "1.52.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", + "@theia/dev-container": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/editor-preview": "1.52.0", + "@theia/electron": "1.52.0", + "@theia/external-terminal": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/getting-started": "1.52.0", + "@theia/keymaps": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/memory-inspector": "1.52.0", + "@theia/messages": "1.52.0", + "@theia/metrics": "1.52.0", + "@theia/mini-browser": "1.52.0", + "@theia/monaco": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/outline-view": "1.52.0", + "@theia/output": "1.52.0", + "@theia/plugin-dev": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/plugin-ext-headless": "1.52.0", + "@theia/plugin-ext-vscode": "1.52.0", + "@theia/preferences": "1.52.0", + "@theia/preview": "1.52.0", + "@theia/process": "1.52.0", + "@theia/property-view": "1.52.0", + "@theia/remote": "1.52.0", + "@theia/scm": "1.52.0", + "@theia/scm-extra": "1.52.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/secondary-window": "1.52.0", + "@theia/task": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/timeline": "1.52.0", + "@theia/toolbar": "1.52.0", + "@theia/typehierarchy": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/vsx-registry": "1.52.0", + "@theia/workspace": "1.52.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -90,7 +90,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.51.0", + "@theia/cli": "1.52.0", "electron": "^28.2.8" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index 893409c36ee98..d9dd3f72aabb5 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.51.0", + "version": "1.52.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index ac67df1f870bd..fedeabfeeb7d2 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.51.0", + "version": "1.52.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 8eded5a23b373..0278d46c71001 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.51.0", + "@theia/workspace": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index 52b5693a6dff2..5caaaee70073d 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 1ebe00e4ac453..5f5f209d0a16b 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index a2076ce8e40f5..3a20fa1bb443f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.51.0", - "@theia/request": "1.51.0", + "@theia/application-package": "1.52.0", + "@theia/request": "1.52.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -209,8 +209,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", - "@theia/re-exports": "1.51.0", + "@theia/ext-scripts": "1.52.0", + "@theia/re-exports": "1.52.0", "minimist": "^1.2.0" }, "nyc": { diff --git a/packages/debug/package.json b/packages/debug/package.json index 16feb45ee2f0c..ae1a8e14c2dd4 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,22 +1,22 @@ { "name": "@theia/debug", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.51.0", - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/console": "1.52.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.51.0", - "@theia/process": "1.51.0", - "@theia/task": "1.51.0", - "@theia/test": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/output": "1.52.0", + "@theia/process": "1.52.0", + "@theia/task": "1.52.0", + "@theia/test": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/workspace": "1.52.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -59,7 +59,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index f42c154f1cc81..dde7c2c865464 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,12 +1,12 @@ { "name": "@theia/dev-container", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/output": "1.51.0", - "@theia/remote": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/output": "1.52.0", + "@theia/remote": "1.52.0", + "@theia/workspace": "1.52.0", "dockerode": "^4.0.2", "jsonc-parser": "^2.2.0", "uuid": "^8.0.0" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index efb642b5c65de..7c1e4566da0ab 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/navigator": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/navigator": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index d4487ea22e004..0556960495382 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/variable-resolver": "1.51.0", + "@theia/core": "1.52.0", + "@theia/variable-resolver": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index f9f583db04432..1b74de295884b 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", - "@theia/re-exports": "1.51.0" + "@theia/ext-scripts": "1.52.0", + "@theia/re-exports": "1.52.0" }, "peerDependencies": { "electron": "^28.2.8" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index cf83d87ebc712..f125bcc1283c8 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/workspace": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 61266eb262e21..48a4598b2782c 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/process": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/process": "1.52.0", + "@theia/workspace": "1.52.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index bbd24ef6c1243..cf7ea16b482f5 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -73,7 +73,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 6366ef4f0314c..0877622695314 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/keymaps": "1.51.0", - "@theia/preview": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/keymaps": "1.52.0", + "@theia/preview": "1.52.0", + "@theia/workspace": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index d004ba306cffb..6af6d23eca82c 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.51.0", - "@theia/scm": "1.51.0", - "@theia/scm-extra": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/navigator": "1.52.0", + "@theia/scm": "1.52.0", + "@theia/scm-extra": "1.52.0", + "@theia/workspace": "1.52.0", "@types/diff": "^5.2.1", "@types/p-queue": "^2.3.1", "diff": "^5.2.0", @@ -67,7 +67,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index bd73b3b64b7c0..8df3ca28c183c 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.51.0", - "@theia/userstorage": "1.51.0", + "@theia/preferences": "1.52.0", + "@theia/userstorage": "1.52.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index 6f29f80ea10a1..a4ae243d95287 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/workspace": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 4be8ab829fdbf..2510402629ac8 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index 9e79a7257bcb4..8ea723c887ccf 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 85ab7f4b07c8d..cc53ef925b854 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 01177f948bfd8..863c2f399e72f 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 0ae02b36fdf02..93417d193a7bc 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/markers": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/markers": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/outline-view": "1.52.0", + "@theia/workspace": "1.52.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index 2bf10cb28e6af..92480dfdf2906 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/workspace": "1.52.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 0c0e083df9f53..89f7d87f6840c 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,14 +1,14 @@ { "name": "@theia/notebook", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.51.0", + "@theia/outline-view": "1.52.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/markdown-it": "^12.2.3", "@types/vscode-notebook-renderer": "^1.72.0" }, diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index 586c17b1902e7..1d0935e68949b 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index ad73b31db7d11..6202626d4b471 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index cf30ce68ffc57..53dbfea468f0c 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/output": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/output": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/workspace": "1.52.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index 3e9ae8bf70fb4..964d6cbd0704c 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/terminal": "1.51.0", + "@theia/core": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/terminal": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 3fbc32bf71c4c..75d95b98bb1c7 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,22 +1,22 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.51.0", - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/callhierarchy": "1.52.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.51.0", - "@theia/outline-view": "1.51.0", - "@theia/plugin": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/typehierarchy": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/navigator": "1.52.0", + "@theia/outline-view": "1.52.0", + "@theia/plugin": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/typehierarchy": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/workspace": "1.52.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index e4471dec9b90d..43fa009a22eca 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.51.0", - "@theia/callhierarchy": "1.51.0", - "@theia/console": "1.51.0", - "@theia/core": "1.51.0", - "@theia/debug": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/editor-preview": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/messages": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/bulk-edit": "1.52.0", + "@theia/callhierarchy": "1.52.0", + "@theia/console": "1.52.0", + "@theia/core": "1.52.0", + "@theia/debug": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/editor-preview": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/messages": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.51.0", - "@theia/notebook": "1.51.0", - "@theia/output": "1.51.0", - "@theia/plugin": "1.51.0", - "@theia/preferences": "1.51.0", - "@theia/scm": "1.51.0", - "@theia/search-in-workspace": "1.51.0", - "@theia/task": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/test": "1.51.0", - "@theia/timeline": "1.51.0", - "@theia/typehierarchy": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/navigator": "1.52.0", + "@theia/notebook": "1.52.0", + "@theia/output": "1.52.0", + "@theia/plugin": "1.52.0", + "@theia/preferences": "1.52.0", + "@theia/scm": "1.52.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/task": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/test": "1.52.0", + "@theia/timeline": "1.52.0", + "@theia/typehierarchy": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/workspace": "1.52.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 281dd4677b5fe..3f54efcc0f54d 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.51.0", - "@theia/metrics": "1.51.0", + "@theia/core": "1.52.0", + "@theia/metrics": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.51.0", - "@theia/plugin-ext": "1.51.0", + "@theia/plugin": "1.52.0", + "@theia/plugin-ext": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 1edb66f81bc66..2ecf9c5d15bf8 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 71c2ab1814299..8b0513b9e860a 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/userstorage": "1.52.0", + "@theia/workspace": "1.52.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index 372e0abd21bc4..c0a876920461f 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/mini-browser": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/mini-browser": "1.52.0", + "@theia/monaco": "1.52.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index bdc16a5f8053c..3b0b8d4158ac9 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index b6159cb323dcd..74880056d9d3e 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 72ff1f1562162..246c914c5a54f 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index d13366d0f91e3..675d0ef784dd6 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/scm": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/scm": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index 5ab8f2e3dc63a..06a55db61c073 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,12 +1,12 @@ { "name": "@theia/scm", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^5.2.1", "diff": "^5.2.0", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 64683c217ecba..73facb28b55bd 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/process": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/process": "1.52.0", + "@theia/workspace": "1.52.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index a012a6c276bf0..ad8ba480d9be1 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index d3d30cf14ee25..6b136daa5492e 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/markers": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/markers": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.51.0", - "@theia/terminal": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/process": "1.52.0", + "@theia/terminal": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/workspace": "1.52.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 8822c064f2700..439290b6e72aa 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/process": "1.51.0", - "@theia/variable-resolver": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/process": "1.52.0", + "@theia/variable-resolver": "1.52.0", + "@theia/workspace": "1.52.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index 58f0230cb45c5..d1e601bf53328 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/terminal": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/terminal": "1.52.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index ec14d70162c61..760112f5bbbd9 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/navigator": "1.51.0", + "@theia/core": "1.52.0", + "@theia/navigator": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index 296b41e0195e4..e75806cf534de 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", - "@theia/file-search": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/monaco": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/file-search": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.51.0", - "@theia/userstorage": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/search-in-workspace": "1.52.0", + "@theia/userstorage": "1.52.0", + "@theia/workspace": "1.52.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index 23dbf1198a79f..ef90ebaa08de3 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/editor": "1.51.0", + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index a880d8caf10eb..3eb1a5c484d6e 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index f8ce61d361b47..0e27cc4599295 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.51.0", + "@theia/core": "1.52.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 28f162a2c4981..25d2e4b2d37d5 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/navigator": "1.51.0", - "@theia/ovsx-client": "1.51.0", - "@theia/plugin-ext": "1.51.0", - "@theia/plugin-ext-vscode": "1.51.0", - "@theia/preferences": "1.51.0", - "@theia/workspace": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/navigator": "1.52.0", + "@theia/ovsx-client": "1.52.0", + "@theia/plugin-ext": "1.52.0", + "@theia/plugin-ext-vscode": "1.52.0", + "@theia/preferences": "1.52.0", + "@theia/workspace": "1.52.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", @@ -54,7 +54,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0", + "@theia/ext-scripts": "1.52.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index e89dfb55ec25e..f361e8e65400f 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.51.0", + "version": "1.52.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.51.0", - "@theia/filesystem": "1.51.0", - "@theia/variable-resolver": "1.51.0", + "@theia/core": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/variable-resolver": "1.52.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.51.0" + "@theia/ext-scripts": "1.52.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 34fde6b5a5358..1ab56e9efe0bb 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.51.0", + "version": "1.52.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index ba54d82b6b9ce..89315d68ce7d2 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.51.0", + "version": "1.52.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index 91a941cc1aa7b..637a229bc13ef 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.51.0", + "version": "1.52.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.51.0" + "@theia/api-provider-sample": "1.52.0" }, "scripts": { "prepare": "yarn -s package", From 63b57c69e230d4737c6493ee5a979e3bfac9d724 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Thu, 25 Jul 2024 10:15:20 +0200 Subject: [PATCH 326/441] Fix flaky playwright Theia Main Menu test We are observing cases where the `.getByRole('button', { name: 'OK' })` resolved to multiple elements (e.g. [here](https://github.com/eclipse-theia/theia/actions/runs/10025491100)). With this change we limit the selection to the dialog shell and hopefully remove this ambiguity. Contributed on behalf of STMicroelectronics. Change-Id: I5ee5b3fc6a683f68da99e219b32784c3f5d4bfb7 --- examples/playwright/src/tests/theia-main-menu.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/playwright/src/tests/theia-main-menu.test.ts b/examples/playwright/src/tests/theia-main-menu.test.ts index a8807535f533d..919cc0400b05f 100644 --- a/examples/playwright/src/tests/theia-main-menu.test.ts +++ b/examples/playwright/src/tests/theia-main-menu.test.ts @@ -97,7 +97,7 @@ test.describe('Theia Main Menu', () => { await (await menuBar.openMenu('Help')).clickMenuItem('About'); const aboutDialog = new TheiaAboutDialog(app); expect(await aboutDialog.isVisible()).toBe(true); - await aboutDialog.page.getByRole('button', { name: 'OK' }).click(); + await aboutDialog.page.locator('#theia-dialog-shell').getByRole('button', { name: 'OK' }).click(); expect(await aboutDialog.isVisible()).toBe(false); }); From 8d247e0e16062d83253effe4bc4efae1a51a26a3 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 5 Aug 2024 15:13:02 +0200 Subject: [PATCH 327/441] Support find widget in notebooks (#13982) --- dependency-check-baseline.json | 2 +- packages/core/src/browser/browser.ts | 11 +- .../browser/common-frontend-contribution.ts | 101 ++++++ packages/notebook/package.json | 1 + .../notebook-actions-contribution.ts | 22 +- .../notebook-cell-actions-contribution.ts | 11 +- .../src/browser/notebook-editor-widget.tsx | 51 ++- .../service/notebook-context-manager.ts | 12 +- packages/notebook/src/browser/style/index.css | 150 ++++++++ .../browser/view-model/notebook-cell-model.ts | 84 +++++ .../src/browser/view-model/notebook-model.ts | 42 ++- .../src/browser/view/notebook-cell-editor.tsx | 83 ++++- .../browser/view/notebook-cell-list-view.tsx | 23 +- .../src/browser/view/notebook-find-widget.tsx | 335 ++++++++++++++++++ .../view/notebook-markdown-cell-view.tsx | 99 +++++- yarn.lock | 36 +- 16 files changed, 1007 insertions(+), 56 deletions(-) create mode 100644 packages/notebook/src/browser/view/notebook-find-widget.tsx diff --git a/dependency-check-baseline.json b/dependency-check-baseline.json index 5790ba796de8f..b4146da9836f4 100644 --- a/dependency-check-baseline.json +++ b/dependency-check-baseline.json @@ -1,3 +1,3 @@ { - "npm/npmjs/@types/qs/6.9.11": "Pending https://gitlab.eclipse.org/eclipsefdn/emo-team/iplab/-/issues/13990" + "npm/npmjs/-/advanced-mark.js/2.6.0": "Manually approved" } diff --git a/packages/core/src/browser/browser.ts b/packages/core/src/browser/browser.ts index 88291aaa9ac91..5cab4f39631ad 100644 --- a/packages/core/src/browser/browser.ts +++ b/packages/core/src/browser/browser.ts @@ -18,7 +18,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { environment } from '../common'; +import { Disposable, environment } from '../common'; const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : ''; @@ -228,3 +228,12 @@ function getMeasurementElement(style?: PartialCSSStyle): HTMLElement { } return measureElement; } + +export function onDomEvent( + element: Node, + type: K, + listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => unknown, + options?: boolean | AddEventListenerOptions): Disposable { + element.addEventListener(type, listener, options); + return { dispose: () => element.removeEventListener(type, listener, options) }; +} diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index fb9cd421f9eea..15fe68122380c 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -444,6 +444,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi protected readonly untitledResourceResolver: UntitledResourceResolver; protected pinnedKey: ContextKey; + protected inputFocus: ContextKey; async configure(app: FrontendApplication): Promise { // FIXME: This request blocks valuable startup time (~200ms). @@ -458,6 +459,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi this.contextKeyService.createKey('isMac', OS.type() === OS.Type.OSX); this.contextKeyService.createKey('isWindows', OS.type() === OS.Type.Windows); this.contextKeyService.createKey('isWeb', !this.isElectron()); + this.inputFocus = this.contextKeyService.createKey('inputFocus', false); + this.updateInputFocus(); + browser.onDomEvent(document, 'focusin', () => this.updateInputFocus()); this.pinnedKey = this.contextKeyService.createKey('activeEditorIsPinned', false); this.updatePinnedKey(); @@ -513,6 +517,15 @@ export class CommonFrontendContribution implements FrontendApplicationContributi } } + protected updateInputFocus(): void { + const activeElement = document.activeElement; + if (activeElement) { + const isInput = activeElement.tagName?.toLowerCase() === 'input' + || activeElement.tagName?.toLowerCase() === 'textarea'; + this.inputFocus.set(isInput); + } + } + protected updatePinnedKey(): void { const activeTab = this.shell.findTabBar(); const pinningTarget = activeTab && this.shell.findTitle(activeTab); @@ -1899,6 +1912,94 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }, description: 'Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.' }, + // editor find + + { + id: 'editor.findMatchBackground', + defaults: { + light: '#A8AC94', + dark: '#515C6A', + hcDark: undefined, + hcLight: undefined + }, + description: 'Color of the current search match.' + }, + + { + id: 'editor.findMatchForeground', + defaults: { + light: undefined, + dark: undefined, + hcDark: undefined, + hcLight: undefined + }, + description: 'Text color of the current search match.' + }, + { + id: 'editor.findMatchHighlightBackground', + defaults: { + light: '#EA5C0055', + dark: '#EA5C0055', + hcDark: undefined, + hcLight: undefined + }, + description: 'Color of the other search matches. The color must not be opaque so as not to hide underlying decorations.' + }, + + { + id: 'editor.findMatchHighlightForeground', + defaults: { + light: undefined, + dark: undefined, + hcDark: undefined, + hcLight: undefined + }, + description: 'Foreground color of the other search matches.' + }, + + { + id: 'editor.findRangeHighlightBackground', + defaults: { + dark: '#3a3d4166', + light: '#b4b4b44d', + hcDark: undefined, + hcLight: undefined + }, + description: 'Color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.' + }, + + { + id: 'editor.findMatchBorder', + defaults: { + light: undefined, + dark: undefined, + hcDark: 'activeContrastBorder', + hcLight: 'activeContrastBorder' + }, + description: 'Border color of the current search match.' + }, + { + id: 'editor.findMatchHighlightBorder', + defaults: { + light: undefined, + dark: undefined, + hcDark: 'activeContrastBorder', + hcLight: 'activeContrastBorder' + }, + description: 'Border color of the other search matches.' + }, + + { + id: 'editor.findRangeHighlightBorder', + defaults: { + dark: undefined, + light: undefined, + hcDark: Color.transparent('activeContrastBorder', 0.4), + hcLight: Color.transparent('activeContrastBorder', 0.4) + }, + description: 'Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations.' + }, + // Quickinput colors should be aligned with https://code.visualstudio.com/api/references/theme-color#quick-picker // if not yet contributed by Monaco, check runtime css variables to learn. { diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 89f7d87f6840c..9a2f6bb8b8e9a 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -9,6 +9,7 @@ "@theia/monaco": "1.52.0", "@theia/monaco-editor-core": "1.83.101", "@theia/outline-view": "1.52.0", + "advanced-mark.js": "^2.6.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" }, diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index f62ebdef7d5b7..da1bcc065397f 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -83,6 +83,11 @@ export namespace NotebookCommands { id: 'notebook.cell.paste', category: 'Notebook', }); + + export const NOTEBOOK_FIND = Command.toDefaultLocalizedCommand({ + id: 'notebook.find', + category: 'Notebook', + }); } export enum CellChangeDirection { @@ -251,6 +256,12 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon } }); + commands.registerCommand(NotebookCommands.NOTEBOOK_FIND, { + execute: () => { + this.notebookEditorWidgetService.focusedEditor?.showFindWidget(); + } + }); + } protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { @@ -326,17 +337,22 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon { command: NotebookCommands.CUT_SELECTED_CELL.id, keybinding: 'ctrlcmd+x', - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus` }, { command: NotebookCommands.COPY_SELECTED_CELL.id, keybinding: 'ctrlcmd+c', - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}` + when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus` }, { command: NotebookCommands.PASTE_CELL.id, keybinding: 'ctrlcmd+v', - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}` + when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus` + }, + { + command: NotebookCommands.NOTEBOOK_FIND.id, + keybinding: 'ctrlcmd+f', + when: `${NOTEBOOK_EDITOR_FOCUSED}` }, ); } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 4b4242df805cb..cafeddddde6e1 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -22,7 +22,8 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED, - NOTEBOOK_CELL_FOCUSED + NOTEBOOK_CELL_FOCUSED, + NOTEBOOK_CELL_LIST_FOCUSED } from './notebook-context-keys'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; @@ -418,12 +419,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.EDIT_COMMAND.id, keybinding: 'Enter', - when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), - when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + when: `editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, @@ -433,12 +434,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(), - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, + when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(), - when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, { command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id, diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index de32c093ac735..2dc5723fcfc03 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -33,6 +33,8 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import { NotebookContextManager } from './service/notebook-context-manager'; import { NotebookViewportService } from './view/notebook-viewport-service'; import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution'; +import { NotebookFindWidget } from './view/notebook-find-widget'; +import debounce = require('lodash/debounce'); const PerfectScrollbar = require('react-perfect-scrollbar'); export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory'); @@ -126,7 +128,16 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected readonly renderers = new Map(); protected _model?: NotebookModel; protected _ready: Deferred = new Deferred(); + protected _findWidgetVisible = false; + protected _findWidgetRef = React.createRef(); protected scrollBarRef = React.createRef<{ updateScroll(): void }>(); + protected debounceFind = debounce(() => { + this._findWidgetRef.current?.search({}); + }, 30, { + trailing: true, + maxWait: 100, + leading: false + }); get notebookType(): string { return this.props.notebookType; @@ -177,6 +188,11 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa // Wait one frame to ensure that the content has been rendered animationFrame().then(() => this.scrollBarRef.current?.updateScroll()); })); + this.toDispose.push(this._model.onContentChanged(() => { + if (this._findWidgetVisible) { + this.debounceFind(); + } + })); this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => { if (readOnly) { lock(this.title); @@ -220,18 +236,41 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa protected render(): ReactNode { if (this._model) { return
    +
    +
    {this.notebookMainToolbarRenderer.render(this._model, this.node)} -
    this.viewportService.viewportElement = ref}> +
    this.viewportService.viewportElement = ref} + > this.viewportService.onScroll(e)}>
    -
    ; +
    ; } else { return
    @@ -260,6 +299,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.onDidChangeOutputInputFocusEmitter.fire(focused); } + showFindWidget(): void { + if (!this._findWidgetVisible) { + this._findWidgetVisible = true; + this.update(); + } + this._findWidgetRef.current?.focusSearch(this._model?.selectedText); + } + override dispose(): void { this.notebookContextManager.dispose(); this.onDidChangeModelEmitter.dispose(); diff --git a/packages/notebook/src/browser/service/notebook-context-manager.ts b/packages/notebook/src/browser/service/notebook-context-manager.ts index 97db41fcae7de..9216285c303e4 100644 --- a/packages/notebook/src/browser/service/notebook-context-manager.ts +++ b/packages/notebook/src/browser/service/notebook-context-manager.ts @@ -21,7 +21,7 @@ import { NotebookKernelService } from './notebook-kernel-service'; import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, - NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, + NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_OUTPUT_INPUT_FOCUSED, NOTEBOOK_VIEW_TYPE @@ -85,7 +85,7 @@ export class NotebookContextManager { this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, !!widget.model?.cells.find(cell => cell.outputs.length > 0)); - // Cell Selection realted keys + // Cell Selection related keys this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell); widget.model?.onDidChangeSelectedCell(e => { this.selectedCellChanged(e.cell); @@ -144,8 +144,12 @@ export class NotebookContextManager { return this.contextKeyService.createOverlay(Object.entries(this.cellContexts.get(cellHandle) ?? {})); } - onDidEditorTextFocus(focus: boolean): void { - this.scopedStore.setContext('inputFocus', focus); + changeCellFocus(focus: boolean): void { + this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, focus); + } + + changeCellListFocus(focus: boolean): void { + this.scopedStore.setContext(NOTEBOOK_CELL_LIST_FOCUSED, focus); } createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent { diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 81bdae86536b9..12eb18739c08f 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -310,3 +310,153 @@ min-height: 100px; background-color: var(--theia-editor-background); } + +/* Notebook Find Widget */ + +.theia-notebook-overlay { + position: absolute; + z-index: 100; + right: 18px; +} + +.theia-notebook-find-widget { + /* position: absolute; + z-index: 35; + height: 33px; + overflow: hidden; */ + line-height: 19px; + transition: transform 200ms linear; + display: flex; + flex-direction: row; + padding: 0 4px; + box-sizing: border-box; + box-shadow: 0 0 8px 2px var(--theia-widget-shadow); + background-color: var(--theia-editorWidget-background); + color: var(--theia-editorWidget-foreground); + border-left: 1px solid var(--theia-widget-border); + border-right: 1px solid var(--theia-widget-border); + border-bottom: 1px solid var(--theia-widget-border); + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; +} + +.theia-notebook-find-widget.hidden { + display: none; + transform: translateY(calc(-100% - 10px)); +} + +.theia-notebook-find-widget.search-mode > * > *:nth-child(2) { + display: none; +} + +.theia-notebook-find-widget-expand { + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; + border-radius: 0; + margin-right: 4px; +} + +.theia-notebook-find-widget-expand:focus { + outline: 1px solid var(--theia-focusBorder); +} + +.theia-notebook-find-widget-expand:hover { + background-color: var(--theia-toolbar-hoverBackground); +} + +.theia-notebook-find-widget-buttons-first { + margin-bottom: 4px; + height: 26px; + display: flex; + flex-direction: row; + align-items: center; +} + +.theia-notebook-find-widget-buttons-first > div, +.theia-notebook-find-widget-buttons-second > div { + margin-right: 4px; +} + +.theia-notebook-find-widget-buttons-second { + height: 26px; + display: flex; + flex-direction: row; + align-items: center; +} + +.theia-notebook-find-widget-inputs { + margin-top: 4px; + display: flex; + flex-direction: column; +} + +.theia-notebook-find-widget-buttons { + margin-top: 4px; + margin-left: 4px; + display: flex; + flex-direction: column; +} + +.theia-notebook-find-widget-matches-count { + width: 72px; + box-sizing: border-box; + overflow: hidden; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; +} + +.theia-notebook-find-widget-input-wrapper { + display: flex; + align-items: center; + background: var(--theia-input-background); + border-style: solid; + border-width: var(--theia-border-width); + border-color: var(--theia-input-background); + border-radius: 2px; + margin-bottom: 4px; +} + +.theia-notebook-find-widget-input-wrapper:focus-within { + border-color: var(--theia-focusBorder); +} + +.theia-notebook-find-widget-input-wrapper .option.enabled { + color: var(--theia-inputOption-activeForeground); + outline: 1px solid var(--theia-inputOption-activeBorder); + background-color: var(--theia-inputOption-activeBackground); +} + +.theia-notebook-find-widget-input-wrapper .option { + margin: 2px; +} + +.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input:focus { + border: none; + outline: none; +} + +.theia-notebook-find-widget-input-wrapper .theia-notebook-find-widget-input { + background: none; + border: none; +} + +.theia-notebook-find-widget-replace { + margin-bottom: 4px; +} + +.theia-notebook-find-widget-buttons .disabled { + opacity: 0.5; +} + +mark.theia-find-match { + color: var(--theia-editor-findMatchHighlightForeground); + background-color: var(--theia-editor-findMatchHighlightBackground); +} + +mark.theia-find-match.theia-find-match-selected { + color: var(--theia-editor-findMatchForeground); + background-color: var(--theia-editor-findMatchBackground); +} diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index df4b2cf96c2cb..8272795d6f124 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -32,6 +32,8 @@ import { NotebookCellOutputModel } from './notebook-cell-output-model'; import { PreferenceService } from '@theia/core/lib/browser'; import { NotebookPreferences } from '../contributions/notebook-preferences'; import { LanguageService } from '@theia/core/lib/browser/language-service'; +import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from '../view/notebook-find-widget'; +import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; export const NotebookCellModelFactory = Symbol('NotebookModelFactory'); export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel; @@ -118,6 +120,12 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly outputVisibilityChangeEmitter = new Emitter(); readonly onDidChangeOutputVisibility: Event = this.outputVisibilityChangeEmitter.event; + protected readonly onDidFindMatchesEmitter = new Emitter(); + readonly onDidFindMatches: Event = this.onDidFindMatchesEmitter.event; + + protected readonly onDidSelectFindMatchEmitter = new Emitter(); + readonly onDidSelectFindMatch: Event = this.onDidSelectFindMatchEmitter.event; + @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @@ -368,6 +376,82 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onDidChangeOutputItemsEmitter.fire(output); } } + + onMarkdownFind: ((options: NotebookEditorFindMatchOptions) => NotebookEditorFindMatch[]) | undefined; + + showMatch(selected: NotebookCodeEditorFindMatch): void { + this.onDidSelectFindMatchEmitter.fire(selected); + } + + findMatches(options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] { + if (this.cellKind === CellKind.Markup && !this.editing) { + return this.onMarkdownFind?.(options) ?? []; + } + if (!this.textModel) { + return []; + } + const matches = options.search ? this.textModel.findMatches({ + searchString: options.search, + isRegex: options.regex, + matchCase: options.matchCase, + matchWholeWord: options.wholeWord + }) : []; + const editorFindMatches = matches.map(match => new NotebookCodeEditorFindMatch(this, match.range, this.textModel!)); + this.onDidFindMatchesEmitter.fire(editorFindMatches); + return editorFindMatches; + } + + replaceAll(matches: NotebookCodeEditorFindMatch[], value: string): void { + const editOperations = matches.map(match => ({ + range: { + startColumn: match.range.start.character, + startLineNumber: match.range.start.line, + endColumn: match.range.end.character, + endLineNumber: match.range.end.line + }, + text: value + })); + this.textModel?.textEditorModel.pushEditOperations( + // eslint-disable-next-line no-null/no-null + null, + editOperations, + // eslint-disable-next-line no-null/no-null + () => null); + } +} + +export interface NotebookCellFindMatches { + matches: NotebookEditorFindMatch[]; + selected: NotebookEditorFindMatch; +} + +export class NotebookCodeEditorFindMatch implements NotebookEditorFindMatch { + + selected = false; + + constructor(readonly cell: NotebookCellModel, readonly range: Range, readonly textModel: MonacoEditorModel) { + } + + show(): void { + this.cell.showMatch(this); + } + replace(value: string): void { + this.textModel.textEditorModel.pushEditOperations( + // eslint-disable-next-line no-null/no-null + null, + [{ + range: { + startColumn: this.range.start.character, + startLineNumber: this.range.start.line, + endColumn: this.range.end.character, + endLineNumber: this.range.end.line + }, + text: value + }], + // eslint-disable-next-line no-null/no-null + () => null); + } + } function computeRunStartTimeAdjustment(oldMetadata: NotebookCellInternalMetadata, newMetadata: NotebookCellInternalMetadata): number | undefined { diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 68e901157286b..e60b5f0ee2fff 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -29,12 +29,13 @@ import { } from '../notebook-types'; import { NotebookSerializer } from '../service/notebook-service'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model'; +import { NotebookCellModel, NotebookCellModelFactory, NotebookCodeEditorFindMatch } from './notebook-cell-model'; import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; import type { NotebookModelResolverService } from '../service/notebook-model-resolver-service'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; +import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from '../view/notebook-find-widget'; export const NotebookModelFactory = Symbol('NotebookModelFactory'); @@ -125,6 +126,16 @@ export class NotebookModel implements Saveable, Disposable { return this.props.resource.readOnly ?? false; } + protected _selectedText = ''; + + set selectedText(value: string) { + this._selectedText = value; + } + + get selectedText(): string { + return this._selectedText; + } + selectedCell?: NotebookCellModel; protected dirtyCells: NotebookCellModel[] = []; @@ -227,6 +238,9 @@ export class NotebookModel implements Saveable, Disposable { } this.dirty = this.dirtyCells.length > 0; + // Only fire `onContentChangedEmitter` here, because `onDidChangeContentEmitter` is used for model level changes only + // However, this event indicates that the content of a cell has changed + this.onContentChangedEmitter.fire(); } setData(data: NotebookData, markDirty = true): void { @@ -267,6 +281,9 @@ export class NotebookModel implements Saveable, Disposable { cell.onDidChangeOutputs(() => { this.dirty = true; }); + cell.onDidRequestCellEditChange(() => { + this.onContentChangedEmitter.fire(); + }); } } @@ -490,4 +507,27 @@ export class NotebookModel implements Saveable, Disposable { return false; } + findMatches(options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] { + const matches: NotebookEditorFindMatch[] = []; + for (const cell of this.cells) { + matches.push(...cell.findMatches(options)); + } + return matches; + } + + replaceAll(matches: NotebookEditorFindMatch[], text: string): void { + const matchMap = new Map(); + for (const match of matches) { + if (match instanceof NotebookCodeEditorFindMatch) { + if (!matchMap.has(match.cell)) { + matchMap.set(match.cell, []); + } + matchMap.get(match.cell)?.push(match); + } + } + for (const [cell, cellMatches] of matchMap) { + cell.replaceAll(cellMatches, text); + } + } + } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index cc7260abc2e5c..de920fc66ee8e 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -16,7 +16,7 @@ import * as React from '@theia/core/shared/react'; import { NotebookModel } from '../view-model/notebook-model'; -import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { NotebookCellModel, NotebookCodeEditorFindMatch } from '../view-model/notebook-cell-model'; import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; import { MonacoEditor, MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider'; @@ -27,6 +27,9 @@ import { NotebookViewportService } from './notebook-viewport-service'; import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo'; import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE } from '../contributions/notebook-context-keys'; import { EditorExtensionsRegistry } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorExtensions'; +import { ModelDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel'; +import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; +import { animationFrame } from '@theia/core/lib/browser'; interface CellEditorProps { notebookModel: NotebookModel, @@ -48,11 +51,39 @@ const DEFAULT_EDITOR_OPTIONS: MonacoEditor.IOptions = { lineDecorationsWidth: 10, }; +export const CURRENT_FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + description: 'current-find-match', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + zIndex: 13, + className: 'currentFindMatch', + inlineClassName: 'currentFindMatchInline', + showIfCollapsed: true, + overviewRuler: { + color: 'editorOverviewRuler.findMatchForeground', + position: OverviewRulerLane.Center + } +}); + +export const FIND_MATCH_DECORATION = ModelDecorationOptions.register({ + description: 'find-match', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + zIndex: 10, + className: 'findMatch', + inlineClassName: 'findMatchInline', + showIfCollapsed: true, + overviewRuler: { + color: 'editorOverviewRuler.findMatchForeground', + position: OverviewRulerLane.Center + } +}); + export class CellEditor extends React.Component { protected editor?: SimpleMonacoEditor; protected toDispose = new DisposableCollection(); protected container?: HTMLDivElement; + protected matches: NotebookCodeEditorFindMatch[] = []; + protected oldMatchDecorations: string[] = []; override componentDidMount(): void { this.disposeEditor(); @@ -68,7 +99,6 @@ export class CellEditor extends React.Component { const currentLine = this.editor?.getControl().getPosition()?.lineNumber; this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_FIRST_LINE, currentLine === 1); this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount); - })); this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { @@ -79,6 +109,26 @@ export class CellEditor extends React.Component { this.editor?.setLanguage(language); })); + this.toDispose.push(this.props.cell.onDidFindMatches(matches => { + this.matches = matches; + animationFrame().then(() => this.setMatches()); + })); + + this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => { + const editorDomNode = this.editor?.getControl().getDomNode(); + if (editorDomNode) { + editorDomNode.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } else { + this.container?.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } + })); + this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => { if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { this.props.notebookContextManager.context?.focus(); @@ -130,11 +180,11 @@ export class CellEditor extends React.Component { notebookModel.cellDirtyChanged(cell, true); })); this.toDispose.push(this.editor.getControl().onDidFocusEditorText(() => { - this.props.notebookContextManager.onDidEditorTextFocus(true); this.props.notebookModel.setSelectedCell(cell, false); })); - this.toDispose.push(this.editor.getControl().onDidBlurEditorText(() => { - this.props.notebookContextManager.onDidEditorTextFocus(false); + this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => { + const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection); + this.props.notebookModel.selectedText = selectedText; })); this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => { if (e.secondaryPositions.length === 0) { @@ -149,7 +199,30 @@ export class CellEditor extends React.Component { if (cell.editing && notebookModel.selectedCell === cell) { this.editor.getControl().focus(); } + this.setMatches(); + } + } + + protected setMatches(): void { + if (!this.editor) { + return; } + const decorations: IModelDeltaDecoration[] = []; + for (const match of this.matches) { + const decoration = match.selected ? CURRENT_FIND_MATCH_DECORATION : FIND_MATCH_DECORATION; + decorations.push({ + range: { + startLineNumber: match.range.start.line, + startColumn: match.range.start.character, + endLineNumber: match.range.end.line, + endColumn: match.range.end.character + }, + options: decoration + }); + } + + this.oldMatchDecorations = this.editor.getControl() + .changeDecorations(accessor => accessor.deltaDecorations(this.oldMatchDecorations, decorations)); } protected setContainer(component: HTMLDivElement | null): void { diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 4ac04b13d785c..a9ecbfc3e3a0f 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -18,10 +18,11 @@ import { CellEditType, CellKind } from '../../common'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory'; -import { codicon } from '@theia/core/lib/browser'; +import { animationFrame, codicon, onDomEvent } from '@theia/core/lib/browser'; import { CommandRegistry, DisposableCollection, nls } from '@theia/core'; import { NotebookCommands } from '../contributions/notebook-actions-contribution'; import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution'; +import { NotebookContextManager } from '../service/notebook-context-manager'; export interface CellRenderer { render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode @@ -31,6 +32,7 @@ export interface CellRenderer { interface CellListProps { renderers: Map; notebookModel: NotebookModel; + notebookContext: NotebookContextManager; toolbarRenderer: NotebookCellToolbarFactory; commandRegistry: CommandRegistry } @@ -80,7 +82,24 @@ export class NotebookCellListView extends React.Component + return
      { + this.toDispose.push(onDomEvent(document, 'focusin', () => { + animationFrame().then(() => { + let hasCellFocus = false; + let hasFocus = false; + if (ref?.contains(document.activeElement)) { + if (this.props.notebookModel.selectedCell) { + hasCellFocus = true; + } + hasFocus = true; + } + this.props.notebookContext.changeCellFocus(hasCellFocus); + this.props.notebookContext.changeCellListFocus(hasFocus); + }); + })); + } + }> {this.props.notebookModel.cells .map((cell, index) => diff --git a/packages/notebook/src/browser/view/notebook-find-widget.tsx b/packages/notebook/src/browser/view/notebook-find-widget.tsx new file mode 100644 index 0000000000000..be643c1a55509 --- /dev/null +++ b/packages/notebook/src/browser/view/notebook-find-widget.tsx @@ -0,0 +1,335 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import * as React from '@theia/core/shared/react'; +import { codicon } from '@theia/core/lib/browser'; +import debounce = require('lodash/debounce'); + +export interface NotebookEditorFindMatch { + selected: boolean; + show(): void; + replace?(value: string): void; +} + +export interface NotebookEditorFindMatchOptions { + search: string; + matchCase: boolean; + wholeWord: boolean; + regex: boolean; + activeFilters: string[]; +} + +export interface NotebookEditorFindFilter { + id: string; + label: string; + active: boolean; +} + +export interface NotebookEditorFindOptions { + search?: string; + jumpToMatch?: boolean; + matchCase?: boolean; + wholeWord?: boolean; + regex?: boolean; + modifyIndex?: (matches: NotebookEditorFindMatch[], index: number) => number; +} + +export interface NotebookFindWidgetProps { + hidden?: boolean; + filters?: NotebookEditorFindFilter[]; + onClose(): void; + onSearch(options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[]; + onReplace(matches: NotebookEditorFindMatch[], value: string): void; +} + +export interface NotebookFindWidgetState { + search: string; + replace: string; + expanded: boolean; + matchCase: boolean; + wholeWord: boolean; + regex: boolean; + activeFilters: string[]; + currentMatch: number; + matches: NotebookEditorFindMatch[]; +} + +export class NotebookFindWidget extends React.Component { + + private searchRef = React.createRef(); + private debounceSearch = debounce(this.search.bind(this), 50); + + constructor(props: NotebookFindWidgetProps) { + super(props); + this.state = { + search: '', + replace: '', + currentMatch: 0, + matches: [], + expanded: false, + matchCase: false, + regex: false, + wholeWord: false, + activeFilters: props.filters?.filter(filter => filter.active).map(filter => filter.id) || [] + }; + } + + override render(): React.ReactNode { + const hasMatches = this.hasMatches(); + const canReplace = this.canReplace(); + const canReplaceAll = this.canReplaceAll(); + return ( +
      { + if (event.key === 'Escape') { + this.props.onClose(); + } + }} className={`theia-notebook-find-widget ${!this.state.expanded ? 'search-mode' : ''} ${this.props.hidden ? 'hidden' : ''}`}> +
      { + this.setState({ + expanded: !this.state.expanded + }); + }}> +
      +
      +
      +
      + { + this.setState({ + search: event.target.value + }); + this.debounceSearch({}); + }} + onKeyDown={event => { + if (event.key === 'Enter') { + if (event.shiftKey) { + this.gotoPreviousMatch(); + } else { + this.gotoNextMatch(); + } + event.preventDefault(); + } + }} + /> +
      { + this.search({ + matchCase: !this.state.matchCase + }); + }}>
      +
      { + this.search({ + wholeWord: !this.state.wholeWord + }); + }}>
      +
      { + this.search({ + regex: !this.state.regex + }); + }}>
      + {/*
      */} +
      + { + this.setState({ + replace: event.target.value + }); + }} + onKeyDown={event => { + if (event.key === 'Enter') { + this.replaceOne(); + event.preventDefault(); + } + }} + /> +
      +
      +
      +
      + {this.getMatchesCount()} +
      +
      { + this.gotoPreviousMatch(); + }} + >
      +
      { + this.gotoNextMatch(); + }} + >
      +
      { + this.props.onClose(); + }} + >
      +
      +
      +
      { + this.replaceOne(); + }} + >
      +
      { + this.replaceAll(); + }} + >
      +
      +
      +
      + ); + } + + private hasMatches(): boolean { + return this.state.matches.length > 0; + } + + private canReplace(): boolean { + return Boolean(this.state.matches[this.state.currentMatch]?.replace); + } + + private canReplaceAll(): boolean { + return this.state.matches.some(match => Boolean(match.replace)); + } + + private getMatchesCount(): string { + if (this.hasMatches()) { + return nls.localizeByDefault('{0} of {1}', this.state.currentMatch + 1, this.state.matches.length); + } else { + return nls.localizeByDefault('No results'); + } + } + + private gotoNextMatch(): void { + this.search({ + modifyIndex: (matches, index) => (index + 1) % matches.length, + jumpToMatch: true + }); + } + + private gotoPreviousMatch(): void { + this.search({ + modifyIndex: (matches, index) => (index === 0 ? matches.length : index) - 1, + jumpToMatch: true + }); + } + + private replaceOne(): void { + const existingMatches = this.state.matches; + const match = existingMatches[this.state.currentMatch]; + if (match) { + match.replace?.(this.state.replace); + this.search({ + jumpToMatch: true, + modifyIndex: (matches, index) => { + if (matches.length < existingMatches.length) { + return index % matches.length; + } else { + const diff = matches.length - existingMatches.length; + return (index + diff + 1) % matches.length; + } + } + }); + } + } + + private replaceAll(): void { + this.props.onReplace(this.state.matches, this.state.replace); + this.search({}); + } + + override componentDidUpdate(prevProps: Readonly, prevState: Readonly): void { + if (!this.props.hidden && prevProps.hidden) { + // Focus the search input when the widget switches from hidden to visible. + this.searchRef.current?.focus(); + } + } + + focusSearch(content?: string): void { + this.searchRef.current?.focus(); + if (content) { + this.search({ + search: content, + jumpToMatch: false + }); + } + } + + search(options: NotebookEditorFindOptions): void { + const matchCase = options.matchCase ?? this.state.matchCase; + const wholeWord = options.wholeWord ?? this.state.wholeWord; + const regex = options.regex ?? this.state.regex; + const search = options.search ?? this.state.search; + const matches = this.props.onSearch({ + search, + matchCase, + wholeWord, + regex, + activeFilters: this.state.activeFilters + }); + let currentMatch = Math.max(0, Math.min(this.state.currentMatch, matches.length - 1)); + if (options.modifyIndex && matches.length > 0) { + currentMatch = options.modifyIndex(matches, currentMatch); + } + const selectedMatch = matches[currentMatch]; + if (selectedMatch) { + selectedMatch.selected = true; + if (options.jumpToMatch) { + selectedMatch.show(); + } + } + this.setState({ + search, + matches, + currentMatch, + matchCase, + wholeWord, + regex + }); + } + +} diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index c67e380b51833..ac01e094460e3 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -25,6 +25,8 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; import { nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; +import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget'; +import * as mark from 'advanced-mark.js'; @injectable() export class NotebookMarkdownCellRenderer implements CellRenderer { @@ -53,26 +55,51 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { } interface MarkdownCellProps { - markdownRenderer: MarkdownRenderer, + markdownRenderer: MarkdownRenderer monacoServices: MonacoEditorServices - cell: NotebookCellModel, + cell: NotebookCellModel notebookModel: NotebookModel - notebookContextManager: NotebookContextManager; + notebookContextManager: NotebookContextManager } function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(cell.editing); + let empty = false; React.useEffect(() => { const listener = cell.onDidRequestCellEditChange(cellEdit => setEditMode(cellEdit)); return () => listener.dispose(); }, [editMode]); + React.useEffect(() => { + if (!editMode) { + const instance = new mark(markdownContent); + cell.onMarkdownFind = options => { + instance.unmark(); + if (empty) { + return []; + } + return searchInMarkdown(instance, options); + }; + const selectListener = cell.onDidSelectFindMatch(match => { + markdownContent.scrollIntoView({ + behavior: 'instant', + block: 'center', + }); + }); + return () => { + selectListener.dispose(); + cell.onMarkdownFind = undefined; + instance.unmark(); + }; + } + }, [editMode, cell.source]); + let markdownContent: HTMLElement = React.useMemo(() => { const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true }); return markdownRenderer.render(markdownString).element; - }, [cell, editMode]); + }, [cell.source]); if (!markdownContent.hasChildNodes()) { const italic = document.createElement('i'); @@ -80,6 +107,7 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n italic.innerText = nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.'); italic.style.pointerEvents = 'none'; markdownContent = italic; + empty = true; } return editMode ? @@ -89,3 +117,66 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n ref={node => node?.replaceChildren(markdownContent)} />; } + +function searchInMarkdown(instance: mark, options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] { + const matches: NotebookEditorFindMatch[] = []; + const markOptions: mark.MarkOptions & mark.RegExpOptions = { + className: 'theia-find-match', + diacritics: false, + caseSensitive: options.matchCase, + acrossElements: true, + separateWordSearch: false, + each: node => { + matches.push(new MarkdownEditorFindMatch(node)); + } + }; + if (options.regex || options.wholeWord) { + let search = options.search; + if (options.wholeWord) { + if (!options.regex) { + search = escapeRegExp(search); + } + search = '\\b' + search + '\\b'; + } + instance.markRegExp(new RegExp(search, options.matchCase ? '' : 'i'), markOptions); + } else { + instance.mark(options.search, markOptions); + } + return matches; +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +class MarkdownEditorFindMatch implements NotebookEditorFindMatch { + + constructor(readonly node: Node) { } + + private _selected = false; + + get selected(): boolean { + return this._selected; + } + + set selected(selected: boolean) { + this._selected = selected; + const className = 'theia-find-match-selected'; + if (this.node instanceof HTMLElement) { + if (selected) { + this.node.classList.add(className); + } else { + this.node.classList.remove(className); + } + } + } + + show(): void { + if (this.node instanceof HTMLElement) { + this.node.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } + } +} diff --git a/yarn.lock b/yarn.lock index 874de58e34b6e..3aabc7d1cd69a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2807,6 +2807,11 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ== +advanced-mark.js@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/advanced-mark.js/-/advanced-mark.js-2.6.0.tgz#86ea8b81152b543db91b1602df4e69282c3b0083" + integrity sha512-b6Q7iNkXk1BTUvmbvtig+/p3Z54qDQqOoOKPJUZXYtCdgeZPz/qj9xZs6GKKQA2/OIOrlNwSt/OvyIq06eYPVw== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -10961,7 +10966,7 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10979,15 +10984,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11053,7 +11049,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11074,13 +11070,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12269,7 +12258,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12287,15 +12276,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 291bc0e41a4eaf14f5b0eb8912a466fce102a83e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 5 Aug 2024 16:03:31 +0200 Subject: [PATCH 328/441] Use correct cell type for selected language (#13983) --- .../browser/contributions/cell-operations.ts | 5 +- .../notebook-cell-actions-contribution.ts | 25 +++++---- packages/notebook/src/browser/style/index.css | 4 +- .../browser/view/notebook-code-cell-view.tsx | 36 +++++++------ .../view/notebook-markdown-cell-view.tsx | 52 ++++++++++++++----- 5 files changed, 79 insertions(+), 43 deletions(-) diff --git a/packages/notebook/src/browser/contributions/cell-operations.ts b/packages/notebook/src/browser/contributions/cell-operations.ts index c77c0665f4d00..6ed1b4b5d7a7d 100644 --- a/packages/notebook/src/browser/contributions/cell-operations.ts +++ b/packages/notebook/src/browser/contributions/cell-operations.ts @@ -22,7 +22,7 @@ import { NotebookModel } from '../view-model/notebook-model'; * a collection of different reusable notbook cell operations */ -export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellModel, type: CellKind): void { +export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellModel, type: CellKind, language?: string): void { if (cell.cellKind === type) { return; } @@ -32,7 +32,8 @@ export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellM count: 1, cells: [{ ...cell.getData(), - cellKind: type + cellKind: type, + language: language ?? cell.language }] }], true); } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index cafeddddde6e1..2775a4afab51c 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -370,18 +370,25 @@ export class NotebookCellActionContribution implements MenuContribution, Command return; } const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language); - if (!language?.value || language.value === 'autoDetect') { + if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) { return; } - if (language.value.id === 'markdown') { - selectedCell.language = 'markdown'; - changeCellType(activeNotebook, selectedCell, CellKind.Markup); + const isMarkdownCell = selectedCell.cellKind === CellKind.Markup; + const isMarkdownLanguage = language.value.id === 'markdown'; + if (isMarkdownLanguage) { + if (!isMarkdownCell) { + changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id); + } } else { - this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ - editType: CellEditType.CellLanguage, - index: activeNotebook.cells.indexOf(selectedCell), - language: language.value.id - }], true); + if (isMarkdownCell) { + changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id); + } else { + this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{ + editType: CellEditType.CellLanguage, + index: activeNotebook.cells.indexOf(selectedCell), + language: language.value.id + }], true); + } } } }); diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 12eb18739c08f..f2d45fdfd4d68 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -79,14 +79,14 @@ } /* Markdown cell edit mode */ -.theia-notebook-cell-content:has(> .theia-notebook-cell-editor) { +.theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { margin-left: 37px; margin-right: var(--theia-notebook-cell-editor-margin-right); outline: 1px solid var(--theia-notebook-cellBorderColor); } /* Markdown cell edit mode focused */ -.theia-notebook-cell.focused .theia-notebook-cell-content:has(> .theia-notebook-cell-editor) { +.theia-notebook-cell.focused .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { outline-color: var(--theia-notebook-focusedEditorBorder); } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index e0fa2fb41e8ca..27764fac9f7af 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -150,7 +150,7 @@ export interface NotebookCodeCellStatusProps { notebook: NotebookModel; cell: NotebookCellModel; commandRegistry: CommandRegistry; - executionStateService: NotebookExecutionStateService; + executionStateService?: NotebookExecutionStateService; onClick: () => void; } @@ -171,22 +171,24 @@ export class NotebookCodeCellStatus extends React.Component { - if (event.affectsCell(this.props.cell.uri)) { - this.setState({ currentExecution: event.changed, executionTime: 0 }); - clearInterval(currentInterval); - if (event.changed?.state === NotebookCellExecutionState.Executing) { - const startTime = Date.now(); - // The resolution of the time display is only a single digit after the decimal point. - // Therefore, we only need to update the display every 100ms. - currentInterval = setInterval(() => { - this.setState({ - executionTime: Date.now() - startTime - }); - }, 100); + if (props.executionStateService) { + this.toDispose.push(props.executionStateService.onDidChangeExecution(event => { + if (event.affectsCell(this.props.cell.uri)) { + this.setState({ currentExecution: event.changed, executionTime: 0 }); + clearInterval(currentInterval); + if (event.changed?.state === NotebookCellExecutionState.Executing) { + const startTime = Date.now(); + // The resolution of the time display is only a single digit after the decimal point. + // Therefore, we only need to update the display every 100ms. + currentInterval = setInterval(() => { + this.setState({ + executionTime: Date.now() - startTime + }); + }, 100); + } } - } - })); + })); + } this.toDispose.push(props.cell.onDidChangeLanguage(() => { this.forceUpdate(); @@ -200,7 +202,7 @@ export class NotebookCodeCellStatus extends React.Component this.props.onClick()}>
      - {this.renderExecutionState()} + {this.props.executionStateService && this.renderExecutionState()}
      { diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index ac01e094460e3..65c96082f9325 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -23,8 +23,10 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { CellEditor } from './notebook-cell-editor'; import { inject, injectable } from '@theia/core/shared/inversify'; import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; -import { nls } from '@theia/core'; +import { CommandRegistry, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; +import { NotebookOptionsService } from '../service/notebook-options'; +import { NotebookCodeCellStatus } from './notebook-code-cell-view'; import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget'; import * as mark from 'advanced-mark.js'; @@ -39,9 +41,21 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager; + @inject(CommandRegistry) + protected readonly commandRegistry: CommandRegistry; + + @inject(NotebookOptionsService) + protected readonly notebookOptionsService: NotebookOptionsService; + render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { - return ; + return ; } renderDragImage(cell: NotebookCellModel): HTMLElement { @@ -55,15 +69,19 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { } interface MarkdownCellProps { - markdownRenderer: MarkdownRenderer - monacoServices: MonacoEditorServices - - cell: NotebookCellModel - notebookModel: NotebookModel - notebookContextManager: NotebookContextManager + markdownRenderer: MarkdownRenderer; + monacoServices: MonacoEditorServices; + + commandRegistry: CommandRegistry; + cell: NotebookCellModel; + notebookModel: NotebookModel; + notebookContextManager: NotebookContextManager; + notebookOptionsService: NotebookOptionsService; } -function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element { +function MarkdownCell({ + markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry +}: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(cell.editing); let empty = false; @@ -111,11 +129,19 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, n } return editMode ? - : -
      + + cell.requestFocusEditor()} /> +
      ) : + (
      cell.requestEdit()} ref={node => node?.replaceChildren(markdownContent)} - />; + />); } function searchInMarkdown(instance: mark, options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] { From effb3ec1af1fac477065a17af90a74ead214ce86 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Mon, 5 Aug 2024 16:08:04 +0200 Subject: [PATCH 329/441] Support for "--headless-hosted-plugin-inspect" cmd argument (#13918) * introduce getServerName method on backend * add preferences to specify node js debug ports per hosted plugin server * adjust PluginDebugConfiguration to also accept and array of server-name to debug port entries --- .../browser/hosted-plugin-manager-client.ts | 6 ++++- .../src/browser/hosted-plugin-preferences.ts | 24 ++++++++++++++++++ .../src/common/plugin-dev-protocol.ts | 7 +++++- .../src/node/hosted-instance-manager.ts | 15 ++++++++++- .../hosted/node/headless-plugin-service.ts | 25 +++++++++++++++++++ .../node/plugin-ext-headless-hosted-module.ts | 7 +++--- .../src/hosted/node/hosted-plugin-process.ts | 4 +-- .../src/hosted/node/hosted-plugin.ts | 4 +-- .../src/hosted/node/plugin-service.ts | 7 +++++- 9 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 packages/plugin-ext-headless/src/hosted/node/headless-plugin-service.ts diff --git a/packages/plugin-dev/src/browser/hosted-plugin-manager-client.ts b/packages/plugin-dev/src/browser/hosted-plugin-manager-client.ts index fb727aa104941..fd7db87b96583 100644 --- a/packages/plugin-dev/src/browser/hosted-plugin-manager-client.ts +++ b/packages/plugin-dev/src/browser/hosted-plugin-manager-client.ts @@ -248,7 +248,8 @@ export class HostedPluginManagerClient { try { if (this.isDebug) { this.pluginInstanceURL = await this.hostedPluginServer.runDebugHostedPluginInstance(this.pluginLocation!.toString(), { - debugMode: this.hostedPluginPreferences['hosted-plugin.debugMode'] + debugMode: this.hostedPluginPreferences['hosted-plugin.debugMode'], + debugPort: [...this.hostedPluginPreferences['hosted-plugin.debugPorts']] }); await this.startDebugSessionManager(); } else { @@ -372,6 +373,9 @@ export class HostedPluginManagerClient { if (config.pluginLocation) { this.pluginLocation = new URI((!config.pluginLocation.startsWith('/') ? '/' : '') + config.pluginLocation.replace(/\\/g, '/')).withScheme('file'); } + if (config.debugPort === undefined) { + config.debugPort = [...this.hostedPluginPreferences['hosted-plugin.debugPorts']]; + } return config; } diff --git a/packages/plugin-dev/src/browser/hosted-plugin-preferences.ts b/packages/plugin-dev/src/browser/hosted-plugin-preferences.ts index 7143bf40bbc61..01b66cc02c47e 100644 --- a/packages/plugin-dev/src/browser/hosted-plugin-preferences.ts +++ b/packages/plugin-dev/src/browser/hosted-plugin-preferences.ts @@ -17,6 +17,7 @@ import { interfaces } from '@theia/core/shared/inversify'; import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser'; import { nls } from '@theia/core/lib/common/nls'; +import { PluginDebugPort } from '../common'; export const HostedPluginConfigSchema: PreferenceSchema = { 'type': 'object', @@ -42,6 +43,28 @@ export const HostedPluginConfigSchema: PreferenceSchema = { 'Array of glob patterns for locating generated JavaScript files (`${pluginPath}` will be replaced by plugin actual path).' ), default: ['${pluginPath}/out/**/*.js'] + }, + 'hosted-plugin.debugPorts': { + type: 'array', + items: { + type: 'object', + properties: { + 'serverName': { + type: 'string', + description: nls.localize('theia/plugin-dev/debugPorts/serverName', + 'The plugin host server name, e.g. "hosted-plugin" as in "--hosted-plugin-inspect=" ' + + 'or "headless-hosted-plugin" as in "--headless-hosted-plugin-inspect="'), + }, + 'debugPort': { + type: 'number', + minimum: 0, + maximum: 65535, + description: nls.localize('theia/plugin-dev/debugPorts/debugPort', 'Port to use for this server\'s Node.js debug'), + } + }, + }, + default: undefined, + description: nls.localize('theia/plugin-dev/debugPorts', 'Port configuration per server for Node.js debug'), } } }; @@ -50,6 +73,7 @@ export interface HostedPluginConfiguration { 'hosted-plugin.watchMode': boolean; 'hosted-plugin.debugMode': string; 'hosted-plugin.launchOutFiles': string[]; + 'hosted-plugin.debugPorts': PluginDebugPort[]; } export const HostedPluginPreferenceContribution = Symbol('HostedPluginPreferenceContribution'); diff --git a/packages/plugin-dev/src/common/plugin-dev-protocol.ts b/packages/plugin-dev/src/common/plugin-dev-protocol.ts index c14689928e7e0..9a242ea6026cc 100644 --- a/packages/plugin-dev/src/common/plugin-dev-protocol.ts +++ b/packages/plugin-dev/src/common/plugin-dev-protocol.ts @@ -38,8 +38,13 @@ export interface PluginDevServer extends RpcServer { export interface PluginDevClient { } +export interface PluginDebugPort { + serverName: string, + debugPort: number, +} + export interface PluginDebugConfiguration { debugMode?: string; pluginLocation?: string; - debugPort?: string; + debugPort?: string | PluginDebugPort[] } diff --git a/packages/plugin-dev/src/node/hosted-instance-manager.ts b/packages/plugin-dev/src/node/hosted-instance-manager.ts index e56ad1d1932f7..c8a43d72cadb8 100644 --- a/packages/plugin-dev/src/node/hosted-instance-manager.ts +++ b/packages/plugin-dev/src/node/hosted-instance-manager.ts @@ -266,7 +266,20 @@ export abstract class AbstractHostedInstanceManager implements HostedInstanceMan } if (debugConfig) { - command.push(`--hosted-plugin-${debugConfig.debugMode || 'inspect'}=0.0.0.0${debugConfig.debugPort ? ':' + debugConfig.debugPort : ''}`); + if (debugConfig.debugPort === undefined) { + command.push(`--hosted-plugin-${debugConfig.debugMode || 'inspect'}=0.0.0.0`); + } else if (typeof debugConfig.debugPort === 'string') { + command.push(`--hosted-plugin-${debugConfig.debugMode || 'inspect'}=0.0.0.0:${debugConfig.debugPort}`); + } else if (Array.isArray(debugConfig.debugPort)) { + if (debugConfig.debugPort.length === 0) { + // treat empty array just like undefined + command.push(`--hosted-plugin-${debugConfig.debugMode || 'inspect'}=0.0.0.0`); + } else { + for (const serverToPort of debugConfig.debugPort) { + command.push(`--${serverToPort.serverName}-${debugConfig.debugMode || 'inspect'}=0.0.0.0:${serverToPort.debugPort}`); + } + } + } } return command; } diff --git a/packages/plugin-ext-headless/src/hosted/node/headless-plugin-service.ts b/packages/plugin-ext-headless/src/hosted/node/headless-plugin-service.ts new file mode 100644 index 0000000000000..3a36482dedce2 --- /dev/null +++ b/packages/plugin-ext-headless/src/hosted/node/headless-plugin-service.ts @@ -0,0 +1,25 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { HostedPluginServerImpl } from '@theia/plugin-ext/lib/hosted/node/plugin-service'; + +@injectable() +export class HeadlessHostedPluginServerImpl extends HostedPluginServerImpl { + protected override getServerName(): string { + return 'headless-hosted-plugin'; + } +} diff --git a/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts b/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts index 51a031ee91d3b..3e651eee6bedc 100644 --- a/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts +++ b/packages/plugin-ext-headless/src/hosted/node/plugin-ext-headless-hosted-module.ts @@ -21,12 +21,13 @@ import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { ExtPluginApiProvider, HostedPluginServer, PluginHostEnvironmentVariable, PluginScanner } from '@theia/plugin-ext'; import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin'; import { HostedPluginProcess, HostedPluginProcessConfiguration } from '@theia/plugin-ext/lib/hosted/node/hosted-plugin-process'; -import { BackendPluginHostableFilter, HostedPluginServerImpl } from '@theia/plugin-ext/lib/hosted/node/plugin-service'; +import { BackendPluginHostableFilter } from '@theia/plugin-ext/lib/hosted/node/plugin-service'; import { MaybePromise } from '@theia/core'; import { HeadlessPluginContainerModule } from '../../common/headless-plugin-container'; import { HeadlessHostedPluginSupport, isHeadlessPlugin } from './headless-hosted-plugin'; import { TheiaHeadlessPluginScanner } from './scanners/scanner-theia-headless'; import { SupportedHeadlessActivationEvents } from '../../common/headless-plugin-protocol'; +import { HeadlessHostedPluginServerImpl } from './headless-plugin-service'; export function bindCommonHostedBackend(bind: interfaces.Bind): void { bind(HostedPluginProcess).toSelf().inSingletonScope(); @@ -36,8 +37,8 @@ export function bindCommonHostedBackend(bind: interfaces.Bind): void { bindContributionProvider(bind, PluginHostEnvironmentVariable); bindContributionProvider(bind, SupportedHeadlessActivationEvents); - bind(HostedPluginServerImpl).toSelf().inSingletonScope(); - bind(HostedPluginServer).toService(HostedPluginServerImpl); + bind(HeadlessHostedPluginServerImpl).toSelf().inSingletonScope(); + bind(HostedPluginServer).toService(HeadlessHostedPluginServerImpl); bind(HeadlessHostedPluginSupport).toSelf().inSingletonScope(); bind(BackendPluginHostableFilter).toConstantValue(isHeadlessPlugin); diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts index aaf11b2dd7315..ec6560559a769 100644 --- a/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts +++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-process.ts @@ -149,13 +149,13 @@ export class HostedPluginProcess implements ServerPluginRunner { } } - public runPluginServer(): void { + public runPluginServer(serverName?: string): void { if (this.childProcess) { this.terminatePluginServer(); } this.terminatingPluginServer = false; this.childProcess = this.fork({ - serverName: 'hosted-plugin', + serverName: serverName ?? 'hosted-plugin', logger: this.logger, args: [] }); diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin.ts index 8728caa80e2f9..8e60f20f184f2 100644 --- a/packages/plugin-ext/src/hosted/node/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/node/hosted-plugin.ts @@ -85,9 +85,9 @@ export class HostedPluginSupport { } } - runPluginServer(): void { + runPluginServer(serverName?: string): void { if (!this.isPluginProcessRunning) { - this.hostedPluginProcess.runPluginServer(); + this.hostedPluginProcess.runPluginServer(serverName); this.isPluginProcessRunning = true; } } diff --git a/packages/plugin-ext/src/hosted/node/plugin-service.ts b/packages/plugin-ext/src/hosted/node/plugin-service.ts index 916009baa0857..9b1d09a899ae7 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-service.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-service.ts @@ -59,6 +59,7 @@ export class HostedPluginServerImpl implements HostedPluginServer { protected toDispose = new DisposableCollection(); protected _ignoredPlugins?: Set; + // We ignore any plugins that are marked as uninstalled the first time the frontend requests information about deployed plugins. protected get ignoredPlugins(): Set { if (!this._ignoredPlugins) { @@ -96,6 +97,10 @@ export class HostedPluginServerImpl implements HostedPluginServer { ]); } + protected getServerName(): string { + return 'hosted-plugin'; + } + dispose(): void { this.toDispose.dispose(); } @@ -109,7 +114,7 @@ export class HostedPluginServerImpl implements HostedPluginServer { const backendPlugins = (await this.deployerHandler.getDeployedBackendPlugins()) .filter(this.backendPluginHostableFilter); if (backendPlugins.length > 0) { - this.hostedPlugin.runPluginServer(); + this.hostedPlugin.runPluginServer(this.getServerName()); } const plugins = new Set(); const addIds = async (identifiers: PluginIdentifiers.VersionedId[]): Promise => { From 9e912411843f32b2a549ef6580b07872edd95e8b Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 6 Aug 2024 02:05:41 +0200 Subject: [PATCH 330/441] Refactor undo-redo action for editors (#13963) --- .../browser/common-frontend-contribution.ts | 14 ++- .../browser/frontend-application-module.ts | 6 ++ packages/core/src/browser/index.ts | 1 + .../core/src/browser/undo-redo-handler.ts | 85 +++++++++++++++++++ packages/monaco/src/browser/monaco-command.ts | 6 +- .../monaco/src/browser/monaco-editor-model.ts | 8 ++ .../src/browser/monaco-frontend-module.ts | 9 +- .../src/browser/monaco-undo-redo-handler.ts | 64 ++++++++++++++ .../notebook-actions-contribution.ts | 18 +--- .../notebook-undo-redo-handler.ts | 41 +++++++++ .../src/browser/notebook-frontend-module.ts | 6 +- packages/notebook/src/browser/style/index.css | 7 +- .../browser/view-model/notebook-cell-model.ts | 18 ++-- .../src/browser/view-model/notebook-model.ts | 55 +++++++----- .../src/browser/view/notebook-cell-editor.tsx | 2 +- .../browser/view/notebook-cell-list-view.tsx | 9 +- .../view/notebook-markdown-cell-view.tsx | 24 +++--- .../custom-editor-contribution.ts | 38 --------- .../custom-editor-undo-redo-handler.ts | 41 +++++++++ .../custom-editors/custom-editor-widget.ts | 8 +- .../custom-editors/custom-editors-main.ts | 15 +++- .../browser/plugin-ext-frontend-module.ts | 10 +-- 22 files changed, 367 insertions(+), 118 deletions(-) create mode 100644 packages/core/src/browser/undo-redo-handler.ts create mode 100644 packages/monaco/src/browser/monaco-undo-redo-handler.ts create mode 100644 packages/notebook/src/browser/contributions/notebook-undo-redo-handler.ts delete mode 100644 packages/plugin-ext/src/main/browser/custom-editors/custom-editor-contribution.ts create mode 100644 packages/plugin-ext/src/main/browser/custom-editors/custom-editor-undo-redo-handler.ts diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index 15fe68122380c..64489486e30f0 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -67,6 +67,7 @@ import { UserWorkingDirectoryProvider } from './user-working-directory-provider' import { UNTITLED_SCHEME, UntitledResourceResolver } from '../common'; import { LanguageQuickPickService } from './i18n/language-quick-pick-service'; import { SidebarMenu } from './shell/sidebar-menu-widget'; +import { UndoRedoHandlerService } from './undo-redo-handler'; export namespace CommonMenus { @@ -443,6 +444,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi @inject(UntitledResourceResolver) protected readonly untitledResourceResolver: UntitledResourceResolver; + @inject(UndoRedoHandlerService) + protected readonly undoRedoHandlerService: UndoRedoHandlerService; + protected pinnedKey: ContextKey; protected inputFocus: ContextKey; @@ -814,10 +818,14 @@ export class CommonFrontendContribution implements FrontendApplicationContributi })); commandRegistry.registerCommand(CommonCommands.UNDO, { - execute: () => document.execCommand('undo') + execute: () => { + this.undoRedoHandlerService.undo(); + } }); commandRegistry.registerCommand(CommonCommands.REDO, { - execute: () => document.execCommand('redo') + execute: () => { + this.undoRedoHandlerService.redo(); + } }); commandRegistry.registerCommand(CommonCommands.SELECT_ALL, { execute: () => document.execCommand('selectAll') @@ -1080,7 +1088,7 @@ export class CommonFrontendContribution implements FrontendApplicationContributi }, { command: CommonCommands.REDO.id, - keybinding: 'ctrlcmd+shift+z' + keybinding: isOSX ? 'ctrlcmd+shift+z' : 'ctrlcmd+y' }, { command: CommonCommands.SELECT_ALL.id, diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 6ab58faec8bf9..3ef33e6fe8238 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -143,6 +143,7 @@ import { LanguageIconLabelProvider } from './language-icon-provider'; import { bindTreePreferences } from './tree'; import { OpenWithService } from './open-with-service'; import { ViewColumnService } from './shell/view-column-service'; +import { DomInputUndoRedoHandler, UndoRedoHandler, UndoRedoHandlerService } from './undo-redo-handler'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -466,4 +467,9 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(SecondaryWindowHandler).toSelf().inSingletonScope(); bind(ViewColumnService).toSelf().inSingletonScope(); + + bind(UndoRedoHandlerService).toSelf().inSingletonScope(); + bindContributionProvider(bind, UndoRedoHandler); + bind(DomInputUndoRedoHandler).toSelf().inSingletonScope(); + bind(UndoRedoHandler).toService(DomInputUndoRedoHandler); }); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index 42277fc2f7833..ecc1ccc4da2f8 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -47,3 +47,4 @@ export * from './decoration-style'; export * from './styling-service'; export * from './hover-service'; export * from './saveable-service'; +export * from './undo-redo-handler'; diff --git a/packages/core/src/browser/undo-redo-handler.ts b/packages/core/src/browser/undo-redo-handler.ts new file mode 100644 index 0000000000000..180ab9098d678 --- /dev/null +++ b/packages/core/src/browser/undo-redo-handler.ts @@ -0,0 +1,85 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, named, postConstruct } from 'inversify'; +import { ContributionProvider } from '../common'; + +export const UndoRedoHandler = Symbol('UndoRedoHandler'); + +export interface UndoRedoHandler { + priority: number; + select(): T | undefined; + undo(item: T): void; + redo(item: T): void; +} + +@injectable() +export class UndoRedoHandlerService { + + @inject(ContributionProvider) @named(UndoRedoHandler) + protected readonly provider: ContributionProvider>; + + protected handlers: UndoRedoHandler[]; + + @postConstruct() + protected init(): void { + this.handlers = this.provider.getContributions().sort((a, b) => b.priority - a.priority); + } + + undo(): void { + for (const handler of this.handlers) { + const selection = handler.select(); + if (selection) { + handler.undo(selection); + return; + } + } + } + + redo(): void { + for (const handler of this.handlers) { + const selection = handler.select(); + if (selection) { + handler.redo(selection); + return; + } + } + } + +} + +@injectable() +export class DomInputUndoRedoHandler implements UndoRedoHandler { + + priority = 1000; + + select(): Element | undefined { + const element = document.activeElement; + if (element && ['input', 'textarea'].includes(element.tagName.toLowerCase())) { + return element; + } + return undefined; + } + + undo(item: Element): void { + document.execCommand('undo'); + } + + redo(item: Element): void { + document.execCommand('redo'); + } + +} diff --git a/packages/monaco/src/browser/monaco-command.ts b/packages/monaco/src/browser/monaco-command.ts index fef9775b2d779..0b194b4397a6f 100644 --- a/packages/monaco/src/browser/monaco-command.ts +++ b/packages/monaco/src/browser/monaco-command.ts @@ -34,8 +34,6 @@ import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/brow export namespace MonacoCommands { export const COMMON_ACTIONS = new Map([ - ['undo', CommonCommands.UNDO.id], - ['redo', CommonCommands.REDO.id], ['editor.action.selectAll', CommonCommands.SELECT_ALL.id], ['actions.find', CommonCommands.FIND.id], ['editor.action.startFindReplaceAction', CommonCommands.REPLACE.id], @@ -47,7 +45,9 @@ export namespace MonacoCommands { export const GO_TO_DEFINITION = 'editor.action.revealDefinition'; export const EXCLUDE_ACTIONS = new Set([ - 'editor.action.quickCommand' + 'editor.action.quickCommand', + 'undo', + 'redo' ]); } diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index 7d05e457d0d02..b1ebdbaffe75f 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -113,6 +113,14 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo ); } + undo(): void { + this.model.undo(); + } + + redo(): void { + this.model.redo(); + } + dispose(): void { this.toDispose.dispose(); } diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 0fc9b9ffaf317..0bd3a74f2500c 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -20,7 +20,8 @@ import { MenuContribution, CommandContribution, quickInputServicePath } from '@t import { FrontendApplicationContribution, KeybindingContribution, PreferenceService, PreferenceSchemaProvider, createPreferenceProxy, - PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService, StylingParticipant, WebSocketConnectionProvider + PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService, StylingParticipant, WebSocketConnectionProvider, + UndoRedoHandler } from '@theia/core/lib/browser'; import { TextEditorProvider, DiffNavigatorProvider, TextEditor } from '@theia/editor/lib/browser'; import { MonacoEditorProvider, MonacoEditorFactory } from './monaco-editor-provider'; @@ -73,6 +74,7 @@ import { ThemeService } from '@theia/core/lib/browser/theming'; import { ThemeServiceWithDB } from './monaco-indexed-db'; import { IContextKeyService } from '@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey'; import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { ActiveMonacoUndoRedoHandler, FocusedMonacoUndoRedoHandler } from './monaco-undo-redo-handler'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoThemingService).toSelf().inSingletonScope(); @@ -175,6 +177,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MonacoIconRegistry).toSelf().inSingletonScope(); bind(IconRegistry).toService(MonacoIconRegistry); + + bind(FocusedMonacoUndoRedoHandler).toSelf().inSingletonScope(); + bind(ActiveMonacoUndoRedoHandler).toSelf().inSingletonScope(); + bind(UndoRedoHandler).toService(FocusedMonacoUndoRedoHandler); + bind(UndoRedoHandler).toService(ActiveMonacoUndoRedoHandler); }); export const MonacoConfigurationService = Symbol('MonacoConfigurationService'); diff --git a/packages/monaco/src/browser/monaco-undo-redo-handler.ts b/packages/monaco/src/browser/monaco-undo-redo-handler.ts new file mode 100644 index 0000000000000..51a5e51c1069d --- /dev/null +++ b/packages/monaco/src/browser/monaco-undo-redo-handler.ts @@ -0,0 +1,64 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { UndoRedoHandler } from '@theia/core/lib/browser'; +import { injectable } from '@theia/core/shared/inversify'; +import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser'; +import { ICodeEditorService } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; + +@injectable() +export abstract class AbstractMonacoUndoRedoHandler implements UndoRedoHandler { + priority: number; + abstract select(): ICodeEditor | undefined; + undo(item: ICodeEditor): void { + item.trigger('MonacoUndoRedoHandler', 'undo', undefined); + } + redo(item: ICodeEditor): void { + item.trigger('MonacoUndoRedoHandler', 'redo', undefined); + } +} + +@injectable() +export class FocusedMonacoUndoRedoHandler extends AbstractMonacoUndoRedoHandler { + override priority = 10000; + + protected codeEditorService = StandaloneServices.get(ICodeEditorService); + + override select(): ICodeEditor | undefined { + const focusedEditor = this.codeEditorService.getFocusedCodeEditor(); + if (focusedEditor && focusedEditor.hasTextFocus()) { + return focusedEditor; + } + return undefined; + } +} + +@injectable() +export class ActiveMonacoUndoRedoHandler extends AbstractMonacoUndoRedoHandler { + override priority = 0; + + protected codeEditorService = StandaloneServices.get(ICodeEditorService); + + override select(): ICodeEditor | undefined { + const focusedEditor = this.codeEditorService.getActiveCodeEditor(); + if (focusedEditor) { + focusedEditor.focus(); + return focusedEditor; + } + return undefined; + } +} diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index da1bcc065397f..3fce82523ed1d 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -16,13 +16,12 @@ import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { ApplicationShell, codicon, CommonCommands, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; +import { ApplicationShell, codicon, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookService } from '../service/notebook-service'; import { CellEditType, CellKind, NotebookCommand } from '../../common'; import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; -import { NotebookEditorWidget } from '../notebook-editor-widget'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; import { NotebookClipboardService } from '../service/notebook-clipboard-service'; @@ -208,21 +207,6 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon } ); - commands.registerHandler(CommonCommands.UNDO.id, { - isEnabled: () => { - const widget = this.shell.activeWidget; - return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly); - }, - execute: () => (this.shell.activeWidget as NotebookEditorWidget).undo() - }); - commands.registerHandler(CommonCommands.REDO.id, { - isEnabled: () => { - const widget = this.shell.activeWidget; - return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly); - }, - execute: () => (this.shell.activeWidget as NotebookEditorWidget).redo() - }); - commands.registerCommand(NotebookCommands.CUT_SELECTED_CELL, this.editableCommandHandler( () => { const model = this.notebookEditorWidgetService.focusedEditor?.model; diff --git a/packages/notebook/src/browser/contributions/notebook-undo-redo-handler.ts b/packages/notebook/src/browser/contributions/notebook-undo-redo-handler.ts new file mode 100644 index 0000000000000..367dae772664d --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-undo-redo-handler.ts @@ -0,0 +1,41 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ApplicationShell, UndoRedoHandler } from '@theia/core/lib/browser'; +import { NotebookEditorWidget } from '../notebook-editor-widget'; + +@injectable() +export class NotebookUndoRedoHandler implements UndoRedoHandler { + + @inject(ApplicationShell) + protected readonly applicationShell: ApplicationShell; + + priority = 200; + select(): NotebookEditorWidget | undefined { + const current = this.applicationShell.currentWidget; + if (current instanceof NotebookEditorWidget) { + return current; + } + return undefined; + } + undo(item: NotebookEditorWidget): void { + item.undo(); + } + redo(item: NotebookEditorWidget): void { + item.redo(); + } +} diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index fda9e983c0d8b..b39d914333a6e 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,7 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory } from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -46,6 +46,7 @@ import { NotebookOutputActionContribution } from './contributions/notebook-outpu import { NotebookClipboardService } from './service/notebook-clipboard-service'; import { bindNotebookPreferences } from './contributions/notebook-preferences'; import { NotebookOptionsService } from './service/notebook-options'; +import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -108,4 +109,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bindNotebookPreferences(bind); bind(NotebookOptionsService).toSelf().inSingletonScope(); + + bind(NotebookUndoRedoHandler).toSelf().inSingletonScope(); + bind(UndoRedoHandler).toService(NotebookUndoRedoHandler); }); diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index f2d45fdfd4d68..844629eb2b36d 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -62,17 +62,22 @@ width: calc(100% - 15px); } +/* Rendered Markdown Content */ + .theia-notebook-markdown-content { padding: 8px 16px 8px 36px; font-size: var(--theia-notebook-markdown-size); } +.theia-notebook-markdown-content>* { + font-weight: 400; +} + .theia-notebook-markdown-content>*:first-child { margin-top: 0; padding-top: 0; } -.theia-notebook-markdown-content>*:only-child, .theia-notebook-markdown-content>*:last-child { margin-bottom: 0; padding-bottom: 0; diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 8272795d6f124..c6ed48582fc31 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -88,22 +88,22 @@ export interface NotebookCellModelProps { export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onDidChangeOutputsEmitter = new Emitter(); - readonly onDidChangeOutputs: Event = this.onDidChangeOutputsEmitter.event; + readonly onDidChangeOutputs = this.onDidChangeOutputsEmitter.event; protected readonly onDidChangeOutputItemsEmitter = new Emitter(); - readonly onDidChangeOutputItems: Event = this.onDidChangeOutputItemsEmitter.event; + readonly onDidChangeOutputItems = this.onDidChangeOutputItemsEmitter.event; protected readonly onDidChangeContentEmitter = new Emitter<'content' | 'language' | 'mime'>(); - readonly onDidChangeContent: Event<'content' | 'language' | 'mime'> = this.onDidChangeContentEmitter.event; + readonly onDidChangeContent = this.onDidChangeContentEmitter.event; protected readonly onDidChangeMetadataEmitter = new Emitter(); - readonly onDidChangeMetadata: Event = this.onDidChangeMetadataEmitter.event; + readonly onDidChangeMetadata = this.onDidChangeMetadataEmitter.event; protected readonly onDidChangeInternalMetadataEmitter = new Emitter(); - readonly onDidChangeInternalMetadata: Event = this.onDidChangeInternalMetadataEmitter.event; + readonly onDidChangeInternalMetadata = this.onDidChangeInternalMetadataEmitter.event; protected readonly onDidChangeLanguageEmitter = new Emitter(); - readonly onDidChangeLanguage: Event = this.onDidChangeLanguageEmitter.event; + readonly onDidChangeLanguage = this.onDidChangeLanguageEmitter.event; protected readonly onDidRequestCellEditChangeEmitter = new Emitter(); readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event; @@ -115,10 +115,10 @@ export class NotebookCellModel implements NotebookCell, Disposable { readonly onWillBlurCellEditor = this.onWillBlurCellEditorEmitter.event; protected readonly onDidChangeEditorOptionsEmitter = new Emitter(); - readonly onDidChangeEditorOptions: Event = this.onDidChangeEditorOptionsEmitter.event; + readonly onDidChangeEditorOptions = this.onDidChangeEditorOptionsEmitter.event; protected readonly outputVisibilityChangeEmitter = new Emitter(); - readonly onDidChangeOutputVisibility: Event = this.outputVisibilityChangeEmitter.event; + readonly onDidChangeOutputVisibility = this.outputVisibilityChangeEmitter.event; protected readonly onDidFindMatchesEmitter = new Emitter(); readonly onDidFindMatches: Event = this.onDidFindMatchesEmitter.event; @@ -183,10 +183,12 @@ export class NotebookCellModel implements NotebookCell, Disposable { get source(): string { return this.props.source; } + set source(source: string) { this.props.source = source; this.textModel?.textEditorModel.setValue(source); } + get language(): string { return this.props.language; } diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index e60b5f0ee2fff..78faa0cdaeeda 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -246,7 +246,7 @@ export class NotebookModel implements Saveable, Disposable { setData(data: NotebookData, markDirty = true): void { // Replace all cells in the model this.dirtyCells = []; - this.replaceCells(0, this.cells.length, data.cells, false); + this.replaceCells(0, this.cells.length, data.cells, false, false); this.metadata = data.metadata; this.dirty = markDirty; this.onDidChangeContentEmitter.fire(); @@ -260,13 +260,15 @@ export class NotebookModel implements Saveable, Disposable { } undo(): void { - // TODO we probably need to check if a monaco editor is focused and if so, not undo - this.undoRedoService.undo(this.uri); + if (!this.readOnly) { + this.undoRedoService.undo(this.uri); + } } redo(): void { - // TODO see undo - this.undoRedoService.redo(this.uri); + if (!this.readOnly) { + this.undoRedoService.redo(this.uri); + } } setSelectedCell(cell: NotebookCellModel, scrollIntoView?: boolean): void { @@ -315,7 +317,7 @@ export class NotebookModel implements Saveable, Disposable { let scrollIntoView = true; switch (edit.editType) { case CellEditType.Replace: - this.replaceCells(edit.index, edit.count, edit.cells, computeUndoRedo); + this.replaceCells(edit.index, edit.count, edit.cells, computeUndoRedo, true); scrollIntoView = edit.cells.length > 0; break; case CellEditType.Output: { @@ -338,10 +340,10 @@ export class NotebookModel implements Saveable, Disposable { break; case CellEditType.Metadata: - this.changeCellMetadata(this.cells[cellIndex], edit.metadata, computeUndoRedo); + this.changeCellMetadata(this.cells[cellIndex], edit.metadata, false); break; case CellEditType.PartialMetadata: - this.changeCellMetadataPartial(this.cells[cellIndex], edit.metadata, computeUndoRedo); + this.changeCellMetadataPartial(this.cells[cellIndex], edit.metadata, false); break; case CellEditType.PartialInternalMetadata: this.changeCellInternalMetadataPartial(this.cells[cellIndex], edit.internalMetadata); @@ -350,7 +352,7 @@ export class NotebookModel implements Saveable, Disposable { this.changeCellLanguage(this.cells[cellIndex], edit.language, computeUndoRedo); break; case CellEditType.DocumentMetadata: - this.updateNotebookMetadata(edit.metadata, computeUndoRedo); + this.updateNotebookMetadata(edit.metadata, false); break; case CellEditType.Move: this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo); @@ -363,11 +365,15 @@ export class NotebookModel implements Saveable, Disposable { } } + this.fireContentChange(); + } + + protected fireContentChange(): void { this.onDidChangeContentEmitter.fire(); this.onContentChangedEmitter.fire(); } - protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): void { + protected replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean, requestEdit: boolean): void { const cells = newCells.map(cell => { const handle = this.nextHandle++; return this.cellModelFactory({ @@ -394,13 +400,20 @@ export class NotebookModel implements Saveable, Disposable { if (computeUndoRedo) { this.undoRedoService.pushElement(this.uri, - async () => this.replaceCells(start, newCells.length, deletedCells.map(cell => cell.getData()), false), - async () => this.replaceCells(start, deleteCount, newCells, false)); + async () => { + this.replaceCells(start, newCells.length, deletedCells.map(cell => cell.getData()), false, false); + this.fireContentChange(); + }, + async () => { + this.replaceCells(start, deleteCount, newCells, false, false); + this.fireContentChange(); + } + ); } this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) }); this.onDidChangeContentEmitter.queue({ kind: NotebookCellsChangeType.ModelChange, changes }); - if (cells.length > 0) { + if (cells.length > 0 && requestEdit) { this.setSelectedCell(cells[cells.length - 1]); cells[cells.length - 1].requestEdit(); } @@ -478,8 +491,14 @@ export class NotebookModel implements Saveable, Disposable { protected moveCellToIndex(fromIndex: number, length: number, toIndex: number, computeUndoRedo: boolean): boolean { if (computeUndoRedo) { this.undoRedoService.pushElement(this.uri, - async () => { this.moveCellToIndex(toIndex, length, fromIndex, false); }, - async () => { this.moveCellToIndex(fromIndex, length, toIndex, false); } + async () => { + this.moveCellToIndex(toIndex, length, fromIndex, false); + this.fireContentChange(); + }, + async () => { + this.moveCellToIndex(fromIndex, length, toIndex, false); + this.fireContentChange(); + } ); } @@ -495,11 +514,9 @@ export class NotebookModel implements Saveable, Disposable { } protected isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata): boolean { - const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); for (const key of keys) { - if ( - (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata]) - ) { + if (a[key] !== b[key]) { return true; } } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index de920fc66ee8e..969c031dc6753 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -90,7 +90,7 @@ export class CellEditor extends React.Component { this.toDispose.push(this.props.cell.onWillFocusCellEditor(focusRequest => { this.editor?.getControl().focus(); const lineCount = this.editor?.getControl().getModel()?.getLineCount(); - if (focusRequest && lineCount) { + if (focusRequest && lineCount !== undefined) { this.editor?.getControl().setPosition(focusRequest === 'lastLine' ? { lineNumber: lineCount, column: 1 } : { lineNumber: focusRequest, column: 1 }, diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index a9ecbfc3e3a0f..41b719be09bcb 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import * as React from '@theia/core/shared/react'; -import { CellEditType, CellKind } from '../../common'; +import { CellEditType, CellKind, NotebookCellsChangeType } from '../../common'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory'; @@ -68,6 +68,13 @@ export class NotebookCellListView extends React.Component { + if (events.some(e => e.kind === NotebookCellsChangeType.Move)) { + // When a cell has been moved, we need to rerender the whole component + this.forceUpdate(); + } + })); + this.toDispose.push(props.notebookModel.onDidChangeSelectedCell(e => { this.setState({ ...this.state, diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index 65c96082f9325..04a835dbf67df 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -100,31 +100,31 @@ function MarkdownCell({ } return searchInMarkdown(instance, options); }; - const selectListener = cell.onDidSelectFindMatch(match => { - markdownContent.scrollIntoView({ - behavior: 'instant', - block: 'center', - }); - }); return () => { - selectListener.dispose(); cell.onMarkdownFind = undefined; instance.unmark(); }; } }, [editMode, cell.source]); - let markdownContent: HTMLElement = React.useMemo(() => { + let markdownContent: HTMLElement[] = React.useMemo(() => { const markdownString = new MarkdownStringImpl(cell.source, { supportHtml: true, isTrusted: true }); - return markdownRenderer.render(markdownString).element; + const rendered = markdownRenderer.render(markdownString).element; + const children: HTMLElement[] = []; + rendered.childNodes.forEach(child => { + if (child instanceof HTMLElement) { + children.push(child); + } + }); + return children; }, [cell.source]); - if (!markdownContent.hasChildNodes()) { + if (markdownContent.length === 0) { const italic = document.createElement('i'); italic.className = 'theia-notebook-empty-markdown'; italic.innerText = nls.localizeByDefault('Empty markdown cell, double-click or press enter to edit.'); italic.style.pointerEvents = 'none'; - markdownContent = italic; + markdownContent = [italic]; empty = true; } @@ -140,7 +140,7 @@ function MarkdownCell({
      ) : (
      cell.requestEdit()} - ref={node => node?.replaceChildren(markdownContent)} + ref={node => node?.replaceChildren(...markdownContent)} />); } diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-contribution.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-contribution.ts deleted file mode 100644 index 485cdaa697eb4..0000000000000 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-contribution.ts +++ /dev/null @@ -1,38 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2021 SAP SE or an SAP affiliate company and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -import { injectable, inject } from '@theia/core/shared/inversify'; -import { CommandRegistry, CommandContribution } from '@theia/core/lib/common'; -import { ApplicationShell, CommonCommands } from '@theia/core/lib/browser'; -import { CustomEditorWidget } from './custom-editor-widget'; - -@injectable() -export class CustomEditorContribution implements CommandContribution { - - @inject(ApplicationShell) - protected readonly shell: ApplicationShell; - - registerCommands(commands: CommandRegistry): void { - commands.registerHandler(CommonCommands.UNDO.id, { - isEnabled: () => this.shell.activeWidget instanceof CustomEditorWidget, - execute: () => (this.shell.activeWidget as CustomEditorWidget).undo() - }); - commands.registerHandler(CommonCommands.REDO.id, { - isEnabled: () => this.shell.activeWidget instanceof CustomEditorWidget, - execute: () => (this.shell.activeWidget as CustomEditorWidget).redo() - }); - } -} diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-undo-redo-handler.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-undo-redo-handler.ts new file mode 100644 index 0000000000000..328433ad36aec --- /dev/null +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-undo-redo-handler.ts @@ -0,0 +1,41 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ApplicationShell, UndoRedoHandler } from '@theia/core/lib/browser'; +import { CustomEditorWidget } from './custom-editor-widget'; + +@injectable() +export class CustomEditorUndoRedoHandler implements UndoRedoHandler { + + @inject(ApplicationShell) + protected readonly applicationShell: ApplicationShell; + + priority = 190; + select(): CustomEditorWidget | undefined { + const current = this.applicationShell.currentWidget; + if (current instanceof CustomEditorWidget) { + return current; + } + return undefined; + } + undo(item: CustomEditorWidget): void { + item.undo(); + } + redo(item: CustomEditorWidget): void { + item.redo(); + } +} diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts index 7c626f3b69357..7d277a44565b5 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts @@ -21,7 +21,6 @@ import { ApplicationShell, NavigatableWidget, Saveable, SaveableSource, SaveOpti import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import { Reference } from '@theia/core/lib/common/reference'; import { WebviewWidget } from '../webview/webview'; -import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { CustomEditorModel } from './custom-editors-main'; @injectable() @@ -43,9 +42,6 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, return this._modelRef.object; } - @inject(UndoRedoService) - protected readonly undoRedoService: UndoRedoService; - @inject(ApplicationShell) protected readonly shell: ApplicationShell; @@ -64,11 +60,11 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, } undo(): void { - this.undoRedoService.undo(this.resource); + this._modelRef.object.undo(); } redo(): void { - this.undoRedoService.redo(this.resource); + this._modelRef.object.redo(); } async save(options?: SaveOptions): Promise { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts index 4368a500f1e69..95e0dcad4ba39 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts @@ -284,6 +284,9 @@ export interface CustomEditorModel extends Saveable, Disposable { revert(options?: Saveable.RevertOptions): Promise; saveCustomEditor(options?: SaveOptions): Promise; saveCustomEditorAs(resource: TheiaURI, targetResource: TheiaURI, options?: SaveOptions): Promise; + + undo(): void; + redo(): void; } export class MainCustomEditorModel implements CustomEditorModel { @@ -436,7 +439,7 @@ export class MainCustomEditorModel implements CustomEditorModel { } } - private async undo(): Promise { + async undo(): Promise { if (!this.editable) { return; } @@ -453,7 +456,7 @@ export class MainCustomEditorModel implements CustomEditorModel { await this.proxy.$undo(this.resource, this.viewType, undoneEdit, this.dirty); } - private async redo(): Promise { + async redo(): Promise { if (!this.editable) { return; } @@ -571,4 +574,12 @@ export class CustomTextEditorModel implements CustomEditorModel { await this.saveCustomEditor(options); await this.fileService.copy(resource, targetResource, { overwrite: false }); } + + undo(): void { + this.editorTextModel.undo(); + } + + redo(): void { + this.editorTextModel.redo(); + } } diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index dc8d2bfdaf5c8..7323df220e868 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -21,7 +21,8 @@ import '../../../src/main/browser/style/comments.css'; import { ContainerModule } from '@theia/core/shared/inversify'; import { FrontendApplicationContribution, WidgetFactory, bindViewContribution, - ViewContainerIdentifier, ViewContainer, createTreeContainer, TreeWidget, LabelProviderContribution + ViewContainerIdentifier, ViewContainer, createTreeContainer, TreeWidget, LabelProviderContribution, + UndoRedoHandler } from '@theia/core/lib/browser'; import { MaybePromise, CommandContribution, ResourceResolver, bindContributionProvider } from '@theia/core/lib/common'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging'; @@ -66,7 +67,6 @@ import { CommentsService, PluginCommentService } from './comments/comments-servi import { CommentingRangeDecorator } from './comments/comments-decorator'; import { CommentsContribution } from './comments/comments-contribution'; import { CommentsContextKeyService } from './comments/comments-context-key-service'; -import { CustomEditorContribution } from './custom-editors/custom-editor-contribution'; import { PluginCustomEditorRegistry } from './custom-editors/plugin-custom-editor-registry'; import { CustomEditorWidgetFactory } from '../browser/custom-editors/custom-editor-widget-factory'; import { CustomEditorWidget } from './custom-editors/custom-editor-widget'; @@ -90,6 +90,7 @@ import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebo import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { ArgumentProcessorContribution } from './command-registry-main'; import { WebviewSecondaryWindowSupport } from './webview/webview-secondary-window-support'; +import { CustomEditorUndoRedoHandler } from './custom-editors/custom-editor-undo-redo-handler'; export default new ContainerModule((bind, unbind, isBound, rebind) => { @@ -191,14 +192,13 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(FrontendApplicationContribution).toService(WebviewSecondaryWindowSupport); bind(FrontendApplicationContribution).toService(WebviewContextKeys); - bind(CustomEditorContribution).toSelf().inSingletonScope(); - bind(CommandContribution).toService(CustomEditorContribution); - bind(PluginCustomEditorRegistry).toSelf().inSingletonScope(); bind(CustomEditorService).toSelf().inSingletonScope(); bind(CustomEditorWidget).toSelf(); bind(CustomEditorWidgetFactory).toDynamicValue(ctx => new CustomEditorWidgetFactory(ctx.container)).inSingletonScope(); bind(WidgetFactory).toService(CustomEditorWidgetFactory); + bind(CustomEditorUndoRedoHandler).toSelf().inSingletonScope(); + bind(UndoRedoHandler).toService(CustomEditorUndoRedoHandler); bind(PluginViewWidget).toSelf(); bind(WidgetFactory).toDynamicValue(({ container }) => ({ From 1e5fb536d65550ad8fa0fefcec731645b2afc74a Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 6 Aug 2024 17:59:08 +0200 Subject: [PATCH 331/441] Fix notebook output scrolling and text rendering (#14016) --- packages/core/src/browser/style/index.css | 3 +- .../browser/contributions/cell-operations.ts | 7 +- .../notebook-actions-contribution.ts | 3 +- .../notebook-cell-actions-contribution.ts | 12 +-- .../src/browser/service/notebook-options.ts | 3 +- .../src/browser/service/notebook-service.ts | 8 +- packages/notebook/src/browser/style/index.css | 26 ++++--- .../browser/view/notebook-cell-list-view.tsx | 39 +++++----- .../renderers/cell-output-webview.tsx | 77 +++++++++++++++++++ .../renderers/output-webview-internal.ts | 61 ++++++++++----- 10 files changed, 180 insertions(+), 59 deletions(-) diff --git a/packages/core/src/browser/style/index.css b/packages/core/src/browser/style/index.css index e2ba596cb6491..137b5bfb7d4d5 100644 --- a/packages/core/src/browser/style/index.css +++ b/packages/core/src/browser/style/index.css @@ -239,7 +239,8 @@ blockquote { -o-user-select: none; } -:focus { +/* Since an iframe has its own focus tracking, we don't show focus on iframes */ +:focus:not(iframe) { outline-width: 1px; outline-style: solid; outline-offset: -1px; diff --git a/packages/notebook/src/browser/contributions/cell-operations.ts b/packages/notebook/src/browser/contributions/cell-operations.ts index 6ed1b4b5d7a7d..1953be0527319 100644 --- a/packages/notebook/src/browser/contributions/cell-operations.ts +++ b/packages/notebook/src/browser/contributions/cell-operations.ts @@ -26,6 +26,11 @@ export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellM if (cell.cellKind === type) { return; } + if (type === CellKind.Markup) { + language = 'markdown'; + } else { + language ??= cell.language; + } notebookModel.applyEdits([{ editType: CellEditType.Replace, index: notebookModel.cells.indexOf(cell), @@ -33,7 +38,7 @@ export function changeCellType(notebookModel: NotebookModel, cell: NotebookCellM cells: [{ ...cell.getData(), cellKind: type, - language: language ?? cell.language + language }] }], true); } diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 3fce82523ed1d..4f781114e12e7 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -133,8 +133,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon let cellLanguage: string = 'markdown'; if (cellKind === CellKind.Code) { - const firstCodeCell = notebookModel.cells.find(cell => cell.cellKind === CellKind.Code); - cellLanguage = firstCodeCell?.language ?? 'plaintext'; + cellLanguage = this.notebookService.getCodeCellLanguage(notebookModel); } notebookModel.applyEdits([{ diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 2775a4afab51c..69606906c6ba9 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -33,6 +33,7 @@ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-s import { NotebookCommands } from './notebook-actions-contribution'; import { changeCellType } from './cell-operations'; import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service'; +import { NotebookService } from '../service/notebook-service'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -118,7 +119,7 @@ export namespace NotebookCellCommands { export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.changeToMarkdown', - label: 'Change Cell to Mardown' + label: 'Change Cell to Markdown' }); export const TOGGLE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({ @@ -147,6 +148,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command @inject(ContextKeyService) protected contextKeyService: ContextKeyService; + @inject(NotebookService) + protected notebookService: NotebookService; + @inject(NotebookExecutionService) protected notebookExecutionService: NotebookExecutionService; @@ -346,7 +350,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below')); commands.registerCommand(NotebookCellCommands.TO_CODE_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { - changeCellType(notebookModel, cell, CellKind.Code); + changeCellType(notebookModel, cell, CellKind.Code, this.notebookService.getCodeCellLanguage(notebookModel)); })); commands.registerCommand(NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { changeCellType(notebookModel, cell, CellKind.Markup); @@ -376,9 +380,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command const isMarkdownCell = selectedCell.cellKind === CellKind.Markup; const isMarkdownLanguage = language.value.id === 'markdown'; if (isMarkdownLanguage) { - if (!isMarkdownCell) { - changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id); - } + changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id); } else { if (isMarkdownCell) { changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id); diff --git a/packages/notebook/src/browser/service/notebook-options.ts b/packages/notebook/src/browser/service/notebook-options.ts index c642e97821e3e..2ecf59f02f31f 100644 --- a/packages/notebook/src/browser/service/notebook-options.ts +++ b/packages/notebook/src/browser/service/notebook-options.ts @@ -37,7 +37,7 @@ const notebookOutputOptionsRelevantPreferences = [ export interface NotebookOutputOptions { // readonly outputNodePadding: number; - // readonly outputNodeLeftPadding: number; + readonly outputNodeLeftPadding: number; // readonly previewNodePadding: number; // readonly markdownLeftMargin: number; // readonly leftMargin: number; @@ -95,6 +95,7 @@ export class NotebookOptionsService { fontSize, outputFontSize: outputFontSize, fontFamily: this.preferenceService.get('editor.fontFamily')!, + outputNodeLeftPadding: 8, outputFontFamily: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_FONT_FAMILY), outputLineHeight: this.computeOutputLineHeight(outputLineHeight, outputFontSize ?? fontSize), outputScrolling: this.getNotebookPreferenceWithDefault(NotebookPreferences.OUTPUT_SCROLLING)!, diff --git a/packages/notebook/src/browser/service/notebook-service.ts b/packages/notebook/src/browser/service/notebook-service.ts index 3707d9f656da0..6f9697e18e67b 100644 --- a/packages/notebook/src/browser/service/notebook-service.ts +++ b/packages/notebook/src/browser/service/notebook-service.ts @@ -17,7 +17,7 @@ import { Disposable, DisposableCollection, Emitter, Resource, URI } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { NotebookData, TransientOptions } from '../../common'; +import { CellKind, NotebookData, TransientOptions } from '../../common'; import { NotebookModel, NotebookModelFactory, NotebookModelProps } from '../view-model/notebook-model'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from '../view-model/notebook-cell-model'; @@ -206,4 +206,10 @@ export class NotebookService implements Disposable { return false; } } + + getCodeCellLanguage(model: NotebookModel): string { + const firstCodeCell = model.cells.find(cellModel => cellModel.cellKind === CellKind.Code); + const cellLanguage = firstCodeCell?.language ?? 'plaintext'; + return cellLanguage; + } } diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 844629eb2b36d..333c792073b9d 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -31,6 +31,10 @@ margin: 10px 0px; } +.theia-notebook-cell:focus { + outline: none; +} + .theia-notebook-cell.draggable { cursor: grab; } @@ -85,7 +89,7 @@ /* Markdown cell edit mode */ .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { - margin-left: 37px; + margin-left: 36px; margin-right: var(--theia-notebook-cell-editor-margin-right); outline: 1px solid var(--theia-notebook-cellBorderColor); } @@ -107,7 +111,7 @@ width: calc(100% - 46px); flex: 1; outline: 1px solid var(--theia-notebook-cellBorderColor); - margin: 0px 10px; + margin: 0px 16px 0px 10px; } .theia-notebook-cell.focused .theia-notebook-cell-editor-container { @@ -288,7 +292,7 @@ .theia-notebook-cell-output-webview { padding: 5px 0px; - margin: 0px 10px; + margin: 0px 15px 0px 9px; width: 100%; } @@ -350,7 +354,7 @@ transform: translateY(calc(-100% - 10px)); } -.theia-notebook-find-widget.search-mode > * > *:nth-child(2) { +.theia-notebook-find-widget.search-mode>*>*:nth-child(2) { display: none; } @@ -379,9 +383,9 @@ align-items: center; } -.theia-notebook-find-widget-buttons-first > div, -.theia-notebook-find-widget-buttons-second > div { - margin-right: 4px; +.theia-notebook-find-widget-buttons-first>div, +.theia-notebook-find-widget-buttons-second>div { + margin-right: 4px; } .theia-notebook-find-widget-buttons-second { @@ -457,11 +461,11 @@ } mark.theia-find-match { - color: var(--theia-editor-findMatchHighlightForeground); - background-color: var(--theia-editor-findMatchHighlightBackground); + color: var(--theia-editor-findMatchHighlightForeground); + background-color: var(--theia-editor-findMatchHighlightBackground); } mark.theia-find-match.theia-find-match-selected { - color: var(--theia-editor-findMatchForeground); - background-color: var(--theia-editor-findMatchBackground); + color: var(--theia-editor-findMatchForeground); + background-color: var(--theia-editor-findMatchBackground); } diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 41b719be09bcb..0c66a6f6531ce 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -48,6 +48,7 @@ export class NotebookCellListView extends React.Component = React.createRef(); constructor(props: CellListProps) { super(props); @@ -82,6 +83,24 @@ export class NotebookCellListView extends React.Component { + animationFrame().then(() => { + if (!this.cellListRef.current) { + return; + } + let hasCellFocus = false; + let hasFocus = false; + if (this.cellListRef.current.contains(document.activeElement)) { + if (this.props.notebookModel.selectedCell) { + hasCellFocus = true; + } + hasFocus = true; + } + this.props.notebookContext.changeCellFocus(hasCellFocus); + this.props.notebookContext.changeCellListFocus(hasFocus); + }); + })); } override componentWillUnmount(): void { @@ -89,24 +108,7 @@ export class NotebookCellListView extends React.Component { - this.toDispose.push(onDomEvent(document, 'focusin', () => { - animationFrame().then(() => { - let hasCellFocus = false; - let hasFocus = false; - if (ref?.contains(document.activeElement)) { - if (this.props.notebookModel.selectedCell) { - hasCellFocus = true; - } - hasFocus = true; - } - this.props.notebookContext.changeCellFocus(hasCellFocus); - this.props.notebookContext.changeCellListFocus(hasFocus); - }); - })); - } - }> + return
        {this.props.notebookModel.cells .map((cell, index) => @@ -129,6 +131,7 @@ export class NotebookCellListView extends React.Component this.onDragOver(e, cell)} onDrop={e => this.onDrop(e, index)} draggable={true} + tabIndex={-1} ref={ref => cell === this.state.selectedCell && this.state.scrollIntoView && ref?.scrollIntoView({ block: 'nearest' })}>
        diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index c2f7555e7bbf3..745c77dfb231e 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -50,7 +50,83 @@ export function createCellOutputWebviewContainer(ctx: interfaces.Container, cell return child; } +// Should be kept up-to-date with: +// https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping.ts +const mapping: ReadonlyMap = new Map([ + ['theme-font-family', 'vscode-font-family'], + ['theme-font-weight', 'vscode-font-weight'], + ['theme-font-size', 'vscode-font-size'], + ['theme-code-font-family', 'vscode-editor-font-family'], + ['theme-code-font-weight', 'vscode-editor-font-weight'], + ['theme-code-font-size', 'vscode-editor-font-size'], + ['theme-scrollbar-background', 'vscode-scrollbarSlider-background'], + ['theme-scrollbar-hover-background', 'vscode-scrollbarSlider-hoverBackground'], + ['theme-scrollbar-active-background', 'vscode-scrollbarSlider-activeBackground'], + ['theme-quote-background', 'vscode-textBlockQuote-background'], + ['theme-quote-border', 'vscode-textBlockQuote-border'], + ['theme-code-foreground', 'vscode-textPreformat-foreground'], + // Editor + ['theme-background', 'vscode-editor-background'], + ['theme-foreground', 'vscode-editor-foreground'], + ['theme-ui-foreground', 'vscode-foreground'], + ['theme-link', 'vscode-textLink-foreground'], + ['theme-link-active', 'vscode-textLink-activeForeground'], + // Buttons + ['theme-button-background', 'vscode-button-background'], + ['theme-button-hover-background', 'vscode-button-hoverBackground'], + ['theme-button-foreground', 'vscode-button-foreground'], + ['theme-button-secondary-background', 'vscode-button-secondaryBackground'], + ['theme-button-secondary-hover-background', 'vscode-button-secondaryHoverBackground'], + ['theme-button-secondary-foreground', 'vscode-button-secondaryForeground'], + ['theme-button-hover-foreground', 'vscode-button-foreground'], + ['theme-button-focus-foreground', 'vscode-button-foreground'], + ['theme-button-secondary-hover-foreground', 'vscode-button-secondaryForeground'], + ['theme-button-secondary-focus-foreground', 'vscode-button-secondaryForeground'], + // Inputs + ['theme-input-background', 'vscode-input-background'], + ['theme-input-foreground', 'vscode-input-foreground'], + ['theme-input-placeholder-foreground', 'vscode-input-placeholderForeground'], + ['theme-input-focus-border-color', 'vscode-focusBorder'], + // Menus + ['theme-menu-background', 'vscode-menu-background'], + ['theme-menu-foreground', 'vscode-menu-foreground'], + ['theme-menu-hover-background', 'vscode-menu-selectionBackground'], + ['theme-menu-focus-background', 'vscode-menu-selectionBackground'], + ['theme-menu-hover-foreground', 'vscode-menu-selectionForeground'], + ['theme-menu-focus-foreground', 'vscode-menu-selectionForeground'], + // Errors + ['theme-error-background', 'vscode-inputValidation-errorBackground'], + ['theme-error-foreground', 'vscode-foreground'], + ['theme-warning-background', 'vscode-inputValidation-warningBackground'], + ['theme-warning-foreground', 'vscode-foreground'], + ['theme-info-background', 'vscode-inputValidation-infoBackground'], + ['theme-info-foreground', 'vscode-foreground'], + // Notebook: + ['theme-notebook-output-background', 'vscode-notebook-outputContainerBackgroundColor'], + ['theme-notebook-output-border', 'vscode-notebook-outputContainerBorderColor'], + ['theme-notebook-cell-selected-background', 'vscode-notebook-selectedCellBackground'], + ['theme-notebook-symbol-highlight-background', 'vscode-notebook-symbolHighlightBackground'], + ['theme-notebook-diff-removed-background', 'vscode-diffEditor-removedTextBackground'], + ['theme-notebook-diff-inserted-background', 'vscode-diffEditor-insertedTextBackground'], +]); + +const constants: Record = { + 'theme-input-border-width': '1px', + 'theme-button-primary-hover-shadow': 'none', + 'theme-button-secondary-hover-shadow': 'none', + 'theme-input-border-color': 'transparent', +}; + export const DEFAULT_NOTEBOOK_OUTPUT_CSS = ` +:root { + ${Array.from(mapping.entries()).map(([key, value]) => `--${key}: var(--${value});`).join('\n')} + ${Object.entries(constants).map(([key, value]) => `--${key}: ${value};`).join('\n')} +} + +body { + padding: 0; +} + table { border-collapse: collapse; border-spacing: 0; @@ -323,6 +399,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { protected generateStyles(): { [key: string]: string } { return { + 'notebook-output-node-left-padding': `${this.options.outputNodeLeftPadding}px`, 'notebook-cell-output-font-size': `${this.options.outputFontSize || this.options.fontSize}px`, 'notebook-cell-output-line-height': `${this.options.outputLineHeight}px`, 'notebook-cell-output-max-height': `${this.options.outputLineHeight * this.options.outputLineLimit}px`, diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index 7e01a55751954..ea348cc8d269b 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -146,14 +146,10 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { renderer: Renderer; element: HTMLElement; + container: HTMLElement; constructor(output: webviewCommunication.Output, items: rendererApi.OutputItem[]) { - this.element = document.createElement('div'); - // padding for scrollbars - this.element.style.paddingBottom = '10px'; - this.element.style.paddingRight = '10px'; - this.element.id = output.id; - document.body.appendChild(this.element); + this.createHtmlElement(output.id); this.outputId = output.id; this.allItems = items; } @@ -172,6 +168,25 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { this.renderer?.disposeOutputItem?.(this.renderedItem?.id); this.element.innerHTML = ''; } + + private createHtmlElement(id: string): void { + // Recreates the output container structure used in VS Code + this.container = document.createElement('div'); + this.container.id = 'container'; + this.container.classList.add('widgetarea'); + const cellContainer = document.createElement('div'); + cellContainer.classList.add('cell_container'); + cellContainer.id = id; + this.container.appendChild(cellContainer); + const outputContainer = document.createElement('div'); + outputContainer.classList.add('output-container'); + cellContainer.appendChild(outputContainer); + this.element = document.createElement('div'); + this.element.id = id; + this.element.classList.add('output'); + outputContainer.appendChild(this.element); + document.body.appendChild(this.container); + } } const outputs: Output[] = []; @@ -469,7 +484,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { function clearOutput(output: Output): void { output.clear(); - output.element.remove(); + output.container.remove(); } function outputsChanged(changedEvent: webviewCommunication.OutputChangedMessage): void { @@ -504,16 +519,16 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { } } - function scrollParent(event: WheelEvent): boolean { + function shouldHandleScroll(event: WheelEvent): boolean { for (let node = event.target as Node | null; node; node = node.parentNode) { if (!(node instanceof Element)) { - continue; + return false; } // scroll up if (event.deltaY < 0 && node.scrollTop > 0) { // there is still some content to scroll - return false; + return true; } // scroll down @@ -521,23 +536,31 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { // per https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight // scrollTop is not rounded but scrollHeight and clientHeight are // so we need to check if the difference is less than some threshold - if (node.scrollHeight - node.scrollTop - node.clientHeight > 2) { - return false; + if (node.scrollHeight - node.scrollTop - node.clientHeight < 2) { + continue; + } + + // if the node is not scrollable, we can continue. We don't check the computed style always as it's expensive + if (window.getComputedStyle(node).overflowY === 'hidden' || window.getComputedStyle(node).overflowY === 'visible') { + continue; } + + return true; } } - return true; + return false; } const handleWheel = (event: WheelEvent & { wheelDeltaX?: number; wheelDeltaY?: number; wheelDelta?: number }) => { - if (scrollParent(event)) { - theia.postMessage({ - type: 'did-scroll-wheel', - deltaY: event.deltaY, - deltaX: event.deltaX, - }); + if (event.defaultPrevented || shouldHandleScroll(event)) { + return; } + theia.postMessage({ + type: 'did-scroll-wheel', + deltaY: event.deltaY, + deltaX: event.deltaX, + }); }; window.addEventListener('message', async rawEvent => { From 5255c512c6c9da44c11cb4d5fca7379de60b71cc Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Wed, 7 Aug 2024 18:21:31 +0200 Subject: [PATCH 332/441] Notebook: Support for Alt+Enter keybinding (#14022) Co-authored-by: Mark Sujew --- .../notebook-actions-contribution.ts | 6 ++- .../notebook-cell-actions-contribution.ts | 42 ++++++++++++++++++- .../src/browser/notebook-editor-widget.tsx | 3 -- .../src/browser/view/notebook-cell-editor.tsx | 10 +++++ .../browser/view/notebook-cell-list-view.tsx | 9 +++- 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 4f781114e12e7..dd11108e0704f 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -187,12 +187,14 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon if (change === CellChangeDirection.Up && currentIndex > 0) { model.setSelectedCell(model.cells[currentIndex - 1]); - if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + if ((model.selectedCell?.cellKind === CellKind.Code + || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) { model.selectedCell.requestFocusEditor('lastLine'); } } else if (change === CellChangeDirection.Down && currentIndex < model.cells.length - 1) { model.setSelectedCell(model.cells[currentIndex + 1]); - if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) { + if ((model.selectedCell?.cellKind === CellKind.Code + || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) { model.selectedCell.requestFocusEditor(); } } diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 69606906c6ba9..8242947a4d116 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -39,11 +39,13 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const EDIT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.edit', + category: 'Notebook', iconClass: codicon('edit') }); /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const STOP_EDIT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.stop-edit', + category: 'Notebook', iconClass: codicon('check') }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ @@ -59,11 +61,21 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell', + category: 'Notebook', + label: nls.localizeByDefault('Execute Cell'), iconClass: codicon('play'), }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.execute-cell-and-focus-next', + label: nls.localizeByDefault('Execute Notebook Cell and Select Below'), + category: 'Notebook', + }); + /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ + export const EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND = Command.toDefaultLocalizedCommand({ + id: 'notebook.cell.execute-cell-and-insert-below', + label: nls.localizeByDefault('Execute Notebook Cell and Insert Below'), + category: 'Notebook', }); export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({ @@ -85,11 +97,13 @@ export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ export const CLEAR_OUTPUTS_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.clear-outputs', + category: 'Notebook', label: 'Clear Cell Outputs', }); /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel | undefined, output: NotebookCellOutputModel */ export const CHANGE_OUTPUT_PRESENTATION_COMMAND = Command.toDefaultLocalizedCommand({ id: 'notebook.cell.change-presentation', + category: 'Notebook', label: 'Change Presentation', }); @@ -114,11 +128,13 @@ export namespace NotebookCellCommands { export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.changeToCode', + category: 'Notebook', label: 'Change Cell to Code' }); export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({ id: 'notebook.cell.changeToMarkdown', + category: 'Notebook', label: 'Change Cell to Markdown' }); @@ -302,6 +318,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command } }) ); + commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND, this.editableCellCommandHandler( + async (notebookModel, cell) => { + if (cell.cellKind === CellKind.Code) { + await commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell); + } + await commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell); + + if (cell.cellKind === CellKind.Code) { + await commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id); + } else { + await commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id); + } + + const index = notebookModel.cells.indexOf(cell); + notebookModel.setSelectedCell(notebookModel.cells[index + 1]); + }) + ); commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { @@ -432,8 +465,8 @@ export class NotebookCellActionContribution implements MenuContribution, Command }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, - keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), - when: `editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt, KeyModifier.CtrlCmd] }).toString(), + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`, }, { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, @@ -450,6 +483,11 @@ export class NotebookCellActionContribution implements MenuContribution, Command keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(), when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, }, + { + command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND.id, + keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(), + when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + }, { command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id, keybinding: KeyCode.createKeyCode({ first: Key.KEY_O, modifiers: [KeyModifier.Alt] }).toString(), diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 2dc5723fcfc03..a88ed0a77b692 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -154,7 +154,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa @postConstruct() protected init(): void { this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString(); - this.node.tabIndex = -1; this.scrollOptions = { suppressScrollY: true @@ -174,8 +173,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]); model.setSelectedCell(model.cells[0]); } - model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus())); - model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus()))); }); } diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 969c031dc6753..db72b0765c267 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -101,6 +101,16 @@ export class CellEditor extends React.Component { this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount); })); + this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => { + let parent = this.container?.parentElement; + while (parent && !parent.classList.contains('theia-notebook-cell')) { + parent = parent.parentElement; + } + if (parent) { + parent.focus(); + } + })); + this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { this.editor?.getControl().updateOptions(options); })); diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 0c66a6f6531ce..14e7a42ef5b5a 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -132,7 +132,14 @@ export class NotebookCellListView extends React.Component this.onDrop(e, index)} draggable={true} tabIndex={-1} - ref={ref => cell === this.state.selectedCell && this.state.scrollIntoView && ref?.scrollIntoView({ block: 'nearest' })}> + ref={ref => { + if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) { + ref.scrollIntoView({ block: 'nearest' }); + if (cell.cellKind === CellKind.Markup && !cell.editing) { + ref.focus(); + } + } + }}>
        {this.renderCellContent(cell, index)} From 44d42ecde0f1db6bcd18b71af033c49b9ebb0e2f Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Fri, 9 Aug 2024 13:17:55 +0200 Subject: [PATCH 333/441] Actually create notebook model on `openNotebookDocument` (#14029) --- .../src/main/browser/notebooks/notebook-documents-main.ts | 1 + packages/plugin-ext/src/plugin/plugin-context.ts | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index e17840b778e5d..427e0a009b627 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -169,6 +169,7 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { async $tryOpenNotebook(uriComponents: UriComponents): Promise { const uri = URI.fromComponents(uriComponents); + await this.notebookModelResolverService.resolve(uri); return uri.toComponents(); } diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 1a66df549a33c..2bbc0d04c7410 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -740,9 +740,8 @@ export function createAPIFactory( } else { throw new Error('Invalid arguments'); } - const result = await notebooksExt.waitForNotebookDocument(uri); - return result.apiNotebook; - + // Notebook extension will create a document in openNotebookDocument() or create openNotebookDocument() + return notebooksExt.getNotebookDocument(uri).apiNotebook; }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): theia.FileSystemWatcher => extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete), From 817c1a00cecdf0770500aaa868daef78d4a52a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 13 Aug 2024 09:07:56 +0200 Subject: [PATCH 334/441] Don't use ChannelMultiplexer in RPCProtocol (#13980) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13960 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../plugin-ext/src/common/proxy-handler.ts | 143 ------------------ .../plugin-ext/src/common/rpc-protocol.ts | 128 ++++++++-------- 2 files changed, 62 insertions(+), 209 deletions(-) delete mode 100644 packages/plugin-ext/src/common/proxy-handler.ts diff --git a/packages/plugin-ext/src/common/proxy-handler.ts b/packages/plugin-ext/src/common/proxy-handler.ts deleted file mode 100644 index 6a96eaf97c934..0000000000000 --- a/packages/plugin-ext/src/common/proxy-handler.ts +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2022 STMicroelectronics and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 - ********************************************************************************/ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Channel, RpcProtocol, RpcProtocolOptions } from '@theia/core/'; -import { RpcMessageDecoder, RpcMessageEncoder } from '@theia/core/lib/common/message-rpc/rpc-message-encoder'; -import { Deferred } from '@theia/core/lib/common/promise-util'; - -export interface RpcHandlerOptions { - id: string - encoder: RpcMessageEncoder, - decoder: RpcMessageDecoder -} -export interface ProxyHandlerOptions extends RpcHandlerOptions { - channelProvider: () => Promise, - proxySynchronizer: ProxySynchronizer, -} - -export interface InvocationHandlerOptions extends RpcHandlerOptions { - target: any -} - -export interface ProxySynchronizer { - startProxyInitialization(id: string, init: Promise): void - pendingProxyInitializations(): Promise -} - -/** - * A proxy handler that will send any method invocation on the proxied object - * as a rcp protocol message over a channel. - */ -export class ClientProxyHandler implements ProxyHandler { - private rpcDeferred: Deferred = new Deferred(); - private isRpcInitialized = false; - - readonly id: string; - private readonly channelProvider: () => Promise; - private readonly proxySynchronizer: ProxySynchronizer; - private readonly encoder: RpcMessageEncoder; - private readonly decoder: RpcMessageDecoder; - - constructor(options: ProxyHandlerOptions) { - Object.assign(this, options); - } - - private initializeRpc(): void { - // we need to set the flag to true before waiting for the channel provider. Otherwise `get` might - // get called again and we'll try to open a channel more than once - this.proxySynchronizer.startProxyInitialization(this.id, this.rpcDeferred.promise.then(() => { })); - this.isRpcInitialized = true; - const clientOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'clientOnly' }; - this.channelProvider().then(channel => { - const rpc = new RpcProtocol(channel, undefined, clientOptions); - this.rpcDeferred.resolve(rpc); - }); - } - - get(target: any, name: string, receiver: any): any { - if (!this.isRpcInitialized) { - this.initializeRpc(); - } - - if (target[name] || name.charCodeAt(0) !== 36 /* CharCode.DollarSign */) { - return target[name]; - } - const isNotify = this.isNotification(name); - return (...args: any[]) => { - const method = name.toString(); - return this.sendWhenNoInit(async (connection: RpcProtocol) => { - if (isNotify) { - connection.sendNotification(method, args); - } else { - return await connection.sendRequest(method, args) as Promise; - } - }); - }; - } - - private sendWhenNoInit(send: (connection: RpcProtocol) => Promise): Promise { - return this.proxySynchronizer.pendingProxyInitializations().then(() => this.rpcDeferred.promise.then(send)); - } - - /** - * Return whether the given property represents a notification. If true, - * the promise returned from the invocation will resolve immediately to `undefined` - * - * A property leads to a notification rather than a method call if its name - * begins with `notify` or `on`. - * - * @param p - The property being called on the proxy. - * @return Whether `p` represents a notification. - */ - protected isNotification(p: PropertyKey): boolean { - let propertyString = p.toString(); - if (propertyString.charCodeAt(0) === 36/* CharCode.DollarSign */) { - propertyString = propertyString.substring(1); - } - return propertyString.startsWith('notify') || propertyString.startsWith('on'); - } -} - -export class RpcInvocationHandler { - readonly id: string; - readonly target: any; - - private rpcDeferred: Deferred = new Deferred(); - private readonly encoder: RpcMessageEncoder; - private readonly decoder: RpcMessageDecoder; - - constructor(options: InvocationHandlerOptions) { - Object.assign(this, options); - } - - listen(channel: Channel): void { - const serverOptions: RpcProtocolOptions = { encoder: this.encoder, decoder: this.decoder, mode: 'serverOnly' }; - const server = new RpcProtocol(channel, (method: string, args: any[]) => this.handleRequest(method, args), serverOptions); - server.onNotification((e: { method: string, args: any }) => this.onNotification(e.method, e.args)); - this.rpcDeferred.resolve(server); - } - - protected handleRequest(method: string, args: any[]): Promise { - return this.rpcDeferred.promise.then(() => this.target[method](...args)); - } - - protected onNotification(method: string, args: any[]): void { - this.rpcDeferred.promise.then(() => { - this.target[method](...args); - }); - } -} - diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index fb8ae1c3ca28a..1cc84d49e4648 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -22,17 +22,14 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Channel, Disposable, DisposableCollection, isObject, ReadBuffer, URI, WriteBuffer } from '@theia/core'; +import { Channel, Disposable, DisposableCollection, isObject, ReadBuffer, RpcProtocol, URI, WriteBuffer } from '@theia/core'; import { Emitter, Event } from '@theia/core/lib/common/event'; -import { ChannelMultiplexer, MessageProvider } from '@theia/core/lib/common/message-rpc/channel'; -import { MsgPackMessageDecoder, MsgPackMessageEncoder } from '@theia/core/lib/common/message-rpc/rpc-message-encoder'; +import { MessageProvider } from '@theia/core/lib/common/message-rpc/channel'; import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '@theia/core/lib/common/message-rpc/uint8-array-message-buffer'; -import { ClientProxyHandler, ProxySynchronizer, RpcInvocationHandler } from './proxy-handler'; import { MsgPackExtensionManager } from '@theia/core/lib/common/message-rpc/msg-pack-extension-manager'; import { URI as VSCodeURI } from '@theia/core/shared/vscode-uri'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { Range, Position } from '../plugin/types-impl'; -import { Deferred } from '@theia/core/lib/common/promise-util'; export interface MessageConnection { send(msg: string): void; @@ -79,21 +76,36 @@ export namespace ConnectionClosedError { } export class RPCProtocolImpl implements RPCProtocol { - private readonly locals = new Map(); + private readonly locals = new Map(); private readonly proxies = new Map(); - private readonly multiplexer: ChannelMultiplexer; - private readonly encoder = new MsgPackMessageEncoder(); - private readonly decoder = new MsgPackMessageDecoder(); - private readonly initCallback: ProxySynchronizer; + private readonly rpc: RpcProtocol; private readonly toDispose = new DisposableCollection( Disposable.create(() => { /* mark as no disposed */ }) ); constructor(channel: Channel) { - this.toDispose.push(this.multiplexer = new ChannelMultiplexer(new BatchingChannel(channel))); + this.rpc = new RpcProtocol(new BatchingChannel(channel), (method, args) => this.handleRequest(method, args)); + this.rpc.onNotification((evt: { method: string; args: any[]; }) => this.handleNotification(evt.method, evt.args)); this.toDispose.push(Disposable.create(() => this.proxies.clear())); - this.initCallback = new ProxySynchronizerImpl(); + } + + handleNotification(method: any, args: any[]): void { + const serviceId = args[0] as string; + const handler: any = this.locals.get(serviceId); + if (!handler) { + throw new Error(`no local service handler with id ${serviceId}`); + } + handler[method](...(args.slice(1))); + } + + handleRequest(method: string, args: any[]): Promise { + const serviceId = args[0] as string; + const handler: any = this.locals.get(serviceId); + if (!handler) { + throw new Error(`no local service handler with id ${serviceId}`); + } + return handler[method](...(args.slice(1))); } dispose(): void { @@ -117,76 +129,60 @@ export class RPCProtocolImpl implements RPCProtocol { } protected createProxy(proxyId: string): T { - const handler = new ClientProxyHandler({ - id: proxyId, encoder: this.encoder, decoder: this.decoder, channelProvider: () => this.multiplexer.open(proxyId), proxySynchronizer: this.initCallback - }); + const handler = { + get: (target: any, name: string, receiver: any): any => { + if (target[name] || name.charCodeAt(0) !== 36 /* CharCode.DollarSign */) { + // not a remote property + return target[name]; + } + const isNotify = this.isNotification(name); + return async (...args: any[]) => { + const method = name.toString(); + if (isNotify) { + this.rpc.sendNotification(method, [proxyId, ...args]); + } else { + return await this.rpc.sendRequest(method, [proxyId, ...args]) as Promise; + } + }; + } + + }; return new Proxy(Object.create(null), handler); } + /** + * Return whether the given property represents a notification. If true, + * the promise returned from the invocation will resolve immediately to `undefined` + * + * A property leads to a notification rather than a method call if its name + * begins with `notify` or `on`. + * + * @param p - The property being called on the proxy. + * @return Whether `p` represents a notification. + */ + protected isNotification(p: PropertyKey): boolean { + let propertyString = p.toString(); + if (propertyString.charCodeAt(0) === 36/* CharCode.DollarSign */) { + propertyString = propertyString.substring(1); + } + return propertyString.startsWith('notify') || propertyString.startsWith('on'); + } + set(identifier: ProxyIdentifier, instance: R): R { if (this.isDisposed) { throw ConnectionClosedError.create(); } - const invocationHandler = this.locals.get(identifier.id); - if (!invocationHandler) { - const handler = new RpcInvocationHandler({ id: identifier.id, target: instance, encoder: this.encoder, decoder: this.decoder }); - - const channel = this.multiplexer.getOpenChannel(identifier.id); - if (channel) { - handler.listen(channel); - } else { - const channelOpenListener = this.multiplexer.onDidOpenChannel(event => { - if (event.id === identifier.id) { - handler.listen(event.channel); - channelOpenListener.dispose(); - } - }); - } - - this.locals.set(identifier.id, handler); + if (!this.locals.has(identifier.id)) { + this.locals.set(identifier.id, instance); if (Disposable.is(instance)) { this.toDispose.push(instance); } this.toDispose.push(Disposable.create(() => this.locals.delete(identifier.id))); - } return instance; } } -export class ProxySynchronizerImpl implements ProxySynchronizer { - - private readonly runningInitializations = new Set(); - - private _pendingProxyInitializations: Deferred; - - constructor() { - this._pendingProxyInitializations = new Deferred(); - /* after creation no init is active */ - this._pendingProxyInitializations.resolve(); - } - - startProxyInitialization(id: string, init: Promise): void { - if (this.runningInitializations.size === 0) { - this._pendingProxyInitializations = new Deferred(); - } - init.then(() => this.finishedProxyInitialization(id)); - this.runningInitializations.add(id); - } - - protected finishedProxyInitialization(id: string): void { - this.runningInitializations.delete(id); - if (this.runningInitializations.size === 0) { - this._pendingProxyInitializations.resolve(); - } - } - - pendingProxyInitializations(): Promise { - return this._pendingProxyInitializations.promise; - } - -} - /** * Wraps and underlying channel to send/receive multiple messages in one go: * - multiple messages to be sent from one stack get sent in bulk at `process.nextTick`. From a9345dab1b6ea175ec98a5bf197e56876f2d58f6 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 13 Aug 2024 14:56:11 +0200 Subject: [PATCH 335/441] Fix 429 errors on OVSX requests (#14030) --- dev-packages/cli/src/download-plugins.ts | 11 ++++--- dev-packages/cli/src/theia.ts | 12 ++++--- dev-packages/ovsx-client/package.json | 1 + dev-packages/ovsx-client/src/index.ts | 2 +- .../ovsx-client/src/ovsx-api-filter.ts | 5 ++- .../ovsx-client/src/ovsx-http-client.ts | 33 +++++++++++++++---- packages/vsx-registry/package.json | 1 + .../src/common/vsx-environment.ts | 1 + .../src/common/vsx-registry-common-module.ts | 13 ++++++-- packages/vsx-registry/src/node/vsx-cli.ts | 6 +++- .../src/node/vsx-environment-impl.ts | 4 +++ 11 files changed, 66 insertions(+), 23 deletions(-) diff --git a/dev-packages/cli/src/download-plugins.ts b/dev-packages/cli/src/download-plugins.ts index dee69530e72c8..ca17aa88cc989 100644 --- a/dev-packages/cli/src/download-plugins.ts +++ b/dev-packages/cli/src/download-plugins.ts @@ -55,8 +55,6 @@ export interface DownloadPluginsOptions { * Fetch plugins in parallel */ parallel?: boolean; - - rateLimit?: number; } interface PluginDownload { @@ -65,16 +63,19 @@ interface PluginDownload { version?: string | undefined } -export default async function downloadPlugins(ovsxClient: OVSXClient, requestService: RequestService, options: DownloadPluginsOptions = {}): Promise { +export default async function downloadPlugins( + ovsxClient: OVSXClient, + rateLimiter: RateLimiter, + requestService: RequestService, + options: DownloadPluginsOptions = {} +): Promise { const { packed = false, ignoreErrors = false, apiVersion = DEFAULT_SUPPORTED_API_VERSION, - rateLimit = 15, parallel = true } = options; - const rateLimiter = new RateLimiter({ tokensPerInterval: rateLimit, interval: 'second' }); const apiFilter = new OVSXApiFilterImpl(ovsxClient, apiVersion); // Collect the list of failures to be appended at the end of the script. diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index d215c578de288..6da5e2c498b1f 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -24,9 +24,10 @@ import { ApplicationProps, DEFAULT_SUPPORTED_API_VERSION } from '@theia/applicat import checkDependencies from './check-dependencies'; import downloadPlugins from './download-plugins'; import runTest from './run-test'; +import { RateLimiter } from 'limiter'; import { LocalizationManager, extract } from '@theia/localization-manager'; import { NodeRequestService } from '@theia/request/lib/node-request-service'; -import { ExtensionIdMatchesFilterFactory, OVSXClient, OVSXHttpClient, OVSXRouterClient, RequestContainsFilterFactory } from '@theia/ovsx-client'; +import { ExtensionIdMatchesFilterFactory, OVSX_RATE_LIMIT, OVSXClient, OVSXHttpClient, OVSXRouterClient, RequestContainsFilterFactory } from '@theia/ovsx-client'; const { executablePath } = require('puppeteer'); @@ -389,7 +390,7 @@ async function theiaCli(): Promise { 'rate-limit': { describe: 'Amount of maximum open-vsx requests per second', number: true, - default: 15 + default: OVSX_RATE_LIMIT }, 'proxy-url': { describe: 'Proxy URL' @@ -415,6 +416,7 @@ async function theiaCli(): Promise { strictSSL: strictSsl }); let client: OVSXClient | undefined; + const rateLimiter = new RateLimiter({ tokensPerInterval: options.rateLimit, interval: 'second' }); if (ovsxRouterConfig) { const routerConfig = await fs.promises.readFile(ovsxRouterConfig, 'utf8').then(JSON.parse, error => { console.error(error); @@ -422,15 +424,15 @@ async function theiaCli(): Promise { if (routerConfig) { client = await OVSXRouterClient.FromConfig( routerConfig, - OVSXHttpClient.createClientFactory(requestService), + OVSXHttpClient.createClientFactory(requestService, rateLimiter), [RequestContainsFilterFactory, ExtensionIdMatchesFilterFactory] ); } } if (!client) { - client = new OVSXHttpClient(apiUrl, requestService); + client = new OVSXHttpClient(apiUrl, requestService, rateLimiter); } - await downloadPlugins(client, requestService, options); + await downloadPlugins(client, rateLimiter, requestService, options); }, }) .command<{ diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index edbc5bb930921..3ef7f91b6d1bd 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@theia/request": "1.52.0", + "limiter": "^2.1.0", "semver": "^7.5.4", "tslib": "^2.6.2" } diff --git a/dev-packages/ovsx-client/src/index.ts b/dev-packages/ovsx-client/src/index.ts index 42455e7897de3..bc836bde8ce98 100644 --- a/dev-packages/ovsx-client/src/index.ts +++ b/dev-packages/ovsx-client/src/index.ts @@ -15,7 +15,7 @@ // ***************************************************************************** export { OVSXApiFilter, OVSXApiFilterImpl, OVSXApiFilterProvider } from './ovsx-api-filter'; -export { OVSXHttpClient } from './ovsx-http-client'; +export { OVSXHttpClient, OVSX_RATE_LIMIT } from './ovsx-http-client'; export { OVSXMockClient } from './ovsx-mock-client'; export { OVSXRouterClient, OVSXRouterConfig, OVSXRouterFilterFactory as FilterFactory } from './ovsx-router-client'; export * from './ovsx-router-filters'; diff --git a/dev-packages/ovsx-client/src/ovsx-api-filter.ts b/dev-packages/ovsx-client/src/ovsx-api-filter.ts index a470a4ee3f989..17a882b1f9796 100644 --- a/dev-packages/ovsx-client/src/ovsx-api-filter.ts +++ b/dev-packages/ovsx-client/src/ovsx-api-filter.ts @@ -67,12 +67,13 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { protected async queryLatestCompatibleExtension(query: VSXQueryOptions): Promise { let offset = 0; + let size = 5; let loop = true; while (loop) { const queryOptions: VSXQueryOptions = { ...query, offset, - size: 5 // there is a great chance that the newest version will work + size // there is a great chance that the newest version will work }; const results = await this.client.query(queryOptions); const compatibleExtension = this.getLatestCompatibleExtension(results.extensions); @@ -83,6 +84,8 @@ export class OVSXApiFilterImpl implements OVSXApiFilter { offset += results.extensions.length; // Continue querying if there are more extensions available loop = results.totalSize > offset; + // Adjust the size to fetch more extensions next time + size = Math.min(size * 2, 100); } return undefined; } diff --git a/dev-packages/ovsx-client/src/ovsx-http-client.ts b/dev-packages/ovsx-client/src/ovsx-http-client.ts index a810680711f30..e6e5c3298827b 100644 --- a/dev-packages/ovsx-client/src/ovsx-http-client.ts +++ b/dev-packages/ovsx-client/src/ovsx-http-client.ts @@ -16,6 +16,9 @@ import { OVSXClient, VSXQueryOptions, VSXQueryResult, VSXSearchOptions, VSXSearchResult } from './ovsx-types'; import { RequestContext, RequestService } from '@theia/request'; +import { RateLimiter } from 'limiter'; + +export const OVSX_RATE_LIMIT = 15; export class OVSXHttpClient implements OVSXClient { @@ -23,15 +26,16 @@ export class OVSXHttpClient implements OVSXClient { * @param requestService * @returns factory that will cache clients based on the requested input URL. */ - static createClientFactory(requestService: RequestService): (url: string) => OVSXClient { + static createClientFactory(requestService: RequestService, rateLimiter?: RateLimiter): (url: string) => OVSXClient { // eslint-disable-next-line no-null/no-null const cachedClients: Record = Object.create(null); - return url => cachedClients[url] ??= new this(url, requestService); + return url => cachedClients[url] ??= new this(url, requestService, rateLimiter); } constructor( protected vsxRegistryUrl: string, - protected requestService: RequestService + protected requestService: RequestService, + protected rateLimiter = new RateLimiter({ tokensPerInterval: OVSX_RATE_LIMIT, interval: 'second' }) ) { } search(searchOptions?: VSXSearchOptions): Promise { @@ -43,10 +47,25 @@ export class OVSXHttpClient implements OVSXClient { } protected async requestJson(url: string): Promise { - return RequestContext.asJson(await this.requestService.request({ - url, - headers: { 'Accept': 'application/json' } - })); + const attempts = 5; + for (let i = 0; i < attempts; i++) { + // Use 1, 2, 4, 8, 16 tokens for each attempt + const tokenCount = Math.pow(2, i); + await this.rateLimiter.removeTokens(tokenCount); + const context = await this.requestService.request({ + url, + headers: { 'Accept': 'application/json' } + }); + if (context.res.statusCode === 429) { + console.warn('OVSX rate limit exceeded. Consider reducing the rate limit.'); + // If there are still more attempts left, retry the request with a higher token count + if (i < attempts - 1) { + continue; + } + } + return RequestContext.asJson(context); + } + throw new Error('Failed to fetch data from OVSX.'); } protected buildUrl(url: string, query?: object): string { diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index 25d2e4b2d37d5..fa9adcd5c5f2b 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -11,6 +11,7 @@ "@theia/plugin-ext-vscode": "1.52.0", "@theia/preferences": "1.52.0", "@theia/workspace": "1.52.0", + "limiter": "^2.1.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", "semver": "^7.5.4", diff --git a/packages/vsx-registry/src/common/vsx-environment.ts b/packages/vsx-registry/src/common/vsx-environment.ts index 4366d5ef56dc7..8b1ef42657bce 100644 --- a/packages/vsx-registry/src/common/vsx-environment.ts +++ b/packages/vsx-registry/src/common/vsx-environment.ts @@ -20,6 +20,7 @@ export const VSX_ENVIRONMENT_PATH = '/services/vsx-environment'; export const VSXEnvironment = Symbol('VSXEnvironment'); export interface VSXEnvironment { + getRateLimit(): Promise; getRegistryUri(): Promise; getRegistryApiUri(): Promise; getVscodeApiVersion(): Promise; diff --git a/packages/vsx-registry/src/common/vsx-registry-common-module.ts b/packages/vsx-registry/src/common/vsx-registry-common-module.ts index fc7fd7de4f3db..5a0940c903d9e 100644 --- a/packages/vsx-registry/src/common/vsx-registry-common-module.ts +++ b/packages/vsx-registry/src/common/vsx-registry-common-module.ts @@ -21,6 +21,7 @@ import { ExtensionIdMatchesFilterFactory, OVSXApiFilter, OVSXApiFilterImpl, OVSXApiFilterProvider, OVSXClient, OVSXHttpClient, OVSXRouterClient, RequestContainsFilterFactory } from '@theia/ovsx-client'; import { VSXEnvironment } from './vsx-environment'; +import { RateLimiter } from 'limiter'; export default new ContainerModule(bind => { bind(OVSXUrlResolver) @@ -34,10 +35,15 @@ export default new ContainerModule(bind => { .all([ vsxEnvironment.getRegistryApiUri(), vsxEnvironment.getOvsxRouterConfig?.(), + vsxEnvironment.getRateLimit() ]) - .then(async ([apiUrl, ovsxRouterConfig]) => { + .then(async ([apiUrl, ovsxRouterConfig, rateLimit]) => { + const rateLimiter = new RateLimiter({ + interval: 'second', + tokensPerInterval: rateLimit + }); if (ovsxRouterConfig) { - const clientFactory = OVSXHttpClient.createClientFactory(requestService); + const clientFactory = OVSXHttpClient.createClientFactory(requestService, rateLimiter); return OVSXRouterClient.FromConfig( ovsxRouterConfig, async url => clientFactory(await urlResolver(url)), @@ -46,7 +52,8 @@ export default new ContainerModule(bind => { } return new OVSXHttpClient( await urlResolver(apiUrl), - requestService + requestService, + rateLimiter ); }); // reuse the promise for subsequent calls to this provider diff --git a/packages/vsx-registry/src/node/vsx-cli.ts b/packages/vsx-registry/src/node/vsx-cli.ts index aeef403518eb6..2b3efab2a4cc8 100644 --- a/packages/vsx-registry/src/node/vsx-cli.ts +++ b/packages/vsx-registry/src/node/vsx-cli.ts @@ -17,17 +17,19 @@ import { CliContribution } from '@theia/core/lib/node'; import { injectable } from '@theia/core/shared/inversify'; import { Argv } from '@theia/core/shared/yargs'; -import { OVSXRouterConfig } from '@theia/ovsx-client'; +import { OVSX_RATE_LIMIT, OVSXRouterConfig } from '@theia/ovsx-client'; import * as fs from 'fs'; @injectable() export class VsxCli implements CliContribution { ovsxRouterConfig: OVSXRouterConfig | undefined; + ovsxRateLimit: number; pluginsToInstall: string[] = []; configure(conf: Argv<{}>): void { conf.option('ovsx-router-config', { description: 'JSON configuration file for the OVSX router client', type: 'string' }); + conf.option('ovsx-rate-limit', { description: 'Limits the number of requests to OVSX per second', type: 'number', default: OVSX_RATE_LIMIT }); conf.option('install-plugin', { alias: 'install-extension', nargs: 1, @@ -47,5 +49,7 @@ export class VsxCli implements CliContribution { if (Array.isArray(pluginsToInstall)) { this.pluginsToInstall = pluginsToInstall; } + const ovsxRateLimit = args.ovsxRateLimit; + this.ovsxRateLimit = typeof ovsxRateLimit === 'number' ? ovsxRateLimit : OVSX_RATE_LIMIT; } } diff --git a/packages/vsx-registry/src/node/vsx-environment-impl.ts b/packages/vsx-registry/src/node/vsx-environment-impl.ts index ff094b09c41f7..8515650c5d119 100644 --- a/packages/vsx-registry/src/node/vsx-environment-impl.ts +++ b/packages/vsx-registry/src/node/vsx-environment-impl.ts @@ -32,6 +32,10 @@ export class VSXEnvironmentImpl implements VSXEnvironment { @inject(VsxCli) protected vsxCli: VsxCli; + async getRateLimit(): Promise { + return this.vsxCli.ovsxRateLimit; + } + async getRegistryUri(): Promise { return this._registryUri.toString(true); } From 004e2b2c7f0b881b97f52dc84441f2abfb569590 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Wed, 14 Aug 2024 12:56:12 +0200 Subject: [PATCH 336/441] Add aliases for `list.focusUp` and `list.focusDown` for notebooks (#14042) Signed-off-by: Jonah Iden --- .../browser/contributions/notebook-actions-contribution.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index dd11108e0704f..6469a249ffb70 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -207,6 +207,12 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon } } ); + commands.registerCommand({ id: 'list.focusUp' }, { + execute: () => commands.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, CellChangeDirection.Up) + }); + commands.registerCommand({ id: 'list.focusDown' }, { + execute: () => commands.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, CellChangeDirection.Down) + }); commands.registerCommand(NotebookCommands.CUT_SELECTED_CELL, this.editableCommandHandler( () => { From 7c0ed59e2b470c662a78187fea2a8fb37083aebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 19 Aug 2024 16:32:49 +0200 Subject: [PATCH 337/441] Drop support for Node 16.x (#14027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13956 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .github/workflows/ci-cd.yml | 6 +++--- .github/workflows/license-check.yml | 2 +- .github/workflows/performance-tests.yml | 4 ++-- .github/workflows/playwright.yml | 4 ++-- .github/workflows/production-smoke-test.yml | 4 ++-- .github/workflows/publish-gh-pages.yml | 4 ++-- .github/workflows/publish-next.yml | 4 ++-- .github/workflows/publish-release.yml | 4 ++-- .github/workflows/translation.yml | 4 ++-- CHANGELOG.md | 4 ++++ doc/Developing.md | 2 +- package.json | 4 ++-- 12 files changed, 25 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index e563ca511eb20..8118098760580 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -20,10 +20,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: 18.x + node-version: 20.x registry-url: 'https://registry.npmjs.org' - name: Use Python 3.11 @@ -52,7 +52,7 @@ jobs: fail-fast: false matrix: os: [windows-2019, ubuntu-latest, macos-14] - node: [16.x, 18.x, 20.x] + node: [18.x, 20.x] runs-on: ${{ matrix.os }} timeout-minutes: 60 diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index c235e3e0a21d6..763f083f96ced 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - node: ['18.x'] + node: ['20.x'] java: ['11'] runs-on: ${{ matrix.os }} diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 8ee67b675cd6d..52245cb0a2ca2 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -14,10 +14,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: "18.x" + node-version: "20.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index bd800baad34e0..d5c3cc49420f1 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -22,10 +22,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js "18.x" + - name: Use Node.js "20.x" uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: "18.x" + node-version: "20.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 diff --git a/.github/workflows/production-smoke-test.yml b/.github/workflows/production-smoke-test.yml index 0f65de998de67..3eed7632c6f6a 100644 --- a/.github/workflows/production-smoke-test.yml +++ b/.github/workflows/production-smoke-test.yml @@ -20,10 +20,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js "18.x" + - name: Use Node.js "20.x" uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: "18.x" + node-version: "20.x" registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 diff --git a/.github/workflows/publish-gh-pages.yml b/.github/workflows/publish-gh-pages.yml index 8840fdda45173..78fecb218d7ac 100644 --- a/.github/workflows/publish-gh-pages.yml +++ b/.github/workflows/publish-gh-pages.yml @@ -19,10 +19,10 @@ jobs: with: fetch-depth: 0 # To fetch all history for all branches and tags. (Will be required for caching with lerna: https://github.com/markuplint/markuplint/pull/111) - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: '18.x' + node-version: '20.x' registry-url: 'https://registry.npmjs.org' - name: Use Python 3.x diff --git a/.github/workflows/publish-next.yml b/.github/workflows/publish-next.yml index 05cec96a6325c..c744bfeab0647 100644 --- a/.github/workflows/publish-next.yml +++ b/.github/workflows/publish-next.yml @@ -18,10 +18,10 @@ jobs: # Required for lerna to determine the version of the next package. fetch-depth: 0 - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: 18.x + node-version: 20.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 7ab6475f09b96..b6d214b80ec72 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -24,10 +24,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: 18.x + node-version: 20.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.11 diff --git a/.github/workflows/translation.yml b/.github/workflows/translation.yml index 2a5f58508fe1a..531695725c166 100644 --- a/.github/workflows/translation.yml +++ b/.github/workflows/translation.yml @@ -12,10 +12,10 @@ jobs: - name: Checkout uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - name: Use Node.js 18.x + - name: Use Node.js 20.x uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: - node-version: 18.x + node-version: 20.x registry-url: "https://registry.npmjs.org" - name: Use Python 3.x diff --git a/CHANGELOG.md b/CHANGELOG.md index a96f61eb0ca6d..57618adc18f33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) +[Breaking Changes:](#breaking_changes_1.51.0) +- [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics + + ## 1.52.0 - 07/25/2024 - [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#13955](https://github.com/eclipse-theia/theia/pull/13955) - Contributed on behalf of STMicroelectronics diff --git a/doc/Developing.md b/doc/Developing.md index 2f8e242dfc811..f8c111a41a656 100644 --- a/doc/Developing.md +++ b/doc/Developing.md @@ -508,7 +508,7 @@ etc.) by opening `packages//coverage/index.html`. - Install [`scoop`](https://github.com/lukesampson/scoop#installation). - Install [`nvm`](https://github.com/coreybutler/nvm-windows) with scoop: `scoop install nvm`. - - Install Node.js with `nvm`: `nvm install 18.17.0`, then use it: `nvm use 18.17.0`. You can list all available Node.js versions with `nvm list available` if you want to pick another version. + - Install Node.js with `nvm`: `nvm install lts`, then use it: `nvm use lts`. You can list all available Node.js versions with `nvm list available` if you want to pick another version. - Install `yarn`: `scoop install yarn`. - If you need to install `windows-build-tools`, see [`Installing Windows Build Tools`](#installing-windows-build-tools). - If you run into problems with installing the required build tools, the `node-gyp` documentation offers a useful [guide](https://github.com/nodejs/node-gyp#on-windows) how to install the dependencies manually. The versions required for building Theia are: diff --git a/package.json b/package.json index 5184f195a7017..e16ac1798c9c2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "engines": { "yarn": ">=1.7.0 <2", - "node": ">=16" + "node": ">=18" }, "resolutions": { "**/@types/node": "18" @@ -114,4 +114,4 @@ "vscode.microsoft-authentication", "ms-vscode.references-view" ] -} \ No newline at end of file +} From d34c0b3a8db8483da5b2bf25176e495110b25e52 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 20 Aug 2024 16:28:43 +0200 Subject: [PATCH 338/441] Add notebook selected cell status bar item and center selected cell command (#14046) Signed-off-by: Jonah Iden --- .../notebook-actions-contribution.ts | 18 +++++ .../notebook-status-bar-contribution.ts | 77 +++++++++++++++++++ .../src/browser/notebook-editor-widget.tsx | 1 + .../src/browser/notebook-frontend-module.ts | 4 + .../browser/view-model/notebook-cell-model.ts | 7 ++ .../src/browser/view/notebook-cell-editor.tsx | 34 ++++---- 6 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 6469a249ffb70..3d4e4ae30e838 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -26,6 +26,7 @@ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-s import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys'; import { NotebookClipboardService } from '../service/notebook-clipboard-service'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; +import { NotebookEditorWidget } from '../notebook-editor-widget'; export namespace NotebookCommands { export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({ @@ -87,6 +88,11 @@ export namespace NotebookCommands { id: 'notebook.find', category: 'Notebook', }); + + export const CENTER_ACTIVE_CELL = Command.toDefaultLocalizedCommand({ + id: 'notebook.centerActiveCell', + category: 'Notebook', + }); } export enum CellChangeDirection { @@ -253,6 +259,13 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon } }); + commands.registerCommand(NotebookCommands.CENTER_ACTIVE_CELL, { + execute: (editor?: NotebookEditorWidget) => { + const model = editor ? editor.model : this.notebookEditorWidgetService.focusedEditor?.model; + model?.selectedCell?.requestCenterEditor(); + } + }); + } protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler { @@ -345,6 +358,11 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon keybinding: 'ctrlcmd+f', when: `${NOTEBOOK_EDITOR_FOCUSED}` }, + { + command: NotebookCommands.CENTER_ACTIVE_CELL.id, + keybinding: 'ctrlcmd+l', + when: `${NOTEBOOK_EDITOR_FOCUSED}` + } ); } diff --git a/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts b/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts new file mode 100644 index 0000000000000..05ded847b5bb3 --- /dev/null +++ b/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts @@ -0,0 +1,77 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; +import { Disposable } from '@theia/core/lib/common'; +import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; +import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { nls } from '@theia/core'; +import { NotebookCommands } from './notebook-actions-contribution'; + +export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position'; + +@injectable() +export class NotebookStatusBarContribution implements FrontendApplicationContribution { + + @inject(StatusBar) protected readonly statusBar: StatusBar; + @inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService; + + protected currentCellSelectionListener: Disposable | undefined; + protected lastFocusedEditor: NotebookEditorWidget | undefined; + + onStart(): void { + this.editorWidgetService.onDidChangeFocusedEditor(editor => { + this.currentCellSelectionListener?.dispose(); + this.currentCellSelectionListener = editor?.model?.onDidChangeSelectedCell(() => + this.updateStatusbar(editor) + ); + editor?.onDidDispose(() => { + this.lastFocusedEditor = undefined; + this.updateStatusbar(); + }); + this.updateStatusbar(editor); + this.lastFocusedEditor = editor; + }); + if (this.editorWidgetService.focusedEditor) { + this.updateStatusbar(); + } + } + + protected async updateStatusbar(editor?: NotebookEditorWidget): Promise { + if ((!editor && !this.lastFocusedEditor?.isVisible) || editor?.model?.cells.length === 0) { + this.statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID); + return; + } + + await editor?.ready; + if (!editor?.model) { + return; + } + + const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : ''; + + this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, { + text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length), + alignment: StatusBarAlignment.RIGHT, + priority: 100, + command: NotebookCommands.CENTER_ACTIVE_CELL.id, + arguments: [editor] + }); + + } + +} diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index a88ed0a77b692..216568353aca0 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -204,6 +204,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa } // Ensure that the model is loaded before adding the editor this.notebookEditorService.addNotebookEditor(this); + this._model.selectedCell = this._model.cells[0]; this.update(); this.notebookContextManager.init(this); return this._model; diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index b39d914333a6e..16da00f9c6676 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -47,6 +47,7 @@ import { NotebookClipboardService } from './service/notebook-clipboard-service'; import { bindNotebookPreferences } from './contributions/notebook-preferences'; import { NotebookOptionsService } from './service/notebook-options'; import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler'; +import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -112,4 +113,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookUndoRedoHandler).toSelf().inSingletonScope(); bind(UndoRedoHandler).toService(NotebookUndoRedoHandler); + + bind(NotebookStatusBarContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution); }); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index c6ed48582fc31..f9ce7ca5d11b6 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -126,6 +126,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected readonly onDidSelectFindMatchEmitter = new Emitter(); readonly onDidSelectFindMatch: Event = this.onDidSelectFindMatchEmitter.event; + protected onDidRequestCenterEditorEmitter = new Emitter(); + readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event; + @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @@ -299,6 +302,10 @@ export class NotebookCellModel implements NotebookCell, Disposable { this.onWillBlurCellEditorEmitter.fire(); } + requestCenterEditor(): void { + this.onDidRequestCenterEditorEmitter.fire(); + } + spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void { if (splice.deleteCount > 0 && splice.newOutputs.length > 0) { const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length); diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index db72b0765c267..a0832cc2a2b82 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -124,20 +124,7 @@ export class CellEditor extends React.Component { animationFrame().then(() => this.setMatches()); })); - this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => { - const editorDomNode = this.editor?.getControl().getDomNode(); - if (editorDomNode) { - editorDomNode.scrollIntoView({ - behavior: 'instant', - block: 'center' - }); - } else { - this.container?.scrollIntoView({ - behavior: 'instant', - block: 'center' - }); - } - })); + this.toDispose.push(this.props.cell.onDidSelectFindMatch(match => this.centerEditorInView())); this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => { if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { @@ -155,6 +142,10 @@ export class CellEditor extends React.Component { }); this.toDispose.push(disposable); } + + this.toDispose.push(this.props.cell.onDidRequestCenterEditor(() => { + this.centerEditorInView(); + })); } override componentWillUnmount(): void { @@ -166,6 +157,21 @@ export class CellEditor extends React.Component { this.toDispose = new DisposableCollection(); } + protected centerEditorInView(): void { + const editorDomNode = this.editor?.getControl().getDomNode(); + if (editorDomNode) { + editorDomNode.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } else { + this.container?.scrollIntoView({ + behavior: 'instant', + block: 'center' + }); + } + } + protected async initEditor(): Promise { const { cell, notebookModel, monacoServices } = this.props; if (this.container) { From b23e7a0da679abd6310d47c9f0cad5b88283fc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 21 Aug 2024 13:50:19 +0200 Subject: [PATCH 339/441] Rely on IConfigurationService change event to update model options (#13994) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13920, #13929 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../src/browser/monaco-text-model-service.ts | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/packages/monaco/src/browser/monaco-text-model-service.ts b/packages/monaco/src/browser/monaco-text-model-service.ts index 250d2e28d4780..6ba76b63c512c 100644 --- a/packages/monaco/src/browser/monaco-text-model-service.ts +++ b/packages/monaco/src/browser/monaco-text-model-service.ts @@ -45,13 +45,6 @@ export interface MonacoEditorModelFactory { export class MonacoTextModelService implements ITextModelService { declare readonly _serviceBrand: undefined; - /** - * This component does some asynchronous work before being fully initialized. - * - * @deprecated since 1.25.0. Is instantly resolved. - */ - readonly ready: Promise = Promise.resolve(); - protected readonly _models = new ReferenceCollection( uri => this.loadModel(new URI(uri)) ); @@ -121,10 +114,6 @@ export class MonacoTextModelService implements ITextModelService { await this.editorPreferences.ready; const resource = await this.resourceProvider(uri); const model = await (await this.createModel(resource)).load(); - this.updateModel(model); - model.textEditorModel.onDidChangeLanguage(() => this.updateModel(model)); - const disposable = this.editorPreferences.onPreferenceChanged(change => this.updateModel(model, change)); - model.onDispose(() => disposable.dispose()); return model; } @@ -154,37 +143,6 @@ export class MonacoTextModelService implements ITextModelService { return undefined; } - protected updateModel(model: MonacoEditorModel, change?: EditorPreferenceChange): void { - if (!change) { - model.textEditorModel.updateOptions(this.getModelOptions(model)); - } else if (change.affects(model.uri, model.languageId)) { - const modelOption = this.toModelOption(change.preferenceName); - if (modelOption) { - model.textEditorModel.updateOptions(this.getModelOptions(model)); - } - } - } - - /** @deprecated pass MonacoEditorModel instead */ - protected getModelOptions(uri: string): ITextModelUpdateOptions; - protected getModelOptions(model: MonacoEditorModel): ITextModelUpdateOptions; - protected getModelOptions(arg: string | MonacoEditorModel): ITextModelUpdateOptions { - const uri = typeof arg === 'string' ? arg : arg.uri; - const overrideIdentifier = typeof arg === 'string' ? undefined : arg.languageId; - return { - tabSize: this.editorPreferences.get({ preferenceName: 'editor.tabSize', overrideIdentifier }, undefined, uri), - // @monaco-uplift: when available, switch to 'editor.indentSize' preference. - indentSize: this.editorPreferences.get({ preferenceName: 'editor.tabSize', overrideIdentifier }, undefined, uri), - insertSpaces: this.editorPreferences.get({ preferenceName: 'editor.insertSpaces', overrideIdentifier }, undefined, uri), - bracketColorizationOptions: { - enabled: this.editorPreferences.get({ preferenceName: 'editor.bracketPairColorization.enabled', overrideIdentifier }, undefined, uri), - independentColorPoolPerBracketType: this.editorPreferences.get( - { preferenceName: 'editor.bracketPairColorization.independentColorPoolPerBracketType', overrideIdentifier }, undefined, uri), - }, - trimAutoWhitespace: this.editorPreferences.get({ preferenceName: 'editor.trimAutoWhitespace', overrideIdentifier }, undefined, uri), - }; - } - registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { return { dispose(): void { From 5d12ee44b8af6a7d184b2dc02eb21152c5f13b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 21 Aug 2024 13:50:47 +0200 Subject: [PATCH 340/441] Update electron to version 30.1.2 (#14041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14014 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 7 +++++-- examples/electron/package.json | 2 +- package.json | 3 ++- packages/core/README.md | 2 +- packages/electron/README.md | 2 +- packages/electron/package.json | 2 +- yarn.lock | 20 ++++++++++---------- 7 files changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57618adc18f33..a0992360c5667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -[Breaking Changes:](#breaking_changes_1.51.0) -- [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics +## Unreleased + +[Breaking Changes:](#breaking_changes_1.53.0) +- [dependencies] Updated electron to version 30.1.2 - [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics +- [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics ## 1.52.0 - 07/25/2024 diff --git a/examples/electron/package.json b/examples/electron/package.json index 44c941c225780..0cfe496522349 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -91,6 +91,6 @@ }, "devDependencies": { "@theia/cli": "1.52.0", - "electron": "^28.2.8" + "electron": "^30.1.2" } } diff --git a/package.json b/package.json index e16ac1798c9c2..157c3aca44982 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "node": ">=18" }, "resolutions": { - "**/@types/node": "18" + "**/@types/node": "18", + "**/nan": "2.20.0" }, "devDependencies": { "@types/chai": "4.3.0", diff --git a/packages/core/README.md b/packages/core/README.md index 56a6688237adc..db504560046e6 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -70,7 +70,7 @@ export class SomeClass { - `@theia/core/electron-shared/...` - `native-keymap` (from [`native-keymap@^2.2.1`](https://www.npmjs.com/package/native-keymap)) - - `electron` (from [`electron@^28.2.8`](https://www.npmjs.com/package/electron)) + - `electron` (from [`electron@^30.1.2`](https://www.npmjs.com/package/electron)) - `electron-store` (from [`electron-store@^8.0.0`](https://www.npmjs.com/package/electron-store)) - `fix-path` (from [`fix-path@^3.0.0`](https://www.npmjs.com/package/fix-path)) - `@theia/core/shared/...` diff --git a/packages/electron/README.md b/packages/electron/README.md index de0e9ec00b7d2..7dd51c63b0d6a 100644 --- a/packages/electron/README.md +++ b/packages/electron/README.md @@ -18,7 +18,7 @@ The `@theia/electron` extension bundles all Electron-specific dependencies and c - `@theia/electron/shared/...` - `native-keymap` (from [`native-keymap@^2.2.1`](https://www.npmjs.com/package/native-keymap)) - - `electron` (from [`electron@^28.2.8`](https://www.npmjs.com/package/electron)) + - `electron` (from [`electron@^30.1.2`](https://www.npmjs.com/package/electron)) - `electron-store` (from [`electron-store@^8.0.0`](https://www.npmjs.com/package/electron-store)) - `fix-path` (from [`fix-path@^3.0.0`](https://www.npmjs.com/package/fix-path)) diff --git a/packages/electron/package.json b/packages/electron/package.json index 1b74de295884b..2a947ac02c81a 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -12,7 +12,7 @@ "@theia/re-exports": "1.52.0" }, "peerDependencies": { - "electron": "^28.2.8" + "electron": "^30.1.2" }, "theiaReExports": { "shared": { diff --git a/yarn.lock b/yarn.lock index 3aabc7d1cd69a..f38c8d1ebaa33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2141,7 +2141,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18": +"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18", "@types/node@^20.9.0": version "18.19.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.18.tgz#7526471b28828d1fef1f7e4960fb9477e6e4369c" integrity sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg== @@ -4963,13 +4963,13 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@^28.2.8: - version "28.2.10" - resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.10.tgz#4e168568406a8b1e9b9a5859e988c905b9a57570" - integrity sha512-0rGBJNogcl2FIRxGRUv9zuMaBP78nSBJW+Bd1U7OGeg8IEkSIbHOhfn71XoGxgbOUSCEXjjyftq4mtAAVbUsZQ== +electron@^30.1.2: + version "30.3.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.1.tgz#fe27ca2a4739bec832b2edd6f46140ab46bf53a0" + integrity sha512-Ai/OZ7VlbFAVYMn9J5lyvtr+ZWyEbXDVd5wBLb5EVrp4352SRmMAmN5chcIe3n9mjzcgehV9n4Hwy15CJW+YbA== dependencies: "@electron/get" "^2.0.0" - "@types/node" "^18.11.18" + "@types/node" "^20.9.0" extract-zip "^2.0.1" emoji-regex@^8.0.0: @@ -8428,10 +8428,10 @@ mute-stream@~1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" - integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== +nan@2.20.0, nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== nano@^10.1.3: version "10.1.3" From 6f36201e0168526fc59f61127a3da1857104d498 Mon Sep 17 00:00:00 2001 From: Vladimir Piskarev Date: Wed, 21 Aug 2024 16:21:21 +0300 Subject: [PATCH 341/441] Allow opening changes for files associated with custom editors (#13916) * Fix loading of webview resources that depend on query params Some resource url (notably git) use the query part of the url to store additional information. The query part of the url was incorrectly being dropped while attempting to load these resources inside of webviews. See also https://github.com/microsoft/vscode/commit/48387dfc3d691558404cff1ea2582e3862a40080 * Fix `Error: Unknown Webview` messages in the log * Fix design and implementation issues surrounding `CustomEditorOpener` This commit ensures that the promise returned by `CustomEditorOpener.open` will only resolve to a properly initialized and opened `CustomEditorWidget`. In particular, it ensures that the widget is opened according to the specified `WidgetOpenerOptions`, including `widgetOptions.ref` and `mode`. Essentially, it revises the work done in #9671 and #10580 to fix #9670 and #10583. * Restore custom editors as part of layout Fixes an incorrect assumption that a custom editor cannot be restored if no `WebviewPanelSerializer` is registered for its view type. (Actually, custom editors are created and restored using a custom editor provider.) Also, ensures that `CustomEditorWidget.modelRef` satisfies the shape for the `CustomEditorWidget` defined in `editor.ts` and cannot return `undefined`. (However, `CustomEditorWidget.modelRef.object` can be `undefined` until the custom editor is resolved.) Fixes #10787 * Fix a race condition when file system provider is activated When file system provider is activated, wait until it is registered. * git: add support for custom editors * Uses `OpenerService` instead of `EditorManager` to open editors * Contributes a `FileSystemProvider` for git-resources * Fixes an issue with getting blob contents * custom editor: open a diff-uri in a side-by-side editor `CustomEditorOpener` is now able to open a diff-uri in a side-by-side editor, which contains the corresponding `CustomEditor`s. Fixes #9079 --- packages/core/src/browser/saveable.ts | 66 ++++++- packages/core/src/browser/style/index.css | 1 + .../core/src/browser/style/split-widget.css | 38 ++++ .../core/src/browser/widget-open-handler.ts | 5 +- packages/core/src/browser/widgets/index.ts | 1 + .../core/src/browser/widgets/split-widget.ts | 163 ++++++++++++++++++ packages/core/src/common/event.ts | 6 + .../filesystem/src/browser/file-service.ts | 4 + packages/git/src/browser/git-contribution.ts | 9 +- .../browser/git-file-service-contribution.ts | 33 ++++ .../src/browser/git-file-system-provider.ts | 84 +++++++++ .../git/src/browser/git-frontend-module.ts | 7 + .../browser/git-repository-provider.spec.ts | 3 +- packages/git/src/browser/git-resource.ts | 15 ++ .../git/src/browser/git-scm-provider.spec.ts | 5 +- packages/git/src/browser/git-scm-provider.ts | 19 +- packages/git/src/node/dugite-git.ts | 6 +- .../src/browser/monaco-editor-service.ts | 2 +- .../plugin-ext/src/common/plugin-api-rpc.ts | 3 +- .../src/hosted/browser/hosted-plugin.ts | 14 +- .../custom-editors/custom-editor-opener.tsx | 135 ++++++++++++--- .../custom-editors/custom-editor-widget.ts | 31 ++-- .../custom-editors/custom-editors-main.ts | 81 ++------- .../plugin-custom-editor-registry.ts | 28 +-- .../browser/plugin-ext-frontend-module.ts | 25 ++- .../browser/webview/pre/service-worker.js | 3 +- .../src/main/browser/webview/webview.ts | 11 +- .../src/main/browser/webviews-main.ts | 7 +- .../plugin-ext/src/plugin/custom-editors.ts | 18 +- .../plugin-ext/src/plugin/plugin-context.ts | 2 +- packages/plugin-ext/src/plugin/webviews.ts | 2 +- 31 files changed, 665 insertions(+), 162 deletions(-) create mode 100644 packages/core/src/browser/style/split-widget.css create mode 100644 packages/core/src/browser/widgets/split-widget.ts create mode 100644 packages/git/src/browser/git-file-service-contribution.ts create mode 100644 packages/git/src/browser/git-file-system-provider.ts diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index ecb4728a1c955..e1a413b3fab0e 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -21,7 +21,7 @@ import { MaybePromise } from '../common/types'; import { Key } from './keyboard/keys'; import { AbstractDialog } from './dialogs'; import { nls } from '../common/nls'; -import { DisposableCollection, isObject } from '../common'; +import { Disposable, DisposableCollection, isObject } from '../common'; import { BinaryBuffer } from '../common/buffer'; export type AutoSaveMode = 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange'; @@ -112,6 +112,70 @@ export class DelegatingSaveable implements Saveable { } +export class CompositeSaveable implements Saveable { + protected isDirty = false; + protected readonly onDirtyChangedEmitter = new Emitter(); + protected readonly onContentChangedEmitter = new Emitter(); + protected readonly toDispose = new DisposableCollection(this.onDirtyChangedEmitter, this.onContentChangedEmitter); + protected readonly saveablesMap = new Map(); + + get dirty(): boolean { + return this.isDirty; + } + + get onDirtyChanged(): Event { + return this.onDirtyChangedEmitter.event; + } + + get onContentChanged(): Event { + return this.onContentChangedEmitter.event; + } + + async save(options?: SaveOptions): Promise { + await Promise.all(this.saveables.map(saveable => saveable.save(options))); + } + + get saveables(): readonly Saveable[] { + return Array.from(this.saveablesMap.keys()); + } + + add(saveable: Saveable): void { + if (this.saveablesMap.has(saveable)) { + return; + } + const toDispose = new DisposableCollection(); + this.toDispose.push(toDispose); + this.saveablesMap.set(saveable, toDispose); + toDispose.push(Disposable.create(() => { + this.saveablesMap.delete(saveable); + })); + toDispose.push(saveable.onDirtyChanged(() => { + const wasDirty = this.isDirty; + this.isDirty = this.saveables.some(s => s.dirty); + if (this.isDirty !== wasDirty) { + this.onDirtyChangedEmitter.fire(); + } + })); + toDispose.push(saveable.onContentChanged(() => { + this.onContentChangedEmitter.fire(); + })); + if (saveable.dirty && !this.isDirty) { + this.isDirty = true; + this.onDirtyChangedEmitter.fire(); + } + } + + remove(saveable: Saveable): boolean { + const toDispose = this.saveablesMap.get(saveable); + toDispose?.dispose(); + return !!toDispose; + } + + dispose(): void { + this.toDispose.dispose(); + } +} + export namespace Saveable { export interface RevertOptions { /** diff --git a/packages/core/src/browser/style/index.css b/packages/core/src/browser/style/index.css index 137b5bfb7d4d5..3a69dc7c2c1c6 100644 --- a/packages/core/src/browser/style/index.css +++ b/packages/core/src/browser/style/index.css @@ -350,3 +350,4 @@ button.secondary[disabled], @import "./progress-bar.css"; @import "./breadcrumbs.css"; @import "./tooltip.css"; +@import "./split-widget.css"; diff --git a/packages/core/src/browser/style/split-widget.css b/packages/core/src/browser/style/split-widget.css new file mode 100644 index 0000000000000..2b18734fb2bea --- /dev/null +++ b/packages/core/src/browser/style/split-widget.css @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (C) 2024 1C-Soft LLC and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + ********************************************************************************/ + +.theia-split-widget > .p-SplitPanel { + height: 100%; + width: 100%; + outline: none; +} + +.theia-split-widget > .p-SplitPanel > .p-SplitPanel-child { + min-width: 50px; + min-height: var(--theia-content-line-height); +} + +.theia-split-widget > .p-SplitPanel > .p-SplitPanel-handle { + box-sizing: border-box; +} + +.theia-split-widget > .p-SplitPanel[data-orientation="horizontal"] > .p-SplitPanel-handle { + border-left: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border); +} + +.theia-split-widget > .p-SplitPanel[data-orientation="vertical"] > .p-SplitPanel-handle { + border-top: var(--theia-border-width) solid var(--theia-sideBarSectionHeader-border); +} diff --git a/packages/core/src/browser/widget-open-handler.ts b/packages/core/src/browser/widget-open-handler.ts index 7e3e4c37fab07..25802c6359869 100644 --- a/packages/core/src/browser/widget-open-handler.ts +++ b/packages/core/src/browser/widget-open-handler.ts @@ -24,7 +24,10 @@ import { WidgetManager } from './widget-manager'; export type WidgetOpenMode = 'open' | 'reveal' | 'activate'; /** - * `WidgetOpenerOptions` define serializable generic options used by the {@link WidgetOpenHandler}. + * `WidgetOpenerOptions` define generic options used by the {@link WidgetOpenHandler}. + * + * _Note:_ This object may contain references to widgets (e.g. `widgetOptions.ref`); + * these need to be transformed before it can be serialized. */ export interface WidgetOpenerOptions extends OpenerOptions { /** diff --git a/packages/core/src/browser/widgets/index.ts b/packages/core/src/browser/widgets/index.ts index a8539dea88602..48ef9cf5ca42e 100644 --- a/packages/core/src/browser/widgets/index.ts +++ b/packages/core/src/browser/widgets/index.ts @@ -18,3 +18,4 @@ export * from './widget'; export * from './react-renderer'; export * from './react-widget'; export * from './extractable-widget'; +export * from './split-widget'; diff --git a/packages/core/src/browser/widgets/split-widget.ts b/packages/core/src/browser/widgets/split-widget.ts new file mode 100644 index 0000000000000..0c1e5dd3d4269 --- /dev/null +++ b/packages/core/src/browser/widgets/split-widget.ts @@ -0,0 +1,163 @@ +// ***************************************************************************** +// Copyright (C) 2024 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Emitter } from 'vscode-languageserver-protocol'; +import { ApplicationShell, StatefulWidget } from '../shell'; +import { BaseWidget, Message, PanelLayout, SplitPanel, Widget } from './widget'; +import { CompositeSaveable, Saveable, SaveableSource } from '../saveable'; +import { Navigatable } from '../navigatable-types'; +import { URI } from '../../common'; + +/** + * A widget containing a number of panes in a split layout. + */ +export class SplitWidget extends BaseWidget implements ApplicationShell.TrackableWidgetProvider, SaveableSource, Navigatable, StatefulWidget { + + protected readonly splitPanel: SplitPanel; + + protected readonly onDidChangeTrackableWidgetsEmitter = new Emitter(); + readonly onDidChangeTrackableWidgets = this.onDidChangeTrackableWidgetsEmitter.event; + + protected readonly compositeSaveable = new CompositeSaveable(); + + protected navigatable?: Navigatable; + + constructor(options?: SplitPanel.IOptions & { navigatable?: Navigatable }) { + super(); + + this.toDispose.pushAll([this.onDidChangeTrackableWidgetsEmitter]); + + this.addClass('theia-split-widget'); + + const layout = new PanelLayout(); + this.layout = layout; + const that = this; + this.splitPanel = new class extends SplitPanel { + + protected override onChildAdded(msg: Widget.ChildMessage): void { + super.onChildAdded(msg); + that.onPaneAdded(msg.child); + } + + protected override onChildRemoved(msg: Widget.ChildMessage): void { + super.onChildRemoved(msg); + that.onPaneRemoved(msg.child); + } + }({ + spacing: 1, // --theia-border-width + ...options + }); + this.splitPanel.node.tabIndex = -1; + layout.addWidget(this.splitPanel); + + this.navigatable = options?.navigatable; + } + + get orientation(): SplitPanel.Orientation { + return this.splitPanel.orientation; + } + + set orientation(value: SplitPanel.Orientation) { + this.splitPanel.orientation = value; + } + + relativeSizes(): number[] { + return this.splitPanel.relativeSizes(); + } + + setRelativeSizes(sizes: number[]): void { + this.splitPanel.setRelativeSizes(sizes); + } + + get handles(): readonly HTMLDivElement[] { + return this.splitPanel.handles; + } + + get saveable(): Saveable { + return this.compositeSaveable; + } + + getResourceUri(): URI | undefined { + return this.navigatable?.getResourceUri(); + } + + createMoveToUri(resourceUri: URI): URI | undefined { + return this.navigatable?.createMoveToUri(resourceUri); + } + + storeState(): SplitWidget.State { + return { orientation: this.orientation, widgets: this.panes, relativeSizes: this.relativeSizes() }; + } + + restoreState(oldState: SplitWidget.State): void { + const { orientation, widgets, relativeSizes } = oldState; + if (orientation) { + this.orientation = orientation; + } + for (const widget of widgets) { + this.addPane(widget); + } + if (relativeSizes) { + this.setRelativeSizes(relativeSizes); + } + } + + get panes(): readonly Widget[] { + return this.splitPanel.widgets; + } + + getTrackableWidgets(): Widget[] { + return [...this.panes]; + } + + protected fireDidChangeTrackableWidgets(): void { + this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets()); + } + + addPane(pane: Widget): void { + this.splitPanel.addWidget(pane); + } + + insertPane(index: number, pane: Widget): void { + this.splitPanel.insertWidget(index, pane); + } + + protected onPaneAdded(pane: Widget): void { + if (Saveable.isSource(pane)) { + this.compositeSaveable.add(pane.saveable); + } + this.fireDidChangeTrackableWidgets(); + } + + protected onPaneRemoved(pane: Widget): void { + if (Saveable.isSource(pane)) { + this.compositeSaveable.remove(pane.saveable); + } + this.fireDidChangeTrackableWidgets(); + } + + protected override onActivateRequest(msg: Message): void { + this.splitPanel.node.focus(); + } +} + +export namespace SplitWidget { + export interface State { + orientation?: SplitPanel.Orientation; + widgets: readonly Widget[]; // note: don't rename this property; it has special meaning for `ShellLayoutRestorer` + relativeSizes?: number[]; + } +} diff --git a/packages/core/src/common/event.ts b/packages/core/src/common/event.ts index 00d6e0d00eab5..94912d7616455 100644 --- a/packages/core/src/common/event.ts +++ b/packages/core/src/common/event.ts @@ -89,6 +89,12 @@ export namespace Event { return new Promise(resolve => once(event)(resolve)); } + export function filter(event: Event, predicate: (e: T) => unknown): Event; + export function filter(event: Event, predicate: (e: T) => e is S): Event; + export function filter(event: Event, predicate: (e: T) => unknown): Event { + return (listener, thisArg, disposables) => event(e => predicate(e) && listener.call(thisArg, e), undefined, disposables); + } + /** * Given an event and a `map` function, returns another event which maps each element * through the mapping function. diff --git a/packages/filesystem/src/browser/file-service.ts b/packages/filesystem/src/browser/file-service.ts index f237cd7560bc5..3945bf64787ee 100644 --- a/packages/filesystem/src/browser/file-service.ts +++ b/packages/filesystem/src/browser/file-service.ts @@ -419,6 +419,10 @@ export class FileService { return activation; } + hasProvider(scheme: string): boolean { + return this.providers.has(scheme); + } + /** * Tests if the service (i.e. any of its registered {@link FileSystemProvider}s) can handle the given resource. * @param resource `URI` of the resource to test. diff --git a/packages/git/src/browser/git-contribution.ts b/packages/git/src/browser/git-contribution.ts index de36bd4d7788a..ad01af1e2567f 100644 --- a/packages/git/src/browser/git-contribution.ts +++ b/packages/git/src/browser/git-contribution.ts @@ -23,9 +23,10 @@ import { MenuAction, MenuContribution, MenuModelRegistry, + MessageService, Mutable } from '@theia/core'; -import { codicon, DiffUris, Widget } from '@theia/core/lib/browser'; +import { codicon, DiffUris, Widget, open, OpenerService } from '@theia/core/lib/browser'; import { TabBarToolbarContribution, TabBarToolbarItem, @@ -281,6 +282,8 @@ export class GitContribution implements CommandContribution, MenuContribution, T protected toDispose = new DisposableCollection(); + @inject(OpenerService) protected openerService: OpenerService; + @inject(MessageService) protected messageService: MessageService; @inject(EditorManager) protected readonly editorManager: EditorManager; @inject(GitQuickOpenService) protected readonly quickOpenService: GitQuickOpenService; @inject(GitRepositoryTracker) protected readonly repositoryTracker: GitRepositoryTracker; @@ -562,7 +565,9 @@ export class GitContribution implements CommandContribution, MenuContribution, T registry.registerCommand(GIT_COMMANDS.OPEN_CHANGED_FILE, { execute: (...arg: ScmResource[]) => { for (const resource of arg) { - this.editorManager.open(resource.sourceUri, { mode: 'reveal' }); + open(this.openerService, resource.sourceUri, { mode: 'reveal' }).catch(e => { + this.messageService.error(e.message); + }); } } }); diff --git a/packages/git/src/browser/git-file-service-contribution.ts b/packages/git/src/browser/git-file-service-contribution.ts new file mode 100644 index 0000000000000..7719eca95a4c7 --- /dev/null +++ b/packages/git/src/browser/git-file-service-contribution.ts @@ -0,0 +1,33 @@ +// ***************************************************************************** +// Copyright (C) 2024 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { interfaces } from '@theia/core/shared/inversify'; +import { FileService, FileServiceContribution } from '@theia/filesystem/lib/browser/file-service'; +import { GitFileSystemProvider } from './git-file-system-provider'; +import { GIT_RESOURCE_SCHEME } from './git-resource'; + +export class GitFileServiceContribution implements FileServiceContribution { + + constructor(protected readonly container: interfaces.Container) { } + + registerFileSystemProviders(service: FileService): void { + service.onWillActivateFileSystemProvider(event => { + if (event.scheme === GIT_RESOURCE_SCHEME) { + service.registerProvider(GIT_RESOURCE_SCHEME, this.container.get(GitFileSystemProvider)); + } + }); + } +} diff --git a/packages/git/src/browser/git-file-system-provider.ts b/packages/git/src/browser/git-file-system-provider.ts new file mode 100644 index 0000000000000..113aeee6d16c7 --- /dev/null +++ b/packages/git/src/browser/git-file-system-provider.ts @@ -0,0 +1,84 @@ +// ***************************************************************************** +// Copyright (C) 2024 1C-Soft LLC and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { Event, URI, Disposable } from '@theia/core'; +import { + FileChange, + FileDeleteOptions, + FileOverwriteOptions, + FileSystemProvider, + FileSystemProviderCapabilities, + FileType, + FileWriteOptions, + Stat, + WatchOptions +} from '@theia/filesystem/lib/common/files'; +import { GitResourceResolver } from './git-resource-resolver'; +import { EncodingService } from '@theia/core/lib/common/encoding-service'; + +@injectable() +export class GitFileSystemProvider implements FileSystemProvider { + + readonly capabilities = FileSystemProviderCapabilities.Readonly | + FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive; + + readonly onDidChangeCapabilities: Event = Event.None; + readonly onDidChangeFile: Event = Event.None; + readonly onFileWatchError: Event = Event.None; + + @inject(GitResourceResolver) + protected readonly resourceResolver: GitResourceResolver; + + @inject(EncodingService) + protected readonly encodingService: EncodingService; + + watch(resource: URI, opts: WatchOptions): Disposable { + return Disposable.NULL; + } + + async stat(resource: URI): Promise { + const gitResource = await this.resourceResolver.getResource(resource); + const size = await gitResource.getSize(); + return { type: FileType.File, mtime: 0, ctime: 0, size }; + } + + async readFile(resource: URI): Promise { + const gitResource = await this.resourceResolver.getResource(resource); + const contents = await gitResource.readContents({ encoding: 'binary' }); + return this.encodingService.encode(contents, { encoding: 'binary', hasBOM: false }).buffer; + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + throw new Error('Method not implemented.'); + } + + mkdir(resource: URI): Promise { + throw new Error('Method not implemented.'); + } + + readdir(resource: URI): Promise<[string, FileType][]> { + throw new Error('Method not implemented.'); + } + + delete(resource: URI, opts: FileDeleteOptions): Promise { + throw new Error('Method not implemented.'); + } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/git/src/browser/git-frontend-module.ts b/packages/git/src/browser/git-frontend-module.ts index 132f15aa2b745..3c10f70a8b732 100644 --- a/packages/git/src/browser/git-frontend-module.ts +++ b/packages/git/src/browser/git-frontend-module.ts @@ -43,6 +43,9 @@ import { ScmHistorySupport } from '@theia/scm-extra/lib/browser/history/scm-hist import { ScmHistoryProvider } from '@theia/scm-extra/lib/browser/history'; import { GitHistorySupport } from './history/git-history-support'; import { GitDecorationProvider } from './git-decoration-provider'; +import { GitFileSystemProvider } from './git-file-system-provider'; +import { GitFileServiceContribution } from './git-file-service-contribution'; +import { FileServiceContribution } from '@theia/filesystem/lib/browser/file-service'; export default new ContainerModule(bind => { bindGitPreferences(bind); @@ -75,6 +78,10 @@ export default new ContainerModule(bind => { bind(GitSyncService).toSelf().inSingletonScope(); bind(GitErrorHandler).toSelf().inSingletonScope(); + + bind(GitFileSystemProvider).toSelf().inSingletonScope(); + bind(GitFileServiceContribution).toDynamicValue(ctx => new GitFileServiceContribution(ctx.container)).inSingletonScope(); + bind(FileServiceContribution).toService(GitFileServiceContribution); }); export function createGitScmProviderFactory(ctx: interfaces.Context): GitScmProvider.Factory { diff --git a/packages/git/src/browser/git-repository-provider.spec.ts b/packages/git/src/browser/git-repository-provider.spec.ts index 01bd5ba328b08..3c940636e6a19 100644 --- a/packages/git/src/browser/git-repository-provider.spec.ts +++ b/packages/git/src/browser/git-repository-provider.spec.ts @@ -26,7 +26,7 @@ import { DugiteGit } from '../node/dugite-git'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { FileStat, FileChangesEvent } from '@theia/filesystem/lib/common/files'; import { Emitter, CommandService, Disposable } from '@theia/core'; -import { LocalStorageService, StorageService, LabelProvider } from '@theia/core/lib/browser'; +import { LocalStorageService, StorageService, LabelProvider, OpenerService } from '@theia/core/lib/browser'; import { GitRepositoryProvider } from './git-repository-provider'; import * as sinon from 'sinon'; import * as chai from 'chai'; @@ -97,6 +97,7 @@ describe('GitRepositoryProvider', () => { testContainer.bind(ScmContextKeyService).toSelf().inSingletonScope(); testContainer.bind(ContextKeyService).to(ContextKeyServiceDummyImpl).inSingletonScope(); testContainer.bind(GitCommitMessageValidator).toSelf().inSingletonScope(); + testContainer.bind(OpenerService).toConstantValue({}); testContainer.bind(EditorManager).toConstantValue({}); testContainer.bind(GitErrorHandler).toConstantValue({}); testContainer.bind(CommandService).toConstantValue({}); diff --git a/packages/git/src/browser/git-resource.ts b/packages/git/src/browser/git-resource.ts index 7389da945c13a..3d74f3dfb28d7 100644 --- a/packages/git/src/browser/git-resource.ts +++ b/packages/git/src/browser/git-resource.ts @@ -36,5 +36,20 @@ export class GitResource implements Resource { return ''; } + async getSize(): Promise { + if (this.repository) { + const path = Repository.relativePath(this.repository, this.uri.withScheme('file'))?.toString(); + if (path) { + const commitish = this.uri.query || 'index'; + const args = commitish !== 'index' ? ['ls-tree', '--format=%(objectsize)', commitish, path] : ['ls-files', '--format=%(objectsize)', '--', path]; + const size = (await this.git.exec(this.repository, args)).stdout.split('\n').filter(line => !!line.trim())[0]; + if (size) { + return parseInt(size); + } + } + } + return 0; + } + dispose(): void { } } diff --git a/packages/git/src/browser/git-scm-provider.spec.ts b/packages/git/src/browser/git-scm-provider.spec.ts index 4458af4af177a..a3255b6b63ffb 100644 --- a/packages/git/src/browser/git-scm-provider.spec.ts +++ b/packages/git/src/browser/git-scm-provider.spec.ts @@ -21,7 +21,7 @@ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/front FrontendApplicationConfigProvider.set({}); import { CommandService, Disposable, ILogger, MessageService } from '@theia/core'; -import { LabelProvider } from '@theia/core/lib/browser'; +import { LabelProvider, OpenerService } from '@theia/core/lib/browser'; import { FileUri } from '@theia/core/lib/node'; import { Container } from '@theia/core/shared/inversify'; import { EditorManager } from '@theia/editor/lib/browser'; @@ -46,6 +46,7 @@ disableJSDOM(); describe('GitScmProvider', () => { let testContainer: Container; + let mockOpenerService: OpenerService; let mockEditorManager: EditorManager; let mockGitErrorHandler: GitErrorHandler; let mockFileService: FileService; @@ -65,6 +66,7 @@ describe('GitScmProvider', () => { }); beforeEach(async () => { + mockOpenerService = {} as OpenerService; mockEditorManager = sinon.createStubInstance(EditorManager); mockGitErrorHandler = sinon.createStubInstance(GitErrorHandler); mockFileService = sinon.createStubInstance(FileService); @@ -73,6 +75,7 @@ describe('GitScmProvider', () => { mockLabelProvider = sinon.createStubInstance(LabelProvider); testContainer = new Container(); + testContainer.bind(OpenerService).toConstantValue(mockOpenerService); testContainer.bind(EditorManager).toConstantValue(mockEditorManager); testContainer.bind(GitErrorHandler).toConstantValue(mockGitErrorHandler); testContainer.bind(FileService).toConstantValue(mockFileService); diff --git a/packages/git/src/browser/git-scm-provider.ts b/packages/git/src/browser/git-scm-provider.ts index 0eb2d33c95096..c2b51109c6b70 100644 --- a/packages/git/src/browser/git-scm-provider.ts +++ b/packages/git/src/browser/git-scm-provider.ts @@ -16,6 +16,7 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; +import { open, OpenerService } from '@theia/core/lib/browser'; import { DiffUris } from '@theia/core/lib/browser/diff-uris'; import { Emitter } from '@theia/core'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; @@ -64,6 +65,9 @@ export class GitScmProvider implements ScmProvider { this.onDidChangeStatusBarCommandsEmitter ); + @inject(OpenerService) + protected openerService: OpenerService; + @inject(EditorManager) protected readonly editorManager: EditorManager; @@ -223,9 +227,12 @@ export class GitScmProvider implements ScmProvider { async open(change: GitFileChange, options?: EditorOpenerOptions): Promise { const uriToOpen = this.getUriToOpen(change); - await this.editorManager.open(uriToOpen, options); + await open(this.openerService, uriToOpen, options); } + // note: the implementation has to ensure that `GIT_RESOURCE_SCHEME` URIs it returns either directly or within a diff-URI always have a query; + // as an example of an issue that can otherwise arise, the VS Code `media-preview` plugin is known to mangle resource URIs without the query: + // https://github.com/microsoft/vscode/blob/6eaf6487a4d8301b981036bfa53976546eb6694f/extensions/media-preview/src/imagePreview/index.ts#L205-L209 getUriToOpen(change: GitFileChange): URI { const changeUri: URI = new URI(change.uri); const fromFileUri = change.oldUri ? new URI(change.oldUri) : changeUri; // set oldUri on renamed and copied @@ -233,14 +240,14 @@ export class GitScmProvider implements ScmProvider { if (change.staged) { return changeUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('HEAD'); } else { - return changeUri.withScheme(GIT_RESOURCE_SCHEME); + return changeUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('index'); } } if (change.status !== GitFileStatus.New) { if (change.staged) { return DiffUris.encode( fromFileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('HEAD'), - changeUri.withScheme(GIT_RESOURCE_SCHEME), + changeUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('index'), nls.localize( 'theia/git/tabTitleIndex', '{0} (Index)', @@ -249,7 +256,7 @@ export class GitScmProvider implements ScmProvider { } if (this.stagedChanges.find(c => c.uri === change.uri)) { return DiffUris.encode( - fromFileUri.withScheme(GIT_RESOURCE_SCHEME), + fromFileUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('index'), changeUri, nls.localize( 'theia/git/tabTitleWorkingTree', @@ -270,11 +277,11 @@ export class GitScmProvider implements ScmProvider { )); } if (change.staged) { - return changeUri.withScheme(GIT_RESOURCE_SCHEME); + return changeUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('index'); } if (this.stagedChanges.find(c => c.uri === change.uri)) { return DiffUris.encode( - changeUri.withScheme(GIT_RESOURCE_SCHEME), + changeUri.withScheme(GIT_RESOURCE_SCHEME).withQuery('index'), changeUri, nls.localize( 'theia/git/tabTitleWorkingTree', diff --git a/packages/git/src/node/dugite-git.ts b/packages/git/src/node/dugite-git.ts index 857c757b12ba6..8fe73bc453039 100644 --- a/packages/git/src/node/dugite-git.ts +++ b/packages/git/src/node/dugite-git.ts @@ -48,6 +48,8 @@ import { GitExecProvider } from './git-exec-provider'; import { GitEnvProvider } from './env/git-env-provider'; import { GitInit } from './init/git-init'; +import upath = require('upath'); + /** * Parsing and converting raw Git output into Git model instances. */ @@ -548,7 +550,9 @@ export class DugiteGit implements Git { const path = this.getFsPath(uri); const [exec, env] = await Promise.all([this.execProvider.exec(), this.gitEnv.promise]); if (encoding === 'binary') { - return (await getBlobContents(repositoryPath, commitish, path, { exec, env })).toString(); + // note: contrary to what its jsdoc says, getBlobContents expects a (normalized) relative path + const relativePath = upath.normalizeSafe(Path.relative(repositoryPath, path)); + return (await getBlobContents(repositoryPath, commitish, relativePath, { exec, env })).toString('binary'); } return (await getTextContents(repositoryPath, commitish, path, { exec, env })).toString(); } diff --git a/packages/monaco/src/browser/monaco-editor-service.ts b/packages/monaco/src/browser/monaco-editor-service.ts index 0138443dabcf3..98d459616aeba 100644 --- a/packages/monaco/src/browser/monaco-editor-service.ts +++ b/packages/monaco/src/browser/monaco-editor-service.ts @@ -67,7 +67,7 @@ export class MonacoEditorService extends StandaloneCodeEditorService { let editor = MonacoEditor.getCurrent(this.editors); if (!editor && CustomEditorWidget.is(this.shell.activeWidget)) { const model = this.shell.activeWidget.modelRef.object; - if (model.editorTextModel instanceof MonacoEditorModel) { + if (model?.editorTextModel instanceof MonacoEditorModel) { editor = MonacoEditor.findByDocument(this.editors, model.editorTextModel)[0]; } } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index fedab10b40ec8..0e6c0570041ea 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -1882,7 +1882,7 @@ export interface CustomEditorsExt { newWebviewHandle: string, viewType: string, title: string, - widgetOpenerOptions: object | undefined, + position: number, options: theia.WebviewPanelOptions, cancellation: CancellationToken): Promise; $createCustomDocument(resource: UriComponents, viewType: string, openContext: theia.CustomDocumentOpenContext, cancellation: CancellationToken): Promise<{ editable: boolean }>; @@ -1905,7 +1905,6 @@ export interface CustomEditorsMain { $registerTextEditorProvider(viewType: string, options: theia.WebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void; $registerCustomEditorProvider(viewType: string, options: theia.WebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void; $unregisterEditorProvider(viewType: string): void; - $createCustomEditorPanel(handle: string, title: string, widgetOpenerOptions: object | undefined, options: theia.WebviewPanelOptions & theia.WebviewOptions): Promise; $onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void; $onContentChange(resource: UriComponents, viewType: string): void; } diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 913fa538f1a47..43fadf44f7e3a 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -40,10 +40,10 @@ import { WorkspaceService } from '@theia/workspace/lib/browser'; import { PluginContributionHandler } from '../../main/browser/plugin-contribution-handler'; import { getQueryParameters } from '../../main/browser/env-main'; import { getPreferences } from '../../main/browser/preference-registry-main'; -import { Deferred } from '@theia/core/lib/common/promise-util'; +import { Deferred, waitForEvent } from '@theia/core/lib/common/promise-util'; import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager'; import { DebugConfigurationManager } from '@theia/debug/lib/browser/debug-configuration-manager'; -import { WaitUntilEvent } from '@theia/core/lib/common/event'; +import { Event, WaitUntilEvent } from '@theia/core/lib/common/event'; import { FileSearchService } from '@theia/file-search/lib/common/file-search-service'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { PluginViewRegistry } from '../../main/browser/view/plugin-view-registry'; @@ -209,7 +209,8 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport this.activateByNotebookRenderer(rendererId)); this.widgets.onDidCreateWidget(({ factoryId, widget }) => { - if ((factoryId === WebviewWidget.FACTORY_ID || factoryId === CustomEditorWidget.FACTORY_ID) && widget instanceof WebviewWidget) { + // note: state restoration of custom editors is handled in `PluginCustomEditorRegistry.init` + if (factoryId === WebviewWidget.FACTORY_ID && widget instanceof WebviewWidget) { const storeState = widget.storeState.bind(widget); const restoreState = widget.restoreState.bind(widget); @@ -448,7 +449,12 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport { + if (!this.fileService.hasProvider(event.scheme)) { + return waitForEvent(Event.filter(this.fileService.onDidChangeFileSystemProviderRegistrations, + ({ added, scheme }) => added && scheme === event.scheme), 3000); + } + })); } protected ensureCommandHandlerRegistration(event: WillExecuteCommandEvent): void { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx index 838c3ad08b162..3671fd12b2687 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx @@ -14,13 +14,13 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; -import { ApplicationShell, OpenHandler, Widget, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; +import { ApplicationShell, DiffUris, OpenHandler, SplitWidget, Widget, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; import { CustomEditor, CustomEditorPriority, CustomEditorSelector } from '../../../common'; import { CustomEditorWidget } from './custom-editor-widget'; +import { PluginCustomEditorRegistry } from './plugin-custom-editor-registry'; import { generateUuid } from '@theia/core/lib/common/uuid'; -import { Emitter } from '@theia/core'; +import { DisposableCollection, Emitter } from '@theia/core'; import { match } from '@theia/core/lib/common/glob'; export class CustomEditorOpener implements OpenHandler { @@ -33,8 +33,9 @@ export class CustomEditorOpener implements OpenHandler { constructor( private readonly editor: CustomEditor, - @inject(ApplicationShell) protected readonly shell: ApplicationShell, - @inject(WidgetManager) protected readonly widgetManager: WidgetManager + protected readonly shell: ApplicationShell, + protected readonly widgetManager: WidgetManager, + protected readonly editorRegistry: PluginCustomEditorRegistry ) { this.id = CustomEditorOpener.toCustomEditorId(this.editor.viewType); this.label = this.editor.displayName; @@ -45,7 +46,13 @@ export class CustomEditorOpener implements OpenHandler { } canHandle(uri: URI): number { - if (this.matches(this.editor.selector, uri)) { + const { selector } = this.editor; + if (DiffUris.isDiffUri(uri)) { + const [left, right] = DiffUris.decode(uri); + if (this.matches(selector, right) && this.matches(selector, left)) { + return this.getPriority(); + } + } else if (this.matches(selector, uri)) { return this.getPriority(); } return 0; @@ -62,34 +69,112 @@ export class CustomEditorOpener implements OpenHandler { } protected readonly pendingWidgetPromises = new Map>(); - async open(uri: URI, options?: WidgetOpenerOptions): Promise { + protected async openCustomEditor(uri: URI, options?: WidgetOpenerOptions): Promise { let widget: CustomEditorWidget | undefined; - const widgets = this.widgetManager.getWidgets(CustomEditorWidget.FACTORY_ID) as CustomEditorWidget[]; - widget = widgets.find(w => w.viewType === this.editor.viewType && w.resource.toString() === uri.toString()); - - if (widget?.isVisible) { - return this.shell.revealWidget(widget.id); - } - if (widget?.isAttached) { - return this.shell.activateWidget(widget.id); - } - if (!widget) { - const uriString = uri.toString(); - let widgetPromise = this.pendingWidgetPromises.get(uriString); - if (!widgetPromise) { + let isNewWidget = false; + const uriString = uri.toString(); + let widgetPromise = this.pendingWidgetPromises.get(uriString); + if (widgetPromise) { + widget = await widgetPromise; + } else { + const widgets = this.widgetManager.getWidgets(CustomEditorWidget.FACTORY_ID) as CustomEditorWidget[]; + widget = widgets.find(w => w.viewType === this.editor.viewType && w.resource.toString() === uriString); + if (!widget) { + isNewWidget = true; const id = generateUuid(); - widgetPromise = this.widgetManager.getOrCreateWidget(CustomEditorWidget.FACTORY_ID, { id }); + widgetPromise = this.widgetManager.getOrCreateWidget(CustomEditorWidget.FACTORY_ID, { id }).then(async w => { + try { + w.viewType = this.editor.viewType; + w.resource = uri; + await this.editorRegistry.resolveWidget(w); + if (options?.widgetOptions) { + await this.shell.addWidget(w, options.widgetOptions); + } + return w; + } catch (e) { + w.dispose(); + throw e; + } + }).finally(() => this.pendingWidgetPromises.delete(uriString)); this.pendingWidgetPromises.set(uriString, widgetPromise); widget = await widgetPromise; - this.pendingWidgetPromises.delete(uriString); - widget.viewType = this.editor.viewType; - widget.resource = uri; - this.onDidOpenCustomEditorEmitter.fire([widget, options]); } } + if (options?.mode === 'activate') { + await this.shell.activateWidget(widget.id); + } else if (options?.mode === 'reveal') { + await this.shell.revealWidget(widget.id); + } + if (isNewWidget) { + this.onDidOpenCustomEditorEmitter.fire([widget, options]); + } return widget; } + protected async openSideBySide(uri: URI, options?: WidgetOpenerOptions): Promise { + const [leftUri, rightUri] = DiffUris.decode(uri); + const widget = await this.widgetManager.getOrCreateWidget( + CustomEditorWidget.SIDE_BY_SIDE_FACTORY_ID, { uri: uri.toString(), viewType: this.editor.viewType }); + if (!widget.panes.length) { // a new widget + const trackedDisposables = new DisposableCollection(widget); + try { + const createPane = async (paneUri: URI) => { + let pane = await this.openCustomEditor(paneUri); + if (pane.isAttached) { + await this.shell.closeWidget(pane.id); + if (!pane.isDisposed) { // user canceled + return undefined; + } + pane = await this.openCustomEditor(paneUri); + } + return pane; + }; + + const rightPane = await createPane(rightUri); + if (!rightPane) { + trackedDisposables.dispose(); + return undefined; + } + trackedDisposables.push(rightPane); + + const leftPane = await createPane(leftUri); + if (!leftPane) { + trackedDisposables.dispose(); + return undefined; + } + trackedDisposables.push(leftPane); + + widget.addPane(leftPane); + widget.addPane(rightPane); + + // dispose the widget if either of its panes gets externally disposed + leftPane.disposed.connect(() => widget.dispose()); + rightPane.disposed.connect(() => widget.dispose()); + + if (options?.widgetOptions) { + await this.shell.addWidget(widget, options.widgetOptions); + } + } catch (e) { + trackedDisposables.dispose(); + console.error(e); + throw e; + } + } + if (options?.mode === 'activate') { + await this.shell.activateWidget(widget.id); + } else if (options?.mode === 'reveal') { + await this.shell.revealWidget(widget.id); + } + return widget; + } + + async open(uri: URI, options?: WidgetOpenerOptions): Promise { + options = { ...options }; + options.mode ??= 'activate'; + options.widgetOptions ??= { area: 'main' }; + return DiffUris.isDiffUri(uri) ? this.openSideBySide(uri, options) : this.openCustomEditor(uri, options); + } + matches(selectors: CustomEditorSelector[], resource: URI): boolean { return selectors.some(selector => this.selectorMatches(selector, resource)); } diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts index 7d277a44565b5..ac804199fc89b 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-widget.ts @@ -17,29 +17,36 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { FileOperation } from '@theia/filesystem/lib/common/files'; -import { ApplicationShell, NavigatableWidget, Saveable, SaveableSource, SaveOptions } from '@theia/core/lib/browser'; +import { ApplicationShell, DelegatingSaveable, NavigatableWidget, Saveable, SaveableSource, SaveOptions } from '@theia/core/lib/browser'; import { SaveableService } from '@theia/core/lib/browser/saveable-service'; import { Reference } from '@theia/core/lib/common/reference'; import { WebviewWidget } from '../webview/webview'; import { CustomEditorModel } from './custom-editors-main'; +import { CustomEditorWidget as CustomEditorWidgetShape } from '@theia/editor/lib/browser'; @injectable() -export class CustomEditorWidget extends WebviewWidget implements SaveableSource, NavigatableWidget { +export class CustomEditorWidget extends WebviewWidget implements CustomEditorWidgetShape, SaveableSource, NavigatableWidget { static override FACTORY_ID = 'plugin-custom-editor'; + static readonly SIDE_BY_SIDE_FACTORY_ID = CustomEditorWidget.FACTORY_ID + '.side-by-side'; override id: string; resource: URI; - protected _modelRef: Reference; - get modelRef(): Reference { + protected _modelRef: Reference = { object: undefined, dispose: () => { } }; + get modelRef(): Reference { return this._modelRef; } set modelRef(modelRef: Reference) { + this._modelRef.dispose(); this._modelRef = modelRef; + this.delegatingSaveable.delegate = modelRef.object; this.doUpdateContent(); } + + // ensures that saveable is available even if modelRef.object is undefined + protected readonly delegatingSaveable = new DelegatingSaveable(); get saveable(): Saveable { - return this._modelRef.object; + return this.delegatingSaveable; } @inject(ApplicationShell) @@ -60,21 +67,23 @@ export class CustomEditorWidget extends WebviewWidget implements SaveableSource, } undo(): void { - this._modelRef.object.undo(); + this._modelRef.object?.undo(); } redo(): void { - this._modelRef.object.redo(); + this._modelRef.object?.redo(); } async save(options?: SaveOptions): Promise { - await this._modelRef.object.saveCustomEditor(options); + await this._modelRef.object?.saveCustomEditor(options); } async saveAs(source: URI, target: URI, options?: SaveOptions): Promise { - const result = await this._modelRef.object.saveCustomEditorAs(source, target, options); - this.doMove(target); - return result; + if (this._modelRef.object) { + const result = await this._modelRef.object.saveCustomEditorAs(source, target, options); + this.doMove(target); + return result; + } } getResourceUri(): URI | undefined { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts index 95e0dcad4ba39..ef5c2d7d60bf4 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editors-main.ts @@ -24,7 +24,6 @@ import { MAIN_RPC_CONTEXT, CustomEditorsMain, CustomEditorsExt, CustomTextEditor import { RPCProtocol } from '../../../common/rpc-protocol'; import { HostedPluginSupport } from '../../../hosted/browser/hosted-plugin'; import { PluginCustomEditorRegistry } from './plugin-custom-editor-registry'; -import { CustomEditorWidget } from './custom-editor-widget'; import { Emitter } from '@theia/core'; import { UriComponents } from '../../../common/uri-components'; import { URI } from '@theia/core/shared/vscode-uri'; @@ -39,11 +38,9 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service'; import { WebviewsMainImpl } from '../webviews-main'; import { WidgetManager } from '@theia/core/lib/browser/widget-manager'; -import { ApplicationShell, DefaultUriLabelProviderContribution, Saveable, SaveOptions, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import { WebviewOptions, WebviewPanelOptions, WebviewPanelShowOptions } from '@theia/plugin'; -import { WebviewWidgetIdentifier } from '../webview/webview'; +import { ApplicationShell, LabelProvider, Saveable, SaveOptions } from '@theia/core/lib/browser'; +import { WebviewPanelOptions } from '@theia/plugin'; import { EditorPreferences } from '@theia/editor/lib/browser'; -import { ViewColumn, WebviewPanelTargetArea } from '../../../plugin/types-impl'; const enum CustomEditorModelType { Custom, @@ -58,7 +55,7 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { protected readonly customEditorService: CustomEditorService; protected readonly undoRedoService: UndoRedoService; protected readonly customEditorRegistry: PluginCustomEditorRegistry; - protected readonly labelProvider: DefaultUriLabelProviderContribution; + protected readonly labelProvider: LabelProvider; protected readonly widgetManager: WidgetManager; protected readonly editorPreferences: EditorPreferences; private readonly proxy: CustomEditorsExt; @@ -75,7 +72,7 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { this.customEditorService = container.get(CustomEditorService); this.undoRedoService = container.get(UndoRedoService); this.customEditorRegistry = container.get(PluginCustomEditorRegistry); - this.labelProvider = container.get(DefaultUriLabelProviderContribution); + this.labelProvider = container.get(LabelProvider); this.editorPreferences = container.get(EditorPreferences); this.widgetManager = container.get(WidgetManager); this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT); @@ -111,7 +108,8 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { const disposables = new DisposableCollection(); disposables.push( - this.customEditorRegistry.registerResolver(viewType, async (widget, widgetOpenerOptions) => { + this.customEditorRegistry.registerResolver(viewType, async widget => { + const { resource, identifier } = widget; widget.options = options; @@ -144,13 +142,16 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { }); } + this.webviewsMain.hookWebview(widget); + widget.title.label = this.labelProvider.getName(resource); + const _cancellationSource = new CancellationTokenSource(); await this.proxy.$resolveWebviewEditor( resource.toComponents(), identifier.id, viewType, - this.labelProvider.getName(resource)!, - widgetOpenerOptions, + widget.title.label, + widget.viewState.position, options, _cancellationSource.token ); @@ -213,66 +214,6 @@ export class CustomEditorsMainImpl implements CustomEditorsMain, Disposable { const model = await this.getCustomEditorModel(resourceComponents, viewType); model.changeContent(); } - - async $createCustomEditorPanel( - panelId: string, - title: string, - widgetOpenerOptions: WidgetOpenerOptions | undefined, - options: WebviewPanelOptions & WebviewOptions - ): Promise { - const view = await this.widgetManager.getOrCreateWidget(CustomEditorWidget.FACTORY_ID, { id: panelId }); - this.webviewsMain.hookWebview(view); - view.title.label = title; - const { enableFindWidget, retainContextWhenHidden, enableScripts, enableForms, localResourceRoots, ...contentOptions } = options; - view.viewColumn = ViewColumn.One; // behaviour might be overridden later using widgetOpenerOptions (if available) - view.options = { enableFindWidget, retainContextWhenHidden }; - view.setContentOptions({ - allowScripts: enableScripts, - allowForms: enableForms, - localResourceRoots: localResourceRoots && localResourceRoots.map(root => root.toString()), - ...contentOptions, - ...view.contentOptions - }); - if (view.isAttached) { - if (view.isVisible) { - this.shell.revealWidget(view.id); - } - return; - } - const showOptions: WebviewPanelShowOptions = { - preserveFocus: true - }; - - if (widgetOpenerOptions) { - if (widgetOpenerOptions.mode === 'reveal') { - showOptions.preserveFocus = false; - } - - if (widgetOpenerOptions.widgetOptions) { - let area: WebviewPanelTargetArea; - switch (widgetOpenerOptions.widgetOptions.area) { - case 'main': - area = WebviewPanelTargetArea.Main; - case 'left': - area = WebviewPanelTargetArea.Left; - case 'right': - area = WebviewPanelTargetArea.Right; - case 'bottom': - area = WebviewPanelTargetArea.Bottom; - default: // includes 'top' and 'secondaryWindow' - area = WebviewPanelTargetArea.Main; - } - showOptions.area = area; - - if (widgetOpenerOptions.widgetOptions.mode === 'split-right' || - widgetOpenerOptions.widgetOptions.mode === 'open-to-right') { - showOptions.viewColumn = ViewColumn.Beside; - } - } - } - - this.webviewsMain.addOrReattachWidget(view, showOptions); - } } export interface CustomEditorModel extends Saveable, Disposable { diff --git a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts index 50bfdb287f8a0..7250e3f4c0908 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts @@ -17,16 +17,17 @@ import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; import { CustomEditor, DeployedPlugin } from '../../../common'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; +import { Deferred } from '@theia/core/lib/common/promise-util'; import { CustomEditorOpener } from './custom-editor-opener'; import { Emitter } from '@theia/core'; -import { ApplicationShell, DefaultOpenerService, OpenWithService, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; +import { ApplicationShell, DefaultOpenerService, OpenWithService, WidgetManager } from '@theia/core/lib/browser'; import { CustomEditorWidget } from './custom-editor-widget'; @injectable() export class PluginCustomEditorRegistry { private readonly editors = new Map(); - private readonly pendingEditors = new Set(); - private readonly resolvers = new Map void>(); + private readonly pendingEditors = new Map, disposable: Disposable }>(); + private readonly resolvers = new Map Promise>(); private readonly onWillOpenCustomEditorEmitter = new Emitter(); readonly onWillOpenCustomEditor = this.onWillOpenCustomEditorEmitter.event; @@ -74,7 +75,8 @@ export class PluginCustomEditorRegistry { const editorOpenHandler = new CustomEditorOpener( editor, this.shell, - this.widgetManager + this.widgetManager, + this ); toDispose.push(this.defaultOpenerService.addHandler(editorOpenHandler)); toDispose.push( @@ -86,30 +88,30 @@ export class PluginCustomEditorRegistry { open: uri => editorOpenHandler.open(uri) }) ); - toDispose.push( - editorOpenHandler.onDidOpenCustomEditor(event => this.resolveWidget(event[0], event[1])) - ); return toDispose; } - resolveWidget = (widget: CustomEditorWidget, options?: WidgetOpenerOptions) => { + async resolveWidget(widget: CustomEditorWidget): Promise { const resolver = this.resolvers.get(widget.viewType); if (resolver) { - resolver(widget, options); + await resolver(widget); } else { - this.pendingEditors.add(widget); + const deferred = new Deferred(); + const disposable = widget.onDidDispose(() => this.pendingEditors.delete(widget)); + this.pendingEditors.set(widget, { deferred, disposable }); this.onWillOpenCustomEditorEmitter.fire(widget.viewType); + return deferred.promise; } }; - registerResolver(viewType: string, resolver: (widget: CustomEditorWidget, options?: WidgetOpenerOptions) => void): Disposable { + registerResolver(viewType: string, resolver: (widget: CustomEditorWidget) => Promise): Disposable { if (this.resolvers.has(viewType)) { throw new Error(`Resolver for ${viewType} already registered`); } - for (const editorWidget of this.pendingEditors) { + for (const [editorWidget, { deferred, disposable }] of this.pendingEditors.entries()) { if (editorWidget.viewType === viewType) { - resolver(editorWidget); + resolver(editorWidget).then(() => deferred.resolve(), err => deferred.reject(err)).finally(() => disposable.dispose()); this.pendingEditors.delete(editorWidget); } } diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 7323df220e868..68874ede43d97 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -21,10 +21,10 @@ import '../../../src/main/browser/style/comments.css'; import { ContainerModule } from '@theia/core/shared/inversify'; import { FrontendApplicationContribution, WidgetFactory, bindViewContribution, - ViewContainerIdentifier, ViewContainer, createTreeContainer, TreeWidget, LabelProviderContribution, - UndoRedoHandler + ViewContainerIdentifier, ViewContainer, createTreeContainer, TreeWidget, LabelProviderContribution, LabelProvider, + UndoRedoHandler, DiffUris, Navigatable, SplitWidget } from '@theia/core/lib/browser'; -import { MaybePromise, CommandContribution, ResourceResolver, bindContributionProvider } from '@theia/core/lib/common'; +import { MaybePromise, CommandContribution, ResourceResolver, bindContributionProvider, URI, generateUuid } from '@theia/core/lib/common'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging'; import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin'; import { HostedPluginWatcher } from '../../hosted/browser/hosted-plugin-watcher'; @@ -200,6 +200,25 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CustomEditorUndoRedoHandler).toSelf().inSingletonScope(); bind(UndoRedoHandler).toService(CustomEditorUndoRedoHandler); + bind(WidgetFactory).toDynamicValue(ctx => ({ + id: CustomEditorWidget.SIDE_BY_SIDE_FACTORY_ID, + createWidget: (arg: { uri: string, viewType: string }) => { + const uri = new URI(arg.uri); + const [leftUri, rightUri] = DiffUris.decode(uri); + const navigatable: Navigatable = { + getResourceUri: () => rightUri, + createMoveToUri: resourceUri => DiffUris.encode(leftUri, rightUri.withPath(resourceUri.path)) + }; + const widget = new SplitWidget({ navigatable }); + widget.id = arg.viewType + '.side-by-side:' + generateUuid(); + const labelProvider = ctx.container.get(LabelProvider); + widget.title.label = labelProvider.getName(uri); + widget.title.iconClass = labelProvider.getIcon(uri); + widget.title.closable = true; + return widget; + } + })).inSingletonScope(); + bind(PluginViewWidget).toSelf(); bind(WidgetFactory).toDynamicValue(({ container }) => ({ id: PLUGIN_VIEW_FACTORY_ID, diff --git a/packages/plugin-ext/src/main/browser/webview/pre/service-worker.js b/packages/plugin-ext/src/main/browser/webview/pre/service-worker.js index c39cc3fd92828..e3a1da7cc82f7 100644 --- a/packages/plugin-ext/src/main/browser/webview/pre/service-worker.js +++ b/packages/plugin-ext/src/main/browser/webview/pre/service-worker.js @@ -226,7 +226,8 @@ async function processResourceRequest(event, requestUrl, resourceRoot) { parentClient.postMessage({ channel: 'load-resource', - path: resourcePath + path: resourcePath, + query: requestUrl.search.replace(/^\?/, '') }); return resourceRequestStore.create(webviewId, resourcePath) diff --git a/packages/plugin-ext/src/main/browser/webview/webview.ts b/packages/plugin-ext/src/main/browser/webview/webview.ts index f841c5be90a15..9ed8585bfc91d 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview.ts @@ -48,7 +48,6 @@ import { isFirefox } from '@theia/core/lib/browser/browser'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileOperationError, FileOperationResult } from '@theia/filesystem/lib/common/files'; import { BinaryBufferReadableStream } from '@theia/core/lib/common/buffer'; -import { ViewColumn } from '../../../plugin/types-impl'; import { ExtractableWidget } from '@theia/core/lib/browser/widgets/extractable-widget'; import { BadgeWidget } from '@theia/core/lib/browser/view-container'; import { MenuPath } from '@theia/core'; @@ -185,7 +184,6 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract } viewType: string; - viewColumn: ViewColumn; options: WebviewPanelOptions = {}; protected ready = new Deferred(); @@ -352,7 +350,7 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract /* no-op: webview loses focus only if another element gains focus in the main window */ })); this.toHide.push(this.on(WebviewMessageChannels.doReload, () => this.reload())); - this.toHide.push(this.on(WebviewMessageChannels.loadResource, (entry: any) => this.loadResource(entry.path))); + this.toHide.push(this.on(WebviewMessageChannels.loadResource, (entry: any) => this.loadResource(entry.path, entry.query))); this.toHide.push(this.on(WebviewMessageChannels.loadLocalhost, (entry: any) => this.loadLocalhost(entry.origin) )); @@ -544,10 +542,11 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract return undefined; } - protected async loadResource(requestPath: string): Promise { - const normalizedUri = this.normalizeRequestUri(requestPath); + protected async loadResource(requestPath: string, requestQuery: string = ''): Promise { + const normalizedUri = this.normalizeRequestUri(requestPath).withQuery(decodeURIComponent(requestQuery)); // browser cache does not support file scheme, normalize to current endpoint scheme and host - const cacheUrl = new Endpoint({ path: normalizedUri.path.toString() }).getRestUrl().toString(); + // use requestPath rather than normalizedUri.path to preserve the scheme of the requested resource as a path segment + const cacheUrl = new Endpoint({ path: requestPath }).getRestUrl().withQuery(decodeURIComponent(requestQuery)).toString(); try { if (this.contentOptions.localResourceRoots) { diff --git a/packages/plugin-ext/src/main/browser/webviews-main.ts b/packages/plugin-ext/src/main/browser/webviews-main.ts index 862010101dbf1..c282979ec12d6 100644 --- a/packages/plugin-ext/src/main/browser/webviews-main.ts +++ b/packages/plugin-ext/src/main/browser/webviews-main.ts @@ -191,7 +191,12 @@ export class WebviewsMainImpl implements WebviewsMain, Disposable { // eslint-disable-next-line @typescript-eslint/no-explicit-any async $postMessage(handle: string, value: any): Promise { - const webview = await this.getWebview(handle); + // Due to async nature of $postMessage, the webview may have been disposed in the meantime. + // Therefore, don't throw an error if the webview is not found, but return false in this case. + const webview = await this.tryGetWebview(handle); + if (!webview) { + return false; + } webview.sendMessage(value); return true; } diff --git a/packages/plugin-ext/src/plugin/custom-editors.ts b/packages/plugin-ext/src/plugin/custom-editors.ts index 4ec88ce886fe9..700151a37303d 100644 --- a/packages/plugin-ext/src/plugin/custom-editors.ts +++ b/packages/plugin-ext/src/plugin/custom-editors.ts @@ -25,11 +25,11 @@ import { RPCProtocol } from '../common/rpc-protocol'; import { Disposable, URI } from './types-impl'; import { UriComponents } from '../common/uri-components'; import { DocumentsExtImpl } from './documents'; -import { WebviewImpl, WebviewsExtImpl } from './webviews'; +import { WebviewsExtImpl } from './webviews'; import { CancellationToken, CancellationTokenSource } from '@theia/core/lib/common/cancellation'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; -import { WorkspaceExtImpl } from './workspace'; import { Cache } from '../common/cache'; +import * as Converters from './type-converters'; export class CustomEditorsExtImpl implements CustomEditorsExt { private readonly proxy: CustomEditorsMain; @@ -38,8 +38,7 @@ export class CustomEditorsExtImpl implements CustomEditorsExt { constructor(rpc: RPCProtocol, private readonly documentExt: DocumentsExtImpl, - private readonly webviewExt: WebviewsExtImpl, - private readonly workspace: WorkspaceExtImpl) { + private readonly webviewExt: WebviewsExtImpl) { this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.CUSTOM_EDITORS_MAIN); } @@ -116,22 +115,21 @@ export class CustomEditorsExtImpl implements CustomEditorsExt { document.dispose(); } - async $resolveWebviewEditor( + async $resolveWebviewEditor( resource: UriComponents, handler: string, viewType: string, title: string, - widgetOpenerOptions: object | undefined, - options: theia.WebviewPanelOptions & theia.WebviewOptions, + position: number, + options: theia.WebviewPanelOptions, cancellation: CancellationToken ): Promise { const entry = this.editorProviders.get(viewType); if (!entry) { throw new Error(`No provider found for '${viewType}'`); } - const panel = this.webviewExt.createWebviewPanel(viewType, title, {}, options, entry.plugin, handler, false); - const webviewOptions = WebviewImpl.toWebviewOptions(options, this.workspace, entry.plugin); - await this.proxy.$createCustomEditorPanel(handler, title, widgetOpenerOptions, webviewOptions); + const viewColumn = Converters.toViewColumn(position); + const panel = this.webviewExt.createWebviewPanel(viewType, title, { viewColumn }, options, entry.plugin, handler, false); const revivedResource = URI.revive(resource); diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 2bbc0d04c7410..9de9d8dcc6a7f 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -319,7 +319,7 @@ export function createAPIFactory( const themingExt = rpc.set(MAIN_RPC_CONTEXT.THEMING_EXT, new ThemingExtImpl(rpc)); const commentsExt = rpc.set(MAIN_RPC_CONTEXT.COMMENTS_EXT, new CommentsExtImpl(rpc, commandRegistry, documents)); const tabsExt = rpc.set(MAIN_RPC_CONTEXT.TABS_EXT, new TabsExtImpl(rpc)); - const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt, workspaceExt)); + const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt)); const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt)); const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl()); const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry)); diff --git a/packages/plugin-ext/src/plugin/webviews.ts b/packages/plugin-ext/src/plugin/webviews.ts index e9aff70fb9bb8..d910fe8beda02 100644 --- a/packages/plugin-ext/src/plugin/webviews.ts +++ b/packages/plugin-ext/src/plugin/webviews.ts @@ -255,7 +255,7 @@ export class WebviewImpl implements theia.Webview { .replace('{{authority}}', resource.authority) .replace('{{path}}', resource.path.replace(/^\//, '')) .replace('{{uuid}}', this.origin ?? this.viewId); - return URI.parse(uri); + return URI.parse(uri).with({ query: resource.query }); } get cspSource(): string { From 5d51320e64819e2d9c7d2060cd43a8f18c573a1e Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 21 Aug 2024 15:24:59 +0200 Subject: [PATCH 342/441] Fix preference tree for plugins (#14036) --- packages/core/src/common/json-schema.ts | 2 + .../src/hosted/node/scanners/scanner-theia.ts | 13 +- .../browser/util/preference-tree-generator.ts | 126 ++++++++++++++---- .../util/preference-tree-label-provider.ts | 14 +- .../src/browser/util/preference-types.ts | 6 +- .../browser/views/preference-tree-widget.tsx | 11 +- 6 files changed, 130 insertions(+), 42 deletions(-) diff --git a/packages/core/src/common/json-schema.ts b/packages/core/src/common/json-schema.ts index 07885c0717a7d..04cb90e30e814 100644 --- a/packages/core/src/common/json-schema.ts +++ b/packages/core/src/common/json-schema.ts @@ -36,6 +36,8 @@ export interface IJSONSchema { $id?: string; $schema?: string; type?: JsonType | JsonType[]; + owner?: string; + group?: string; title?: string; default?: JSONValue; definitions?: IJSONSchemaMap; diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 1d309445f0bfb..338f5eea7368e 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -192,11 +192,22 @@ export class TheiaPluginScanner extends AbstractPluginScanner { try { if (rawPlugin.contributes.configuration) { const configurations = Array.isArray(rawPlugin.contributes.configuration) ? rawPlugin.contributes.configuration : [rawPlugin.contributes.configuration]; + const hasMultipleConfigs = configurations.length > 1; contributions.configuration = []; for (const c of configurations) { const config = this.readConfiguration(c, rawPlugin.packagePath); if (config) { - Object.values(config.properties).forEach(property => property.title = config.title); + Object.values(config.properties).forEach(property => { + if (hasMultipleConfigs) { + // If there are multiple configuration contributions, we need to distinguish them by their title in the settings UI. + // They are placed directly under the plugin's name in the settings UI. + property.owner = rawPlugin.displayName; + property.group = config.title; + } else { + // If there's only one configuration contribution, we display the title in the settings UI. + property.owner = config.title; + } + }); contributions.configuration.push(config); } } diff --git a/packages/preferences/src/browser/util/preference-tree-generator.ts b/packages/preferences/src/browser/util/preference-tree-generator.ts index 05648a634b5b2..bacd4035d8534 100644 --- a/packages/preferences/src/browser/util/preference-tree-generator.ts +++ b/packages/preferences/src/browser/util/preference-tree-generator.ts @@ -22,6 +22,15 @@ import debounce = require('@theia/core/shared/lodash.debounce'); import { Preference } from './preference-types'; import { COMMONLY_USED_SECTION_PREFIX, PreferenceLayoutProvider } from './preference-layout'; +export interface CreatePreferencesGroupOptions { + id: string, + group: string, + root: CompositeTreeNode, + expanded?: boolean, + depth?: number, + label?: string +} + @injectable() export class PreferenceTreeGenerator { @@ -57,10 +66,22 @@ export class PreferenceTreeGenerator { const root = this.createRootNode(); const commonlyUsedLayout = this.layoutProvider.getCommonlyUsedLayout(); - const commonlyUsed = this.getOrCreatePreferencesGroup(commonlyUsedLayout.id, commonlyUsedLayout.id, root, groups, commonlyUsedLayout.label); + const commonlyUsed = this.getOrCreatePreferencesGroup({ + id: commonlyUsedLayout.id, + group: commonlyUsedLayout.id, + root, + groups, + label: commonlyUsedLayout.label + }); for (const layout of this.layoutProvider.getLayout()) { - this.getOrCreatePreferencesGroup(layout.id, layout.id, root, groups, layout.label); + this.getOrCreatePreferencesGroup({ + id: layout.id, + group: layout.id, + root, + groups, + label: layout.label + }); } for (const preference of commonlyUsedLayout.settings ?? []) { if (preference in preferencesSchema.properties) { @@ -70,16 +91,11 @@ export class PreferenceTreeGenerator { for (const propertyName of propertyNames) { const property = preferencesSchema.properties[propertyName]; if (!this.preferenceConfigs.isSectionName(propertyName) && !OVERRIDE_PROPERTY_PATTERN.test(propertyName) && !property.deprecationMessage) { - const layoutItem = this.layoutProvider.getLayoutForPreference(propertyName); - const labels = layoutItem ? layoutItem.id.split('.') : propertyName.split('.'); - // If a title is set, this property belongs to the 'extensions' category - const groupID = property.title ? this.defaultTopLevelCategory : this.getGroupName(labels); - // Automatically assign all properties with the same title to the same subgroup - const subgroupName = property.title ?? this.getSubgroupName(labels, groupID); - const subgroupID = [groupID, subgroupName].join('.'); - const toplevelParent = this.getOrCreatePreferencesGroup(groupID, groupID, root, groups); - const immediateParent = subgroupName && this.getOrCreatePreferencesGroup(subgroupID, groupID, toplevelParent, groups, property.title ?? layoutItem?.label); - this.createLeafNode(propertyName, immediateParent || toplevelParent, property); + if (property.owner) { + this.createPluginLeafNode(propertyName, property, root, groups); + } else { + this.createBuiltinLeafNode(propertyName, property, root, groups); + } } } @@ -103,6 +119,63 @@ export class PreferenceTreeGenerator { return root; }; + protected createBuiltinLeafNode(name: string, property: PreferenceDataProperty, root: CompositeTreeNode, groups: Map): void { + const layoutItem = this.layoutProvider.getLayoutForPreference(name); + const labels = layoutItem ? layoutItem.id.split('.') : name.split('.'); + const groupID = this.getGroupName(labels); + const subgroupName = this.getSubgroupName(labels, groupID); + const subgroupID = [groupID, subgroupName].join('.'); + const toplevelParent = this.getOrCreatePreferencesGroup({ + id: groupID, + group: groupID, + root, + groups + }); + const immediateParent = subgroupName ? this.getOrCreatePreferencesGroup({ + id: subgroupID, + group: groupID, + root: toplevelParent, + groups, + label: layoutItem?.label + }) : undefined; + this.createLeafNode(name, immediateParent || toplevelParent, property); + } + + protected createPluginLeafNode(name: string, property: PreferenceDataProperty, root: CompositeTreeNode, groups: Map): void { + if (!property.owner) { + return; + } + const groupID = this.defaultTopLevelCategory; + const subgroupName = property.owner; + const subsubgroupName = property.group; + const hasGroup = Boolean(subsubgroupName); + const toplevelParent = this.getOrCreatePreferencesGroup({ + id: groupID, + group: groupID, + root, + groups + }); + const subgroupID = [groupID, subgroupName].join('.'); + const subgroupParent = this.getOrCreatePreferencesGroup({ + id: subgroupID, + group: groupID, + root: toplevelParent, + groups, + expanded: hasGroup, + label: subgroupName + }); + const subsubgroupID = [groupID, subgroupName, subsubgroupName].join('.'); + const subsubgroupParent = hasGroup ? this.getOrCreatePreferencesGroup({ + id: subsubgroupID, + group: subgroupID, + root: subgroupParent, + groups, + depth: 2, + label: subsubgroupName + }) : undefined; + this.createLeafNode(name, subsubgroupParent || subgroupParent, property); + } + getNodeId(preferenceId: string): string { const expectedGroup = this.getGroupName(preferenceId.split('.')); const expectedId = `${expectedGroup}@${preferenceId}`; @@ -151,40 +224,37 @@ export class PreferenceTreeGenerator { preferenceId: property, parent: preferencesGroup, preference: { data }, - depth: Preference.TreeNode.isTopLevel(preferencesGroup) ? 1 : 2, + depth: Preference.TreeNode.isTopLevel(preferencesGroup) ? 1 : 2 }; CompositeTreeNode.addChild(preferencesGroup, newNode); return newNode; } - protected createPreferencesGroup(id: string, group: string, root: CompositeTreeNode, label?: string): Preference.CompositeTreeNode { + protected createPreferencesGroup(options: CreatePreferencesGroupOptions): Preference.CompositeTreeNode { const newNode: Preference.CompositeTreeNode = { - id: `${group}@${id}`, + id: `${options.group}@${options.id}`, visible: true, - parent: root, + parent: options.root, children: [], expanded: false, selected: false, depth: 0, - label + label: options.label }; const isTopLevel = Preference.TreeNode.isTopLevel(newNode); - if (!isTopLevel) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - delete (newNode as any).expanded; + if (!(options.expanded ?? isTopLevel)) { + delete newNode.expanded; } - newNode.depth = isTopLevel ? 0 : 1; - CompositeTreeNode.addChild(root, newNode); + newNode.depth = options.depth ?? (isTopLevel ? 0 : 1); + CompositeTreeNode.addChild(options.root, newNode); return newNode; } - protected getOrCreatePreferencesGroup( - id: string, group: string, root: CompositeTreeNode, groups: Map, label?: string - ): Preference.CompositeTreeNode { - const existingGroup = groups.get(id); + protected getOrCreatePreferencesGroup(options: CreatePreferencesGroupOptions & { groups: Map }): Preference.CompositeTreeNode { + const existingGroup = options.groups.get(options.id); if (existingGroup) { return existingGroup; } - const newNode = this.createPreferencesGroup(id, group, root, label); - groups.set(id, newNode); + const newNode = this.createPreferencesGroup(options); + options.groups.set(options.id, newNode); return newNode; }; } diff --git a/packages/preferences/src/browser/util/preference-tree-label-provider.ts b/packages/preferences/src/browser/util/preference-tree-label-provider.ts index a40f1fbd272af..7ed9b0d35f23a 100644 --- a/packages/preferences/src/browser/util/preference-tree-label-provider.ts +++ b/packages/preferences/src/browser/util/preference-tree-label-provider.ts @@ -30,19 +30,13 @@ export class PreferenceTreeLabelProvider implements LabelProviderContribution { } getName(node: Preference.TreeNode): string { - if (Preference.CompositeTreeNode.is(node) && node.label) { + if (Preference.TreeNode.is(node) && node.label) { return node.label; } const { id } = Preference.TreeNode.getGroupAndIdFromNodeId(node.id); - const layouts = this.layoutProvider.getLayout(); - const layout = layouts.find(e => e.id === id); - if (layout) { - return layout.label; - } else { - const labels = id.split('.'); - const groupName = labels[labels.length - 1]; - return this.formatString(groupName); - } + const labels = id.split('.'); + const groupName = labels[labels.length - 1]; + return this.formatString(groupName); } getPrefix(node: Preference.TreeNode, fullPath = false): string | undefined { diff --git a/packages/preferences/src/browser/util/preference-types.ts b/packages/preferences/src/browser/util/preference-types.ts index 29ee2bf3f67c9..bf4a81521548c 100644 --- a/packages/preferences/src/browser/util/preference-types.ts +++ b/packages/preferences/src/browser/util/preference-types.ts @@ -18,7 +18,7 @@ import { PreferenceDataProperty, PreferenceScope, TreeNode as BaseTreeNode, - ExpandableTreeNode, + CompositeTreeNode as BaseCompositeTreeNode, SelectableTreeNode, PreferenceInspection, CommonCommands, @@ -59,7 +59,8 @@ export namespace Preference { }; } - export interface CompositeTreeNode extends ExpandableTreeNode, SelectableTreeNode { + export interface CompositeTreeNode extends BaseCompositeTreeNode, SelectableTreeNode { + expanded?: boolean; depth: number; label?: string; } @@ -69,6 +70,7 @@ export namespace Preference { } export interface LeafNode extends BaseTreeNode { + label?: string; depth: number; preference: { data: PreferenceDataProperty }; preferenceId: string; diff --git a/packages/preferences/src/browser/views/preference-tree-widget.tsx b/packages/preferences/src/browser/views/preference-tree-widget.tsx index 29a94d1875b90..60f51b341c0cd 100644 --- a/packages/preferences/src/browser/views/preference-tree-widget.tsx +++ b/packages/preferences/src/browser/views/preference-tree-widget.tsx @@ -24,6 +24,7 @@ import { } from '@theia/core/lib/browser'; import React = require('@theia/core/shared/react'); import { PreferenceTreeModel, PreferenceTreeNodeRow, PreferenceTreeNodeProps } from '../preference-tree-model'; +import { Preference } from '../util/preference-types'; @injectable() export class PreferencesTreeWidget extends TreeWidget { @@ -50,13 +51,21 @@ export class PreferencesTreeWidget extends TreeWidget { this.rows = new Map(); let index = 0; for (const [id, nodeRow] of this.model.currentRows.entries()) { - if (nodeRow.visibleChildren > 0 && (ExpandableTreeNode.is(nodeRow.node) || ExpandableTreeNode.isExpanded(nodeRow.node.parent))) { + if (nodeRow.visibleChildren > 0 && this.isVisibleNode(nodeRow.node)) { this.rows.set(id, { ...nodeRow, index: index++ }); } } this.updateScrollToRow(); } + protected isVisibleNode(node: Preference.TreeNode): boolean { + if (Preference.TreeNode.isTopLevel(node)) { + return true; + } else { + return ExpandableTreeNode.isExpanded(node.parent) && Preference.TreeNode.is(node.parent) && this.isVisibleNode(node.parent); + } + } + protected override doRenderNodeRow({ depth, visibleChildren, node, isExpansible }: PreferenceTreeNodeRow): React.ReactNode { return this.renderNode(node, { depth, visibleChildren, isExpansible }); } From 5af9da6d941070a5025690a9f6b86e152a7097ba Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 21 Aug 2024 15:29:15 +0200 Subject: [PATCH 343/441] Stub Terminal Shell Integration VS Code API (#14058) --- .../plugin-ext/src/plugin/plugin-context.ts | 12 +- .../plugin-ext/src/plugin/terminal-ext.ts | 2 + packages/plugin-ext/src/plugin/types-impl.ts | 10 + packages/plugin/src/theia.d.ts | 1 + ...eia.proposed.terminalShellIntegration.d.ts | 329 ++++++++++++++++++ .../src/node/vsx-extension-resolver.ts | 6 +- 6 files changed, 355 insertions(+), 5 deletions(-) create mode 100644 packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 9de9d8dcc6a7f..edac3d7b2b7dc 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -228,7 +228,8 @@ import { LanguageModelError, PortAutoForwardAction, PortAttributes, - DebugVisualization + DebugVisualization, + TerminalShellExecutionCommandLineConfidence } from './types-impl'; import { AuthenticationExtImpl } from './authentication-ext'; import { SymbolKind } from '../common/plugin-api-rpc-model'; @@ -636,6 +637,12 @@ export function createAPIFactory( /** @stubbed ShareProvider */ registerShareProvider: () => Disposable.NULL, + /** @stubbed Terminal Shell Ingration */ + onDidChangeTerminalShellIntegration: Event.None, + /** @stubbed Terminal Shell Ingration */ + onDidEndTerminalShellExecution: Event.None, + /** @stubbed Terminal Shell Ingration */ + onDidStartTerminalShellExecution: Event.None }; const workspace: typeof theia.workspace = { @@ -1494,7 +1501,8 @@ export function createAPIFactory( LanguageModelError, PortAutoForwardAction, PortAttributes, - DebugVisualization + DebugVisualization, + TerminalShellExecutionCommandLineConfidence }; }; } diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index d8e2a3f01bd70..3e450bc88a329 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -478,6 +478,8 @@ export class TerminalExtImpl implements theia.Terminal { this.creationOptions = this.options; } + shellIntegration: theia.TerminalShellIntegration | undefined = undefined; + sendText(text: string, shouldExecute: boolean = true): void { this.id.promise.then(id => this.proxy.$sendText(id, text, shouldExecute)); } diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index e1b1dd10bb3a7..1aee501f2e810 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -3999,3 +3999,13 @@ export class DebugVisualization { } // #endregion + +// #region Terminal Shell Integration + +export enum TerminalShellExecutionCommandLineConfidence { + Low = 0, + Medium = 1, + High = 2 +} + +// #endregion diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 7134d063da377..4ce0f20f46c2a 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -43,6 +43,7 @@ import './theia.proposed.resolvers'; import './theia.proposed.scmValidation'; import './theia.proposed.shareProvider'; import './theia.proposed.terminalQuickFixProvider'; +import './theia.proposed.terminalShellIntegration'; import './theia.proposed.textSearchProvider'; import './theia.proposed.timeline'; diff --git a/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts b/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts new file mode 100644 index 0000000000000..e9f945fb87e20 --- /dev/null +++ b/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts @@ -0,0 +1,329 @@ +// ***************************************************************************** +// Copyright (C) 2024 Typefox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module '@theia/plugin' { + + // https://github.com/microsoft/vscode/issues/145234 + + /** + * A command that was executed in a terminal. + */ + export interface TerminalShellExecution { + /** + * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} + * of this value depends on the specific shell's shell integration implementation. This + * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is + * fired. + * + * @example + * // Log the details of the command line on start and end + * window.onDidStartTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command started\n${summarizeCommandLine(commandLine)}`); + * }); + * window.onDidEndTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command ended\n${summarizeCommandLine(commandLine)}`); + * }); + * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { + * return [ + * ` Command line: ${command.ommandLine.value}`, + * ` Confidence: ${command.ommandLine.confidence}`, + * ` Trusted: ${command.ommandLine.isTrusted} + * ].join('\n'); + * } + */ + readonly commandLine: TerminalShellExecutionCommandLine; + + /** + * The working directory that was reported by the shell when this command executed. This + * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This + * requires the shell integration to support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Creates a stream of raw data (including escape sequences) that is written to the + * terminal. This will only include data that was written after `read` was called for + * the first time, ie. you must call `read` immediately after the command is executed via + * {@link TerminalShellIntegration.executeCommand} or + * {@link window.onDidStartTerminalShellExecution} to not miss any data. + * + * @example + * // Log all data written to the terminal for a command + * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); + * const stream = command.read(); + * for await (const data of stream) { + * console.log(data); + * } + */ + read(): AsyncIterable; + } + + /** + * A command line that was executed in a terminal. + */ + export interface TerminalShellExecutionCommandLine { + /** + * The full command line that was executed, including both the command and its arguments. + */ + readonly value: string; + + /** + * Whether the command line value came from a trusted source and is therefore safe to + * execute without user additional confirmation, such as a notification that asks "Do you + * want to execute (command)?". This verification is likely only needed if you are going to + * execute the command again. + * + * This is `true` only when the command line was reported explicitly by the shell + * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) + * and it used a nonce for verification. + */ + readonly isTrusted: boolean; + + /** + * The confidence of the command line value which is determined by how the value was + * obtained. This depends upon the implementation of the shell integration script. + */ + readonly confidence: TerminalShellExecutionCommandLineConfidence; + } + + /** + * The confidence of a {@link TerminalShellExecutionCommandLine} value. + */ + enum TerminalShellExecutionCommandLineConfidence { + /** + * The command line value confidence is low. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. Additionally one + * of the following conditions will be met: + * + * - The command started on the very left-most column which is unusual, or + * - The command is multi-line which is more difficult to accurately detect due to line + * continuation characters and right prompts. + * - Command line markers were not reported by the shell integration script. + */ + Low = 0, + + /** + * The command line value confidence is medium. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. The command is + * single-line and does not start on the very left-most column (which is unusual). + */ + Medium = 1, + + /** + * The command line value confidence is high. This means that the value was explicitly sent + * from the shell integration script or the command was executed via the + * {@link TerminalShellIntegration.executeCommand} API. + */ + High = 2 + } + + export interface Terminal { + /** + * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered + * features for the terminal. This will always be `undefined` immediately after the terminal + * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified + * when shell integration is activated for a terminal. + * + * Note that this object may remain undefined if shell integation never activates. For + * example Command Prompt does not support shell integration and a user's shell setup could + * conflict with the automatic shell integration activation. + */ + readonly shellIntegration: TerminalShellIntegration | undefined; + } + + /** + * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. + */ + export interface TerminalShellIntegration { + /** + * The current working directory of the terminal. This {@link Uri} may represent a file on + * another machine (eg. ssh into another machine). This requires the shell integration to + * support working directory reporting. + */ + readonly cwd: Uri | undefined; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * @param commandLine The command line to execute, this is the exact text that will be sent + * to the terminal. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const command = shellIntegration.executeCommand('echo "Hello world"'); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const command = term.shellIntegration.executeCommand({ commandLine }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(commandLine: string): TerminalShellExecution; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) + * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to + * verify whether it was successful. + * + * @param command A command to run. + * @param args Arguments to launch the executable with which will be automatically escaped + * based on the executable type. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const command = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const command = term.shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + */ + executeCommand(executable: string, args: string[]): TerminalShellExecution; + } + + export interface TerminalShellIntegrationChangeEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + } + + export interface TerminalShellExecutionStartEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + } + + export interface TerminalShellExecutionEndEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + */ + readonly execution: TerminalShellExecution; + + /** + * The exit code reported by the shell. `undefined` means the shell did not report an exit + * code or the shell reported a command started before the command finished. + */ + readonly exitCode: number | undefined; + } + + export namespace window { + /** + * Fires when shell integration activates or one of its properties changes in a terminal. + */ + export const onDidChangeTerminalShellIntegration: Event; + + /** + * This will be fired when a terminal command is started. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidStartTerminalShellExecution: Event; + + /** + * This will be fired when a terminal command is ended. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + */ + export const onDidEndTerminalShellExecution: Event; + } +} diff --git a/packages/vsx-registry/src/node/vsx-extension-resolver.ts b/packages/vsx-registry/src/node/vsx-extension-resolver.ts index 01f6b5f2173d3..5f21d3cb53df0 100644 --- a/packages/vsx-registry/src/node/vsx-extension-resolver.ts +++ b/packages/vsx-registry/src/node/vsx-extension-resolver.ts @@ -54,7 +54,7 @@ export class VSXExtensionResolver implements PluginDeployerResolver { const filter = await this.vsxApiFilter(); const version = options?.version || id.version; if (version) { - console.log(`[${id}]: trying to resolve version ${version}...`); + console.log(`[${id.id}]: trying to resolve version ${version}...`); extension = await filter.findLatestCompatibleExtension({ extensionId: id.id, extensionVersion: version, @@ -62,7 +62,7 @@ export class VSXExtensionResolver implements PluginDeployerResolver { targetPlatform: VSXExtensionResolver.TARGET_PLATFORM }); } else { - console.log(`[${id}]: trying to resolve latest version...`); + console.log(`[${id.id}]: trying to resolve latest version...`); extension = await filter.findLatestCompatibleExtension({ extensionId: id.id, includeAllVersions: true, @@ -82,7 +82,7 @@ export class VSXExtensionResolver implements PluginDeployerResolver { if (!options?.ignoreOtherVersions) { const existingVersion = this.hasSameOrNewerVersion(id.id, extension); if (existingVersion) { - console.log(`[${id}]: is already installed with the same or newer version '${existingVersion}'`); + console.log(`[${id.id}]: is already installed with the same or newer version '${existingVersion}'`); return; } } From c3ffc5fa81879943e3b3ad3caec3738ed2d0e5f9 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 22 Aug 2024 17:12:02 +0200 Subject: [PATCH 344/441] Correctly revert saveable on widget close (#14062) --- packages/core/src/browser/saveable-service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/saveable-service.ts b/packages/core/src/browser/saveable-service.ts index 12b273019885a..15fbf85fe3c1d 100644 --- a/packages/core/src/browser/saveable-service.ts +++ b/packages/core/src/browser/saveable-service.ts @@ -174,7 +174,10 @@ export class SaveableService implements FrontendApplicationContribution { setDirty(saveableWidget, saveable.dirty); saveable.onDirtyChanged(() => setDirty(saveableWidget, saveable.dirty)); const closeWithSaving = this.createCloseWithSaving(); - const closeWithoutSaving = () => this.closeWithoutSaving(saveableWidget, false); + const closeWithoutSaving = async () => { + const revert = Saveable.closingWidgetWouldLoseSaveable(saveableWidget, Array.from(this.saveThrottles.keys())); + await this.closeWithoutSaving(saveableWidget, revert); + }; Object.assign(saveableWidget, { closeWithoutSaving, closeWithSaving, @@ -224,7 +227,8 @@ export class SaveableService implements FrontendApplicationContribution { } const notLastWithDocument = !Saveable.closingWidgetWouldLoseSaveable(widget, Array.from(this.saveThrottles.keys())); if (notLastWithDocument) { - return widget.closeWithoutSaving(false).then(() => undefined); + await widget.closeWithoutSaving(false); + return undefined; } if (options && options.shouldSave) { return options.shouldSave(); From 9c379c668fceb387ab36962408447fb5473d469c Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Fri, 23 Aug 2024 13:44:48 +0200 Subject: [PATCH 345/441] Creating a new untitled notebook doesn't work (#14031) --- .../notebooks/notebook-documents-main.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts index 427e0a009b627..de36dd66bf491 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-main.ts @@ -21,7 +21,7 @@ import { NotebookModelResolverService } from '@theia/notebook/lib/browser'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { NotebookCellsChangeType } from '@theia/notebook/lib/common'; import { NotebookMonacoTextModelService } from '@theia/notebook/lib/browser/service/notebook-monaco-text-model-service'; -import { MAIN_RPC_CONTEXT, NotebookCellsChangedEventDto, NotebookDataDto, NotebookDocumentsExt, NotebookDocumentsMain } from '../../../common'; +import { MAIN_RPC_CONTEXT, NotebookCellsChangedEventDto, NotebookDataDto, NotebookDocumentsExt, NotebookDocumentsMain, NotebookRawContentEventDto } from '../../../common'; import { RPCProtocol } from '../../../common/rpc-protocol'; import { NotebookDto } from './notebook-dto'; import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; @@ -156,15 +156,34 @@ export class NotebookDocumentsMainImpl implements NotebookDocumentsMain { // ref.dispose(); // }); + const uriComponents = ref.uri.toComponents(); // untitled notebooks are dirty by default - this.proxy.$acceptDirtyStateChanged(ref.uri.toComponents(), true); + this.proxy.$acceptDirtyStateChanged(uriComponents, true); - // apply content changes... slightly HACKY -> this triggers a change event + // apply content changes... if (options.content) { const data = NotebookDto.fromNotebookDataDto(options.content); ref.setData(data); + + // Create and send a change events + const rawEvents: NotebookRawContentEventDto[] = []; + if (options.content.cells && options.content.cells.length > 0) { + rawEvents.push({ + kind: NotebookCellsChangeType.ModelChange, + changes: [{ start: 0, deleteCount: 0, newItems: ref.cells.map(NotebookDto.toNotebookCellDto) }] + }); + } + if (options.content.metadata) { + rawEvents.push({ + kind: NotebookCellsChangeType.ChangeDocumentMetadata, + metadata: options.content.metadata + }); + } + if (rawEvents.length > 0) { + this.proxy.$acceptModelChanged(uriComponents, { versionId: 1, rawEvents }, true); + } } - return ref.uri.toComponents(); + return uriComponents; } async $tryOpenNotebook(uriComponents: UriComponents): Promise { From acaa3dfb6733665af5ab6ab9025fdf37ea758668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 26 Aug 2024 14:22:16 +0200 Subject: [PATCH 346/441] Download json schema catalog at build-time instead of run-time (#14065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #11881 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 1 + packages/core/package.json | 6 ++-- packages/core/scripts/download-catalog.js | 26 ++++++++++++++ .../core/src/browser/json-schema-store.ts | 13 ++----- yarn.lock | 34 +++++++++++++++++++ 5 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 packages/core/scripts/download-catalog.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a0992360c5667..697912237e877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## Unreleased +- [core] Download json schema catalog at build-time - [#14065](https://github.com/eclipse-theia/theia/pull/14065/) - Contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_1.53.0) - [dependencies] Updated electron to version 30.1.2 - [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics diff --git a/packages/core/package.json b/packages/core/package.json index 3a20fa1bb443f..e5349a14818d8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -203,7 +203,8 @@ "generate-layout": "electron ./scripts/generate-layout", "generate-theia-re-exports": "theia-re-exports generate && theia-re-exports template README_TEMPLATE.md > README.md", "lint": "theiaext lint", - "prepare": "yarn -s generate-theia-re-exports", + "prepare": "yarn -s generate-theia-re-exports && yarn download:json-schema", + "download:json-schema": "node ./scripts/download-catalog.js", "test": "theiaext test", "version": "yarn -s generate-theia-re-exports", "watch": "theiaext watch" @@ -211,7 +212,8 @@ "devDependencies": { "@theia/ext-scripts": "1.52.0", "@theia/re-exports": "1.52.0", - "minimist": "^1.2.0" + "minimist": "^1.2.0", + "nodejs-file-downloader": "4.13.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/scripts/download-catalog.js b/packages/core/scripts/download-catalog.js new file mode 100644 index 0000000000000..11c9fb1be7a1a --- /dev/null +++ b/packages/core/scripts/download-catalog.js @@ -0,0 +1,26 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +const { Downloader } = require('nodejs-file-downloader'); + +new Downloader({ + url: 'https://schemastore.org/api/json/catalog.json', + directory: './lib/browser', + fileName: 'catalog.json', + timeout: 60000, + cloneFiles: false +}).download(); + diff --git a/packages/core/src/browser/json-schema-store.ts b/packages/core/src/browser/json-schema-store.ts index 729d14bfa1c45..e9401c3f191e9 100644 --- a/packages/core/src/browser/json-schema-store.ts +++ b/packages/core/src/browser/json-schema-store.ts @@ -18,9 +18,7 @@ import { injectable, inject, named } from 'inversify'; import { ContributionProvider } from '../common/contribution-provider'; import { FrontendApplicationContribution } from './frontend-application-contribution'; import { MaybePromise } from '../common'; -import { Endpoint } from './endpoint'; import { timeout, Deferred } from '../common/promise-util'; -import { RequestContext, RequestService } from '@theia/request'; export interface JsonSchemaConfiguration { fileMatch: string | string[]; @@ -95,16 +93,9 @@ export class JsonSchemaStore implements FrontendApplicationContribution { @injectable() export class DefaultJsonSchemaContribution implements JsonSchemaContribution { - - @inject(RequestService) - protected readonly requestService: RequestService; - - protected readonly jsonSchemaUrl = `${new Endpoint().httpScheme}//schemastore.org/api/json/catalog.json`; - async registerSchemas(context: JsonSchemaRegisterContext): Promise { - const response = await this.requestService.request({ url: this.jsonSchemaUrl }); - const schemas = RequestContext.asJson<{ schemas: DefaultJsonSchemaContribution.SchemaData[] }>(response).schemas; - for (const s of schemas) { + const catalog = require('./catalog.json') as { schemas: DefaultJsonSchemaContribution.SchemaData[] }; + for (const s of catalog.schemas) { if (s.fileMatch) { context.registerSchema({ fileMatch: s.fileMatch, diff --git a/yarn.lock b/yarn.lock index f38c8d1ebaa33..290518ecbc8d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5839,6 +5839,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.15.4: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + font-awesome@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -8613,6 +8618,16 @@ node-ssh@^12.0.1: shell-escape "^0.2.0" ssh2 "^1.5.0" +nodejs-file-downloader@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/nodejs-file-downloader/-/nodejs-file-downloader-4.13.0.tgz#da87c30081de5ff4e8b864062c98cdec03e66ad0" + integrity sha512-nI2fKnmJWWFZF6SgMPe1iBodKhfpztLKJTtCtNYGhm/9QXmWa/Pk9Sv00qHgzEvNLe1x7hjGDRor7gcm/ChaIQ== + dependencies: + follow-redirects "^1.15.6" + https-proxy-agent "^5.0.0" + mime-types "^2.1.27" + sanitize-filename "^1.6.3" + noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -10374,6 +10389,13 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sanitize-filename@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + sax@>=0.6.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" @@ -11451,6 +11473,13 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== + dependencies: + utf8-byte-length "^1.0.1" + ts-api-utils@^1.0.1: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -11890,6 +11919,11 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" +utf8-byte-length@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e" + integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From c40f10c055c87833ca134f90049861f97b23e4da Mon Sep 17 00:00:00 2001 From: Vladimir Piskarev Date: Tue, 27 Aug 2024 17:56:19 +0300 Subject: [PATCH 347/441] Add support for reverting a composite saveable (#14079) --- packages/core/src/browser/saveable.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index e1a413b3fab0e..fff0ae99a6297 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -135,6 +135,10 @@ export class CompositeSaveable implements Saveable { await Promise.all(this.saveables.map(saveable => saveable.save(options))); } + async revert(options?: Saveable.RevertOptions): Promise { + await Promise.all(this.saveables.map(saveable => saveable.revert?.(options))); + } + get saveables(): readonly Saveable[] { return Array.from(this.saveablesMap.keys()); } From 30fc45c3ea5368689e22a0d0fa1b9e6d253317d1 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Tue, 27 Aug 2024 18:21:14 +0200 Subject: [PATCH 348/441] Support the menu contribution point "testing/profiles/context" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #14013 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder Co-authored-by: Thomas Mäder --- .../menu/sample-electron-menu-module.ts | 5 +- .../core/src/browser/context-key-service.ts | 6 +- .../tab-bar-toolbar-menu-adapters.ts | 4 +- .../tab-bar-toolbar-registry.ts | 51 ++--- .../tab-bar-toolbar/tab-bar-toolbar-types.ts | 186 ++++++------------ .../shell/tab-bar-toolbar/tab-bar-toolbar.tsx | 65 +++--- packages/core/src/browser/style/tabs.css | 37 ++-- .../core/src/browser/style/view-container.css | 7 - packages/core/src/browser/view-container.ts | 4 +- packages/core/src/common/menu/menu-types.ts | 1 + .../menu/electron-main-menu-factory.ts | 13 +- .../src/electron-main/electron-api-main.ts | 15 +- ...debug-frontend-application-contribution.ts | 4 +- packages/git/src/browser/git-contribution.ts | 2 +- .../src/browser/monaco-context-key-service.ts | 4 +- .../src/browser/navigator-contribution.ts | 4 +- .../menus/menus-contribution-handler.ts | 3 +- .../menus/plugin-menu-command-adapter.ts | 1 + .../menus/vscode-theia-menu-mappings.ts | 4 +- .../browser/view/test-view-contribution.ts | 9 + .../toolbar/src/browser/toolbar-controller.ts | 3 +- .../toolbar/src/browser/toolbar-interfaces.ts | 4 +- packages/toolbar/src/browser/toolbar.tsx | 10 +- 23 files changed, 194 insertions(+), 248 deletions(-) diff --git a/examples/api-samples/src/electron-browser/menu/sample-electron-menu-module.ts b/examples/api-samples/src/electron-browser/menu/sample-electron-menu-module.ts index d9f7e657660a7..d8e3e75183e2a 100644 --- a/examples/api-samples/src/electron-browser/menu/sample-electron-menu-module.ts +++ b/examples/api-samples/src/electron-browser/menu/sample-electron-menu-module.ts @@ -29,12 +29,13 @@ class SampleElectronMainMenuFactory extends ElectronMainMenuFactory { protected override fillMenuTemplate(parentItems: MenuDto[], menu: MenuNode, args: unknown[] = [], - options: ElectronMenuOptions + options: ElectronMenuOptions, + skipRoot: boolean ): MenuDto[] { if (menu instanceof PlaceholderMenuNode) { parentItems.push({ label: menu.label, enabled: false, visible: true }); } else { - super.fillMenuTemplate(parentItems, menu, args, options); + super.fillMenuTemplate(parentItems, menu, args, options, skipRoot); } return parentItems; } diff --git a/packages/core/src/browser/context-key-service.ts b/packages/core/src/browser/context-key-service.ts index 0bb30ad313df4..c250c18999948 100644 --- a/packages/core/src/browser/context-key-service.ts +++ b/packages/core/src/browser/context-key-service.ts @@ -16,6 +16,7 @@ import { injectable } from 'inversify'; import { Emitter, Event } from '../common/event'; +import { Disposable } from '../common'; export type ContextKeyValue = null | undefined | boolean | number | string | Array @@ -83,11 +84,10 @@ export interface ContextKeyService extends ContextMatcher { setContext(key: string, value: unknown): void; } -export type ScopedValueStore = Omit; +export type ScopedValueStore = Omit & Disposable; @injectable() export class ContextKeyServiceDummyImpl implements ContextKeyService { - protected readonly onDidChangeEmitter = new Emitter(); readonly onDidChange = this.onDidChangeEmitter.event; protected fireDidChange(event: ContextKeyChangeEvent): void { @@ -122,7 +122,7 @@ export class ContextKeyServiceDummyImpl implements ContextKeyService { /** * Details should implemented by an extension, e.g. by the monaco extension. */ - createScoped(target: HTMLElement): ContextKeyService { + createScoped(target: HTMLElement): ScopedValueStore { return this; } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts index 1af64ee75bc75..76edc12e0a1f9 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts @@ -15,11 +15,11 @@ // ***************************************************************************** import { MenuNode, MenuPath } from '../../../common'; -import { NAVIGATION, TabBarToolbarItem } from './tab-bar-toolbar-types'; +import { NAVIGATION, RenderedToolbarItem } from './tab-bar-toolbar-types'; export const TOOLBAR_WRAPPER_ID_SUFFIX = '-as-tabbar-toolbar-item'; -export class ToolbarMenuNodeWrapper implements TabBarToolbarItem { +export class ToolbarMenuNodeWrapper implements RenderedToolbarItem { constructor(protected readonly menuNode: MenuNode, readonly group?: string, readonly menuPath?: MenuPath) { } get id(): string { return this.menuNode.id + TOOLBAR_WRAPPER_ID_SUFFIX; } get command(): string { return this.menuNode.command ?? ''; }; diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts index e3946be66d197..5851830895b23 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts @@ -17,11 +17,11 @@ import debounce = require('lodash.debounce'); import { inject, injectable, named } from 'inversify'; // eslint-disable-next-line max-len -import { CommandMenuNode, CommandRegistry, CompoundMenuNode, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuNode, MenuPath } from '../../../common'; +import { CommandRegistry, ContributionProvider, Disposable, DisposableCollection, Emitter, Event, MenuModelRegistry, MenuNode, MenuPath } from '../../../common'; import { ContextKeyService } from '../../context-key-service'; import { FrontendApplicationContribution } from '../../frontend-application-contribution'; import { Widget } from '../../widgets'; -import { AnyToolbarItem, ConditionalToolbarItem, MenuDelegate, MenuToolbarItem, ReactTabBarToolbarItem, TabBarToolbarItem } from './tab-bar-toolbar-types'; +import { MenuDelegate, ReactTabBarToolbarItem, RenderedToolbarItem, TabBarToolbarItem } from './tab-bar-toolbar-types'; import { ToolbarMenuNodeWrapper } from './tab-bar-toolbar-menu-adapters'; /** @@ -75,7 +75,7 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { * * @param item the item to register. */ - registerItem(item: TabBarToolbarItem | ReactTabBarToolbarItem): Disposable { + registerItem(item: RenderedToolbarItem | ReactTabBarToolbarItem): Disposable { const { id } = item; if (this.items.has(id)) { throw new Error(`A toolbar item is already registered with the '${id}' ID.`); @@ -110,24 +110,17 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { for (const delegate of this.menuDelegates.values()) { if (delegate.isVisible(widget)) { const menu = this.menuRegistry.getMenu(delegate.menuPath); - const children = CompoundMenuNode.getFlatChildren(menu.children); - for (const child of children) { + for (const child of menu.children) { if (!child.when || this.contextKeyService.match(child.when, widget.node)) { if (child.children) { for (const grandchild of child.children) { if (!grandchild.when || this.contextKeyService.match(grandchild.when, widget.node)) { - if (CommandMenuNode.is(grandchild)) { - result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, delegate.menuPath)); - } else if (CompoundMenuNode.is(grandchild)) { - let menuPath; - if (menuPath = this.menuRegistry.getPath(grandchild)) { - result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, menuPath)); - } - } + const menuPath = this.menuRegistry.getPath(grandchild); + result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, menuPath)); } } } else if (child.command) { - result.push(new ToolbarMenuNodeWrapper(child, '', delegate.menuPath)); + result.push(new ToolbarMenuNodeWrapper(child, '')); } } } @@ -145,15 +138,17 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { * @returns `false` if the `item` should be suppressed, otherwise `true` */ protected isItemVisible(item: TabBarToolbarItem | ReactTabBarToolbarItem, widget: Widget): boolean { - if (TabBarToolbarItem.is(item) && item.command && !this.isTabBarToolbarItemVisible(item, widget)) { + if (!this.isConditionalItemVisible(item, widget)) { return false; } - if (MenuToolbarItem.is(item) && !this.isMenuToolbarItemVisible(item, widget)) { + + if (item.command && !this.commandRegistry.isVisible(item.command, widget)) { return false; } - if (AnyToolbarItem.isConditional(item) && !this.isConditionalItemVisible(item, widget)) { + if (item.menuPath && !this.isNonEmptyMenu(item, widget)) { return false; } + // The item is not vetoed. Accept it return true; } @@ -166,7 +161,7 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { * @param widget the widget that is updating the toolbar * @returns `false` if the `item` should be suppressed, otherwise `true` */ - protected isConditionalItemVisible(item: ConditionalToolbarItem, widget: Widget): boolean { + protected isConditionalItemVisible(item: TabBarToolbarItem, widget: Widget): boolean { if (item.isVisible && !item.isVisible(widget)) { return false; } @@ -176,19 +171,6 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { return true; } - /** - * Query whether a tab-bar toolbar `item` that has a command should be shown in the toolbar. - * This implementation returns `false` if the `item`'s command is not visible in the - * `widget` according to the command registry. - * - * @param item a tab-bar toolbar item that has a non-empty `command` - * @param widget the widget that is updating the toolbar - * @returns `false` if the `item` should be suppressed, otherwise `true` - */ - protected isTabBarToolbarItemVisible(item: TabBarToolbarItem, widget: Widget): boolean { - return this.commandRegistry.isVisible(item.command, widget); - } - /** * Query whether a menu toolbar `item` should be shown in the toolbar. * This implementation returns `false` if the `item` does not have any actual menu to show. @@ -197,7 +179,10 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { * @param widget the widget that is updating the toolbar * @returns `false` if the `item` should be suppressed, otherwise `true` */ - protected isMenuToolbarItemVisible(item: MenuToolbarItem, widget: Widget): boolean { + isNonEmptyMenu(item: TabBarToolbarItem, widget: Widget | undefined): boolean { + if (!item.menuPath) { + return false; + } const menu = this.menuRegistry.getMenu(item.menuPath); const isVisible: (node: MenuNode) => boolean = node => node.children?.length @@ -220,7 +205,7 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { } } - registerMenuDelegate(menuPath: MenuPath, when?: string | ((widget: Widget) => boolean)): Disposable { + registerMenuDelegate(menuPath: MenuPath, when?: ((widget: Widget) => boolean)): Disposable { const id = this.toElementId(menuPath); if (!this.menuDelegates.has(id)) { const isVisible: MenuDelegate['isVisible'] = !when diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts index 00ad879b4d761..e59f9f63c2384 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import * as React from 'react'; -import { ArrayUtils, Event, isFunction, isObject, isString, MenuPath } from '../../../common'; +import { ArrayUtils, Event, isFunction, isObject, MenuPath } from '../../../common'; import { Widget } from '../../widgets'; /** Items whose group is exactly 'navigation' will be rendered inline. */ @@ -32,58 +32,21 @@ export namespace TabBarDelegator { } } -interface RegisteredToolbarItem { +export type TabBarToolbarItem = RenderedToolbarItem | ReactTabBarToolbarItem; + +/** + * Representation of an item in the tab + */ +export interface TabBarToolbarItemBase { /** * The unique ID of the toolbar item. */ id: string; -} - -interface RenderedToolbarItem { - /** - * Optional icon for the item. - */ - icon?: string | (() => string); - - /** - * Optional text of the item. - * - * Strings in the format `$(iconIdentifier~animationType) will be treated as icon references. - * If the iconIdentifier begins with fa-, Font Awesome icons will be used; otherwise it will be treated as Codicon name. - * - * You can find Codicon classnames here: https://microsoft.github.io/vscode-codicons/dist/codicon.html - * You can find Font Awesome classnames here: http://fontawesome.io/icons/ - * The type of animation can be either `spin` or `pulse`. - */ - text?: string; - - /** - * Optional tooltip for the item. - */ - tooltip?: string; -} - -interface SelfRenderingToolbarItem { - render(widget?: Widget): React.ReactNode; -} - -interface ExecutableToolbarItem { /** * The command to execute when the item is selected. */ - command: string; -} - -export interface MenuToolbarItem { - /** - * A menu path with which this item is associated. - * If accompanied by a command, this data will be passed to the {@link MenuCommandExecutor}. - * If no command is present, this menu will be opened. - */ - menuPath: MenuPath; -} + command?: string; -export interface ConditionalToolbarItem { /** * https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts */ @@ -98,61 +61,67 @@ export interface ConditionalToolbarItem { * Note: currently, each item of the container toolbar will be re-rendered if any of the items have changed. */ onDidChange?: Event; -} -interface InlineToolbarItemMetadata { /** * Priority among the items. Can be negative. The smaller the number the left-most the item will be placed in the toolbar. It is `0` by default. */ priority?: number; - group: 'navigation' | undefined; -} - -interface MenuToolbarItemMetadata { + group?: string; /** - * Optional group for the item. Default `navigation`. - * `navigation` group will be inlined, while all the others will appear in the `...` dropdown. - * A group in format `submenu_group_1/submenu 1/.../submenu_group_n/ submenu n/item_group` means that the item will be located in a submenu(s) of the `...` dropdown. - * The submenu's title is named by the submenu section name, e.g. `group//subgroup`. + * A menu path with which this item is associated. + * If accompanied by a command, this data will be passed to the {@link MenuCommandExecutor}. + * If no command is present, this menu will be opened. */ - group: string; + menuPath?: MenuPath; + contextKeyOverlays?: Record; /** * Optional ordering string for placing the item within its group */ order?: string; } -/** - * Representation of an item in the tab - */ -export interface TabBarToolbarItem extends RegisteredToolbarItem, - ExecutableToolbarItem, - RenderedToolbarItem, - Omit, - Pick, - Partial, - Partial { } +export interface RenderedToolbarItem extends TabBarToolbarItemBase { + /** + * Optional icon for the item. + */ + icon?: string | (() => string); + + /** + * Optional text of the item. + * + * Strings in the format `$(iconIdentifier~animationType) will be treated as icon references. + * If the iconIdentifier begins with fa-, Font Awesome icons will be used; otherwise it will be treated as Codicon name. + * + * You can find Codicon classnames here: https://microsoft.github.io/vscode-codicons/dist/codicon.html + * You can find Font Awesome classnames here: http://fontawesome.io/icons/ + * The type of animation can be either `spin` or `pulse`. + */ + text?: string; + + /** + * Optional tooltip for the item. + */ + tooltip?: string; +} /** * Tab-bar toolbar item backed by a `React.ReactNode`. * Unlike the `TabBarToolbarItem`, this item is not connected to the command service. */ -export interface ReactTabBarToolbarItem extends RegisteredToolbarItem, - SelfRenderingToolbarItem, - ConditionalToolbarItem, - Pick, - Pick, 'group'> { } - -export interface AnyToolbarItem extends RegisteredToolbarItem, - Partial, - Partial, - Partial, - Partial, - Partial, - Pick, - Partial { } - -export interface MenuDelegate extends MenuToolbarItem, Required> { } +export interface ReactTabBarToolbarItem extends TabBarToolbarItemBase { + render(widget?: Widget): React.ReactNode; +} + +export namespace ReactTabBarToolbarItem { + export function is(item: TabBarToolbarItem): item is ReactTabBarToolbarItem { + return isObject(item) && typeof item.render === 'function'; + } +} + +export interface MenuDelegate { + menuPath: MenuPath; + isVisible(widget?: Widget): boolean; +} export namespace TabBarToolbarItem { @@ -160,48 +129,17 @@ export namespace TabBarToolbarItem { * Compares the items by `priority` in ascending. Undefined priorities will be treated as `0`. */ export const PRIORITY_COMPARATOR = (left: TabBarToolbarItem, right: TabBarToolbarItem) => { - const leftGroup = left.group ?? NAVIGATION; - const rightGroup = right.group ?? NAVIGATION; - if (leftGroup === NAVIGATION && rightGroup !== NAVIGATION) { return ArrayUtils.Sort.LeftBeforeRight; } - if (rightGroup === NAVIGATION && leftGroup !== NAVIGATION) { return ArrayUtils.Sort.RightBeforeLeft; } - if (leftGroup !== rightGroup) { return leftGroup.localeCompare(rightGroup); } + const leftGroup: string = left.group ?? NAVIGATION; + const rightGroup: string = right.group ?? NAVIGATION; + if (leftGroup === NAVIGATION && rightGroup !== NAVIGATION) { + return ArrayUtils.Sort.LeftBeforeRight; + } + if (rightGroup === NAVIGATION && leftGroup !== NAVIGATION) { + return ArrayUtils.Sort.RightBeforeLeft; + } + if (leftGroup !== rightGroup) { + return leftGroup.localeCompare(rightGroup); + } return (left.priority || 0) - (right.priority || 0); }; - - export function is(arg: unknown): arg is TabBarToolbarItem { - return isObject(arg) && isString(arg.command); - } - -} - -export namespace MenuToolbarItem { - /** - * Type guard for a toolbar item that actually is a menu item, amongst - * the other kinds of item that it may also be. - * - * @param item a toolbar item - * @returns whether the `item` is a menu item - */ - export function is(item: T): item is T & MenuToolbarItem { - return Array.isArray(item.menuPath); - } - - export function getMenuPath(item: AnyToolbarItem): MenuPath | undefined { - return Array.isArray(item.menuPath) ? item.menuPath : undefined; - } -} - -export namespace AnyToolbarItem { - /** - * Type guard for a toolbar item that actually manifests any of the - * features of a conditional toolbar item. - * - * @param item a toolbar item - * @returns whether the `item` is a conditional item - */ - export function isConditional(item: T): item is T & ConditionalToolbarItem { - return 'isVisible' in item && typeof item.isVisible === 'function' - || 'onDidChange' in item && typeof item.onDidChange === 'function' - || 'when' in item && typeof item.when === 'string'; - } } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx index eda4d12ad9956..6d4f21b3d3264 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx @@ -16,13 +16,13 @@ import { inject, injectable, postConstruct } from 'inversify'; import * as React from 'react'; -import { ContextKeyService } from '../../context-key-service'; +import { ContextKeyService, ContextMatcher } from '../../context-key-service'; import { CommandRegistry, Disposable, DisposableCollection, MenuCommandExecutor, MenuModelRegistry, MenuPath, nls } from '../../../common'; import { Anchor, ContextMenuAccess, ContextMenuRenderer } from '../../context-menu-renderer'; import { LabelIcon, LabelParser } from '../../label-parser'; import { ACTION_ITEM, codicon, ReactWidget, Widget } from '../../widgets'; import { TabBarToolbarRegistry } from './tab-bar-toolbar-registry'; -import { AnyToolbarItem, ReactTabBarToolbarItem, TabBarDelegator, TabBarToolbarItem, TAB_BAR_TOOLBAR_CONTEXT_MENU, MenuToolbarItem } from './tab-bar-toolbar-types'; +import { ReactTabBarToolbarItem, TabBarDelegator, TabBarToolbarItem, TAB_BAR_TOOLBAR_CONTEXT_MENU, RenderedToolbarItem } from './tab-bar-toolbar-types'; import { KeybindingRegistry } from '../..//keybinding'; /** @@ -87,7 +87,7 @@ export class TabBarToolbar extends ReactWidget { const contextKeys = new Set(); for (const item of items.sort(TabBarToolbarItem.PRIORITY_COMPARATOR).reverse()) { - if ('command' in item) { + if (item.command) { this.commands.getAllHandlers(item.command).forEach(handler => { if (handler.onDidChangeEnabled) { this.toDisposeOnUpdateItems.push(handler.onDidChangeEnabled(() => this.maybeUpdate())); @@ -154,9 +154,13 @@ export class TabBarToolbar extends ReactWidget { this.keybindingContextKeys.clear(); return {this.renderMore()} - {[...this.inline.values()].map(item => TabBarToolbarItem.is(item) - ? (MenuToolbarItem.is(item) && !item.command ? this.renderMenuItem(item) : this.renderItem(item)) - : item.render(this.current))} + {[...this.inline.values()].map(item => { + if (ReactTabBarToolbarItem.is(item)) { + return item.render(this.current); + } else { + return (item.menuPath && this.toolbarRegistry.isNonEmptyMenu(item, this.current) ? this.renderMenuItem(item) : this.renderItem(item)); + } + })} ; } @@ -180,7 +184,7 @@ export class TabBarToolbar extends ReactWidget { return result; } - protected renderItem(item: AnyToolbarItem): React.ReactNode { + protected renderItem(item: RenderedToolbarItem): React.ReactNode { let innerText = ''; const classNames = []; const command = item.command ? this.commands.getCommand(item.command) : undefined; @@ -222,13 +226,13 @@ export class TabBarToolbar extends ReactWidget { onMouseUp={this.onMouseUpEvent} onMouseOut={this.onMouseUpEvent} >
        this.executeCommand(e, item)} title={tooltip}>{innerText}
        ; } - protected isEnabled(item: AnyToolbarItem): boolean { + protected isEnabled(item: TabBarToolbarItem): boolean { if (!!item.command) { return this.commandIsEnabled(item.command) && this.evaluateWhenClause(item.when); } else { @@ -236,7 +240,7 @@ export class TabBarToolbar extends ReactWidget { } } - protected getToolbarItemClassNames(item: AnyToolbarItem): string[] { + protected getToolbarItemClassNames(item: TabBarToolbarItem): string[] { const classNames = [TabBarToolbar.Styles.TAB_BAR_TOOLBAR_ITEM]; if (item.command) { if (this.isEnabled(item)) { @@ -279,7 +283,7 @@ export class TabBarToolbar extends ReactWidget { if (subpath) { toDisposeOnHide.push(this.menus.linkSubmenu(TAB_BAR_TOOLBAR_CONTEXT_MENU, subpath)); } else { - for (const item of this.more.values() as IterableIterator) { + for (const item of this.more.values()) { if (item.menuPath && !item.command) { toDisposeOnHide.push(this.menus.linkSubmenu(TAB_BAR_TOOLBAR_CONTEXT_MENU, item.menuPath, undefined, item.group)); } else if (item.command) { @@ -293,10 +297,10 @@ export class TabBarToolbar extends ReactWidget { } } toDisposeOnHide.push(this.menus.registerMenuAction([...TAB_BAR_TOOLBAR_CONTEXT_MENU, ...item.group!.split('/')], { - label: item.tooltip, + label: (item as RenderedToolbarItem).tooltip, commandId: item.command, when: item.when, - order: item.order, + order: item.order })); } } @@ -318,14 +322,26 @@ export class TabBarToolbar extends ReactWidget { * @param item a toolbar item that is a menu item * @returns the rendered toolbar item */ - protected renderMenuItem(item: TabBarToolbarItem & MenuToolbarItem): React.ReactNode { - const icon = typeof item.icon === 'function' ? item.icon() : item.icon ?? 'ellipsis'; + protected renderMenuItem(item: RenderedToolbarItem): React.ReactNode { + const command = item.command ? this.commands.getCommand(item.command) : undefined; + const icon = (typeof item.icon === 'function' && item.icon()) || item.icon as string || (command && command.iconClass) || 'ellipsis'; + + let contextMatcher: ContextMatcher = this.contextKeyService; + if (item.contextKeyOverlays) { + contextMatcher = this.contextKeyService.createOverlay(Object.keys(item.contextKeyOverlays).map(key => [key, item.contextKeyOverlays![key]])); + } + return
        -
        -
        + > +
        this.executeCommand(e, item)} + /> +
        this.showPopupMenu(item.menuPath!, event, contextMatcher)}> +
        +
        +
        ; } @@ -336,11 +352,11 @@ export class TabBarToolbar extends ReactWidget { * @param menuPath the path of the registered menu to show * @param event the mouse event triggering the menu */ - protected showPopupMenu = (menuPath: MenuPath, event: React.MouseEvent) => { + protected showPopupMenu = (menuPath: MenuPath, event: React.MouseEvent, contextMatcher: ContextMatcher) => { event.stopPropagation(); event.preventDefault(); const anchor = this.toAnchor(event); - this.renderPopupMenu(menuPath, anchor); + this.renderPopupMenu(menuPath, anchor, contextMatcher); }; /** @@ -350,7 +366,7 @@ export class TabBarToolbar extends ReactWidget { * @param anchor a description of where to render the menu * @returns platform-specific access to the rendered context menu */ - protected renderPopupMenu(menuPath: MenuPath, anchor: Anchor): ContextMenuAccess { + protected renderPopupMenu(menuPath: MenuPath, anchor: Anchor, contextMatcher: ContextMatcher): ContextMenuAccess { const toDisposeOnHide = new DisposableCollection(); this.addClass('menu-open'); toDisposeOnHide.push(Disposable.create(() => this.removeClass('menu-open'))); @@ -360,6 +376,7 @@ export class TabBarToolbar extends ReactWidget { args: [this.current], anchor, context: this.current?.node, + contextKeyService: contextMatcher, onHide: () => toDisposeOnHide.dispose() }); } @@ -380,12 +397,10 @@ export class TabBarToolbar extends ReactWidget { return whenClause ? this.contextKeyService.match(whenClause, this.current?.node) : true; } - protected executeCommand = (e: React.MouseEvent) => { + protected executeCommand(e: React.MouseEvent, item: TabBarToolbarItem): void { e.preventDefault(); e.stopPropagation(); - const item: AnyToolbarItem | undefined = this.inline.get(e.currentTarget.id); - if (!item || !this.isEnabled(item)) { return; } diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css index 18db27f235de4..d81927dca9e90 100644 --- a/packages/core/src/browser/style/tabs.css +++ b/packages/core/src/browser/style/tabs.css @@ -464,11 +464,16 @@ } .p-TabBar-toolbar .item { - display: flex; - align-items: center; - margin-left: 4px; /* `padding` + `margin-right` from the container toolbar */ opacity: var(--theia-mod-disabled-opacity); cursor: default; + display: flex; + flex-direction: row; + column-gap: 0px; + align-items: centery; +} + +.p-TabBar-toolbar .item>div { + height: 100%; } .p-TabBar-toolbar .item.enabled { @@ -476,10 +481,6 @@ cursor: pointer; } -.p-TabBar-toolbar .item.enabled .action-label::before { - display: flex; -} - .p-TabBar-toolbar :not(.item.enabled) .action-label { background: transparent; cursor: default; @@ -491,8 +492,8 @@ } .p-TabBar-toolbar .item > div { - height: 18px; - width: 18px; + line-height: calc(var(--theia-icon-size)+2px); + height: calc(var(--theia-icon-size)+2px); background-repeat: no-repeat; line-height: 18px; } @@ -522,25 +523,13 @@ background: var(--theia-icon-close) no-repeat; } -/** Configure layout of a toolbar item that shows a pop-up menu. */ -.p-TabBar-toolbar .item.menu { - display: grid; -} - -/** The elements of the item that shows a pop-up menu are stack atop one other. */ -.p-TabBar-toolbar .item.menu > div { - grid-area: 1 / 1; -} - /** * The chevron for the pop-up menu indication is shrunk and * stuffed in the bottom-right corner. */ -.p-TabBar-toolbar .item.menu > .chevron { - scale: 50%; - align-self: end; - justify-self: end; - translate: 5px 3px; +.p-TabBar-toolbar .item.menu .chevron { + font-size: 8px; + vertical-align: bottom; } #theia-main-content-panel diff --git a/packages/core/src/browser/style/view-container.css b/packages/core/src/browser/style/view-container.css index f3e6ce00bc5d2..59a9f9b5bd7e6 100644 --- a/packages/core/src/browser/style/view-container.css +++ b/packages/core/src/browser/style/view-container.css @@ -168,13 +168,6 @@ padding-right: calc(var(--theia-ui-padding) * 2 / 3); } -.theia-view-container-part-title .item > div { - height: var(--theia-icon-size); - width: var(--theia-icon-size); - background-size: var(--theia-icon-size); - line-height: var(--theia-icon-size); -} - .theia-view-container-part-title { display: none; } diff --git a/packages/core/src/browser/view-container.ts b/packages/core/src/browser/view-container.ts index b45fe54169901..ef39d86d12980 100644 --- a/packages/core/src/browser/view-container.ts +++ b/packages/core/src/browser/view-container.ts @@ -29,7 +29,7 @@ import { MAIN_AREA_ID, BOTTOM_AREA_ID } from './shell/theia-dock-panel'; import { FrontendApplicationStateService } from './frontend-application-state'; import { ContextMenuRenderer, Anchor } from './context-menu-renderer'; import { parseCssMagnitude } from './browser'; -import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar, TabBarDelegator, TabBarToolbarItem } from './shell/tab-bar-toolbar'; +import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar, TabBarDelegator, RenderedToolbarItem } from './shell/tab-bar-toolbar'; import { isEmpty, isObject, nls } from '../common'; import { WidgetManager } from './widget-manager'; import { Key } from './keys'; @@ -324,7 +324,7 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica return 'view'; } - protected registerToolbarItem(commandId: string, options?: Partial>): void { + protected registerToolbarItem(commandId: string, options?: Partial>): void { const newId = `${this.id}-tabbar-toolbar-${commandId}`; const existingHandler = this.commandRegistry.getAllHandlers(commandId)[0]; const existingCommand = this.commandRegistry.getCommand(commandId); diff --git a/packages/core/src/common/menu/menu-types.ts b/packages/core/src/common/menu/menu-types.ts index 23081a88e3ad1..b3a443816a010 100644 --- a/packages/core/src/common/menu/menu-types.ts +++ b/packages/core/src/common/menu/menu-types.ts @@ -68,6 +68,7 @@ export interface MenuNodeBase extends MenuNodeMetadata, MenuNodeRenderingData { * A menu entry representing an action, e.g. "New File". */ export interface MenuAction extends MenuNodeRenderingData, Pick { + /** * The command to execute. */ diff --git a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts index 68a305c29b094..4f0b97a51a113 100644 --- a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts +++ b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts @@ -117,7 +117,7 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { const maxWidget = document.getElementsByClassName(MAXIMIZED_CLASS); if (preference === 'visible' || (preference === 'classic' && maxWidget.length === 0)) { const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR); - this._menu = this.fillMenuTemplate([], menuModel, [], { honorDisabled: false, rootMenuPath: MAIN_MENU_BAR }); + this._menu = this.fillMenuTemplate([], menuModel, [], { honorDisabled: false, rootMenuPath: MAIN_MENU_BAR }, false); if (isOSX) { this._menu.unshift(this.createOSXMenu()); } @@ -130,13 +130,14 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { createElectronContextMenu(menuPath: MenuPath, args?: any[], context?: HTMLElement, contextKeyService?: ContextMatcher, skipSingleRootNode?: boolean): MenuDto[] { const menuModel = skipSingleRootNode ? this.menuProvider.removeSingleRootNode(this.menuProvider.getMenu(menuPath), menuPath) : this.menuProvider.getMenu(menuPath); - return this.fillMenuTemplate([], menuModel, args, { showDisabled: true, context, rootMenuPath: menuPath, contextKeyService }); + return this.fillMenuTemplate([], menuModel, args, { showDisabled: true, context, rootMenuPath: menuPath, contextKeyService }, true); } protected fillMenuTemplate(parentItems: MenuDto[], menu: MenuNode, args: unknown[] = [], - options: ElectronMenuOptions + options: ElectronMenuOptions, + skipRoot: boolean ): MenuDto[] { const showDisabled = options?.showDisabled !== false; const honorDisabled = options?.honorDisabled !== false; @@ -148,13 +149,13 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { } const children = CompoundMenuNode.getFlatChildren(menu.children); const myItems: MenuDto[] = []; - children.forEach(child => this.fillMenuTemplate(myItems, child, args, options)); + children.forEach(child => this.fillMenuTemplate(myItems, child, args, options, false)); if (myItems.length === 0) { return parentItems; } - if (role === CompoundMenuNodeRole.Submenu) { + if (!skipRoot && role === CompoundMenuNodeRole.Submenu) { parentItems.push({ label: menu.label, submenu: myItems }); - } else if (role === CompoundMenuNodeRole.Group && menu.id !== 'inline') { + } else { if (parentItems.length && parentItems[parentItems.length - 1].type !== 'separator') { parentItems.push({ type: 'separator' }); } diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index d5966536dd470..b6a600e4d3a6d 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -241,9 +241,20 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { }); } + private isASCI(accelerator: string | undefined): boolean { + if (typeof accelerator !== 'string') { + return false; + } + for (let i = 0; i < accelerator.length; i++) { + if (accelerator.charCodeAt(i) > 127) { + return false; + } + } + return true; + } + fromMenuDto(sender: WebContents, menuId: number, menuDto: InternalMenuDto[]): MenuItemConstructorOptions[] { return menuDto.map(dto => { - const result: MenuItemConstructorOptions = { id: dto.id, label: dto.label, @@ -252,7 +263,7 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { enabled: dto.enabled, visible: dto.visible, role: dto.role, - accelerator: dto.accelerator + accelerator: this.isASCI(dto.accelerator) ? dto.accelerator : undefined }; if (dto.submenu) { result.submenu = this.fromMenuDto(sender, menuId, dto.submenu); diff --git a/packages/debug/src/browser/debug-frontend-application-contribution.ts b/packages/debug/src/browser/debug-frontend-application-contribution.ts index 5e833a8a39f29..51401d39a53ec 100644 --- a/packages/debug/src/browser/debug-frontend-application-contribution.ts +++ b/packages/debug/src/browser/debug-frontend-application-contribution.ts @@ -42,7 +42,7 @@ import { DebugConsoleContribution } from './console/debug-console-contribution'; import { DebugService } from '../common/debug-service'; import { DebugSchemaUpdater } from './debug-schema-updater'; import { DebugPreferences } from './debug-preferences'; -import { TabBarToolbarContribution, TabBarToolbarRegistry, TabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { TabBarToolbarContribution, TabBarToolbarRegistry, RenderedToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { DebugWatchWidget } from './view/debug-watch-widget'; import { DebugWatchExpression } from './view/debug-watch-expression'; import { DebugWatchManager } from './debug-watch-manager'; @@ -1079,7 +1079,7 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi registerToolbarItems(toolbar: TabBarToolbarRegistry): void { const onDidChangeToggleBreakpointsEnabled = new Emitter(); - const toggleBreakpointsEnabled: Mutable = { + const toggleBreakpointsEnabled: Mutable = { id: DebugCommands.TOGGLE_BREAKPOINTS_ENABLED.id, command: DebugCommands.TOGGLE_BREAKPOINTS_ENABLED.id, icon: codicon('activate-breakpoints'), diff --git a/packages/git/src/browser/git-contribution.ts b/packages/git/src/browser/git-contribution.ts index ad01af1e2567f..042f2f84cdf5e 100644 --- a/packages/git/src/browser/git-contribution.ts +++ b/packages/git/src/browser/git-contribution.ts @@ -659,7 +659,7 @@ export class GitContribution implements CommandContribution, MenuContribution, T tooltip: GIT_COMMANDS.INIT_REPOSITORY.label }); - const registerItem = (item: Mutable) => { + const registerItem = (item: Mutable) => { const commandId = item.command; const id = '__git.tabbar.toolbar.' + commandId; const command = this.commands.getCommand(commandId); diff --git a/packages/monaco/src/browser/monaco-context-key-service.ts b/packages/monaco/src/browser/monaco-context-key-service.ts index 3de29a0826c4c..38640ba236c00 100644 --- a/packages/monaco/src/browser/monaco-context-key-service.ts +++ b/packages/monaco/src/browser/monaco-context-key-service.ts @@ -109,9 +109,9 @@ export class MonacoContextKeyService implements TheiaContextKeyService { createScoped(target: HTMLElement): ScopedValueStore { const scoped = this.contextKeyService.createScoped(target); if (scoped instanceof AbstractContextKeyService) { - return scoped as AbstractContextKeyService & { createScoped(): ScopedValueStore }; + return scoped as unknown as ScopedValueStore; } - return this; + throw new Error('Could not created scoped value store'); } createOverlay(overlay: Iterable<[string, unknown]>): ContextMatcher { diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts index 3a41805dc16b6..956436fbafe2a 100644 --- a/packages/navigator/src/browser/navigator-contribution.ts +++ b/packages/navigator/src/browser/navigator-contribution.ts @@ -57,8 +57,8 @@ import { FileNavigatorFilter } from './navigator-filter'; import { WorkspaceNode } from './navigator-tree'; import { NavigatorContextKeyService } from './navigator-context-key-service'; import { + RenderedToolbarItem, TabBarToolbarContribution, - TabBarToolbarItem, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { FileSystemCommands } from '@theia/filesystem/lib/browser/filesystem-frontend-contribution'; @@ -588,7 +588,7 @@ export class FileNavigatorContribution extends AbstractViewContribution) => { + public registerMoreToolbarItem = (item: Mutable & { command: string }) => { const commandId = item.command; const id = 'navigator.tabbar.toolbar.' + commandId; const command = this.commandRegistry.getCommand(commandId); diff --git a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts index 9410875ce1f1c..ae3823e2a7c01 100644 --- a/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/menus/menus-contribution-handler.ts @@ -107,10 +107,11 @@ export class MenusContributionPointHandler { if (command) { toDispose.push(this.commandAdapter.addCommand(command)); targets.forEach(target => { + const node = new ActionMenuNode({ commandId: command, when: item.when, - order, + order }, this.commands); const parent = this.menuRegistry.getMenuNode(target, group); toDispose.push(parent.addNode(node)); diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index cc8be6f6fe2b2..ec91bc9ef7c95 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -108,6 +108,7 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { ['scm/resourceState/context', toScmArgs], ['scm/title', () => [this.toScmArg(this.scmService.selectedRepository)]], ['testing/message/context', toTestMessageArgs], + ['testing/profiles/context', noArgs], ['scm/change/title', (...args) => this.toScmChangeArgs(...args)], ['timeline/item/context', (...args) => this.toTimelineArgs(...args)], ['view/item/context', (...args) => this.toTreeArgs(...args)], diff --git a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts index 6bbc91f01bfa0..3a519199040f2 100644 --- a/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts +++ b/packages/plugin-ext/src/main/browser/menus/vscode-theia-menu-mappings.ts @@ -32,7 +32,7 @@ import { COMMENT_CONTEXT, COMMENT_THREAD_CONTEXT, COMMENT_TITLE } from '../comme import { VIEW_ITEM_CONTEXT_MENU } from '../view/tree-view-widget'; import { WEBVIEW_CONTEXT_MENU, WebviewWidget } from '../webview/webview'; import { EDITOR_LINENUMBER_CONTEXT_MENU } from '@theia/editor/lib/browser/editor-linenumber-contribution'; -import { TEST_VIEW_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-view-contribution'; +import { PLUGIN_TEST_VIEW_TITLE_MENU, TEST_VIEW_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-view-contribution'; import { TEST_RUNS_CONTEXT_MENU } from '@theia/test/lib/browser/view/test-run-view-contribution'; import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; @@ -62,6 +62,7 @@ export const implementedVSCodeContributionPoints = [ 'timeline/item/context', 'testing/item/context', 'testing/message/context', + 'testing/profiles/context', 'view/item/context', 'view/title', 'webview/context', @@ -93,6 +94,7 @@ export const codeToTheiaMappings = new Map([ ['scm/title', [PLUGIN_SCM_TITLE_MENU]], ['testing/item/context', [TEST_VIEW_CONTEXT_MENU]], ['testing/message/context', [TEST_RUNS_CONTEXT_MENU]], + ['testing/profiles/context', [PLUGIN_TEST_VIEW_TITLE_MENU]], ['timeline/item/context', [TIMELINE_ITEM_CONTEXT_MENU]], ['view/item/context', [VIEW_ITEM_CONTEXT_MENU]], ['view/title', [PLUGIN_VIEW_TITLE_MENU]], diff --git a/packages/test/src/browser/view/test-view-contribution.ts b/packages/test/src/browser/view/test-view-contribution.ts index 7b672083dab52..37f123be2590d 100644 --- a/packages/test/src/browser/view/test-view-contribution.ts +++ b/packages/test/src/browser/view/test-view-contribution.ts @@ -26,6 +26,7 @@ import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/ import { NavigationLocation } from '@theia/editor/lib/browser/navigation/navigation-location'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileNavigatorCommands } from '@theia/navigator/lib/browser/file-navigator-commands'; +export const PLUGIN_TEST_VIEW_TITLE_MENU = ['plugin_test', 'title']; export namespace TestViewCommands { /** @@ -293,12 +294,20 @@ export class TestViewContribution extends AbstractViewContribution this.executeCommand(e, item)} onDragOver={this.handleOnDragEnter} onDragLeave={this.handleOnDragLeave} onContextMenu={this.handleContextMenu} @@ -279,7 +279,7 @@ export class ToolbarImpl extends TabBarToolbar { } protected override renderItem( - item: TabBarToolbarItem, + item: RenderedToolbarItem, ): React.ReactNode { const classNames = []; if (item.text) { @@ -290,7 +290,7 @@ export class ToolbarImpl extends TabBarToolbar { } } } - const command = this.commands.getCommand(item.command); + const command = this.commands.getCommand(item.command!); const iconClass = (typeof item.icon === 'function' && item.icon()) || item.icon || command?.iconClass; if (iconClass) { classNames.push(iconClass); From 2fe7d5d1f0ff2eb1846473b510539bb7ab9e05be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Tue, 27 Aug 2024 18:22:02 +0200 Subject: [PATCH 349/441] Bump API version to 1.92.2 (#14076) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- dev-packages/application-package/src/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/application-package/src/api.ts b/dev-packages/application-package/src/api.ts index 9df0ceecc069b..2b0276ac5d6f2 100644 --- a/dev-packages/application-package/src/api.ts +++ b/dev-packages/application-package/src/api.ts @@ -18,4 +18,4 @@ * The default supported API version the framework supports. * The version should be in the format `x.y.z`. */ -export const DEFAULT_SUPPORTED_API_VERSION = '1.91.1'; +export const DEFAULT_SUPPORTED_API_VERSION = '1.92.2'; From 754eb84caecc8e318def10c6b96e4d11ba7081b9 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Wed, 28 Aug 2024 08:20:08 +0200 Subject: [PATCH 350/441] Implement stubbed window#registerURIhandler (#13306) fixes #13169 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 3 +- .../src/application-props.ts | 11 ++- .../browser/frontend-application-module.ts | 1 - packages/core/src/browser/opener-service.ts | 14 +++- .../electron-browser/electron-uri-handler.ts | 42 ++++++++++ packages/core/src/electron-browser/preload.ts | 17 +++- .../window/electron-window-module.ts | 3 + .../window/external-app-open-handler.ts | 2 +- .../core/src/electron-common/electron-api.ts | 3 + .../src/electron-main/electron-api-main.ts | 21 ++++- .../electron-main-application.ts | 82 ++++++++++++++----- .../electron-main/theia-electron-window.ts | 5 ++ .../plugin-ext/src/common/plugin-api-rpc.ts | 18 +++- .../src/hosted/browser/hosted-plugin.ts | 7 +- .../src/main/browser/main-context.ts | 4 + .../browser/plugin-ext-frontend-module.ts | 2 +- .../plugin-ext/src/main/browser/uri-main.ts | 72 ++++++++++++++++ packages/plugin-ext/src/plugin/env.ts | 7 +- .../plugin-ext/src/plugin/plugin-context.ts | 5 +- .../plugin-ext/src/plugin/plugin-manager.ts | 3 +- packages/plugin-ext/src/plugin/uri-ext.ts | 60 ++++++++++++++ 21 files changed, 343 insertions(+), 39 deletions(-) create mode 100644 packages/core/src/electron-browser/electron-uri-handler.ts create mode 100644 packages/plugin-ext/src/main/browser/uri-main.ts create mode 100644 packages/plugin-ext/src/plugin/uri-ext.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 697912237e877..950e4dc037a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,15 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) - ## Unreleased +- [plugin] implement stubbed API window registerUriHandler() [#13306](https://github.com/eclipse-theia/theia/pull/13306) - contributed on behalf of STMicroelectronics - [core] Download json schema catalog at build-time - [#14065](https://github.com/eclipse-theia/theia/pull/14065/) - Contributed on behalf of STMicroelectronics [Breaking Changes:](#breaking_changes_1.53.0) - [dependencies] Updated electron to version 30.1.2 - [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics - [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics + ## 1.52.0 - 07/25/2024 - [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#13955](https://github.com/eclipse-theia/theia/pull/13955) - Contributed on behalf of STMicroelectronics diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index cf9320e7120fd..ca843a7e6729e 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -34,7 +34,8 @@ export namespace ElectronFrontendApplicationConfig { export const DEFAULT: ElectronFrontendApplicationConfig = { windowOptions: {}, showWindowEarly: true, - splashScreenOptions: {} + splashScreenOptions: {}, + uriScheme: 'theia' }; export interface SplashScreenOptions { /** @@ -85,6 +86,11 @@ export namespace ElectronFrontendApplicationConfig { * Defaults to `{}` which results in no splash screen being displayed. */ readonly splashScreenOptions?: SplashScreenOptions; + + /** + * The custom uri scheme the application registers to in the operating system. + */ + readonly uriScheme: string; } } @@ -122,7 +128,8 @@ export namespace FrontendApplicationConfig { electron: ElectronFrontendApplicationConfig.DEFAULT, defaultLocale: '', validatePreferencesSchema: true, - reloadOnReconnect: false + reloadOnReconnect: false, + uriScheme: 'theia' }; export interface Partial extends ApplicationConfig { diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 3ef33e6fe8238..51131f9b1ce93 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -465,7 +465,6 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bind(FrontendApplicationContribution).toService(StylingService); bind(SecondaryWindowHandler).toSelf().inSingletonScope(); - bind(ViewColumnService).toSelf().inSingletonScope(); bind(UndoRedoHandlerService).toSelf().inSingletonScope(); diff --git a/packages/core/src/browser/opener-service.ts b/packages/core/src/browser/opener-service.ts index e15418e3e6b29..d4f58f44a6fac 100644 --- a/packages/core/src/browser/opener-service.ts +++ b/packages/core/src/browser/opener-service.ts @@ -79,6 +79,12 @@ export interface OpenerService { * Add open handler i.e. for custom editors */ addHandler?(openHandler: OpenHandler): Disposable; + + /** + * Remove open handler + */ + removeHandler?(openHandler: OpenHandler): void; + /** * Event that fires when a new opener is added or removed. */ @@ -108,11 +114,15 @@ export class DefaultOpenerService implements OpenerService { this.onDidChangeOpenersEmitter.fire(); return Disposable.create(() => { - this.customEditorOpenHandlers.splice(this.customEditorOpenHandlers.indexOf(openHandler), 1); - this.onDidChangeOpenersEmitter.fire(); + this.removeHandler(openHandler); }); } + removeHandler(openHandler: OpenHandler): void { + this.customEditorOpenHandlers.splice(this.customEditorOpenHandlers.indexOf(openHandler), 1); + this.onDidChangeOpenersEmitter.fire(); + } + async getOpener(uri: URI, options?: OpenerOptions): Promise { const handlers = await this.prioritize(uri, options); if (handlers.length >= 1) { diff --git a/packages/core/src/electron-browser/electron-uri-handler.ts b/packages/core/src/electron-browser/electron-uri-handler.ts new file mode 100644 index 0000000000000..c3fdd3a993dae --- /dev/null +++ b/packages/core/src/electron-browser/electron-uri-handler.ts @@ -0,0 +1,42 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { FrontendApplicationContribution, OpenerService } from '../browser'; + +import { injectable, inject } from 'inversify'; +import { URI } from '../common'; + +@injectable() +export class ElectronUriHandlerContribution implements FrontendApplicationContribution { + @inject(OpenerService) + protected readonly openenerService: OpenerService; + + initialize(): void { + window.electronTheiaCore.setOpenUrlHandler(async url => { + const uri = new URI(url); + try { + const handler = await this.openenerService.getOpener(uri); + if (handler) { + await handler.open(uri); + return true; + } + } catch (e) { + // no handler + } + return false; + }); + } +} diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index 43eabd8271cca..fb87355fc8dda 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -26,7 +26,8 @@ import { CHANNEL_IS_FULL_SCREEN, CHANNEL_SET_MENU_BAR_VISIBLE, CHANNEL_REQUEST_CLOSE, CHANNEL_SET_TITLE_STYLE, CHANNEL_RESTART, CHANNEL_REQUEST_RELOAD, CHANNEL_APP_STATE_CHANGED, CHANNEL_SHOW_ITEM_IN_FOLDER, CHANNEL_READ_CLIPBOARD, CHANNEL_WRITE_CLIPBOARD, CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR, - CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP + CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP, + CHANNEL_OPEN_URL } from '../electron-common/electron-api'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -38,6 +39,16 @@ let nextHandlerId = 0; const mainMenuId = 0; let nextMenuId = mainMenuId + 1; +let openUrlHandler: ((url: string) => Promise) | undefined; + +ipcRenderer.on(CHANNEL_OPEN_URL, async (event: Electron.IpcRendererEvent, url: string, replyChannel: string) => { + if (openUrlHandler) { + event.sender.send(replyChannel, await openUrlHandler(url)); + } else { + event.sender.send(replyChannel, false); + } +}); + function convertMenu(menu: MenuDto[] | undefined, handlerMap: Map void>): InternalMenuDto[] | undefined { if (!menu) { return undefined; @@ -135,6 +146,10 @@ const api: TheiaCoreAPI = { return Disposable.create(() => ipcRenderer.off(CHANNEL_ABOUT_TO_CLOSE, h)); }, + setOpenUrlHandler(handler: (url: string) => Promise): void { + openUrlHandler = handler; + }, + onWindowEvent: function (event: WindowEvent, handler: () => void): Disposable { const h = (_event: unknown, evt: WindowEvent) => { if (event === evt) { diff --git a/packages/core/src/electron-browser/window/electron-window-module.ts b/packages/core/src/electron-browser/window/electron-window-module.ts index 78f490c1c98db..b4edcee810a10 100644 --- a/packages/core/src/electron-browser/window/electron-window-module.ts +++ b/packages/core/src/electron-browser/window/electron-window-module.ts @@ -29,6 +29,7 @@ import { ElectronSecondaryWindowService } from './electron-secondary-window-serv import { bindWindowPreferences } from './electron-window-preferences'; import { ElectronWindowService } from './electron-window-service'; import { ExternalAppOpenHandler } from './external-app-open-handler'; +import { ElectronUriHandlerContribution } from '../electron-uri-handler'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ElectronMainWindowService).toDynamicValue(context => @@ -37,6 +38,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bindWindowPreferences(bind); bind(WindowService).to(ElectronWindowService).inSingletonScope(); bind(FrontendApplicationContribution).toService(WindowService); + bind(ElectronUriHandlerContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(ElectronUriHandlerContribution); bind(ClipboardService).to(ElectronClipboardService).inSingletonScope(); rebind(FrontendApplicationStateService).to(ElectronFrontendApplicationStateService).inSingletonScope(); bind(SecondaryWindowService).to(ElectronSecondaryWindowService).inSingletonScope(); diff --git a/packages/core/src/electron-browser/window/external-app-open-handler.ts b/packages/core/src/electron-browser/window/external-app-open-handler.ts index d74c3a2378e1d..26f86c64be266 100644 --- a/packages/core/src/electron-browser/window/external-app-open-handler.ts +++ b/packages/core/src/electron-browser/window/external-app-open-handler.ts @@ -36,7 +36,7 @@ export class ExternalAppOpenHandler implements OpenHandler { async open(uri: URI): Promise { // For files 'file:' scheme, system accepts only the path. // For other protocols e.g. 'vscode:' we use the full URI to propagate target app information. - window.electronTheiaCore.openWithSystemApp(uri.scheme === 'file' ? uri.path.fsPath() : uri.toString(true)); + window.electronTheiaCore.openWithSystemApp(uri.toString(true)); return undefined; } } diff --git a/packages/core/src/electron-common/electron-api.ts b/packages/core/src/electron-common/electron-api.ts index 4edab9cb6f7b0..79035b3e00d37 100644 --- a/packages/core/src/electron-common/electron-api.ts +++ b/packages/core/src/electron-common/electron-api.ts @@ -74,6 +74,8 @@ export interface TheiaCoreAPI { onAboutToClose(handler: () => void): Disposable; setCloseRequestHandler(handler: (reason: StopReason) => Promise): void; + setOpenUrlHandler(handler: (url: string) => Promise): void; + setSecondaryWindowCloseRequestHandler(windowName: string, handler: () => Promise): void; toggleDevTools(): void; @@ -129,6 +131,7 @@ export const CHANNEL_MAXIMIZE = 'Maximize'; export const CHANNEL_IS_MAXIMIZED = 'IsMaximized'; export const CHANNEL_ABOUT_TO_CLOSE = 'AboutToClose'; +export const CHANNEL_OPEN_URL = 'OpenUrl'; export const CHANNEL_UNMAXIMIZE = 'UnMaximize'; export const CHANNEL_ON_WINDOW_EVENT = 'OnWindowEvent'; diff --git a/packages/core/src/electron-main/electron-api-main.ts b/packages/core/src/electron-main/electron-api-main.ts index b6a600e4d3a6d..b96942633e0e4 100644 --- a/packages/core/src/electron-main/electron-api-main.ts +++ b/packages/core/src/electron-main/electron-api-main.ts @@ -54,7 +54,8 @@ import { CHANNEL_SET_BACKGROUND_COLOR, CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, - CHANNEL_OPEN_WITH_SYSTEM_APP + CHANNEL_OPEN_WITH_SYSTEM_APP, + CHANNEL_OPEN_URL } from '../electron-common/electron-api'; import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application'; import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common'; @@ -165,8 +166,8 @@ export class TheiaMainApi implements ElectronMainApplicationContribution { shell.showItemInFolder(fsPath); }); - ipcMain.on(CHANNEL_OPEN_WITH_SYSTEM_APP, (event, fsPath) => { - shell.openPath(fsPath); + ipcMain.on(CHANNEL_OPEN_WITH_SYSTEM_APP, (event, uri) => { + shell.openExternal(uri); }); ipcMain.handle(CHANNEL_GET_TITLE_STYLE_AT_STARTUP, event => application.getTitleBarStyleAtStartup(event.sender)); @@ -285,6 +286,20 @@ export namespace TheiaRendererAPI { wc.send(CHANNEL_ON_WINDOW_EVENT, event); } + export function openUrl(wc: WebContents, url: string): Promise { + return new Promise(resolve => { + const channelNr = nextReplyChannel++; + const replyChannel = `openUrl${channelNr}`; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const l = createDisposableListener(ipcMain, replyChannel, (e, args: any[]) => { + l.dispose(); + resolve(args[0]); + }); + + wc.send(CHANNEL_OPEN_URL, url, replyChannel); + }); + } + export function sendAboutToClose(wc: WebContents): Promise { return new Promise(resolve => { const channelNr = nextReplyChannel++; diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 5a895f7ca2221..b464c916a84d9 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -117,13 +117,13 @@ export class ElectronMainProcessArgv { return 1; } - protected get isBundledElectronApp(): boolean { + get isBundledElectronApp(): boolean { // process.defaultApp is either set by electron in an electron unbundled app, or undefined // see https://github.com/electron/electron/blob/master/docs/api/process.md#processdefaultapp-readonly return this.isElectronApp && !(process as ElectronMainProcessArgv.ElectronMainProcess).defaultApp; } - protected get isElectronApp(): boolean { + get isElectronApp(): boolean { // process.versions.electron is either set by electron, or undefined // see https://github.com/electron/electron/blob/master/docs/api/process.md#processversionselectron-readonly return !!(process as ElectronMainProcessArgv.ElectronMainProcess).versions.electron; @@ -183,6 +183,7 @@ export class ElectronMainApplication { protected customBackgroundColor?: string; protected didUseNativeWindowFrameOnStart = new Map(); protected windows = new Map(); + protected activeWindowStack: number[] = []; protected restarting = false; /** Used to temporarily store the reference to an early created main window */ @@ -229,7 +230,7 @@ export class ElectronMainApplication { this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native'; this._config = config; this.hookApplicationEvents(); - this.showInitialWindow(); + this.showInitialWindow(argv.includes('--open-url') ? argv[argv.length - 1] : undefined); const port = await this.startBackend(); this._backendPort.resolve(port); await app.whenReady(); @@ -317,7 +318,7 @@ export class ElectronMainApplication { !('THEIA_ELECTRON_NO_EARLY_WINDOW' in process.env && process.env.THEIA_ELECTRON_NO_EARLY_WINDOW === '1'); } - protected showInitialWindow(): void { + protected showInitialWindow(urlToOpen: string | undefined): void { if (this.isShowWindowEarly() || this.isShowSplashScreen()) { app.whenReady().then(async () => { const options = await this.getLastWindowOptions(); @@ -326,7 +327,11 @@ export class ElectronMainApplication { options.preventAutomaticShow = true; } this.initialWindow = await this.createWindow({ ...options }); - + TheiaRendererAPI.onApplicationStateChanged(this.initialWindow.webContents, state => { + if (state === 'ready' && urlToOpen) { + this.openUrl(urlToOpen); + } + }); if (this.isShowSplashScreen()) { console.log('Showing splash screen'); this.configureAndShowSplashScreen(this.initialWindow); @@ -410,11 +415,25 @@ export class ElectronMainApplication { options = this.avoidOverlap(options); const electronWindow = this.windowFactory(options, this.config); const id = electronWindow.window.webContents.id; + this.activeWindowStack.push(id); this.windows.set(id, electronWindow); - electronWindow.onDidClose(() => this.windows.delete(id)); + electronWindow.onDidClose(() => { + const stackIndex = this.activeWindowStack.indexOf(id); + if (stackIndex >= 0) { + this.activeWindowStack.splice(stackIndex, 1); + } + this.windows.delete(id); + }); electronWindow.window.on('maximize', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'maximize')); electronWindow.window.on('unmaximize', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'unmaximize')); - electronWindow.window.on('focus', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'focus')); + electronWindow.window.on('focus', () => { + const stackIndex = this.activeWindowStack.indexOf(id); + if (stackIndex >= 0) { + this.activeWindowStack.splice(stackIndex, 1); + } + this.activeWindowStack.unshift(id); + TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'focus'); + }); this.attachSaveWindowState(electronWindow.window); return electronWindow.window; @@ -521,6 +540,15 @@ export class ElectronMainApplication { } } + async openUrl(url: string): Promise { + for (const id of this.activeWindowStack) { + const window = this.windows.get(id); + if (window && await window.openUrl(url)) { + break; + } + } + } + protected async createWindowUri(params: WindowSearchParams = {}): Promise { if (!('port' in params)) { params.port = (await this.backendPort).toString(); @@ -696,6 +724,16 @@ export class ElectronMainApplication { app.on('second-instance', this.onSecondInstance.bind(this)); app.on('window-all-closed', this.onWindowAllClosed.bind(this)); app.on('web-contents-created', this.onWebContentsCreated.bind(this)); + + if (isWindows) { + const args = this.processArgv.isBundledElectronApp ? [] : [app.getAppPath()]; + args.push('--open-url'); + app.setAsDefaultProtocolClient(this.config.electron.uriScheme, process.execPath, args); + } else { + app.on('open-url', (evt, url) => { + this.openUrl(url); + }); + } } protected onWillQuit(event: ElectronEvent): void { @@ -703,19 +741,23 @@ export class ElectronMainApplication { } protected async onSecondInstance(event: ElectronEvent, argv: string[], cwd: string): Promise { - createYargs(this.processArgv.getProcessArgvWithoutBin(argv), process.cwd()) - .help(false) - .command('$0 [file]', false, - cmd => cmd - .positional('file', { type: 'string' }), - async args => { - this.handleMainCommand({ - file: args.file, - cwd: process.cwd(), - secondInstance: true - }); - }, - ).parse(); + if (argv.includes('--open-url')) { + this.openUrl(argv[argv.length - 1]); + } else { + createYargs(this.processArgv.getProcessArgvWithoutBin(argv), process.cwd()) + .help(false) + .command('$0 [file]', false, + cmd => cmd + .positional('file', { type: 'string' }), + async args => { + await this.handleMainCommand({ + file: args.file, + cwd: process.cwd(), + secondInstance: true + }); + }, + ).parse(); + } } protected onWebContentsCreated(event: ElectronEvent, webContents: WebContents): void { diff --git a/packages/core/src/electron-main/theia-electron-window.ts b/packages/core/src/electron-main/theia-electron-window.ts index 29569d4970593..720865f8f59e1 100644 --- a/packages/core/src/electron-main/theia-electron-window.ts +++ b/packages/core/src/electron-main/theia-electron-window.ts @@ -58,6 +58,7 @@ enum ClosingState { @injectable() export class TheiaElectronWindow { + @inject(TheiaBrowserWindowOptions) protected readonly options: TheiaBrowserWindowOptions; @inject(WindowApplicationConfig) protected readonly config: WindowApplicationConfig; @inject(ElectronMainApplicationGlobals) protected readonly globals: ElectronMainApplicationGlobals; @@ -202,6 +203,10 @@ export class TheiaElectronWindow { this.toDispose.push(TheiaRendererAPI.onRequestReload(this.window.webContents, (newUrl?: string) => this.reload(newUrl))); } + openUrl(url: string): Promise { + return TheiaRendererAPI.openUrl(this.window.webContents, url); + } + dispose(): void { this.toDispose.dispose(); } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index 0e6c0570041ea..ed117bb1eac19 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -181,6 +181,7 @@ export interface EnvInit { appName: string; appHost: string; appRoot: string; + appUriScheme: string; } export interface PluginAPI { @@ -2241,6 +2242,17 @@ export interface TestingExt { $onResolveChildren(controllerId: string, path: string[]): void; } +// based from https://github.com/microsoft/vscode/blob/1.85.1/src/vs/workbench/api/common/extHostUrls.ts +export interface UriExt { + registerUriHandler(handler: theia.UriHandler, plugin: PluginInfo): theia.Disposable; + $handleExternalUri(uri: UriComponents): Promise; +} + +export interface UriMain { + $registerUriHandler(extensionId: string, extensionName: string): void; + $unregisterUriHandler(extensionId: string): void; +} + export interface TestControllerUpdate { label: string; canRefresh: boolean; @@ -2318,7 +2330,8 @@ export const PLUGIN_RPC_CONTEXT = { TABS_MAIN: >createProxyIdentifier('TabsMain'), TELEMETRY_MAIN: >createProxyIdentifier('TelemetryMain'), LOCALIZATION_MAIN: >createProxyIdentifier('LocalizationMain'), - TESTING_MAIN: createProxyIdentifier('TestingMain') + TESTING_MAIN: createProxyIdentifier('TestingMain'), + URI_MAIN: createProxyIdentifier('UriMain') }; export const MAIN_RPC_CONTEXT = { @@ -2360,7 +2373,8 @@ export const MAIN_RPC_CONTEXT = { COMMENTS_EXT: createProxyIdentifier('CommentsExt'), TABS_EXT: createProxyIdentifier('TabsExt'), TELEMETRY_EXT: createProxyIdentifier('TelemetryExt)'), - TESTING_EXT: createProxyIdentifier('TestingExt') + TESTING_EXT: createProxyIdentifier('TestingExt'), + URI_EXT: createProxyIdentifier('UriExt') }; export interface TasksExt { diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 43fadf44f7e3a..fb4c21585fcf5 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -328,7 +328,8 @@ export class HostedPluginSupport extends AbstractHostedPluginSupport { + await this.activateByEvent(`onUri:${scheme}://${authority}`); + } + async activateByCommand(commandId: string): Promise { await this.activateByEvent(`onCommand:${commandId}`); } diff --git a/packages/plugin-ext/src/main/browser/main-context.ts b/packages/plugin-ext/src/main/browser/main-context.ts index 262926efc80d9..6467bf99c2b35 100644 --- a/packages/plugin-ext/src/main/browser/main-context.ts +++ b/packages/plugin-ext/src/main/browser/main-context.ts @@ -64,6 +64,7 @@ import { NotebookDocumentsMainImpl } from './notebooks/notebook-documents-main'; import { NotebookKernelsMainImpl } from './notebooks/notebook-kernels-main'; import { NotebooksAndEditorsMain } from './notebooks/notebook-documents-and-editors-main'; import { TestingMainImpl } from './test-main'; +import { UriMainImpl } from './uri-main'; export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void { const authenticationMain = new AuthenticationMainImpl(rpc, container); @@ -203,4 +204,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container const localizationMain = new LocalizationMainImpl(container); rpc.set(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN, localizationMain); + + const uriMain = new UriMainImpl(rpc, container); + rpc.set(PLUGIN_RPC_CONTEXT.URI_MAIN, uriMain); } diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 68874ede43d97..a8510268870f3 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -286,6 +286,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CellOutputWebviewFactory).toFactory(ctx => async (cell: NotebookCellModel, notebook: NotebookModel) => createCellOutputWebviewContainer(ctx.container, cell, notebook).getAsync(CellOutputWebviewImpl) ); - bindContributionProvider(bind, ArgumentProcessorContribution); + }); diff --git a/packages/plugin-ext/src/main/browser/uri-main.ts b/packages/plugin-ext/src/main/browser/uri-main.ts new file mode 100644 index 0000000000000..6e18cdea5241c --- /dev/null +++ b/packages/plugin-ext/src/main/browser/uri-main.ts @@ -0,0 +1,72 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Disposable, URI } from '@theia/core'; +import { MAIN_RPC_CONTEXT, UriExt, UriMain } from '../../common'; +import { RPCProtocol } from '../../common/rpc-protocol'; +import { interfaces } from '@theia/core/shared/inversify'; +import { OpenHandler, OpenerOptions, OpenerService } from '@theia/core/lib/browser'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin'; + +export class UriMainImpl implements UriMain, Disposable { + private readonly proxy: UriExt; + private handlers = new Set(); + private readonly openerService: OpenerService; + private readonly pluginSupport: HostedPluginSupport; + private readonly openHandler: OpenHandler; + + constructor(rpc: RPCProtocol, container: interfaces.Container) { + this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.URI_EXT); + this.openerService = container.get(OpenerService); + this.pluginSupport = container.get(HostedPluginSupport); + + this.openHandler = { + id: 'theia-plugin-open-handler', + canHandle: async (uri: URI, options?: OpenerOptions | undefined): Promise => { + if (uri.scheme !== FrontendApplicationConfigProvider.get().electron.uriScheme) { + return 0; + } + await this.pluginSupport.activateByUri(uri.scheme, uri.authority); + if (this.handlers.has(uri.authority)) { + return 500; + } + return 0; + }, + open: async (uri: URI, options?: OpenerOptions | undefined): Promise => { + if (!this.handlers.has(uri.authority)) { + throw new Error(`No plugin to handle this uri: : '${uri}'`); + } + this.proxy.$handleExternalUri(uri.toComponents()); + } + }; + + this.openerService.addHandler?.(this.openHandler); + } + + dispose(): void { + this.openerService.removeHandler?.(this.openHandler); + this.handlers.clear(); + } + + async $registerUriHandler(pluginId: string, extensionDisplayName: string): Promise { + this.handlers.add(pluginId); + } + + async $unregisterUriHandler(pluginId: string): Promise { + this.handlers.delete(pluginId); + } +} diff --git a/packages/plugin-ext/src/plugin/env.ts b/packages/plugin-ext/src/plugin/env.ts index 14d2bda0b9878..bb9c405bb6a55 100644 --- a/packages/plugin-ext/src/plugin/env.ts +++ b/packages/plugin-ext/src/plugin/env.ts @@ -35,6 +35,7 @@ export abstract class EnvExtImpl { private envSessionId: string; private host: string; private applicationRoot: string; + private appUriScheme: string; private _remoteName: string | undefined; constructor() { @@ -89,6 +90,10 @@ export abstract class EnvExtImpl { this.applicationRoot = appRoot; } + setAppUriScheme(uriScheme: string): void { + this.appUriScheme = uriScheme; + } + getClientOperatingSystem(): Promise { return this.proxy.$getClientOperatingSystem(); } @@ -121,7 +126,7 @@ export abstract class EnvExtImpl { return this.envSessionId; } get uriScheme(): string { - return 'theia'; + return this.appUriScheme; } get uiKind(): theia.UIKind { return this.ui; diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index edac3d7b2b7dc..8a36492244edc 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -276,6 +276,7 @@ import { NotebookKernelsExtImpl } from './notebook/notebook-kernels'; import { NotebookDocumentsExtImpl } from './notebook/notebook-documents'; import { NotebookEditorsExtImpl } from './notebook/notebook-editors'; import { TestingExtImpl } from './tests'; +import { UriExtImpl } from './uri-ext'; export function createAPIFactory( rpc: RPCProtocol, @@ -324,6 +325,7 @@ export function createAPIFactory( const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt)); const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl()); const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry)); + const uriExt = rpc.set(MAIN_RPC_CONTEXT.URI_EXT, new UriExtImpl(rpc)); rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt); return function (plugin: InternalPlugin): typeof theia { @@ -596,8 +598,7 @@ export function createAPIFactory( return decorationsExt.registerFileDecorationProvider(provider, pluginToPluginInfo(plugin)); }, registerUriHandler(handler: theia.UriHandler): theia.Disposable { - // TODO ? - return new Disposable(() => { }); + return uriExt.registerUriHandler(handler, pluginToPluginInfo(plugin)); }, createInputBox(): theia.InputBox { return quickOpenExt.createInputBox(plugin); diff --git a/packages/plugin-ext/src/plugin/plugin-manager.ts b/packages/plugin-ext/src/plugin/plugin-manager.ts index 47ced24b00a76..b0fa44833e77e 100644 --- a/packages/plugin-ext/src/plugin/plugin-manager.ts +++ b/packages/plugin-ext/src/plugin/plugin-manager.ts @@ -252,7 +252,7 @@ export abstract class AbstractPluginManagerExtImpl

        } for (let activationEvent of activationEvents) { if (activationEvent === 'onUri') { - activationEvent = `onUri:theia://${plugin.model.id}`; + activationEvent = `onUri:${this.envExt.uriScheme}://${plugin.model.id}`; } this.setActivation(activationEvent, activation); } @@ -481,6 +481,7 @@ export class PluginManagerExtImpl extends AbstractPluginManagerExtImpl(); + + private readonly proxy: UriMain; + + constructor(readonly rpc: RPCProtocol) { + this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.URI_MAIN); + console.log(this.proxy); + } + + registerUriHandler(handler: theia.UriHandler, plugin: PluginInfo): theia.Disposable { + const pluginId = plugin.id; + if (this.handlers.has(pluginId)) { + throw new Error(`URI handler already registered for plugin ${pluginId}`); + } + + this.handlers.set(pluginId, handler); + this.proxy.$registerUriHandler(pluginId, plugin.displayName || plugin.name); + + return new Disposable(() => { + this.proxy.$unregisterUriHandler(pluginId); + this.handlers.delete(pluginId); + }); + } + + $handleExternalUri(uri: UriComponents): Promise { + const handler = this.handlers.get(uri.authority); + if (!handler) { + return Promise.resolve(); + } + handler.handleUri(URI.revive(uri)); + return Promise.resolve(); + } +} From eea51ca99486cd2e43d006c2d789a0f9a05a8bd7 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 28 Aug 2024 17:06:21 +0200 Subject: [PATCH 351/441] Enhance notebook cell divider (#14081) --- .../src/browser/notebook-editor-widget.tsx | 3 +- packages/notebook/src/browser/style/index.css | 14 ++++-- .../browser/view/notebook-cell-list-view.tsx | 48 +++++++++++-------- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 216568353aca0..d69c556d794e2 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -265,7 +265,8 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa notebookModel={this._model} notebookContext={this.notebookContextManager} toolbarRenderer={this.cellToolbarFactory} - commandRegistry={this.commandRegistry} /> + commandRegistry={this.commandRegistry} + menuRegistry={this.menuRegistry} />

        ; diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index 333c792073b9d..64c6fa1b3091b 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -272,10 +272,10 @@ border: 1px solid var(--theia-notebook-cellToolbarSeparator); background-color: var(--theia-editor-background); color: var(--theia-foreground); - vertical-align: middle; - text-align: center; + display: flex; height: 24px; margin: 0 8px; + padding: 2px 4px; } .theia-notebook-add-cell-button:hover { @@ -286,10 +286,18 @@ background-color: var(--theia-toolbar-active); } -.theia-notebook-add-cell-button-icon { +.theia-notebook-add-cell-button>* { vertical-align: middle; } +.theia-notebook-add-cell-button-icon::before { + font: normal normal normal 14px/1 codicon; +} + +.theia-notebook-add-cell-button-text { + margin: 1px 0 0 4px; +} + .theia-notebook-cell-output-webview { padding: 5px 0px; margin: 0px 15px 0px 9px; diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 14e7a42ef5b5a..5a156328e7859 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -18,9 +18,9 @@ import { CellEditType, CellKind, NotebookCellsChangeType } from '../../common'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookModel } from '../view-model/notebook-model'; import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory'; -import { animationFrame, codicon, onDomEvent } from '@theia/core/lib/browser'; -import { CommandRegistry, DisposableCollection, nls } from '@theia/core'; -import { NotebookCommands } from '../contributions/notebook-actions-contribution'; +import { animationFrame, onDomEvent } from '@theia/core/lib/browser'; +import { CommandRegistry, DisposableCollection, MenuModelRegistry, MenuNode, nls } from '@theia/core'; +import { NotebookCommands, NotebookMenus } from '../contributions/notebook-actions-contribution'; import { NotebookCellActionContribution } from '../contributions/notebook-cell-actions-contribution'; import { NotebookContextManager } from '../service/notebook-context-manager'; @@ -34,7 +34,8 @@ interface CellListProps { notebookModel: NotebookModel; notebookContext: NotebookContextManager; toolbarRenderer: NotebookCellToolbarFactory; - commandRegistry: CommandRegistry + commandRegistry: CommandRegistry; + menuRegistry: MenuModelRegistry; } interface NotebookCellListState { @@ -113,8 +114,9 @@ export class NotebookCellListView extends React.Component this.isEnabled()} - onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, index)} + onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, index)} onDrop={e => this.onDrop(e, index)} onDragOver={e => this.onDragOver(e, cell, 'top')} /> {this.shouldRenderDragOverIndicator(cell, 'top') && } @@ -155,8 +157,9 @@ export class NotebookCellListView extends React.Component this.isEnabled()} - onAddNewCell={(kind: CellKind) => this.onAddNewCell(kind, this.props.notebookModel.cells.length)} + onAddNewCell={(commandId: string) => this.onAddNewCell(commandId, this.props.notebookModel.cells.length)} onDrop={e => this.onDrop(e, this.props.notebookModel.cells.length - 1)} onDragOver={e => this.onDragOver(e, this.props.notebookModel.cells[this.props.notebookModel.cells.length - 1], 'bottom')} />
      ; @@ -221,11 +224,11 @@ export class NotebookCellListView extends React.Component boolean; - onAddNewCell: (type: CellKind) => void; + onAddNewCell: (commandId: string) => void; onDrop: (event: React.DragEvent) => void; onDragOver: (event: React.DragEvent) => void; + menuRegistry: MenuModelRegistry; } -export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver }: NotebookCellDividerProps): React.JSX.Element { +export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOver, menuRegistry }: NotebookCellDividerProps): React.JSX.Element { const [hover, setHover] = React.useState(false); + const menuPath = NotebookMenus.NOTEBOOK_MAIN_TOOLBAR_CELL_ADD_GROUP; + const menuItems = menuRegistry.getMenuNode(menuPath).children; + + const renderItem = (item: MenuNode): React.ReactNode => ; + return
    • setHover(true)} onMouseLeave={() => setHover(false)} onDrop={onDrop} onDragOver={onDragOver}> {hover && isVisible() &&
      - - + {menuItems.map((item: MenuNode) => renderItem(item))}
      }
    • ; } From dacccf3bf4ed0c9a80615d98a9899ad1801398ef Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 28 Aug 2024 18:20:59 +0200 Subject: [PATCH 352/441] Support collaboration feature (#13309) --- examples/browser-only/package.json | 1 + examples/browser-only/tsconfig.json | 3 + examples/browser/package.json | 1 + examples/browser/tsconfig.json | 3 + examples/electron/package.json | 1 + examples/electron/tsconfig.json | 3 + packages/collaboration/.eslintrc.js | 10 + packages/collaboration/README.md | 33 + packages/collaboration/package.json | 57 ++ .../browser/collaboration-color-service.ts | 77 ++ .../collaboration-file-system-provider.ts | 119 +++ .../collaboration-frontend-contribution.ts | 327 +++++++ .../browser/collaboration-frontend-module.ts | 37 + .../src/browser/collaboration-instance.ts | 819 ++++++++++++++++++ .../src/browser/collaboration-utils.ts | 59 ++ .../collaboration-workspace-service.ts | 69 ++ .../collaboration/src/browser/style/index.css | 22 + packages/collaboration/src/package.spec.ts | 28 + packages/collaboration/tsconfig.json | 28 + packages/editor/src/browser/editor-manager.ts | 5 +- packages/editor/src/browser/editor.ts | 8 +- .../monaco/src/browser/monaco-editor-model.ts | 20 +- packages/monaco/src/browser/monaco-editor.ts | 18 +- .../src/browser/monaco-quick-input-service.ts | 29 +- .../browser/monaco-to-protocol-converter.ts | 11 + .../src/browser/preview-contribution.ts | 5 +- .../src/browser/workspace-commands.ts | 4 +- .../src/browser/workspace-service.ts | 2 +- tsconfig.json | 3 + yarn.lock | 75 +- 30 files changed, 1841 insertions(+), 36 deletions(-) create mode 100644 packages/collaboration/.eslintrc.js create mode 100644 packages/collaboration/README.md create mode 100644 packages/collaboration/package.json create mode 100644 packages/collaboration/src/browser/collaboration-color-service.ts create mode 100644 packages/collaboration/src/browser/collaboration-file-system-provider.ts create mode 100644 packages/collaboration/src/browser/collaboration-frontend-contribution.ts create mode 100644 packages/collaboration/src/browser/collaboration-frontend-module.ts create mode 100644 packages/collaboration/src/browser/collaboration-instance.ts create mode 100644 packages/collaboration/src/browser/collaboration-utils.ts create mode 100644 packages/collaboration/src/browser/collaboration-workspace-service.ts create mode 100644 packages/collaboration/src/browser/style/index.css create mode 100644 packages/collaboration/src/package.spec.ts create mode 100644 packages/collaboration/tsconfig.json diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 087fd5c6548a3..2913b38d8658e 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -18,6 +18,7 @@ "@theia/api-samples": "1.52.0", "@theia/bulk-edit": "1.52.0", "@theia/callhierarchy": "1.52.0", + "@theia/collaboration": "1.52.0", "@theia/console": "1.52.0", "@theia/core": "1.52.0", "@theia/debug": "1.52.0", diff --git a/examples/browser-only/tsconfig.json b/examples/browser-only/tsconfig.json index d4bcfc14426b9..eef920de150af 100644 --- a/examples/browser-only/tsconfig.json +++ b/examples/browser-only/tsconfig.json @@ -14,6 +14,9 @@ { "path": "../../packages/callhierarchy" }, + { + "path": "../../packages/collaboration" + }, { "path": "../../packages/console" }, diff --git a/examples/browser/package.json b/examples/browser/package.json index 5a938e845cb07..0e2e714ea1756 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -24,6 +24,7 @@ "@theia/api-samples": "1.52.0", "@theia/bulk-edit": "1.52.0", "@theia/callhierarchy": "1.52.0", + "@theia/collaboration": "1.52.0", "@theia/console": "1.52.0", "@theia/core": "1.52.0", "@theia/debug": "1.52.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index c04673f8d70a7..60de2a565c967 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -14,6 +14,9 @@ { "path": "../../packages/callhierarchy" }, + { + "path": "../../packages/collaboration" + }, { "path": "../../packages/console" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index 0cfe496522349..52eceabf186bd 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -30,6 +30,7 @@ "@theia/api-samples": "1.52.0", "@theia/bulk-edit": "1.52.0", "@theia/callhierarchy": "1.52.0", + "@theia/collaboration": "1.52.0", "@theia/console": "1.52.0", "@theia/core": "1.52.0", "@theia/debug": "1.52.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index 91edb2ac8dc55..8e8e306fc9c1a 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -17,6 +17,9 @@ { "path": "../../packages/callhierarchy" }, + { + "path": "../../packages/collaboration" + }, { "path": "../../packages/console" }, diff --git a/packages/collaboration/.eslintrc.js b/packages/collaboration/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/collaboration/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/collaboration/README.md b/packages/collaboration/README.md new file mode 100644 index 0000000000000..f97352acc298f --- /dev/null +++ b/packages/collaboration/README.md @@ -0,0 +1,33 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - COLLABORATION EXTENSION

      + +
      + +
      + +## Description + +The `@theia/collaboration` extension features to enable collaboration between multiple peers using Theia. +This is built on top of the [Open Collaboration Tools](https://www.open-collab.tools/) ([GitHub](https://github.com/TypeFox/open-collaboration-tools)) project. + +Note that the project is still in a beta phase and can be subject to unexpected breaking changes. This package is therefore in a beta phase as well. + +## Additional Information + +- [API documentation for `@theia/collaboration`](https://eclipse-theia.github.io/theia/docs/next/modules/collaboration.html) +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/collaboration/package.json b/packages/collaboration/package.json new file mode 100644 index 0000000000000..130c2b6718399 --- /dev/null +++ b/packages/collaboration/package.json @@ -0,0 +1,57 @@ +{ + "name": "@theia/collaboration", + "version": "1.52.0", + "description": "Theia - Collaboration Extension", + "dependencies": { + "@theia/core": "1.52.0", + "@theia/editor": "1.52.0", + "@theia/filesystem": "1.52.0", + "@theia/monaco": "1.52.0", + "@theia/monaco-editor-core": "1.83.101", + "@theia/workspace": "1.52.0", + "open-collaboration-protocol": "0.2.0", + "open-collaboration-yjs": "0.2.0", + "socket.io-client": "^4.5.3", + "yjs": "^13.6.7", + "lib0": "^0.2.52", + "y-protocols": "^1.0.6" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/collaboration-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.52.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/collaboration/src/browser/collaboration-color-service.ts b/packages/collaboration/src/browser/collaboration-color-service.ts new file mode 100644 index 0000000000000..adfac8e819118 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-color-service.ts @@ -0,0 +1,77 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; + +export interface CollaborationColor { + r: number; + g: number; + b: number; +} + +export namespace CollaborationColor { + export function fromString(code: string): CollaborationColor { + if (code.startsWith('#')) { + code = code.substring(1); + } + const r = parseInt(code.substring(0, 2), 16); + const g = parseInt(code.substring(2, 4), 16); + const b = parseInt(code.substring(4, 6), 16); + return { r, g, b }; + } + + export const Gold = fromString('#FFD700'); + export const Tomato = fromString('#FF6347'); + export const Aquamarine = fromString('#7FFFD4'); + export const Beige = fromString('#F5F5DC'); + export const Coral = fromString('#FF7F50'); + export const DarkOrange = fromString('#FF8C00'); + export const VioletRed = fromString('#C71585'); + export const DodgerBlue = fromString('#1E90FF'); + export const Chocolate = fromString('#D2691E'); + export const LightGreen = fromString('#90EE90'); + export const MediumOrchid = fromString('#BA55D3'); + export const Orange = fromString('#FFA500'); +} + +@injectable() +export class CollaborationColorService { + + light = 'white'; + dark = 'black'; + + getColors(): CollaborationColor[] { + return [ + CollaborationColor.Gold, + CollaborationColor.Aquamarine, + CollaborationColor.Tomato, + CollaborationColor.MediumOrchid, + CollaborationColor.LightGreen, + CollaborationColor.Orange, + CollaborationColor.Beige, + CollaborationColor.Chocolate, + CollaborationColor.VioletRed, + CollaborationColor.Coral, + CollaborationColor.DodgerBlue, + CollaborationColor.DarkOrange + ]; + } + + requiresDarkFont(color: CollaborationColor): boolean { + // From https://stackoverflow.com/a/3943023 + return ((color.r * 0.299) + (color.g * 0.587) + (color.b * 0.114)) > 186; + } +} diff --git a/packages/collaboration/src/browser/collaboration-file-system-provider.ts b/packages/collaboration/src/browser/collaboration-file-system-provider.ts new file mode 100644 index 0000000000000..87c9f556b0d57 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-file-system-provider.ts @@ -0,0 +1,119 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as Y from 'yjs'; +import { Disposable, Emitter, Event, URI } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { + FileChange, FileDeleteOptions, + FileOverwriteOptions, FileSystemProviderCapabilities, FileType, Stat, WatchOptions, FileSystemProviderWithFileReadWriteCapability, FileWriteOptions +} from '@theia/filesystem/lib/common/files'; +import { ProtocolBroadcastConnection, Workspace, Peer } from 'open-collaboration-protocol'; + +export namespace CollaborationURI { + + export const scheme = 'collaboration'; + + export function create(workspace: Workspace, path?: string): URI { + return new URI(`${scheme}:///${workspace.name}${path ? '/' + path : ''}`); + } +} + +@injectable() +export class CollaborationFileSystemProvider implements FileSystemProviderWithFileReadWriteCapability { + + capabilities = FileSystemProviderCapabilities.FileReadWrite; + + protected _readonly: boolean; + + get readonly(): boolean { + return this._readonly; + } + + set readonly(value: boolean) { + if (this._readonly !== value) { + this._readonly = value; + if (value) { + this.capabilities |= FileSystemProviderCapabilities.Readonly; + } else { + this.capabilities &= ~FileSystemProviderCapabilities.Readonly; + } + this.onDidChangeCapabilitiesEmitter.fire(); + } + } + + constructor(readonly connection: ProtocolBroadcastConnection, readonly host: Peer, readonly yjs: Y.Doc) { + } + + protected encoder = new TextEncoder(); + protected decoder = new TextDecoder(); + protected onDidChangeCapabilitiesEmitter = new Emitter(); + protected onDidChangeFileEmitter = new Emitter(); + protected onFileWatchErrorEmitter = new Emitter(); + + get onDidChangeCapabilities(): Event { + return this.onDidChangeCapabilitiesEmitter.event; + } + get onDidChangeFile(): Event { + return this.onDidChangeFileEmitter.event; + } + get onFileWatchError(): Event { + return this.onFileWatchErrorEmitter.event; + } + async readFile(resource: URI): Promise { + const path = this.getHostPath(resource); + if (this.yjs.share.has(path)) { + const stringValue = this.yjs.getText(path); + return this.encoder.encode(stringValue.toString()); + } else { + const data = await this.connection.fs.readFile(this.host.id, path); + return data.content; + } + } + async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + const path = this.getHostPath(resource); + await this.connection.fs.writeFile(this.host.id, path, { content }); + } + watch(resource: URI, opts: WatchOptions): Disposable { + return Disposable.NULL; + } + stat(resource: URI): Promise { + return this.connection.fs.stat(this.host.id, this.getHostPath(resource)); + } + mkdir(resource: URI): Promise { + return this.connection.fs.mkdir(this.host.id, this.getHostPath(resource)); + } + async readdir(resource: URI): Promise<[string, FileType][]> { + const record = await this.connection.fs.readdir(this.host.id, this.getHostPath(resource)); + return Object.entries(record); + } + delete(resource: URI, opts: FileDeleteOptions): Promise { + return this.connection.fs.delete(this.host.id, this.getHostPath(resource)); + } + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return this.connection.fs.rename(this.host.id, this.getHostPath(from), this.getHostPath(to)); + } + + protected getHostPath(uri: URI): string { + const path = uri.path.toString().substring(1).split('/'); + return path.slice(1).join('/'); + } + + triggerEvent(changes: FileChange[]): void { + this.onDidChangeFileEmitter.fire(changes); + } + +} diff --git a/packages/collaboration/src/browser/collaboration-frontend-contribution.ts b/packages/collaboration/src/browser/collaboration-frontend-contribution.ts new file mode 100644 index 0000000000000..3d8784b84a9b3 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-frontend-contribution.ts @@ -0,0 +1,327 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import '../../src/browser/style/index.css'; + +import { + CancellationToken, CancellationTokenSource, Command, CommandContribution, CommandRegistry, MessageService, nls, Progress, QuickInputService, QuickPickItem +} from '@theia/core'; +import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify'; +import { ConnectionProvider, SocketIoTransportProvider } from 'open-collaboration-protocol'; +import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { CollaborationInstance, CollaborationInstanceFactory } from './collaboration-instance'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { CollaborationWorkspaceService } from './collaboration-workspace-service'; +import { StatusBar, StatusBarAlignment, StatusBarEntry } from '@theia/core/lib/browser/status-bar'; +import { codiconArray } from '@theia/core/lib/browser/widgets/widget'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; + +export const COLLABORATION_CATEGORY = 'Collaboration'; + +export namespace CollaborationCommands { + export const CREATE_ROOM: Command = { + id: 'collaboration.create-room' + }; + export const JOIN_ROOM: Command = { + id: 'collaboration.join-room' + }; +} + +export const COLLABORATION_STATUS_BAR_ID = 'statusBar.collaboration'; + +export const COLLABORATION_AUTH_TOKEN = 'THEIA_COLLAB_AUTH_TOKEN'; +export const COLLABORATION_SERVER_URL = 'COLLABORATION_SERVER_URL'; +export const DEFAULT_COLLABORATION_SERVER_URL = 'https://api.open-collab.tools/'; + +@injectable() +export class CollaborationFrontendContribution implements CommandContribution { + + protected readonly connectionProvider = new Deferred(); + + @inject(WindowService) + protected readonly windowService: WindowService; + + @inject(QuickInputService) @optional() + protected readonly quickInputService?: QuickInputService; + + @inject(EnvVariablesServer) + protected readonly envVariables: EnvVariablesServer; + + @inject(CollaborationWorkspaceService) + protected readonly workspaceService: CollaborationWorkspaceService; + + @inject(MessageService) + protected readonly messageService: MessageService; + + @inject(CommandRegistry) + protected readonly commands: CommandRegistry; + + @inject(StatusBar) + protected readonly statusBar: StatusBar; + + @inject(CollaborationInstanceFactory) + protected readonly collaborationInstanceFactory: CollaborationInstanceFactory; + + protected currentInstance?: CollaborationInstance; + + @postConstruct() + protected init(): void { + this.setStatusBarEntryDefault(); + this.getCollaborationServerUrl().then(serverUrl => { + const authHandler = new ConnectionProvider({ + url: serverUrl, + client: FrontendApplicationConfigProvider.get().applicationName, + fetch: window.fetch.bind(window), + opener: url => this.windowService.openNewWindow(url, { external: true }), + transports: [SocketIoTransportProvider], + userToken: localStorage.getItem(COLLABORATION_AUTH_TOKEN) ?? undefined + }); + this.connectionProvider.resolve(authHandler); + }, err => this.connectionProvider.reject(err)); + } + + protected async onStatusDefaultClick(): Promise { + const items: QuickPickItem[] = []; + if (this.workspaceService.opened) { + items.push({ + label: nls.localize('theia/collaboration/createRoom', 'Create New Collaboration Session'), + iconClasses: codiconArray('add'), + execute: () => this.commands.executeCommand(CollaborationCommands.CREATE_ROOM.id) + }); + } + items.push({ + label: nls.localize('theia/collaboration/joinRoom', 'Join Collaboration Session'), + iconClasses: codiconArray('vm-connect'), + execute: () => this.commands.executeCommand(CollaborationCommands.JOIN_ROOM.id) + }); + await this.quickInputService?.showQuickPick(items, { + placeholder: nls.localize('theia/collaboration/selectCollaboration', 'Select collaboration option') + }); + } + + protected async onStatusSharedClick(code: string): Promise { + const items: QuickPickItem[] = [{ + label: nls.localize('theia/collaboration/invite', 'Invite Others'), + detail: nls.localize('theia/collaboration/inviteDetail', 'Copy the invitation code for sharing it with others to join the session.'), + iconClasses: codiconArray('clippy'), + execute: () => this.displayCopyNotification(code) + }]; + if (this.currentInstance) { + // TODO: Implement readonly mode + // if (this.currentInstance.readonly) { + // items.push({ + // label: nls.localize('theia/collaboration/enableEditing', 'Enable Workspace Editing'), + // detail: nls.localize('theia/collaboration/enableEditingDetail', 'Allow collaborators to modify content in your workspace.'), + // iconClasses: codiconArray('unlock'), + // execute: () => { + // if (this.currentInstance) { + // this.currentInstance.readonly = false; + // } + // } + // }); + // } else { + // items.push({ + // label: nls.localize('theia/collaboration/disableEditing', 'Disable Workspace Editing'), + // detail: nls.localize('theia/collaboration/disableEditingDetail', 'Restrict others from making changes to your workspace.'), + // iconClasses: codiconArray('lock'), + // execute: () => { + // if (this.currentInstance) { + // this.currentInstance.readonly = true; + // } + // } + // }); + // } + } + items.push({ + label: nls.localize('theia/collaboration/end', 'End Collaboration Session'), + detail: nls.localize('theia/collaboration/endDetail', 'Terminate the session, cease content sharing, and revoke access for others.'), + iconClasses: codiconArray('circle-slash'), + execute: () => this.currentInstance?.dispose() + }); + await this.quickInputService?.showQuickPick(items, { + placeholder: nls.localize('theia/collaboration/whatToDo', 'What would you like to do with other collaborators?') + }); + } + + protected async onStatusConnectedClick(code: string): Promise { + const items: QuickPickItem[] = [{ + label: nls.localize('theia/collaboration/invite', 'Invite Others'), + detail: nls.localize('theia/collaboration/inviteDetail', 'Copy the invitation code for sharing it with others to join the session.'), + iconClasses: codiconArray('clippy'), + execute: () => this.displayCopyNotification(code) + }]; + items.push({ + label: nls.localize('theia/collaboration/leave', 'Leave Collaboration Session'), + detail: nls.localize('theia/collaboration/leaveDetail', 'Disconnect from the current collaboration session and close the workspace.'), + iconClasses: codiconArray('circle-slash'), + execute: () => this.currentInstance?.dispose() + }); + await this.quickInputService?.showQuickPick(items, { + placeholder: nls.localize('theia/collaboration/whatToDo', 'What would you like to do with other collaborators?') + }); + } + + protected async setStatusBarEntryDefault(): Promise { + await this.setStatusBarEntry({ + text: '$(codicon-live-share) ' + nls.localize('theia/collaboration/collaborate', 'Collaborate'), + tooltip: nls.localize('theia/collaboration/startSession', 'Start or join collaboration session'), + onclick: () => this.onStatusDefaultClick() + }); + } + + protected async setStatusBarEntryShared(code: string): Promise { + await this.setStatusBarEntry({ + text: '$(codicon-broadcast) ' + nls.localizeByDefault('Shared'), + tooltip: nls.localize('theia/collaboration/sharedSession', 'Shared a collaboration session'), + onclick: () => this.onStatusSharedClick(code) + }); + } + + protected async setStatusBarEntryConnected(code: string): Promise { + await this.setStatusBarEntry({ + text: '$(codicon-broadcast) ' + nls.localize('theia/collaboration/connected', 'Connected'), + tooltip: nls.localize('theia/collaboration/connectedSession', 'Connected to a collaboration session'), + onclick: () => this.onStatusConnectedClick(code) + }); + } + + protected async setStatusBarEntry(entry: Omit): Promise { + await this.statusBar.setElement(COLLABORATION_STATUS_BAR_ID, { + ...entry, + alignment: StatusBarAlignment.LEFT, + priority: 5 + }); + } + + protected async getCollaborationServerUrl(): Promise { + const serverUrlVariable = await this.envVariables.getValue(COLLABORATION_SERVER_URL); + return serverUrlVariable?.value || DEFAULT_COLLABORATION_SERVER_URL; + } + + registerCommands(commands: CommandRegistry): void { + commands.registerCommand(CollaborationCommands.CREATE_ROOM, { + execute: async () => { + const cancelTokenSource = new CancellationTokenSource(); + const progress = await this.messageService.showProgress({ + text: nls.localize('theia/collaboration/creatingRoom', 'Creating Session'), + options: { + cancelable: true + } + }, () => cancelTokenSource.cancel()); + try { + const authHandler = await this.connectionProvider.promise; + const roomClaim = await authHandler.createRoom({ + reporter: info => progress.report({ message: info.message }), + abortSignal: this.toAbortSignal(cancelTokenSource.token) + }); + if (roomClaim.loginToken) { + localStorage.setItem(COLLABORATION_AUTH_TOKEN, roomClaim.loginToken); + } + this.currentInstance?.dispose(); + const connection = await authHandler.connect(roomClaim.roomToken); + this.currentInstance = this.collaborationInstanceFactory({ + role: 'host', + connection + }); + this.currentInstance.onDidClose(() => { + this.setStatusBarEntryDefault(); + }); + const roomCode = roomClaim.roomId; + this.setStatusBarEntryShared(roomCode); + this.displayCopyNotification(roomCode, true); + } catch (err) { + await this.messageService.error(nls.localize('theia/collaboration/failedCreate', 'Failed to create room: {0}', err.message)); + } finally { + progress.cancel(); + } + } + }); + commands.registerCommand(CollaborationCommands.JOIN_ROOM, { + execute: async () => { + let joinRoomProgress: Progress | undefined; + const cancelTokenSource = new CancellationTokenSource(); + try { + const authHandler = await this.connectionProvider.promise; + const id = await this.quickInputService?.input({ + placeHolder: nls.localize('theia/collaboration/enterCode', 'Enter collaboration session code') + }); + if (!id) { + return; + } + joinRoomProgress = await this.messageService.showProgress({ + text: nls.localize('theia/collaboration/joiningRoom', 'Joining Session'), + options: { + cancelable: true + } + }, () => cancelTokenSource.cancel()); + const roomClaim = await authHandler.joinRoom({ + roomId: id, + reporter: info => joinRoomProgress?.report({ message: info.message }), + abortSignal: this.toAbortSignal(cancelTokenSource.token) + }); + joinRoomProgress.cancel(); + if (roomClaim.loginToken) { + localStorage.setItem(COLLABORATION_AUTH_TOKEN, roomClaim.loginToken); + } + this.currentInstance?.dispose(); + const connection = await authHandler.connect(roomClaim.roomToken, roomClaim.host); + this.currentInstance = this.collaborationInstanceFactory({ + role: 'guest', + connection + }); + this.currentInstance.onDidClose(() => { + this.setStatusBarEntryDefault(); + }); + this.setStatusBarEntryConnected(roomClaim.roomId); + } catch (err) { + joinRoomProgress?.cancel(); + await this.messageService.error(nls.localize('theia/collaboration/failedJoin', 'Failed to join room: {0}', err.message)); + } + } + }); + } + + protected toAbortSignal(...tokens: CancellationToken[]): AbortSignal { + const controller = new AbortController(); + tokens.forEach(token => token.onCancellationRequested(() => controller.abort())); + return controller.signal; + } + + protected async displayCopyNotification(code: string, firstTime = false): Promise { + navigator.clipboard.writeText(code); + const notification = nls.localize('theia/collaboration/copiedInvitation', 'Invitation code copied to clipboard.'); + if (firstTime) { + // const makeReadonly = nls.localize('theia/collaboration/makeReadonly', 'Make readonly'); + const copyAgain = nls.localize('theia/collaboration/copyAgain', 'Copy Again'); + const copyResult = await this.messageService.info( + notification, + // makeReadonly, + copyAgain + ); + // if (copyResult === makeReadonly && this.currentInstance) { + // this.currentInstance.readonly = true; + // } + if (copyResult === copyAgain) { + navigator.clipboard.writeText(code); + } + } else { + await this.messageService.info( + notification + ); + } + } +} diff --git a/packages/collaboration/src/browser/collaboration-frontend-module.ts b/packages/collaboration/src/browser/collaboration-frontend-module.ts new file mode 100644 index 0000000000000..f0b9080e3113c --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-frontend-module.ts @@ -0,0 +1,37 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CommandContribution } from '@theia/core'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { WorkspaceService } from '@theia/workspace/lib/browser'; +import { CollaborationColorService } from './collaboration-color-service'; +import { CollaborationFrontendContribution } from './collaboration-frontend-contribution'; +import { CollaborationInstance, CollaborationInstanceFactory, CollaborationInstanceOptions, createCollaborationInstanceContainer } from './collaboration-instance'; +import { CollaborationUtils } from './collaboration-utils'; +import { CollaborationWorkspaceService } from './collaboration-workspace-service'; + +export default new ContainerModule((bind, _, __, rebind) => { + bind(CollaborationWorkspaceService).toSelf().inSingletonScope(); + rebind(WorkspaceService).toService(CollaborationWorkspaceService); + bind(CollaborationUtils).toSelf().inSingletonScope(); + bind(CollaborationFrontendContribution).toSelf().inSingletonScope(); + bind(CommandContribution).toService(CollaborationFrontendContribution); + bind(CollaborationInstanceFactory).toFactory(context => (options: CollaborationInstanceOptions) => { + const container = createCollaborationInstanceContainer(context.container, options); + return container.get(CollaborationInstance); + }); + bind(CollaborationColorService).toSelf().inSingletonScope(); +}); diff --git a/packages/collaboration/src/browser/collaboration-instance.ts b/packages/collaboration/src/browser/collaboration-instance.ts new file mode 100644 index 0000000000000..00c89e1908a77 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-instance.ts @@ -0,0 +1,819 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as types from 'open-collaboration-protocol'; +import * as Y from 'yjs'; +import * as awarenessProtocol from 'y-protocols/awareness'; + +import { Disposable, DisposableCollection, Emitter, Event, MessageService, URI, nls } from '@theia/core'; +import { Container, inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; +import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell'; +import { EditorManager } from '@theia/editor/lib/browser/editor-manager'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service'; +import { CollaborationWorkspaceService } from './collaboration-workspace-service'; +import { Range as MonacoRange } from '@theia/monaco-editor-core'; +import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { EditorDecoration, EditorWidget, Selection, TextEditorDocument, TrackedRangeStickiness } from '@theia/editor/lib/browser'; +import { DecorationStyle, OpenerService, SaveReason } from '@theia/core/lib/browser'; +import { CollaborationFileSystemProvider, CollaborationURI } from './collaboration-file-system-provider'; +import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; +import { CollaborationColorService } from './collaboration-color-service'; +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; +import { FileChange, FileChangeType, FileOperation } from '@theia/filesystem/lib/common/files'; +import { OpenCollaborationYjsProvider } from 'open-collaboration-yjs'; +import { createMutex } from 'lib0/mutex'; +import { CollaborationUtils } from './collaboration-utils'; +import debounce = require('@theia/core/shared/lodash.debounce'); + +export const CollaborationInstanceFactory = Symbol('CollaborationInstanceFactory'); +export type CollaborationInstanceFactory = (connection: CollaborationInstanceOptions) => CollaborationInstance; + +export const CollaborationInstanceOptions = Symbol('CollaborationInstanceOptions'); +export interface CollaborationInstanceOptions { + role: 'host' | 'guest'; + connection: types.ProtocolBroadcastConnection; +} + +export function createCollaborationInstanceContainer(parent: interfaces.Container, options: CollaborationInstanceOptions): Container { + const child = new Container(); + child.parent = parent; + child.bind(CollaborationInstance).toSelf().inTransientScope(); + child.bind(CollaborationInstanceOptions).toConstantValue(options); + return child; +} + +export interface DisposablePeer extends Disposable { + peer: types.Peer; +} + +export const COLLABORATION_SELECTION = 'theia-collaboration-selection'; +export const COLLABORATION_SELECTION_MARKER = 'theia-collaboration-selection-marker'; +export const COLLABORATION_SELECTION_INVERTED = 'theia-collaboration-selection-inverted'; + +@injectable() +export class CollaborationInstance implements Disposable { + + @inject(MessageService) + protected readonly messageService: MessageService; + + @inject(CollaborationWorkspaceService) + protected readonly workspaceService: CollaborationWorkspaceService; + + @inject(FileService) + protected readonly fileService: FileService; + + @inject(MonacoTextModelService) + protected readonly monacoModelService: MonacoTextModelService; + + @inject(EditorManager) + protected readonly editorManager: EditorManager; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(ApplicationShell) + protected readonly shell: ApplicationShell; + + @inject(CollaborationInstanceOptions) + protected readonly options: CollaborationInstanceOptions; + + @inject(CollaborationColorService) + protected readonly collaborationColorService: CollaborationColorService; + + @inject(CollaborationUtils) + protected readonly utils: CollaborationUtils; + + protected identity = new Deferred(); + protected peers = new Map(); + protected yjs = new Y.Doc(); + protected yjsAwareness = new awarenessProtocol.Awareness(this.yjs); + protected yjsProvider: OpenCollaborationYjsProvider; + protected colorIndex = 0; + protected editorDecorations = new Map(); + protected fileSystem?: CollaborationFileSystemProvider; + protected permissions: types.Permissions = { + readonly: false + }; + + protected onDidCloseEmitter = new Emitter(); + + get onDidClose(): Event { + return this.onDidCloseEmitter.event; + } + + protected toDispose = new DisposableCollection(); + protected _readonly = false; + + get readonly(): boolean { + return this._readonly; + } + + set readonly(value: boolean) { + if (value !== this.readonly) { + if (this.options.role === 'guest' && this.fileSystem) { + this.fileSystem.readonly = value; + } else if (this.options.role === 'host') { + this.options.connection.room.updatePermissions({ + ...(this.permissions ?? {}), + readonly: value + }); + } + if (this.permissions) { + this.permissions.readonly = value; + } + this._readonly = value; + } + } + + get isHost(): boolean { + return this.options.role === 'host'; + } + + get host(): types.Peer { + return Array.from(this.peers.values()).find(e => e.peer.host)!.peer; + } + + @postConstruct() + protected init(): void { + const connection = this.options.connection; + connection.onDisconnect(() => this.dispose()); + connection.onConnectionError(message => { + this.messageService.error(message); + this.dispose(); + }); + this.yjsProvider = new OpenCollaborationYjsProvider(connection, this.yjs, this.yjsAwareness); + this.yjsProvider.connect(); + this.toDispose.push(Disposable.create(() => this.yjs.destroy())); + this.toDispose.push(this.yjsProvider); + this.toDispose.push(connection); + this.toDispose.push(this.onDidCloseEmitter); + + this.registerProtocolEvents(connection); + this.registerEditorEvents(connection); + this.registerFileSystemEvents(connection); + + if (this.isHost) { + this.registerFileSystemChanges(); + } + } + + protected registerProtocolEvents(connection: types.ProtocolBroadcastConnection): void { + connection.peer.onJoinRequest(async (_, user) => { + const allow = nls.localizeByDefault('Allow'); + const deny = nls.localizeByDefault('Deny'); + const result = await this.messageService.info( + nls.localize('theia/collaboration/userWantsToJoin', "User '{0}' wants to join the collaboration room", user.email ? `${user.name} (${user.email})` : user.name), + allow, + deny + ); + if (result === allow) { + const roots = await this.workspaceService.roots; + return { + workspace: { + name: this.workspaceService.workspace?.name ?? nls.localize('theia/collaboration/collaboration', 'Collaboration'), + folders: roots.map(e => e.name) + } + }; + } else { + return undefined; + } + }); + connection.room.onJoin(async (_, peer) => { + this.addPeer(peer); + if (this.isHost) { + const roots = await this.workspaceService.roots; + const data: types.InitData = { + protocol: types.VERSION, + host: await this.identity.promise, + guests: Array.from(this.peers.values()).map(e => e.peer), + capabilities: {}, + permissions: this.permissions, + workspace: { + name: this.workspaceService.workspace?.name ?? nls.localize('theia/collaboration/collaboration', 'Collaboration'), + folders: roots.map(e => e.name) + } + }; + connection.peer.init(peer.id, data); + } + }); + connection.room.onLeave((_, peer) => { + this.peers.get(peer.id)?.dispose(); + }); + connection.room.onClose(() => { + this.dispose(); + }); + connection.room.onPermissions((_, permissions) => { + if (this.fileSystem) { + this.fileSystem.readonly = permissions.readonly; + } + }); + connection.peer.onInfo((_, peer) => { + this.yjsAwareness.setLocalStateField('peer', peer.id); + this.identity.resolve(peer); + }); + connection.peer.onInit(async (_, data) => { + await this.initialize(data); + }); + } + + protected registerEditorEvents(connection: types.ProtocolBroadcastConnection): void { + for (const model of this.monacoModelService.models) { + if (this.isSharedResource(new URI(model.uri))) { + this.registerModelUpdate(model); + } + } + this.toDispose.push(this.monacoModelService.onDidCreate(newModel => { + if (this.isSharedResource(new URI(newModel.uri))) { + this.registerModelUpdate(newModel); + } + })); + this.toDispose.push(this.editorManager.onCreated(widget => { + if (this.isSharedResource(widget.getResourceUri())) { + this.registerPresenceUpdate(widget); + } + })); + this.getOpenEditors().forEach(widget => { + if (this.isSharedResource(widget.getResourceUri())) { + this.registerPresenceUpdate(widget); + } + }); + this.shell.onDidChangeActiveWidget(e => { + if (e.newValue instanceof EditorWidget) { + this.updateEditorPresence(e.newValue); + } + }); + + this.yjsAwareness.on('change', () => { + this.rerenderPresence(); + }); + + connection.editor.onOpen(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + await this.openUri(uri); + } else { + throw new Error('Could find file: ' + path); + } + return undefined; + }); + } + + protected isSharedResource(resource?: URI): boolean { + if (!resource) { + return false; + } + return this.isHost ? resource.scheme === 'file' : resource.scheme === CollaborationURI.scheme; + } + + protected registerFileSystemEvents(connection: types.ProtocolBroadcastConnection): void { + connection.fs.onReadFile(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + const content = await this.fileService.readFile(uri); + return { + content: content.value.buffer + }; + } else { + throw new Error('Could find file: ' + path); + } + }); + connection.fs.onReaddir(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + const resolved = await this.fileService.resolve(uri); + if (resolved.children) { + const dir: Record = {}; + for (const child of resolved.children) { + dir[child.name] = child.isDirectory ? types.FileType.Directory : types.FileType.File; + } + return dir; + } else { + return {}; + } + } else { + throw new Error('Could find directory: ' + path); + } + }); + connection.fs.onStat(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + const content = await this.fileService.resolve(uri, { + resolveMetadata: true + }); + return { + type: content.isDirectory ? types.FileType.Directory : types.FileType.File, + ctime: content.ctime, + mtime: content.mtime, + size: content.size, + permissions: content.isReadonly ? types.FilePermission.Readonly : undefined + }; + } else { + throw new Error('Could find file: ' + path); + } + }); + connection.fs.onWriteFile(async (_, path, data) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + const model = this.getModel(uri); + if (model) { + const content = new TextDecoder().decode(data.content); + if (content !== model.getText()) { + model.textEditorModel.setValue(content); + } + await model.save({ saveReason: SaveReason.Manual }); + } else { + await this.fileService.createFile(uri, BinaryBuffer.wrap(data.content)); + } + } else { + throw new Error('Could find file: ' + path); + } + }); + connection.fs.onMkdir(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + await this.fileService.createFolder(uri); + } else { + throw new Error('Could find path: ' + path); + } + }); + connection.fs.onDelete(async (_, path) => { + const uri = this.utils.getResourceUri(path); + if (uri) { + await this.fileService.delete(uri); + } else { + throw new Error('Could find entry: ' + path); + } + }); + connection.fs.onRename(async (_, from, to) => { + const fromUri = this.utils.getResourceUri(from); + const toUri = this.utils.getResourceUri(to); + if (fromUri && toUri) { + await this.fileService.move(fromUri, toUri); + } else { + throw new Error('Could find entries: ' + from + ' -> ' + to); + } + }); + connection.fs.onChange(async (_, event) => { + // Only guests need to handle file system changes + if (!this.isHost && this.fileSystem) { + const changes: FileChange[] = []; + for (const change of event.changes) { + const uri = this.utils.getResourceUri(change.path); + if (uri) { + changes.push({ + type: change.type === types.FileChangeEventType.Create + ? FileChangeType.ADDED + : change.type === types.FileChangeEventType.Update + ? FileChangeType.UPDATED + : FileChangeType.DELETED, + resource: uri + }); + } + } + this.fileSystem.triggerEvent(changes); + } + }); + } + + protected rerenderPresence(...widgets: EditorWidget[]): void { + const decorations = new Map(); + const states = this.yjsAwareness.getStates() as Map; + for (const [clientID, state] of states.entries()) { + if (clientID === this.yjs.clientID) { + // Ignore own awareness state + continue; + } + const peer = state.peer; + if (!state.selection || !this.peers.has(peer)) { + continue; + } + if (!types.ClientTextSelection.is(state.selection)) { + continue; + } + const { path, textSelections } = state.selection; + const selection = textSelections[0]; + if (!selection) { + continue; + } + const uri = this.utils.getResourceUri(path); + if (uri) { + const model = this.getModel(uri); + if (model) { + let existing = decorations.get(path); + if (!existing) { + existing = []; + decorations.set(path, existing); + } + const forward = selection.direction === types.SelectionDirection.LeftToRight; + let startIndex = Y.createAbsolutePositionFromRelativePosition(selection.start, this.yjs); + let endIndex = Y.createAbsolutePositionFromRelativePosition(selection.end, this.yjs); + if (startIndex && endIndex) { + if (startIndex.index > endIndex.index) { + [startIndex, endIndex] = [endIndex, startIndex]; + } + const start = model.positionAt(startIndex.index); + const end = model.positionAt(endIndex.index); + const inverted = (forward && end.line === 0) || (!forward && start.line === 0); + const range = { + start, + end + }; + const contentClassNames: string[] = [COLLABORATION_SELECTION_MARKER, `${COLLABORATION_SELECTION_MARKER}-${peer}`]; + if (inverted) { + contentClassNames.push(COLLABORATION_SELECTION_INVERTED); + } + const item: EditorDecoration = { + range, + options: { + className: `${COLLABORATION_SELECTION} ${COLLABORATION_SELECTION}-${peer}`, + beforeContentClassName: !forward ? contentClassNames.join(' ') : undefined, + afterContentClassName: forward ? contentClassNames.join(' ') : undefined, + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + } + }; + existing.push(item); + } + } + } + } + this.rerenderPresenceDecorations(decorations, ...widgets); + } + + protected rerenderPresenceDecorations(decorations: Map, ...widgets: EditorWidget[]): void { + for (const editor of new Set(this.getOpenEditors().concat(widgets))) { + const uri = editor.getResourceUri(); + const path = this.utils.getProtocolPath(uri); + if (path) { + const old = this.editorDecorations.get(editor) ?? []; + this.editorDecorations.set(editor, editor.editor.deltaDecorations({ + newDecorations: decorations.get(path) ?? [], + oldDecorations: old + })); + } + } + } + + protected registerFileSystemChanges(): void { + // Event listener for disk based events + this.fileService.onDidFilesChange(event => { + const changes: types.FileChange[] = []; + for (const change of event.changes) { + const path = this.utils.getProtocolPath(change.resource); + if (path) { + let type: types.FileChangeEventType | undefined; + if (change.type === FileChangeType.ADDED) { + type = types.FileChangeEventType.Create; + } else if (change.type === FileChangeType.DELETED) { + type = types.FileChangeEventType.Delete; + } + // Updates to files on disk are not sent + if (type !== undefined) { + changes.push({ + path, + type + }); + } + } + } + if (changes.length) { + this.options.connection.fs.change({ changes }); + } + }); + // Event listener for user based events + this.fileService.onDidRunOperation(operation => { + const path = this.utils.getProtocolPath(operation.resource); + if (!path) { + return; + } + let type = types.FileChangeEventType.Update; + if (operation.isOperation(FileOperation.CREATE) || operation.isOperation(FileOperation.COPY)) { + type = types.FileChangeEventType.Create; + } else if (operation.isOperation(FileOperation.DELETE)) { + type = types.FileChangeEventType.Delete; + } + this.options.connection.fs.change({ + changes: [{ + path, + type + }] + }); + }); + } + + protected async registerPresenceUpdate(widget: EditorWidget): Promise { + const uri = widget.getResourceUri(); + const path = this.utils.getProtocolPath(uri); + if (path) { + if (!this.isHost) { + this.options.connection.editor.open(this.host.id, path); + } + let currentSelection = widget.editor.selection; + // // Update presence information when the selection changes + const selectionChange = widget.editor.onSelectionChanged(selection => { + if (!this.rangeEqual(currentSelection, selection)) { + this.updateEditorPresence(widget); + currentSelection = selection; + } + }); + const widgetDispose = widget.onDidDispose(() => { + widgetDispose.dispose(); + selectionChange.dispose(); + // Remove presence information when the editor closes + const state = this.yjsAwareness.getLocalState(); + if (state?.currentSelection?.path === path) { + delete state.currentSelection; + } + this.yjsAwareness.setLocalState(state); + }); + this.toDispose.push(selectionChange); + this.toDispose.push(widgetDispose); + this.rerenderPresence(widget); + } + } + + protected updateEditorPresence(widget: EditorWidget): void { + const uri = widget.getResourceUri(); + const path = this.utils.getProtocolPath(uri); + if (path) { + const ytext = this.yjs.getText(path); + const selection = widget.editor.selection; + let start = widget.editor.document.offsetAt(selection.start); + let end = widget.editor.document.offsetAt(selection.end); + if (start > end) { + [start, end] = [end, start]; + } + const direction = selection.direction === 'ltr' + ? types.SelectionDirection.LeftToRight + : types.SelectionDirection.RightToLeft; + const editorSelection: types.RelativeTextSelection = { + start: Y.createRelativePositionFromTypeIndex(ytext, start), + end: Y.createRelativePositionFromTypeIndex(ytext, end), + direction + }; + const textSelection: types.ClientTextSelection = { + path, + textSelections: [editorSelection] + }; + this.setSharedSelection(textSelection); + } + } + + protected setSharedSelection(selection?: types.ClientSelection): void { + this.yjsAwareness.setLocalStateField('selection', selection); + } + + protected rangeEqual(a: Range, b: Range): boolean { + return a.start.line === b.start.line + && a.start.character === b.start.character + && a.end.line === b.end.line + && a.end.character === b.end.character; + } + + async initialize(data: types.InitData): Promise { + this.permissions = data.permissions; + this.readonly = data.permissions.readonly; + for (const peer of [...data.guests, data.host]) { + this.addPeer(peer); + } + this.fileSystem = new CollaborationFileSystemProvider(this.options.connection, data.host, this.yjs); + this.fileSystem.readonly = this.readonly; + this.toDispose.push(this.fileService.registerProvider(CollaborationURI.scheme, this.fileSystem)); + const workspaceDisposable = await this.workspaceService.setHostWorkspace(data.workspace, this.options.connection); + this.toDispose.push(workspaceDisposable); + } + + protected addPeer(peer: types.Peer): void { + const collection = new DisposableCollection(); + collection.push(this.createPeerStyleSheet(peer)); + collection.push(Disposable.create(() => this.peers.delete(peer.id))); + const disposablePeer = { + peer, + dispose: () => collection.dispose() + }; + this.peers.set(peer.id, disposablePeer); + } + + protected createPeerStyleSheet(peer: types.Peer): Disposable { + const style = DecorationStyle.createStyleElement(`${peer.id}-collaboration-selection`); + const colors = this.collaborationColorService.getColors(); + const sheet = style.sheet!; + const color = colors[this.colorIndex++ % colors.length]; + const colorString = `rgb(${color.r}, ${color.g}, ${color.b})`; + sheet.insertRule(` + .${COLLABORATION_SELECTION}-${peer.id} { + opacity: 0.2; + background: ${colorString}; + } + `); + sheet.insertRule(` + .${COLLABORATION_SELECTION_MARKER}-${peer.id} { + background: ${colorString}; + border-color: ${colorString}; + }` + ); + sheet.insertRule(` + .${COLLABORATION_SELECTION_MARKER}-${peer.id}::after { + content: "${peer.name}"; + background: ${colorString}; + color: ${this.collaborationColorService.requiresDarkFont(color) + ? this.collaborationColorService.dark + : this.collaborationColorService.light}; + z-index: ${(100 + this.colorIndex).toFixed()} + }` + ); + return Disposable.create(() => style.remove()); + } + + protected getOpenEditors(uri?: URI): EditorWidget[] { + const widgets = this.shell.widgets; + let editors = widgets.filter(e => e instanceof EditorWidget) as EditorWidget[]; + if (uri) { + const uriString = uri.toString(); + editors = editors.filter(e => e.getResourceUri()?.toString() === uriString); + } + return editors; + } + + protected createSelectionFromRelative(selection: types.RelativeTextSelection, model: MonacoEditorModel): Selection | undefined { + const start = Y.createAbsolutePositionFromRelativePosition(selection.start, this.yjs); + const end = Y.createAbsolutePositionFromRelativePosition(selection.end, this.yjs); + if (start && end) { + return { + start: model.positionAt(start.index), + end: model.positionAt(end.index), + direction: selection.direction === types.SelectionDirection.LeftToRight ? 'ltr' : 'rtl' + }; + } + return undefined; + } + + protected createRelativeSelection(selection: Selection, model: TextEditorDocument, ytext: Y.Text): types.RelativeTextSelection { + const start = Y.createRelativePositionFromTypeIndex(ytext, model.offsetAt(selection.start)); + const end = Y.createRelativePositionFromTypeIndex(ytext, model.offsetAt(selection.end)); + return { + start, + end, + direction: selection.direction === 'ltr' + ? types.SelectionDirection.LeftToRight + : types.SelectionDirection.RightToLeft + }; + } + + protected readonly yjsMutex = createMutex(); + + protected registerModelUpdate(model: MonacoEditorModel): void { + let updating = false; + const modelPath = this.utils.getProtocolPath(new URI(model.uri)); + if (!modelPath) { + return; + } + const unknownModel = !this.yjs.share.has(modelPath); + const ytext = this.yjs.getText(modelPath); + const modelText = model.textEditorModel.getValue(); + if (this.isHost && unknownModel) { + // If we are hosting the room, set the initial content + // First off, reset the shared content to be empty + // This has the benefit of effectively clearing the memory of the shared content across all peers + // This is important because the shared content accumulates changes/memory usage over time + this.resetYjsText(ytext, modelText); + } else { + this.options.connection.editor.open(this.host.id, modelPath); + } + // The Ytext instance is our source of truth for the model content + // Sometimes (especially after a lot of sequential undo/redo operations) our model content can get out of sync + // This resyncs the model content with the Ytext content after a delay + const resyncDebounce = debounce(() => { + this.yjsMutex(() => { + const newContent = ytext.toString(); + if (model.textEditorModel.getValue() !== newContent) { + updating = true; + this.softReplaceModel(model, newContent); + updating = false; + } + }); + }, 200); + const disposable = new DisposableCollection(); + disposable.push(model.onDidChangeContent(e => { + if (updating) { + return; + } + this.yjsMutex(() => { + this.yjs.transact(() => { + for (const change of e.contentChanges) { + ytext.delete(change.rangeOffset, change.rangeLength); + ytext.insert(change.rangeOffset, change.text); + } + }); + resyncDebounce(); + }); + })); + + const observer = (textEvent: Y.YTextEvent) => { + if (textEvent.transaction.local || model.getText() === ytext.toString()) { + // Ignore local changes and changes that are already reflected in the model + return; + } + this.yjsMutex(() => { + updating = true; + try { + let index = 0; + const operations: { range: MonacoRange, text: string }[] = []; + textEvent.delta.forEach(delta => { + if (delta.retain !== undefined) { + index += delta.retain; + } else if (delta.insert !== undefined) { + const pos = model.textEditorModel.getPositionAt(index); + const range = new MonacoRange(pos.lineNumber, pos.column, pos.lineNumber, pos.column); + const insert = delta.insert as string; + operations.push({ range, text: insert }); + index += insert.length; + } else if (delta.delete !== undefined) { + const pos = model.textEditorModel.getPositionAt(index); + const endPos = model.textEditorModel.getPositionAt(index + delta.delete); + const range = new MonacoRange(pos.lineNumber, pos.column, endPos.lineNumber, endPos.column); + operations.push({ range, text: '' }); + } + }); + this.pushChangesToModel(model, operations); + } catch (err) { + console.error(err); + } + resyncDebounce(); + updating = false; + }); + }; + + ytext.observe(observer); + disposable.push(Disposable.create(() => ytext.unobserve(observer))); + model.onDispose(() => disposable.dispose()); + } + + protected resetYjsText(yjsText: Y.Text, text: string): void { + this.yjs.transact(() => { + yjsText.delete(0, yjsText.length); + yjsText.insert(0, text); + }); + } + + protected getModel(uri: URI): MonacoEditorModel | undefined { + const existing = this.monacoModelService.models.find(e => e.uri === uri.toString()); + if (existing) { + return existing; + } else { + return undefined; + } + } + + protected pushChangesToModel(model: MonacoEditorModel, changes: { range: MonacoRange, text: string, forceMoveMarkers?: boolean }[]): void { + const editor = MonacoEditor.findByDocument(this.editorManager, model)[0]; + const cursorState = editor?.getControl().getSelections() ?? []; + model.textEditorModel.pushStackElement(); + try { + model.textEditorModel.pushEditOperations(cursorState, changes, () => cursorState); + model.textEditorModel.pushStackElement(); + } catch (err) { + console.error(err); + } + } + + protected softReplaceModel(model: MonacoEditorModel, text: string): void { + this.pushChangesToModel(model, [{ + range: model.textEditorModel.getFullModelRange(), + text, + forceMoveMarkers: false + }]); + } + + protected async openUri(uri: URI): Promise { + const ref = await this.monacoModelService.createModelReference(uri); + if (ref.object) { + this.toDispose.push(ref); + } else { + ref.dispose(); + } + } + + dispose(): void { + for (const peer of this.peers.values()) { + peer.dispose(); + } + this.onDidCloseEmitter.fire(); + this.toDispose.dispose(); + } +} diff --git a/packages/collaboration/src/browser/collaboration-utils.ts b/packages/collaboration/src/browser/collaboration-utils.ts new file mode 100644 index 0000000000000..c1398033a4026 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-utils.ts @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { URI } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CollaborationWorkspaceService } from './collaboration-workspace-service'; + +@injectable() +export class CollaborationUtils { + + @inject(CollaborationWorkspaceService) + protected readonly workspaceService: CollaborationWorkspaceService; + + getProtocolPath(uri?: URI): string | undefined { + if (!uri) { + return undefined; + } + const path = uri.path.toString(); + const roots = this.workspaceService.tryGetRoots(); + for (const root of roots) { + const rootUri = root.resource.path.toString() + '/'; + if (path.startsWith(rootUri)) { + return root.name + '/' + path.substring(rootUri.length); + } + } + return undefined; + } + + getResourceUri(path?: string): URI | undefined { + if (!path) { + return undefined; + } + const parts = path.split('/'); + const root = parts[0]; + const rest = parts.slice(1); + const stat = this.workspaceService.tryGetRoots().find(e => e.name === root); + if (stat) { + const uriPath = stat.resource.path.join(...rest); + const uri = stat.resource.withPath(uriPath); + return uri; + } else { + return undefined; + } + } + +} diff --git a/packages/collaboration/src/browser/collaboration-workspace-service.ts b/packages/collaboration/src/browser/collaboration-workspace-service.ts new file mode 100644 index 0000000000000..ac1cd59914e67 --- /dev/null +++ b/packages/collaboration/src/browser/collaboration-workspace-service.ts @@ -0,0 +1,69 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { nls } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol'; +import { FileStat } from '@theia/filesystem/lib/common/files'; +import { WorkspaceService } from '@theia/workspace/lib/browser'; +import { Workspace, ProtocolBroadcastConnection } from 'open-collaboration-protocol'; +import { CollaborationURI } from './collaboration-file-system-provider'; + +@injectable() +export class CollaborationWorkspaceService extends WorkspaceService { + + protected collabWorkspace?: Workspace; + protected connection?: ProtocolBroadcastConnection; + + async setHostWorkspace(workspace: Workspace, connection: ProtocolBroadcastConnection): Promise { + this.collabWorkspace = workspace; + this.connection = connection; + await this.setWorkspace({ + isDirectory: false, + isFile: true, + isReadonly: false, + isSymbolicLink: false, + name: nls.localize('theia/collaboration/collaborationWorkspace', 'Collaboration Workspace'), + resource: CollaborationURI.create(this.collabWorkspace) + }); + return Disposable.create(() => { + this.collabWorkspace = undefined; + this.connection = undefined; + this.setWorkspace(undefined); + }); + } + + protected override async computeRoots(): Promise { + if (this.collabWorkspace) { + return this.collabWorkspace.folders.map(e => this.entryToStat(e)); + } else { + return super.computeRoots(); + } + } + + protected entryToStat(entry: string): FileStat { + const uri = CollaborationURI.create(this.collabWorkspace!, entry); + return { + resource: uri, + name: entry, + isDirectory: true, + isFile: false, + isReadonly: false, + isSymbolicLink: false + }; + } + +} diff --git a/packages/collaboration/src/browser/style/index.css b/packages/collaboration/src/browser/style/index.css new file mode 100644 index 0000000000000..1d1eac50c03c8 --- /dev/null +++ b/packages/collaboration/src/browser/style/index.css @@ -0,0 +1,22 @@ +.theia-collaboration-selection-marker { + position: absolute; + content: " "; + border-right: solid 2px; + border-top: solid 2px; + border-bottom: solid 2px; + height: 100%; + box-sizing: border-box; +} + +.theia-collaboration-selection-marker::after { + position: absolute; + transform: translateY(-100%); + padding: 0 4px; + border-radius: 4px 4px 4px 0px; +} + +.theia-collaboration-selection-marker.theia-collaboration-selection-inverted::after { + transform: translateY(100%); + margin-top: -2px; + border-radius: 0px 4px 4px 4px; +} diff --git a/packages/collaboration/src/package.spec.ts b/packages/collaboration/src/package.spec.ts new file mode 100644 index 0000000000000..4e6f3abdcdccd --- /dev/null +++ b/packages/collaboration/src/package.spec.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2023 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('request package', () => { + + it('should support code coverage statistics', () => true); +}); diff --git a/packages/collaboration/tsconfig.json b/packages/collaboration/tsconfig.json new file mode 100644 index 0000000000000..5920c2dd0ba35 --- /dev/null +++ b/packages/collaboration/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../core" + }, + { + "path": "../editor" + }, + { + "path": "../filesystem" + }, + { + "path": "../monaco" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 1da41c8a31246..50cf0d7d2bc03 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -268,7 +268,10 @@ export class EditorManager extends NavigatableWidgetOpenHandler { editor.revealPosition(selection); } else if (Range.is(selection)) { editor.cursor = selection.end; - editor.selection = selection; + editor.selection = { + ...selection, + direction: 'ltr' + }; editor.revealRange(selection); } } diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index 0613b7b60936f..0ee8222ac59f9 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -216,8 +216,8 @@ export interface TextEditor extends Disposable, TextEditorSelection, Navigatable cursor: Position; readonly onCursorPositionChanged: Event; - selection: Range; - readonly onSelectionChanged: Event; + selection: Selection; + readonly onSelectionChanged: Event; /** * The text editor should be revealed, @@ -297,6 +297,10 @@ export interface TextEditor extends Disposable, TextEditorSelection, Navigatable shouldDisplayDirtyDiff(): boolean; } +export interface Selection extends Range { + direction: 'ltr' | 'rtl'; +} + export interface Dimension { width: number; height: number; diff --git a/packages/monaco/src/browser/monaco-editor-model.ts b/packages/monaco/src/browser/monaco-editor-model.ts index b1ebdbaffe75f..9e0651118a8ba 100644 --- a/packages/monaco/src/browser/monaco-editor-model.ts +++ b/packages/monaco/src/browser/monaco-editor-model.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Position, Range, TextDocumentSaveReason, TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol'; +import { Position, Range, TextDocumentSaveReason } from '@theia/core/shared/vscode-languageserver-protocol'; import { TextEditorDocument, EncodingMode, FindMatchesOptions, FindMatch, EditorPreferences } from '@theia/editor/lib/browser'; import { DisposableCollection, Disposable } from '@theia/core/lib/common/disposable'; import { Emitter, Event } from '@theia/core/lib/common/event'; @@ -48,7 +48,14 @@ export interface WillSaveMonacoModelEvent { export interface MonacoModelContentChangedEvent { readonly model: MonacoEditorModel; - readonly contentChanges: TextDocumentContentChangeEvent[]; + readonly contentChanges: MonacoTextDocumentContentChange[]; +} + +export interface MonacoTextDocumentContentChange { + readonly range: Range; + readonly rangeOffset: number; + readonly rangeLength: number; + readonly text: string; } export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDocument { @@ -479,8 +486,8 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo } protected ignoreContentChanges = false; - protected readonly contentChanges: TextDocumentContentChangeEvent[] = []; - protected pushContentChanges(contentChanges: TextDocumentContentChangeEvent[]): void { + protected readonly contentChanges: MonacoTextDocumentContentChange[] = []; + protected pushContentChanges(contentChanges: MonacoTextDocumentContentChange[]): void { if (!this.ignoreContentChanges) { this.contentChanges.push(...contentChanges); } @@ -503,11 +510,12 @@ export class MonacoEditorModel implements IResolvedTextEditorModel, TextEditorDo const contentChanges = event.changes.map(change => this.asTextDocumentContentChangeEvent(change)); return { model: this, contentChanges }; } - protected asTextDocumentContentChangeEvent(change: monaco.editor.IModelContentChange): TextDocumentContentChangeEvent { + protected asTextDocumentContentChangeEvent(change: monaco.editor.IModelContentChange): MonacoTextDocumentContentChange { const range = this.m2p.asRange(change.range); + const rangeOffset = change.rangeOffset; const rangeLength = change.rangeLength; const text = change.text; - return { range, rangeLength, text }; + return { range, rangeOffset, rangeLength, text }; } protected applyEdits( diff --git a/packages/monaco/src/browser/monaco-editor.ts b/packages/monaco/src/browser/monaco-editor.ts index 5df77abe242ca..069e5ae66b4cf 100644 --- a/packages/monaco/src/browser/monaco-editor.ts +++ b/packages/monaco/src/browser/monaco-editor.ts @@ -62,6 +62,7 @@ import { IAccessibilityService } from '@theia/monaco-editor-core/esm/vs/platform import { ILanguageConfigurationService } from '@theia/monaco-editor-core/esm/vs/editor/common/languages/languageConfigurationRegistry'; import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures'; import * as objects from '@theia/monaco-editor-core/esm/vs/base/common/objects'; +import { Selection } from '@theia/editor/lib/browser/editor'; export type ServicePair = [ServiceIdentifier, T]; @@ -94,7 +95,7 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { protected editor: monaco.editor.IStandaloneCodeEditor; protected readonly onCursorPositionChangedEmitter = new Emitter(); - protected readonly onSelectionChangedEmitter = new Emitter(); + protected readonly onSelectionChangedEmitter = new Emitter(); protected readonly onFocusChangedEmitter = new Emitter(); protected readonly onDocumentContentChangedEmitter = new Emitter(); protected readonly onMouseDownEmitter = new Emitter(); @@ -192,8 +193,11 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { this.toDispose.push(codeEditor.onDidChangeCursorPosition(() => this.onCursorPositionChangedEmitter.fire(this.cursor) )); - this.toDispose.push(codeEditor.onDidChangeCursorSelection(() => - this.onSelectionChangedEmitter.fire(this.selection) + this.toDispose.push(codeEditor.onDidChangeCursorSelection(event => + this.onSelectionChangedEmitter.fire({ + ...this.m2p.asRange(event.selection), + direction: event.selection.getDirection() === monaco.SelectionDirection.LTR ? 'ltr' : 'rtl' + }) )); this.toDispose.push(codeEditor.onDidFocusEditorText(() => this.onFocusChangedEmitter.fire(this.isFocused()) @@ -261,16 +265,16 @@ export class MonacoEditor extends MonacoEditorServices implements TextEditor { return this.onCursorPositionChangedEmitter.event; } - get selection(): Range { - return this.m2p.asRange(this.editor.getSelection()!); + get selection(): Selection { + return this.m2p.asSelection(this.editor.getSelection()!); } - set selection(selection: Range) { + set selection(selection: Selection) { const range = this.p2m.asRange(selection); this.editor.setSelection(range); } - get onSelectionChanged(): Event { + get onSelectionChanged(): Event { return this.onSelectionChangedEmitter.event; } diff --git a/packages/monaco/src/browser/monaco-quick-input-service.ts b/packages/monaco/src/browser/monaco-quick-input-service.ts index 777649ceb87b6..a611b6fe33e21 100644 --- a/packages/monaco/src/browser/monaco-quick-input-service.ts +++ b/packages/monaco/src/browser/monaco-quick-input-service.ts @@ -375,21 +375,6 @@ export class MonacoQuickInputService implements QuickInputService { wrapped.activeItems = [options.activeItem]; } - wrapped.onDidAccept(() => { - if (options?.onDidAccept) { - options.onDidAccept(); - } - wrapped.hide(); - resolve(wrapped.selectedItems[0]); - }); - - wrapped.onDidHide(() => { - if (options.onDidHide) { - options.onDidHide(); - }; - wrapped.dispose(); - setTimeout(() => resolve(undefined)); - }); wrapped.onDidChangeValue((filter: string) => { if (options.onDidChangeValue) { options.onDidChangeValue(wrapped, filter); @@ -425,6 +410,20 @@ export class MonacoQuickInputService implements QuickInputService { } }); } + wrapped.onDidAccept(() => { + if (options?.onDidAccept) { + options.onDidAccept(); + } + wrapped.hide(); + resolve(wrapped.selectedItems[0]); + }); + wrapped.onDidHide(() => { + if (options?.onDidHide) { + options?.onDidHide(); + }; + wrapped.dispose(); + setTimeout(() => resolve(undefined)); + }); wrapped.show(); }).then(item => { if (item?.execute) { diff --git a/packages/monaco/src/browser/monaco-to-protocol-converter.ts b/packages/monaco/src/browser/monaco-to-protocol-converter.ts index 4b054e8fb8ab5..c88629e92b5a5 100644 --- a/packages/monaco/src/browser/monaco-to-protocol-converter.ts +++ b/packages/monaco/src/browser/monaco-to-protocol-converter.ts @@ -18,6 +18,7 @@ import { injectable } from '@theia/core/shared/inversify'; import { Position, Range } from '@theia/core/shared/vscode-languageserver-protocol'; import { RecursivePartial } from '@theia/core/lib/common/types'; import * as monaco from '@theia/monaco-editor-core'; +import { Selection } from '@theia/editor/lib/browser'; export interface MonacoRangeReplace { insert: monaco.IRange; @@ -68,4 +69,14 @@ export class MonacoToProtocolConverter { } } + asSelection(selection: monaco.Selection): Selection { + const start = this.asPosition(selection.selectionStartLineNumber, selection.selectionStartColumn); + const end = this.asPosition(selection.positionLineNumber, selection.positionColumn); + return { + start, + end, + direction: selection.getDirection() === monaco.SelectionDirection.LTR ? 'ltr' : 'rtl' + }; + } + } diff --git a/packages/preview/src/browser/preview-contribution.ts b/packages/preview/src/browser/preview-contribution.ts index 18f105327ba91..048af03188dbf 100644 --- a/packages/preview/src/browser/preview-contribution.ts +++ b/packages/preview/src/browser/preview-contribution.ts @@ -163,7 +163,10 @@ export class PreviewContribution extends NavigatableWidgetOpenHandler { const { editor } = await this.openSource(ref); editor.revealPosition(location.range.start); - editor.selection = location.range; + editor.selection = { + ...location.range, + direction: 'ltr' + }; ref.revealForSourceLine(location.range.start.line); }); ref.disposed.connect(() => disposable.dispose()); diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index a60ea862efc84..f000ffacf755f 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -544,8 +544,8 @@ export class WorkspaceRootUriAwareCommandHandler extends UriAwareCommandHandler< // eslint-disable-next-line @typescript-eslint/no-explicit-any protected override getUri(...args: any[]): URI | undefined { const uri = super.getUri(...args); - // Return the `uri` immediately if the resource exists in any of the workspace roots and is of `file` scheme. - if (uri && uri.scheme === 'file' && this.workspaceService.getWorkspaceRootUri(uri)) { + // Return the `uri` immediately if the resource exists in any of the workspace roots. + if (uri && this.workspaceService.getWorkspaceRootUri(uri)) { return uri; } // Return the first root if available. diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts index 178bd9cf98141..44eeb15e58043 100644 --- a/packages/workspace/src/browser/workspace-service.ts +++ b/packages/workspace/src/browser/workspace-service.ts @@ -660,7 +660,7 @@ export class WorkspaceService implements FrontendApplicationContribution { const rootUris: URI[] = []; for (const root of this.tryGetRoots()) { const rootUri = root.resource; - if (rootUri && rootUri.isEqualOrParent(uri)) { + if (rootUri && rootUri.scheme === uri.scheme && rootUri.isEqualOrParent(uri)) { rootUris.push(rootUri); } } diff --git a/tsconfig.json b/tsconfig.json index fec10e328dcec..53c3b18c75a75 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -60,6 +60,9 @@ { "path": "packages/callhierarchy" }, + { + "path": "packages/collaboration" + }, { "path": "packages/console" }, diff --git a/yarn.lock b/yarn.lock index 290518ecbc8d7..881c9757b4b88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3362,7 +3362,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -5666,6 +5666,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -7122,6 +7127,11 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isomorphic.js@^0.2.4: + version "0.2.5" + resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.5.tgz#13eecf36f2dba53e85d355e11bf9d4208c6f7f88" + integrity sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -7590,6 +7600,20 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lib0@^0.2.52, lib0@^0.2.94: + version "0.2.94" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.94.tgz#fc28b4b65f816599f1e2f59d3401e231709535b3" + integrity sha512-hZ3p54jL4Wpu7IOg26uC7dnEWiMyNlUrb9KoG7+xYs45WkQwpVvKFndVq2+pqLYKe1u8Fp3+zAfZHVvTK34PvQ== + dependencies: + isomorphic.js "^0.2.4" + +lib0@^0.2.85, lib0@^0.2.86: + version "0.2.93" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.93.tgz#95487c2a97657313cb1d91fbcf9f6d64b7fcd062" + integrity sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q== + dependencies: + isomorphic.js "^0.2.4" + libnpmaccess@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" @@ -9031,6 +9055,26 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open-collaboration-protocol@0.2.0, open-collaboration-protocol@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/open-collaboration-protocol/-/open-collaboration-protocol-0.2.0.tgz#f3f93f22bb5fbb46e3fd31e6bb87f52a9ce6526b" + integrity sha512-ZaLMTMyVoJJ0vPjoMXGhNZqiycbfyJPbNCkbI9uHTOYRsvZqreRAFhSd7p9RbxLJNS5xeQGNSfldrhhec94Bmg== + dependencies: + base64-js "^1.5.1" + fflate "^0.8.2" + msgpackr "^1.10.2" + semver "^7.6.2" + socket.io-client "^4.7.5" + +open-collaboration-yjs@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/open-collaboration-yjs/-/open-collaboration-yjs-0.2.0.tgz#7c7e30dba444b9f6947fe76ae02a7c3fdaec6172" + integrity sha512-HT2JU/HJObIaQMF/MHt5/5VdOnGn+bVTaTJnyYfyaa/vjqg4Z4Glas3Hc9Ua970ssP3cOIRUQoHQumM0giaxrw== + dependencies: + lib0 "^0.2.94" + open-collaboration-protocol "^0.2.0" + y-protocols "^1.0.6" + open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -10496,6 +10540,11 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semve dependencies: lru-cache "^6.0.0" +semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -10757,6 +10806,16 @@ socket.io-client@^4.5.3: engine.io-client "~6.5.2" socket.io-parser "~4.2.4" +socket.io-client@^4.7.5: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" + integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.5.2" + socket.io-parser "~4.2.4" + socket.io-parser@~4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" @@ -12467,6 +12526,13 @@ xterm@^5.3.0: resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== +y-protocols@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.6.tgz#66dad8a95752623443e8e28c0e923682d2c0d495" + integrity sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q== + dependencies: + lib0 "^0.2.85" + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -12588,6 +12654,13 @@ yazl@^2.2.2: dependencies: buffer-crc32 "~0.2.3" +yjs@^13.6.7: + version "13.6.15" + resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.15.tgz#5a2402632aabf83e5baf56342b4c82fe40859306" + integrity sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ== + dependencies: + lib0 "^0.2.86" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From 9b02b02dd3ba8459a76a813bfbb709aa851a2577 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 29 Aug 2024 10:05:00 +0200 Subject: [PATCH 353/441] Align available locales to VS Code (#14039) --- .../localization-manager/src/deepl-api.ts | 21 +- .../src/localization-manager.ts | 8 +- packages/core/i18n/nls.cs.json | 3 + packages/core/i18n/nls.de.json | 3 + packages/core/i18n/nls.es.json | 3 + packages/core/i18n/nls.fr.json | 3 + packages/core/i18n/nls.hu.json | 3 + packages/core/i18n/nls.it.json | 3 + packages/core/i18n/nls.ja.json | 3 + packages/core/i18n/nls.json | 5 +- packages/core/i18n/nls.ko.json | 555 ++++++++++++++++++ packages/core/i18n/nls.pl.json | 3 + packages/core/i18n/nls.pt-br.json | 3 + packages/core/i18n/nls.pt-pt.json | 552 ----------------- packages/core/i18n/nls.ru.json | 3 + packages/core/i18n/nls.tr.json | 555 ++++++++++++++++++ packages/core/i18n/nls.zh-cn.json | 3 + packages/core/i18n/nls.zh-tw.json | 555 ++++++++++++++++++ .../i18n/theia-localization-contribution.ts | 20 +- scripts/translation-update.js | 9 +- 20 files changed, 1742 insertions(+), 571 deletions(-) create mode 100644 packages/core/i18n/nls.ko.json delete mode 100644 packages/core/i18n/nls.pt-pt.json create mode 100644 packages/core/i18n/nls.tr.json create mode 100644 packages/core/i18n/nls.zh-tw.json diff --git a/dev-packages/localization-manager/src/deepl-api.ts b/dev-packages/localization-manager/src/deepl-api.ts index 95ee5df4635c3..aa3b620b435e5 100644 --- a/dev-packages/localization-manager/src/deepl-api.ts +++ b/dev-packages/localization-manager/src/deepl-api.ts @@ -54,7 +54,9 @@ export async function deepl( */ function coerceLanguage(parameters: DeeplParameters): void { if (parameters.target_lang === 'ZH-CN') { - parameters.target_lang = 'ZH'; + parameters.target_lang = 'ZH-HANS'; + } else if (parameters.target_lang === 'ZH-TW') { + parameters.target_lang = 'ZH-HANT'; } } @@ -101,10 +103,13 @@ export type DeeplLanguage = | 'FI' | 'FR' | 'HU' + | 'ID' | 'IT' | 'JA' + | 'KO' | 'LT' | 'LV' + | 'NB' | 'NL' | 'PL' | 'PT-PT' @@ -115,14 +120,24 @@ export type DeeplLanguage = | 'SK' | 'SL' | 'SV' + | 'TR' + | 'UK' | 'ZH-CN' + | 'ZH-TW' + | 'ZH-HANS' + | 'ZH-HANT' | 'ZH'; export const supportedLanguages = [ - 'BG', 'CS', 'DA', 'DE', 'EL', 'EN-GB', 'EN-US', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'IT', - 'JA', 'LT', 'LV', 'NL', 'PL', 'PT-PT', 'PT-BR', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'ZH-CN' + 'BG', 'CS', 'DA', 'DE', 'EL', 'EN-GB', 'EN-US', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'ID', 'IT', + 'JA', 'KO', 'LT', 'LV', 'NL', 'PL', 'PT-PT', 'PT-BR', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'TR', 'UK', 'ZH-CN', 'ZH-TW' ]; +// From https://code.visualstudio.com/docs/getstarted/locales#_available-locales +export const defaultLanguages = [ + 'ZH-CN', 'ZH-TW', 'FR', 'DE', 'IT', 'ES', 'JA', 'KO', 'RU', 'PT-BR', 'TR', 'PL', 'CS', 'HU' +] as const; + export function isSupportedLanguage(language: string): language is DeeplLanguage { return supportedLanguages.includes(language.toUpperCase()); } diff --git a/dev-packages/localization-manager/src/localization-manager.ts b/dev-packages/localization-manager/src/localization-manager.ts index 90d650e59f225..f8468d0057abc 100644 --- a/dev-packages/localization-manager/src/localization-manager.ts +++ b/dev-packages/localization-manager/src/localization-manager.ts @@ -18,7 +18,7 @@ import * as chalk from 'chalk'; import * as fs from 'fs-extra'; import * as path from 'path'; import { Localization, sortLocalization } from './common'; -import { deepl, DeeplLanguage, DeeplParameters, isSupportedLanguage, supportedLanguages } from './deepl-api'; +import { deepl, DeeplLanguage, DeeplParameters, defaultLanguages, isSupportedLanguage } from './deepl-api'; export interface LocalizationOptions { freeApi: Boolean @@ -52,8 +52,10 @@ export class LocalizationManager { languages.push(targetLanguage); } } - if (languages.length !== options.targetLanguages.length) { - console.log('Supported languages: ' + supportedLanguages.join(', ')); + if (languages.length === 0) { + // No supported languages were found, default to all supported languages + console.log('No languages were specified, defaulting to all supported languages for VS Code'); + languages.push(...defaultLanguages); } const existingTranslations: Map = new Map(); for (const targetLanguage of languages) { diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 667870259197b..c72543a040b4b 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -362,6 +362,9 @@ "alreadyRunning": "Hostovaná instance je již spuštěna.", "debugInstance": "Instance ladění", "debugMode": "Použití inspect nebo inspect-brk pro ladění Node.js", + "debugPorts": { + "debugPort": "Port, který se použije pro ladění Node.js tohoto serveru" + }, "devHost": "Vývojový hostitel", "failed": "Nepodařilo se spustit hostovanou instanci zásuvného modulu: {0}", "hostedPlugin": "Hostovaný plugin", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index f228f86b0585c..dc15c4127beee 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -362,6 +362,9 @@ "alreadyRunning": "Die gehostete Instanz läuft bereits.", "debugInstance": "Debug-Instanz", "debugMode": "Verwendung von inspect oder inspect-brk zur Fehlersuche in Node.js", + "debugPorts": { + "debugPort": "Zu verwendender Port für das Node.js-Debugging dieses Servers" + }, "devHost": "Entwicklung Host", "failed": "Die gehostete Plugin-Instanz konnte nicht ausgeführt werden: {0}", "hostedPlugin": "Gehostetes Plugin", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index a1263780b4ec3..04ec8f9efe3da 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -362,6 +362,9 @@ "alreadyRunning": "La instancia alojada ya está funcionando.", "debugInstance": "Instancia de depuración", "debugMode": "Uso de inspect o inspect-brk para la depuración de Node.js", + "debugPorts": { + "debugPort": "Puerto a usar para la depuración Node.js de este servidor" + }, "devHost": "Anfitrión del desarrollo", "failed": "Fallo en la ejecución de la instancia del plugin alojado: {0}", "hostedPlugin": "Plugin alojado", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index 8f7fac91973bf..b1811a82b60a2 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -362,6 +362,9 @@ "alreadyRunning": "L'instance hébergée est déjà en cours d'exécution.", "debugInstance": "Instance de débogage", "debugMode": "Utilisation de inspect ou inspect-brk pour le débogage de Node.js", + "debugPorts": { + "debugPort": "Port à utiliser pour le débogage Node.js de ce serveur" + }, "devHost": "Hôte de développement", "failed": "Échec de l'exécution de l'instance du plugin hébergé : {0}", "hostedPlugin": "Plugin hébergé", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index a3f94dac3b23d..0d29584db5731 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -362,6 +362,9 @@ "alreadyRunning": "A hosztolt példány már fut.", "debugInstance": "Hibakeresési példány", "debugMode": "Az inspect vagy inspect-brk használata a Node.js hibakereséshez", + "debugPorts": { + "debugPort": "A kiszolgáló Node.js hibakereséséhez használni kívánt port" + }, "devHost": "Fejlesztés Host", "failed": "Nem sikerült futtatni a hosztolt plugin példányt: {0}", "hostedPlugin": "Hostolt bővítmény", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index b8c549a307e18..a160d7d08a2d0 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -362,6 +362,9 @@ "alreadyRunning": "L'istanza ospitata è già in esecuzione.", "debugInstance": "Istanza di debug", "debugMode": "Usare inspect o inspect-brk per il debug di Node.js", + "debugPorts": { + "debugPort": "Porta da utilizzare per il debug Node.js di questo server" + }, "devHost": "Sviluppo Host", "failed": "Impossibile eseguire l'istanza del plugin ospitato: {0}", "hostedPlugin": "Plugin ospitato", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 56720be8ebb68..58d37b01bed79 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -362,6 +362,9 @@ "alreadyRunning": "ホストされているインスタンスはすでに実行されています。", "debugInstance": "デバッグインスタンス", "debugMode": "Node.js のデバッグに inspect または inspect-brk を使用する", + "debugPorts": { + "debugPort": "このサーバーのNode.jsデバッグに使用するポート。" + }, "devHost": "開発ホスト", "failed": "ホストされたプラグインインスタンスの実行に失敗しました。{0}", "hostedPlugin": "ホスト型プラグイン", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index 541a15ea15152..174bc39c6f65c 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -1,7 +1,7 @@ { "debug.breakpoint.editCondition": "Edit Condition...", "notebook.cell.changeToCode": "Change Cell to Code", - "notebook.cell.changeToMarkdown": "Change Cell to Mardown", + "notebook.cell.changeToMarkdown": "Change Cell to Markdown", "notebook.cell.insertMarkdownCellAbove": "Insert Markdown Cell Above", "notebook.cell.insertMarkdownCellBelow": "Insert Markdown Cell Below", "terminal:new:profile": "Create New Integrated Terminal from a Profile", @@ -362,6 +362,9 @@ "alreadyRunning": "Hosted instance is already running.", "debugInstance": "Debug Instance", "debugMode": "Using inspect or inspect-brk for Node.js debug", + "debugPorts": { + "debugPort": "Port to use for this server's Node.js debug" + }, "devHost": "Development Host", "failed": "Failed to run hosted plugin instance: {0}", "hostedPlugin": "Hosted Plugin", diff --git a/packages/core/i18n/nls.ko.json b/packages/core/i18n/nls.ko.json new file mode 100644 index 0000000000000..09f20c64dad05 --- /dev/null +++ b/packages/core/i18n/nls.ko.json @@ -0,0 +1,555 @@ +{ + "debug.breakpoint.editCondition": "조건 편집...", + "notebook.cell.changeToCode": "셀을 코드로 변경", + "notebook.cell.changeToMarkdown": "셀을 마크다운으로 변경", + "notebook.cell.insertMarkdownCellAbove": "위에 마크다운 셀 삽입", + "notebook.cell.insertMarkdownCellBelow": "아래에 마크다운 셀 삽입", + "terminal:new:profile": "프로필에서 새 통합 터미널 만들기", + "terminal:profile:default": "기본 터미널 프로필을 선택합니다.", + "theia": { + "callhierarchy": { + "noCallers": "발신자가 감지되지 않았습니다.", + "open": "오픈 콜 계층 구조" + }, + "core": { + "about": { + "compatibility": "{0} 호환성", + "defaultApi": "기본값 {0} API", + "version": "버전" + }, + "common": { + "closeAll": "모든 탭 닫기", + "closeAllTabMain": "메인 영역의 모든 탭 닫기", + "closeOtherTabMain": "메인 영역에서 다른 탭 닫기", + "closeOthers": "다른 탭 닫기", + "closeRight": "오른쪽 탭 닫기", + "closeTab": "탭 닫기", + "closeTabMain": "메인 영역에서 탭 닫기", + "collapseAllTabs": "모든 측면 패널 접기", + "collapseBottomPanel": "하단 패널 토글", + "collapseTab": "측면 패널 접기", + "showNextTabGroup": "다음 탭 그룹으로 전환", + "showNextTabInGroup": "그룹에서 다음 탭으로 전환", + "showPreviousTabGroup": "이전 탭 그룹으로 전환", + "showPreviousTabInGroup": "그룹에서 이전 탭으로 전환", + "toggleMaximized": "최대화 토글" + }, + "copyInfo": "먼저 파일을 열어 경로를 복사합니다.", + "copyWarn": "브라우저의 복사 명령 또는 바로 가기를 사용하세요.", + "cutWarn": "브라우저의 잘라내기 명령 또는 바로 가기를 사용하세요.", + "enhancedPreview": { + "classic": "기본 정보가 포함된 탭의 간단한 미리 보기를 표시합니다.", + "enhanced": "추가 정보가 포함된 탭의 향상된 미리 보기를 표시합니다.", + "visual": "탭의 시각적 미리 보기를 표시합니다." + }, + "file": { + "browse": "찾아보기" + }, + "highlightModifiedTabs": "수정된(더티) 편집기 탭에 상단 테두리를 그릴지 여부를 제어합니다.", + "keybindingStatus": "{0} 키를 누르고 더 많은 키를 기다렸습니다.", + "keyboard": { + "choose": "키보드 레이아웃 선택", + "chooseLayout": "키보드 레이아웃 선택", + "current": "(현재: {0})", + "currentLayout": " - 현재 레이아웃", + "mac": "Mac 키보드", + "pc": "PC 키보드", + "tryDetect": "브라우저 정보 및 누른 키에서 키보드 레이아웃을 감지해 보세요." + }, + "navigator": { + "clipboardWarn": "클립보드에 대한 액세스가 거부되었습니다. 브라우저의 권한을 확인하세요.", + "clipboardWarnFirefox": "클립보드 API를 사용할 수 없습니다. '{0}' 페이지의 '{1}' 환경 설정에서 활성화할 수 있습니다. 그런 다음 Theia를 다시 로드합니다. 이렇게 하면 FireFox가 시스템 클립보드에 대한 전체 액세스 권한을 갖게 됩니다." + }, + "offline": "오프라인", + "pasteWarn": "브라우저의 붙여넣기 명령 또는 바로 가기를 사용하세요.", + "quitMessage": "저장하지 않은 변경 사항은 저장되지 않습니다.", + "resetWorkbenchLayout": "워크벤치 레이아웃 재설정", + "searchbox": { + "close": "닫기(탈출)", + "next": "다음 (아래로)", + "previous": "이전 (위로)" + }, + "secondaryWindow": { + "alwaysOnTop": "활성화하면 보조 창이 다른 애플리케이션의 창을 포함한 다른 모든 창 위에 표시됩니다.", + "description": "추출된 보조 창의 초기 위치와 크기를 설정합니다.", + "fullSize": "추출된 위젯의 위치와 크기는 실행 중인 테아 애플리케이션과 동일합니다.", + "halfWidth": "추출된 위젯의 위치와 크기는 실행 중인 테아 애플리케이션 너비의 절반이 됩니다.", + "originalSize": "추출된 위젯의 위치와 크기는 원래 위젯과 동일합니다." + }, + "silentNotifications": "알림 팝업 표시 여부를 제어합니다.", + "tabDefaultSize": "탭의 기본 크기를 지정합니다.", + "tabMaximize": "더블 클릭 시 탭을 최대화할지 여부를 제어합니다.", + "tabMinimumSize": "탭의 최소 크기를 지정합니다.", + "tabShrinkToFit": "사용 가능한 공간에 맞게 탭을 축소합니다." + }, + "debug": { + "addConfigurationPlaceholder": "구성을 추가할 작업 공간 루트를 선택합니다.", + "breakpoint": "브레이크포인트", + "compound-cycle": "실행 구성 '{0}' 자체에 사이클이 포함되어 있습니다.", + "continueAll": "모두 계속하기", + "copyExpressionValue": "표현식 값 복사", + "dataBreakpoint": "데이터 중단점", + "debugVariableInput": "{0} 값 설정", + "entry": "항목", + "exception": "예외", + "functionBreakpoint": "함수 중단점", + "goto": "goto", + "instruction-breakpoint": "명령어 중단점", + "instructionBreakpoint": "명령어 중단점", + "missingConfiguration": "동적 구성 '{0}:{1}' 이 누락되었거나 해당되지 않습니다.", + "pause": "일시 중지", + "pauseAll": "모두 일시 중지", + "reveal": "공개", + "step": "단계", + "threads": "스레드", + "toggleTracing": "디버그 어댑터와의 추적 통신 활성화/비활성화" + }, + "editor": { + "diffEditor.wordWrap2": "줄 바꿈은 `#편집기.wordWrap#` 설정에 따라 줄 바꿈됩니다.", + "dirtyEncoding": "파일이 더럽습니다. 다른 인코딩으로 다시 열기 전에 먼저 저장해 주세요.", + "editor.accessibilitySupport0": "플랫폼 API를 사용하여 스크린 리더가 연결된 경우 감지하기", + "editor.accessibilitySupport1": "화면 리더 사용에 맞게 최적화", + "editor.accessibilitySupport2": "화면 리더가 연결되어 있지 않다고 가정합니다.", + "editor.bracketPairColorization.enabled": "대괄호 쌍 색상화 사용 여부를 제어합니다. 대괄호 하이라이트 색상을 재정의하려면 `#workbench.colorCustomizations#`를 사용합니다.", + "editor.codeActionWidget.includeNearbyQuickfixes": "현재 진단 중이 아닐 때 줄 내에서 가장 가까운 퀵픽스 표시를 사용/사용 안 함으로 설정합니다.", + "editor.cursorSurroundingLinesStyle": "커서 서라운드 라인 적용 시기를 제어합니다.", + "editor.detectIndentation": "파일 내용을 기반으로 파일을 열 때 `#편집기.탭 크기#` 및 `#편집기.삽입 공백#`을 자동으로 감지할지 여부를 제어합니다.", + "editor.dropIntoEditor.enabled": "편집기에서 파일을 여는 대신 'Shift' 키를 누른 채로 파일을 텍스트 편집기로 끌어다 놓을 수 있는지 여부를 제어합니다.", + "editor.formatOnSaveMode.modificationsIfAvailable": "수정 사항만 포맷하려고 시도합니다(소스 제어 필요). 소스 제어를 사용할 수 없는 경우 전체 파일이 포맷됩니다.", + "editor.hover.hidingDelay": "호버가 숨겨지는 지연 시간(밀리초)을 제어합니다. editor.hover.sticky`가 활성화되어 있어야 합니다.", + "editor.inlayHints.enabled1": "인레이 힌트는 기본적으로 표시되며 Ctrl+Alt를 누르고 있으면 숨겨집니다.", + "editor.inlayHints.enabled2": "인레이 힌트는 기본적으로 숨겨져 있으며 Ctrl+Alt를 누르고 있으면 표시됩니다.", + "editor.inlayHints.fontFamily": "편집기에서 인레이 힌트의 글꼴 패밀리를 제어합니다. 비워두면 `#editor.fontFamily#`가 사용됩니다.", + "editor.inlayHints.fontSize": "에디터에서 인레이 힌트의 글꼴 크기를 제어합니다. 기본적으로 설정된 값이 `5`보다 작거나 편집기 글꼴 크기보다 큰 경우 `#editor.fontSize#`가 사용됩니다.", + "editor.insertSpaces": "Tab`을 누를 때 공백을 삽입합니다. 이 설정은 `#편집기 감지 들여쓰기#`가 켜져 있을 때 파일 내용에 따라 재정의됩니다.", + "editor.occurrencesHighlight": "편집기에서 의미 기호 발생을 강조 표시할지 여부를 제어합니다.", + "editor.quickSuggestions": "입력하는 동안 제안을 자동으로 표시할지 여부를 제어합니다. 주석, 문자열 및 기타 코드를 입력할 때 이 기능을 제어할 수 있습니다. 빠른 제안은 고스트 텍스트로 표시하거나 제안 위젯과 함께 표시하도록 구성할 수 있습니다. 또한 특수 문자에 의해 제안이 트리거되는지 여부를 제어하는 '#editor.suggestOnTriggerCharacters#'설정도 알아두세요.", + "editor.stickyScroll.scrollWithEditor": "편집기의 가로 스크롤 막대를 사용하여 고정 스크롤 위젯의 스크롤을 활성화합니다.", + "editor.suggestFontSize": "추천 위젯의 글꼴 크기입니다. 0`으로 설정하면 `#editor.fontSize#`의 값이 사용됩니다.", + "editor.suggestLineHeight": "제안 위젯의 줄 높이입니다. '0'으로 설정하면 '#편집기줄높이#'의 값이 사용됩니다. 최소값은 8입니다.", + "editor.tabSize": "탭의 공백 수입니다. 이 설정은 `#편집기 감지 들여쓰기#`가 켜져 있을 때 파일 내용에 따라 재정의됩니다.", + "editor.useTabStops": "공백을 삽입하고 삭제하면 탭이 멈춥니다.", + "editor.wordBasedSuggestions": "문서의 단어를 기준으로 완성을 계산할지 여부를 제어합니다.", + "editor.wordBasedSuggestionsMode": "문서에서 단어 기반 완성을 계산하는 컨트롤입니다.", + "files.autoSave": "저장되지 않은 변경 사항이 있는 편집기의 [자동 저장](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)을 제어합니다.", + "files.autoSave.afterDelay": "변경 사항이 있는 편집기는 설정된 `#files.autoSaveDelay#`가 지난 후에 자동으로 저장됩니다.", + "files.autoSave.off": "변경 사항이 있는 편집기는 자동으로 저장되지 않습니다.", + "files.autoSave.onFocusChange": "편집기가 포커스를 잃으면 변경 사항이 있는 편집기가 자동으로 저장됩니다.", + "files.autoSave.onWindowChange": "창이 포커스를 잃으면 변경 사항이 있는 편집기가 자동으로 저장됩니다.", + "formatOnSaveTimeout": "파일 저장 시 실행되는 서식이 취소되는 시간 초과(밀리초)입니다.", + "persistClosedEditors": "창을 다시 로드할 때 작업 영역의 닫힌 편집기 기록을 유지할지 여부를 제어합니다.", + "showAllEditors": "열려 있는 모든 편집기 표시", + "splitHorizontal": "편집기 가로 분할", + "splitVertical": "편집기 세로 분할", + "toggleStickyScroll": "고정 스크롤 토글" + }, + "file-search": { + "toggleIgnoredFiles": " (무시된 파일을 표시/숨기려면 {0} 을 누르세요.)" + }, + "fileDialog": { + "showHidden": "숨겨진 파일 표시" + }, + "fileSystem": { + "fileResource": { + "overWriteBody": "파일 시스템의 '{0}' 변경 내용을 덮어쓰시겠습니까?" + } + }, + "filesystem": { + "copiedToClipboard": "다운로드 링크를 클립보드에 복사합니다.", + "copyDownloadLink": "다운로드 링크 복사", + "dialog": { + "initialLocation": "초기 위치로 이동", + "multipleItemMessage": "하나의 항목만 선택할 수 있습니다.", + "name": "이름:", + "navigateBack": "뒤로 이동", + "navigateForward": "앞으로 탐색", + "navigateUp": "하나의 디렉터리로 이동" + }, + "fileResource": { + "binaryFileQuery": "열면 시간이 걸리고 IDE가 응답하지 않을 수 있습니다. 그래도 '{0}' 을 열시겠습니까?", + "binaryTitle": "파일이 바이너리이거나 지원되지 않는 텍스트 인코딩을 사용합니다.", + "largeFileTitle": "파일이 너무 큽니다({0}).", + "overwriteTitle": "파일 시스템에서 '{0}' 파일이 변경되었습니다." + }, + "filesExclude": "파일 및 폴더를 제외하기 위한 글로브 패턴을 구성합니다. 예를 들어, 파일 탐색기는 이 설정에 따라 표시하거나 숨길 파일 및 폴더를 결정합니다.", + "format": "형식:", + "maxConcurrentUploads": "여러 파일을 업로드할 때 업로드할 수 있는 최대 동시 파일 수입니다. 0은 모든 파일이 동시에 업로드됨을 의미합니다.", + "maxFileSizeMB": "열 수 있는 최대 파일 크기(MB)를 제어합니다.", + "prepareDownload": "다운로드 준비 중...", + "prepareDownloadLink": "다운로드 링크 준비 중...", + "processedOutOf": "처리된 {0} 중 {1}", + "replaceTitle": "파일 바꾸기", + "uploadFiles": "파일 업로드...", + "uploadedOutOf": "업로드 {0} 중 {1}" + }, + "getting-started": { + "apiComparator": "{0} API 호환성", + "newExtension": "새 확장 프로그램 구축", + "newPlugin": "새 플러그인 구축", + "startup-editor": { + "welcomePage": "{0} 및 확장 프로그램을 시작하는 데 도움이 되는 콘텐츠가 있는 시작 페이지를 엽니다." + } + }, + "git": { + "aFewSecondsAgo": "몇 초 전", + "addSignedOff": "결재자 추가", + "amendReuseMessage": "마지막 커밋 메시지를 다시 사용하려면 'Enter' 또는 'Escape'를 눌러 취소합니다.", + "amendRewrite": "이전 커밋 메시지를 다시 작성합니다. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "checkoutCreateLocalBranchWithName": "{0} 이라는 이름으로 새 로컬 브랜치를 만듭니다. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "checkoutProvideBranchName": "지점 이름을 입력하세요. ", + "checkoutSelectRef": "결제할 참조를 선택하거나 새 로컬 지점을 생성합니다:", + "cloneQuickInputLabel": "Git 리포지토리 위치를 입력하세요. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "cloneRepository": "Git 리포지토리를 복제합니다: {0}. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "compareWith": "비교 대상...", + "compareWithBranchOrTag": "현재 활성화된 {0} 브랜치와 비교할 브랜치 또는 태그를 선택합니다:", + "diff": "Diff", + "dirtyDiffLinesLimit": "편집기의 줄 수가 이 제한을 초과하는 경우 더티 디프 장식을 표시하지 않습니다.", + "dropStashMessage": "보관함이 성공적으로 제거되었습니다.", + "editorDecorationsEnabled": "에디터에서 git 장식을 표시합니다.", + "fetchPickRemote": "가져올 리모컨을 선택합니다:", + "gitDecorationsColors": "내비게이터에서 색상 장식을 사용합니다.", + "mergeQuickPickPlaceholder": "현재 활성 상태인 {0} 브랜치에 병합할 브랜치를 선택합니다:", + "missingUserInfo": "git에서 'user.name'과 'user.email'을 구성했는지 확인하세요.", + "noHistoryForError": "다음에 사용할 수 있는 기록이 없습니다. {0}", + "noPreviousCommit": "수정할 이전 커밋이 없습니다.", + "noRepositoriesSelected": "선택한 리포지토리가 없습니다.", + "prepositionIn": "in", + "repositoryNotInitialized": "리포지토리 {0} 가 아직 초기화되지 않았습니다.", + "stashChanges": "변경 사항을 저장합니다. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "stashChangesWithMessage": "메시지와 함께 변경 사항을 저장합니다: {0}. 'Enter'를 눌러 확인하거나 'Escape'를 눌러 취소합니다.", + "tabTitleIndex": "{0} (색인)", + "tabTitleWorkingTree": "{0} (작업 트리)", + "toggleBlameAnnotations": "비난 주석 토글" + }, + "keybinding-schema-updater": { + "deprecation": "대신 '언제' 절을 사용합니다." + }, + "keymaps": { + "addKeybindingTitle": "다음에 대한 키 바인딩 추가 {0}", + "editKeybinding": "키 바인딩 편집...", + "editKeybindingTitle": "다음에 대한 키 바인딩 편집 {0}", + "editWhenExpression": "표현식 편집...", + "editWhenExpressionTitle": "다음에 대한 표현식 편집 {0}", + "keybinding": { + "copy": "키 바인딩 복사", + "copyCommandId": "키 바인딩 명령 ID 복사", + "copyCommandTitle": "키 바인딩 명령 제목 복사", + "edit": "키 바인딩 편집...", + "editWhenExpression": "표현식 편집 시 키 바인딩..." + }, + "keybindingCollidesValidation": "현재 충돌하는 키 바인딩", + "requiredKeybindingValidation": "키 바인딩 값은 필수입니다.", + "resetKeybindingConfirmation": "이 키 바인딩을 정말 기본값으로 재설정하시겠습니까?", + "resetKeybindingTitle": "다음에 대한 키 바인딩 재설정 {0}", + "resetMultipleKeybindingsWarning": "이 명령에 대해 여러 개의 키 바인딩이 있는 경우 모두 재설정됩니다." + }, + "localize": { + "offlineTooltip": "백엔드에 연결할 수 없습니다." + }, + "markers": { + "clearAll": "모두 지우기", + "noProblems": "지금까지 워크스페이스에서 문제가 감지되지 않았습니다.", + "tabbarDecorationsEnabled": "탭 표시줄에 문제 데코레이터(진단 마커)를 표시합니다." + }, + "memory-inspector": { + "addressTooltip": "표시할 메모리 위치, 주소 또는 주소로 평가하는 표현식", + "ascii": "ASCII", + "binary": "바이너리", + "byteSize": "바이트 크기", + "bytesPerGroup": "그룹당 바이트 수", + "closeSettings": "설정 닫기", + "columns": "열", + "command": { + "createNewMemory": "새 메모리 검사기 만들기", + "createNewRegisterView": "새 등록 보기 만들기", + "followPointer": "포인터 팔로우", + "followPointerMemory": "메모리 인스펙터에서 포인터 팔로우", + "resetValue": "값 재설정", + "showRegister": "메모리 검사기에서 등록 표시", + "viewVariable": "메모리 인스펙터에서 변수 표시" + }, + "data": "데이터", + "decimal": "십진수", + "diff": { + "label": "Diff: {0}" + }, + "diff-widget": { + "offset-label": "{0} 오프셋", + "offset-title": "메모리에서 오프셋할 바이트 {0}" + }, + "editable": { + "apply": "변경 사항 적용", + "clear": "변경 사항 지우기" + }, + "endianness": "엔디안", + "extraColumn": "추가 열", + "groupsPerRow": "행별 그룹", + "hexadecimal": "16진수", + "length": "길이", + "lengthTooltip": "가져올 바이트 수(10진수 또는 16진수)", + "memory": { + "addressField": { + "memoryReadError": "위치 필드에 주소 또는 표현식을 입력합니다." + }, + "freeze": "메모리 고정 보기", + "hideSettings": "설정 패널 숨기기", + "readError": { + "bounds": "메모리 한도를 초과하면 결과가 잘립니다.", + "noContents": "현재 사용할 수 있는 메모리 콘텐츠가 없습니다." + }, + "readLength": { + "memoryReadError": "길이 필드에 길이(10진수 또는 16진수)를 입력합니다." + }, + "showSettings": "설정 패널 표시", + "unfreeze": "메모리 고정 해제 보기", + "userError": "메모리를 가져오는 동안 오류가 발생했습니다." + }, + "memoryCategory": "메모리 검사기", + "memoryInspector": "메모리 검사기", + "memoryTitle": "메모리", + "octal": "옥탈", + "offset": "오프셋", + "offsetTooltip": "탐색 시 현재 메모리 위치에 추가할 오프셋입니다.", + "provider": { + "localsError": "로컬 변수를 읽을 수 없습니다. 활성 디버그 세션이 없습니다.", + "readError": "메모리를 읽을 수 없습니다. 활성 디버그 세션이 없습니다.", + "writeError": "메모리를 쓸 수 없습니다. 활성 디버그 세션이 없습니다." + }, + "register": "등록하기", + "register-widget": { + "filter-placeholder": "필터(로 시작)" + }, + "registerReadError": "레지스터를 가져오는 동안 오류가 발생했습니다.", + "registers": "레지스터", + "toggleComparisonWidgetVisibility": "비교 위젯 표시 여부 토글", + "utils": { + "afterBytes": "비교하려는 두 위젯 모두에 메모리를 로드해야 합니다. {0} 메모리가 로드되지 않았습니다.", + "bytesMessage": "비교하려는 두 위젯 모두에 메모리를 로드해야 합니다. {0} 메모리가 로드되지 않았습니다." + } + }, + "messages": { + "notificationTimeout": "이 시간 초과 후에는 정보 알림이 숨겨집니다.", + "toggleNotifications": "알림 토글" + }, + "mini-browser": { + "typeUrl": "URL 입력" + }, + "monaco": { + "noSymbolsMatching": "일치하는 기호 없음", + "typeToSearchForSymbols": "기호를 검색하려면 입력하세요." + }, + "navigator": { + "autoReveal": "자동 공개", + "clipboardWarn": "클립보드에 대한 액세스가 거부되었습니다. 브라우저의 권한을 확인하세요.", + "clipboardWarnFirefox": "클립보드 API를 사용할 수 없습니다. '{0}' 페이지의 '{1}' 환경 설정에서 활성화할 수 있습니다. 그런 다음 Theia를 다시 로드합니다. 이렇게 하면 FireFox가 시스템 클립보드에 대한 전체 액세스 권한을 갖게 됩니다.", + "refresh": "탐색기에서 새로 고침", + "reveal": "탐색기에서 공개", + "toggleHiddenFiles": "숨겨진 파일 토글" + }, + "output": { + "clearOutputChannel": "출력 채널 지우기...", + "closeOutputChannel": "출력 채널 닫기...", + "hiddenChannels": "숨겨진 채널", + "hideOutputChannel": "출력 채널 숨기기...", + "maxChannelHistory": "출력 채널의 최대 항목 수입니다.", + "outputChannels": "출력 채널", + "showOutputChannel": "출력 채널 표시..." + }, + "plugin": { + "blockNewTab": "브라우저가 새 탭을 열지 못했습니다." + }, + "plugin-dev": { + "alreadyRunning": "호스팅된 인스턴스가 이미 실행 중입니다.", + "debugInstance": "인스턴스 디버그", + "debugMode": "Node.js 디버그에 inspect 또는 inspect-brk 사용", + "debugPorts": { + "debugPort": "이 서버의 Node.js 디버그에 사용할 포트" + }, + "devHost": "개발 호스트", + "failed": "호스팅된 플러그인 인스턴스를 실행하지 못했습니다: {0}", + "hostedPlugin": "호스팅 플러그인", + "hostedPluginRunning": "호스팅 플러그인: 실행 중", + "hostedPluginStarting": "호스팅 플러그인: 시작", + "hostedPluginStopped": "호스팅 플러그인: 중지됨", + "hostedPluginWatching": "호스팅 플러그인: 보기", + "instanceTerminated": "{0} 종료되었습니다.", + "launchOutFiles": "생성된 자바스크립트 파일을 찾기 위한 글로브 패턴 배열(`${플러그인패스}`는 플러그인 실제 경로로 대체됨).", + "noValidPlugin": "지정한 폴더에 유효한 플러그인이 포함되어 있지 않습니다.", + "notRunning": "호스팅된 인스턴스가 실행되고 있지 않습니다.", + "pluginFolder": "플러그인 폴더로 설정됩니다: {0}", + "preventedNewTab": "브라우저가 새 탭을 열지 못했습니다.", + "restartInstance": "인스턴스 다시 시작", + "running": "호스팅된 인스턴스가 실행 중입니다:", + "select": "선택", + "selectPath": "경로 선택", + "startInstance": "인스턴스 시작", + "starting": "호스팅 인스턴스 서버 시작 ...", + "stopInstance": "인스턴스 중지", + "unknownTerminated": "인스턴스가 종료되었습니다.", + "watchMode": "개발 중인 플러그인에서 감시자 실행" + }, + "plugin-ext": { + "authentication-main": { + "loginTitle": "로그인" + }, + "plugins": "플러그인", + "webviewTrace": "웹뷰로 통신 추적을 제어합니다.", + "webviewWarnIfUnsecure": "웹뷰가 현재 안전하지 않게 배포되었음을 사용자에게 경고합니다." + }, + "preferences": { + "hostedPlugin": "호스팅 플러그인", + "toolbar": "도구 모음" + }, + "preview": { + "openByDefault": "기본적으로 편집기 대신 미리 보기를 엽니다." + }, + "property-view": { + "created": "생성됨", + "directory": "디렉토리", + "lastModified": "마지막 수정", + "location": "위치", + "noProperties": "사용 가능한 숙소가 없습니다.", + "properties": "속성", + "size": "크기", + "symbolicLink": "기호 링크" + }, + "scm": { + "amend": "수정", + "amendHeadCommit": "HEAD 커밋", + "amendLastCommit": "마지막 커밋 수정", + "changeRepository": "리포지토리 변경...", + "config.untrackedChanges": "추적되지 않은 변경 사항의 작동 방식을 제어합니다.", + "config.untrackedChanges.hidden": "숨겨진", + "config.untrackedChanges.mixed": "혼합", + "config.untrackedChanges.separate": "분리", + "dirtyDiff": { + "close": "변경 사항 미리 보기 닫기" + }, + "history": "역사", + "noRepositoryFound": "리포지토리를 찾을 수 없습니다.", + "unamend": "수정 취소", + "unamendCommit": "커밋 수정 취소" + }, + "search-in-workspace": { + "includeIgnoredFiles": "무시된 파일 포함", + "noFolderSpecified": "폴더를 열거나 지정하지 않았습니다. 현재 열려 있는 파일만 검색됩니다.", + "resultSubset": "이는 전체 결과의 일부일 뿐입니다. 보다 구체적인 검색어를 사용하여 결과 목록의 범위를 좁히세요.", + "searchOnEditorModification": "수정한 경우 활성 편집기를 검색합니다." + }, + "secondary-window": { + "extract-widget": "보기를 보조 창으로 이동" + }, + "shell-area": { + "secondary": "보조 창" + }, + "task": { + "attachTask": "작업 첨부...", + "clearHistory": "기록 지우기", + "noTaskToRun": "실행할 작업을 찾을 수 없습니다. 작업 구성...", + "openUserTasks": "사용자 작업 열기" + }, + "terminal": { + "defaultProfile": "다음에서 사용되는 기본 프로필 {0}", + "enableCopy": "ctrl-c(macOS의 경우 cmd-c)를 활성화하여 선택한 텍스트를 복사합니다.", + "enablePaste": "클립보드에서 붙여넣기하려면 ctrl-v(macOS의 경우 cmd-v)를 사용하도록 설정합니다.", + "profileArgs": "이 프로필이 사용하는 셸 인수입니다.", + "profileColor": "단말기와 연결할 단말기 테마 색상 ID입니다.", + "profileDefault": "기본 프로필...을 선택합니다.", + "profileIcon": "터미널 아이콘과 연결할 코디콘 ID입니다.\nterminal-tmux:\"$(terminal-tmux)\"", + "profileNew": "새 터미널(프로필 포함)...", + "profilePath": "이 프로필이 사용하는 셸의 경로입니다.", + "profiles": "새 터미널을 만들 때 표시할 프로필입니다. 선택적 인수를 사용하여 경로 속성을 수동으로 설정합니다.\n기존 프로필을 `null`로 설정하면 목록에서 프로필을 숨길 수 있습니다(예: `\"{0}\": null`).", + "rendererType": "터미널 렌더링 방식을 제어합니다.", + "rendererTypeDeprecationMessage": "렌더러 유형은 더 이상 옵션으로 지원되지 않습니다.", + "selectProfile": "새 터미널의 프로필을 선택합니다.", + "shell.deprecated": "이 방법은 더 이상 사용되지 않으며, 기본 셸을 구성하는 새로운 권장 방법은 'terminal.integrated.profiles.{0}' 에서 터미널 프로필을 생성하고 'terminal.integrated.defaultProfile.{0}' 에서 프로필 이름을 기본값으로 설정하는 것입니다.", + "shellArgsLinux": "Linux 터미널에서 사용할 명령줄 인수입니다.", + "shellArgsOsx": "macOS 터미널에서 사용할 명령줄 인수입니다.", + "shellArgsWindows": "Windows 터미널에서 사용할 명령줄 인수입니다.", + "shellLinux": "터미널이 Linux에서 사용하는 셸 경로입니다(기본값: '{0}'}).", + "shellOsx": "터미널이 macOS에서 사용하는 셸의 경로(기본값: '{0}'})입니다.", + "shellWindows": "터미널이 Windows에서 사용하는 셸의 경로입니다. (기본값: '{0}')." + }, + "test": { + "cancelAllTestRuns": "모든 테스트 실행 취소", + "testRunDefaultName": "{0} 실행 {1}", + "testRuns": "테스트 실행" + }, + "toolbar": { + "addCommand": "도구 모음에 명령 추가", + "addCommandPlaceholder": "도구 모음에 추가할 명령 찾기", + "centerColumn": "중앙 열", + "failedUpdate": "'{1}'의 '{0}' 값을 업데이트하지 못했습니다.", + "filterIcons": "필터 아이콘", + "iconSelectDialog": "'{0}' 아이콘을 선택합니다.", + "iconSet": "아이콘 세트", + "insertGroupLeft": "그룹 구분 기호 삽입(왼쪽)", + "insertGroupRight": "그룹 구분 기호 삽입(오른쪽)", + "leftColumn": "왼쪽 열", + "openJSON": "툴바 사용자 지정(JSON 열기)", + "removeCommand": "도구 모음에서 명령 제거", + "restoreDefaults": "도구 모음 기본값 복원", + "rightColumn": "오른쪽 열", + "selectIcon": "아이콘 선택", + "toggleToolbar": "툴바 토글", + "toolbarLocationPlaceholder": "명령어를 어디에 추가하고 싶으신가요?", + "useDefaultIcon": "기본 아이콘 사용" + }, + "typehierarchy": { + "subtypeHierarchy": "하위 유형 계층 구조", + "supertypeHierarchy": "슈퍼타입 계층 구조" + }, + "vsx-registry": { + "confirmDialogMessage": "확장자 \"{0}\"는 확인되지 않았으며 보안 위험을 초래할 수 있습니다.", + "confirmDialogTitle": "설치를 계속 진행하시겠습니까?", + "downloadCount": "다운로드 횟수: {0}", + "errorFetching": "확장 프로그램 가져오기 오류가 발생했습니다.", + "errorFetchingConfigurationHint": "이는 네트워크 구성 문제로 인해 발생할 수 있습니다.", + "failedInstallingVSIX": "VSIX에서 {0} 설치에 실패했습니다.", + "invalidVSIX": "선택한 파일이 유효한 \"*.vsix\" 플러그인이 아닙니다.", + "license": "라이선스: {0}", + "onlyShowVerifiedExtensionsDescription": "이렇게 하면 {0} 에 확인된 확장자만 표시할 수 있습니다.", + "onlyShowVerifiedExtensionsTitle": "인증된 확장 프로그램만 표시", + "recommendedExtensions": "이 리포지토리에 권장되는 확장 프로그램을 설치하시겠습니까?", + "searchPlaceholder": "에서 확장 프로그램 검색 {0}", + "showInstalled": "설치된 확장 프로그램 표시", + "showRecommendedExtensions": "확장 프로그램 권장 사항에 대한 알림 표시 여부를 제어합니다.", + "vsx-extensions-contribution": { + "update-version-uninstall-error": "확장 프로그램을 제거하는 동안 오류가 발생했습니다: {0}.", + "update-version-version-error": "{1} 의 버전 {0} 을 설치하지 못했습니다." + } + }, + "webview": { + "goToReadme": "README로 이동", + "messageWarning": " {0} 엔드포인트의 호스트 패턴이 `{1}`로 변경되었으며, 패턴을 변경하면 보안 취약점이 발생할 수 있습니다. 자세한 내용은 `{2}`를 참조하세요." + }, + "workspace": { + "compareWithEachOther": "서로 비교하기", + "confirmDeletePermanently.description": "휴지통을 사용하여 \"{0}\"를 삭제하지 못했습니다. 대신 영구 삭제를 하시겠습니까?", + "confirmDeletePermanently.solution": "환경설정에서 휴지통 사용을 비활성화할 수 있습니다.", + "confirmDeletePermanently.title": "파일 삭제 중 오류 발생", + "confirmMessage.delete": "다음 파일을 정말 삭제하시겠습니까?", + "confirmMessage.dirtyMultiple": "저장되지 않은 변경 사항이 있는 {0} 파일을 정말 삭제하시겠습니까?", + "confirmMessage.dirtySingle": "저장되지 않은 변경 사항이 있는 {0} 을 정말 삭제하시겠습니까?", + "confirmMessage.uriMultiple": "선택한 {0} 파일을 모두 삭제하시겠습니까?", + "confirmMessage.uriSingle": "{0} 을 삭제하시겠습니까?", + "duplicate": "중복", + "failSaveAs": "현재 위젯에 대해 \"{0}\"를 실행할 수 없습니다.", + "newFilePlaceholder": "파일 이름", + "newFolderPlaceholder": "폴더 이름", + "noErasure": "참고: 디스크에서 아무것도 지워지지 않습니다.", + "openRecentPlaceholder": "열려는 워크스페이스의 이름을 입력합니다.", + "openRecentWorkspace": "최근 작업 공간 열기...", + "preserveWindow": "현재 창에서 작업 공간 열기를 활성화합니다.", + "removeFolder": "작업 공간에서 다음 폴더를 제거하시겠습니까?", + "removeFolders": "작업 공간에서 다음 폴더를 제거하시겠습니까?", + "trashTitle": "{0} 을 휴지통으로 이동", + "trustEmptyWindow": "빈 작업 공간을 기본적으로 신뢰할지 여부를 제어합니다.", + "trustEnabled": "워크스페이스 신뢰의 사용 여부를 제어합니다. 비활성화하면 모든 워크스페이스가 신뢰됩니다.", + "trustRequest": "확장 프로그램에서 워크스페이스 신뢰를 요청하지만 해당 API가 아직 완전히 지원되지 않습니다. 이 워크스페이스를 신뢰하시겠습니까?", + "untitled-cleanup": "제목이 없는 작업 공간 파일이 많이 있는 것 같습니다. {0} 을 확인하여 사용하지 않는 파일을 제거하세요.", + "workspaceFolderAdded": "여러 루트가 있는 워크스페이스가 만들어졌습니다. 워크스페이스 구성을 파일로 저장하시겠습니까?", + "workspaceFolderAddedTitle": "워크스페이스에 폴더 추가" + } + } +} diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 812494e767e90..02fe620f30810 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -362,6 +362,9 @@ "alreadyRunning": "Hostowana instancja jest już uruchomiona.", "debugInstance": "Instancja debugowania", "debugMode": "Używanie inspect lub inspect-brk do debugowania Node.js", + "debugPorts": { + "debugPort": "Port używany do debugowania Node.js tego serwera" + }, "devHost": "Gospodarz ds. rozwoju", "failed": "Failed to run hosted plugin instance: {0}", "hostedPlugin": "Wtyczka hostowana", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index e4c8dbdcc09e1..e186c31779bf0 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -362,6 +362,9 @@ "alreadyRunning": "A instância anfitriã já está funcionando.", "debugInstance": "Instância de depuração", "debugMode": "Usando a inspeção ou a inspeção-brk para o Node.js debug", + "debugPorts": { + "debugPort": "Porta a ser usada para a depuração do Node.js deste servidor" + }, "devHost": "Anfitrião do desenvolvimento", "failed": "Falha na execução da instância de plugin hospedado: {0}", "hostedPlugin": "Plugin hospedado", diff --git a/packages/core/i18n/nls.pt-pt.json b/packages/core/i18n/nls.pt-pt.json deleted file mode 100644 index f8faf49af638c..0000000000000 --- a/packages/core/i18n/nls.pt-pt.json +++ /dev/null @@ -1,552 +0,0 @@ -{ - "debug.breakpoint.editCondition": "Editar condição...", - "notebook.cell.changeToCode": "Alterar célula para código", - "notebook.cell.changeToMarkdown": "Alterar Célula para Mardown", - "notebook.cell.insertMarkdownCellAbove": "Inserir célula Markdown acima", - "notebook.cell.insertMarkdownCellBelow": "Inserir célula Markdown abaixo", - "terminal:new:profile": "Criar Novo Terminal Integrado a partir de um Perfil", - "terminal:profile:default": "Escolha o Perfil Terminal padrão", - "theia": { - "callhierarchy": { - "noCallers": "Não foram detectadas pessoas que tenham telefonado.", - "open": "Hierarquia de Chamadas Abertas" - }, - "core": { - "about": { - "compatibility": "{0} Compatibilidade", - "defaultApi": "Padrão {0} API", - "version": "Versão" - }, - "common": { - "closeAll": "Fechar todos os separadores", - "closeAllTabMain": "Fechar todos os separadores na área principal", - "closeOtherTabMain": "Fechar outros separadores na área principal", - "closeOthers": "Fechar outros separadores", - "closeRight": "Fechar abas para a direita", - "closeTab": "Aba fechar", - "closeTabMain": "Fechar separador na área principal", - "collapseAllTabs": "Colapso de todos os painéis laterais", - "collapseBottomPanel": "Alternar Painel Inferior", - "collapseTab": "Painel lateral de colapso", - "showNextTabGroup": "Mudar para Grupo de Separador Seguinte", - "showNextTabInGroup": "Mudar para o separador seguinte no grupo", - "showPreviousTabGroup": "Mudar para Grupo de Separador Anterior", - "showPreviousTabInGroup": "Mudar para o separador Anterior em Grupo", - "toggleMaximized": "Alternar Maximizado" - }, - "copyInfo": "Abra um ficheiro primeiro para copiar o seu caminho", - "copyWarn": "Utilize o comando de cópia ou o atalho do browser.", - "cutWarn": "Utilize o comando de corte ou o atalho do browser.", - "enhancedPreview": { - "classic": "Apresenta uma pré-visualização simples do separador com informações básicas.", - "enhanced": "Apresentar uma pré-visualização melhorada do separador com informações adicionais.", - "visual": "Apresenta uma pré-visualização visual do separador." - }, - "file": { - "browse": "Navegar" - }, - "highlightModifiedTabs": "Controla se uma borda superior é desenhada ou não em abas de editor modificadas (sujas).", - "keybindingStatus": "{0} foi premido, à espera de mais teclas", - "keyboard": { - "choose": "Escolha o Layout do Teclado", - "chooseLayout": "Escolha um layout de teclado", - "current": "(actual: {0})", - "currentLayout": " - layout actual", - "mac": "Teclados Mac", - "pc": "Teclados de PC", - "tryDetect": "Tente detectar a disposição do teclado a partir da informação do browser e prima as teclas." - }, - "navigator": { - "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão do seu browser.", - "clipboardWarnFirefox": "A API da área de transferência não está disponível. Pode ser activada através da preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Note que isso permitirá ao FireFox obter acesso total à área de transferência do sistema." - }, - "offline": "Offline", - "pasteWarn": "Utilize o comando de colar ou o atalho do browser.", - "quitMessage": "Quaisquer alterações não guardadas não serão salvas.", - "resetWorkbenchLayout": "Repor o layout da bancada de trabalho", - "searchbox": { - "close": "Fechar (Escape)", - "next": "Próximo (Para baixo)", - "previous": "Anterior (Para cima)" - }, - "secondaryWindow": { - "alwaysOnTop": "Quando activada, a janela secundária permanece acima de todas as outras janelas, incluindo as de diferentes aplicações.", - "description": "Define a posição inicial e o tamanho da janela secundária extraída.", - "fullSize": "A posição e o tamanho do widget extraído serão os mesmos da aplicação Theia em execução.", - "halfWidth": "A posição e o tamanho do widget extraído serão metade da largura da aplicação Theia em execução.", - "originalSize": "A posição e o tamanho do widget extraído serão os mesmos que os do widget original." - }, - "silentNotifications": "Controla se deve suprimir popups de notificação.", - "tabDefaultSize": "Especifica o tamanho predefinido dos separadores.", - "tabMaximize": "Controla se pretende maximizar os separadores com um duplo clique.", - "tabMinimumSize": "Especifica o tamanho mínimo dos separadores.", - "tabShrinkToFit": "Encolher os separadores de acordo com o espaço disponível." - }, - "debug": { - "addConfigurationPlaceholder": "Seleccione a raiz do espaço de trabalho para adicionar configuração a", - "breakpoint": "ponto de interrupção", - "compound-cycle": "Configuração de lançamento '{0}' contém um ciclo consigo mesmo", - "continueAll": "Continuar Tudo", - "copyExpressionValue": "Valor de Expressão da Cópia", - "dataBreakpoint": "ponto de interrupção de dados", - "debugVariableInput": "Conjunto {0} Valor", - "entry": "entrada", - "exception": "exceção", - "functionBreakpoint": "ponto de interrupção da função", - "goto": "goto", - "instruction-breakpoint": "Ponto de Interrupção da Instrução", - "instructionBreakpoint": "ponto de interrupção de instrução", - "missingConfiguration": "Configuração dinâmica '{0}:{1}' está em falta ou não é aplicável", - "pause": "pausa", - "pauseAll": "Pausa Todos", - "reveal": "Revelar", - "step": "passo", - "threads": "Tópicos", - "toggleTracing": "Activar/desactivar as comunicações de rastreio com adaptadores de depuração" - }, - "editor": { - "diffEditor.wordWrap2": "As linhas serão quebradas de acordo com a configuração `#editor.wordWrap#`.", - "dirtyEncoding": "O ficheiro está sujo. Por favor guardá-lo primeiro antes de o reabrir com outra codificação.", - "editor.accessibilitySupport0": "Utilizar APIs da plataforma para detetar quando um leitor de ecrã está ligado", - "editor.accessibilitySupport1": "Otimizar para utilização com um leitor de ecrã", - "editor.accessibilitySupport2": "Assumir que um leitor de ecrã não está ligado", - "editor.bracketPairColorization.enabled": "Controla se a colorização de pares de colchetes está habilitada ou não. Utilize `#workbench.colorCustomizations#` para substituir as cores de destaque dos colchetes.", - "editor.codeActionWidget.includeNearbyQuickfixes": "Ativar/desativar a apresentação da reparação rápida mais próxima dentro de uma linha quando não se está a fazer um diagnóstico.", - "editor.cursorSurroundingLinesStyle": "Controla quando o `#cursorSurroundingLines#` deve ser aplicado.", - "editor.detectIndentation": "Controla se `#editor.tabSize#` e `#editor.insertSpaces#` serão automaticamente detectados quando um arquivo é aberto com base no conteúdo do arquivo.", - "editor.dropIntoEditor.enabled": "Controla se é possível arrastar e soltar um arquivo em um editor de texto mantendo pressionada a tecla `shift` (em vez de abrir o arquivo em um editor).", - "editor.formatOnSaveMode.modificationsIfAvailable": "Tentará apenas formatar modificações (requer controlo da fonte). Se o controlo da fonte não puder ser utilizado, então o ficheiro inteiro será formatado.", - "editor.hover.hidingDelay": "Controla o atraso em milissegundos após o qual o hover é escondido. Requer que `editor.hover.sticky` esteja habilitado.", - "editor.inlayHints.enabled1": "Dicas de Inlay estão a mostrar por defeito e escondem-se quando se segura \"Ctrl+Alt\".", - "editor.inlayHints.enabled2": "Dicas de Inlay são ocultadas por defeito e mostram quando se mantém 'Ctrl+Alt'.", - "editor.inlayHints.fontFamily": "Controla a família da fonte das dicas de inlay no editor. Quando definido como vazio, a `#editor.fontFamily#` é utilizada.", - "editor.inlayHints.fontSize": "Controla o tamanho da fonte das dicas de inlay no editor. Como padrão o `#editor.fontSize#` é utilizado quando o valor configurado é menor que `5` ou maior que o tamanho da fonte do editor.", - "editor.insertSpaces": "Inserir espaços ao pressionar `Tab`. Esta configuração é substituída com base no conteúdo do ficheiro quando `#editor.detectIndentation#` está ligado.", - "editor.occurrencesHighlight": "Controla se o editor deve realçar as ocorrências de símbolos semânticos.", - "editor.quickSuggestions": "Controla se as sugestões devem aparecer automaticamente durante a dactilografia. Isto pode ser controlado para digitação de comentários, strings, e outros códigos. A sugestão rápida pode ser configurada para aparecer como texto fantasma ou com o widget de sugestão. Esteja também ciente do '#editor.suggestOnTriggerCharacters#'-setting que controla se as sugestões são accionadas por caracteres especiais.", - "editor.stickyScroll.scrollWithEditor": "Ativar a deslocação do widget de deslocação autocolante com a barra de deslocação horizontal do editor.", - "editor.suggestFontSize": "Tamanho da fonte para o widget de sugestão. Quando definido como `0`, o valor de `#editor.fontSize#` é utilizado.", - "editor.suggestLineHeight": "Altura da linha para o widget de sugestão. Quando definido como `0`, o valor de `#editor.lineHeight#` é utilizado. O valor mínimo é 8.", - "editor.tabSize": "O número de espaços a que uma tabulação é igual. Esta configuração é substituída com base no conteúdo do ficheiro quando `#editor.detectIndentation#` está ligado.", - "editor.useTabStops": "A inserção e eliminação de espaços em branco segue as paragens de tabulação.", - "editor.wordBasedSuggestions": "Controla se as conclusões devem ser calculadas com base nas palavras do documento.", - "editor.wordBasedSuggestionsMode": "Controla a partir de que documentos são calculadas as conclusões baseadas em palavras.", - "files.autoSave": "Controla [auto guardar](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) de editores que tenham alterações não guardadas.", - "files.autoSave.afterDelay": "Um editor com alterações é automaticamente guardado após a configuração `#files.autoSaveDelay#``.", - "files.autoSave.off": "Um editor com alterações nunca é automaticamente guardado.", - "files.autoSave.onFocusChange": "Um editor com alterações é automaticamente guardado quando o editor perde o foco.", - "files.autoSave.onWindowChange": "Um editor com alterações é automaticamente guardado quando a janela perde o foco.", - "formatOnSaveTimeout": "Timeout em milissegundos após o qual a formatação que é executada em ficheiro guardado é cancelada.", - "persistClosedEditors": "Controla se deve persistir o histórico do editor fechado para o espaço de trabalho através de recargas de janela.", - "showAllEditors": "Mostrar todos os editores abertos", - "splitHorizontal": "Editor dividido Horizontal", - "splitVertical": "Split Editor Vertical", - "toggleStickyScroll": "Manípulo de rotação" - }, - "file-search": { - "toggleIgnoredFiles": " (Prima {0} para mostrar/ocultar ficheiros ignorados)" - }, - "fileDialog": { - "showHidden": "Mostrar ficheiros ocultos" - }, - "fileSystem": { - "fileResource": { - "overWriteBody": "Quer sobrescrever as alterações feitas a '{0}' no sistema de ficheiros?" - } - }, - "filesystem": { - "copiedToClipboard": "Copiou a ligação de transferência para a área de transferência.", - "copyDownloadLink": "Link para download de cópias", - "dialog": { - "initialLocation": "Ir para o local inicial", - "multipleItemMessage": "Pode seleccionar apenas um item", - "name": "Nome:", - "navigateBack": "Navegar para trás", - "navigateForward": "Navegar para a frente", - "navigateUp": "Navegar para cima de um directório" - }, - "fileResource": { - "binaryFileQuery": "A sua abertura pode demorar algum tempo e pode tornar a IDE pouco reactiva. Quer abrir '{0}' de qualquer forma?", - "binaryTitle": "O ficheiro ou é binário ou utiliza uma codificação de texto não suportada.", - "largeFileTitle": "O ficheiro é demasiado grande ({0}).", - "overwriteTitle": "O ficheiro '{0}' foi alterado no sistema de ficheiros." - }, - "filesExclude": "Configurar padrões globais para excluir ficheiros e pastas. Por exemplo, o Explorador de ficheiros decide quais os ficheiros e pastas a mostrar ou esconder com base nesta configuração.", - "format": "Formato:", - "maxConcurrentUploads": "Número máximo de ficheiros simultâneos a carregar ao carregar vários ficheiros. 0 significa que todos os ficheiros serão carregados ao mesmo tempo.", - "maxFileSizeMB": "Controla o tamanho máximo do ficheiro em MB que é possível abrir.", - "prepareDownload": "A preparar o download...", - "prepareDownloadLink": "Preparar o link para descarregar...", - "processedOutOf": "Processado {0} a partir de {1}", - "replaceTitle": "Substituir ficheiro", - "uploadFiles": "Carregar ficheiros...", - "uploadedOutOf": "Carregado {0} a partir de {1}" - }, - "getting-started": { - "apiComparator": "{0} Compatibilidade API", - "newExtension": "Construir uma nova extensão", - "newPlugin": "Construir um Novo Plugin", - "startup-editor": { - "welcomePage": "Abra a página de boas-vindas, com conteúdo para ajudar a começar a utilizar {0} e as extensões." - } - }, - "git": { - "aFewSecondsAgo": "há alguns segundos", - "addSignedOff": "Adicionar Signed-off-by", - "amendReuseMessage": "Para reutilizar a última mensagem de compromisso, prima 'Enter' ou 'Escape' para cancelar.", - "amendRewrite": "Reescrever mensagem de compromisso anterior. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "checkoutCreateLocalBranchWithName": "Criar uma nova filial local com o nome: {0}. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "checkoutProvideBranchName": "Por favor, forneça um nome de filial.", - "checkoutSelectRef": "Seleccionar um árbitro para fazer a caixa ou criar uma nova sucursal local:", - "cloneQuickInputLabel": "Por favor, forneça a localização de um repositório Git. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "cloneRepository": "Clonar o repositório Git: {0}. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "compareWith": "Comparar com...", - "compareWithBranchOrTag": "Escolher um ramo ou etiqueta para comparar com o ramo actualmente activo {0}:", - "diff": "Diff", - "dirtyDiffLinesLimit": "Não mostrar decorações difusas sujas, se a contagem de linhas do editor exceder este limite.", - "dropStashMessage": "Armazenamento removido com sucesso.", - "editorDecorationsEnabled": "Mostrar decorações de git no editor.", - "fetchPickRemote": "Escolher um comando de onde ir buscar:", - "gitDecorationsColors": "Utilizar decoração a cores no navegador.", - "mergeQuickPickPlaceholder": "Escolher um ramo para se fundir no ramo actualmente activo {0}:", - "missingUserInfo": "Certifique-se de configurar o seu 'user.name' e 'user.email' em git.", - "noHistoryForError": "Não existe um historial disponível para {0}", - "noPreviousCommit": "Nenhum compromisso anterior para emendar", - "noRepositoriesSelected": "Não foram seleccionados repositórios.", - "prepositionIn": "em", - "repositoryNotInitialized": "Repositório {0} ainda não foi inicializado.", - "stashChanges": "Mudanças de stock. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "stashChangesWithMessage": "Mudanças de stock com mensagem: {0}. Prima 'Enter' para confirmar ou 'Escape' para cancelar.", - "tabTitleIndex": "{0} (índice)", - "tabTitleWorkingTree": "{0} (Árvore de trabalho)", - "toggleBlameAnnotations": "Anotações de culpa alternadas" - }, - "keybinding-schema-updater": { - "deprecation": "Utilize antes a cláusula `when`." - }, - "keymaps": { - "addKeybindingTitle": "Adicionar uma ligação de teclas para {0}", - "editKeybinding": "Editar vinculação de teclas...", - "editKeybindingTitle": "Editar encadernação de chaves para {0}", - "editWhenExpression": "Editar quando a expressão...", - "editWhenExpressionTitle": "Editar quando a expressão para {0}", - "keybinding": { - "copy": "Copiar encadernação de teclas", - "copyCommandId": "Copiar ID do comando de ligação de teclas", - "copyCommandTitle": "Copiar vinculação de teclas Título do comando", - "edit": "Editar vinculação de teclas...", - "editWhenExpression": "Editar vinculação de teclas quando a expressão..." - }, - "keybindingCollidesValidation": "ligação de chaves actualmente colide", - "requiredKeybindingValidation": "é necessário um valor de encadernação", - "resetKeybindingConfirmation": "Quer mesmo repor esta encadernação de chave no seu valor por defeito?", - "resetKeybindingTitle": "Repor a encadernação para {0}", - "resetMultipleKeybindingsWarning": "Se existirem múltiplas ligações de teclas para este comando, todas elas serão reiniciadas." - }, - "localize": { - "offlineTooltip": "Não se pode ligar ao backend." - }, - "markers": { - "clearAll": "Limpar tudo", - "noProblems": "Até agora não foram detectados problemas no espaço de trabalho.", - "tabbarDecorationsEnabled": "Mostrar decoradores de problemas (marcadores de diagnóstico) nas barras de tabulação." - }, - "memory-inspector": { - "addressTooltip": "Localização da memória a exibir, um endereço ou expressão a avaliar para um endereço", - "ascii": "ASCII", - "binary": "Binário", - "byteSize": "Tamanho do byte", - "bytesPerGroup": "Bytes Por Grupo", - "closeSettings": "Fechar Definições", - "columns": "Colunas", - "command": { - "createNewMemory": "Criar Novo Inspector de Memória", - "createNewRegisterView": "Criar Nova Vista de Registo", - "followPointer": "Siga o Ponteiro", - "followPointerMemory": "Seguir o Ponto em Memória Inspector", - "resetValue": "Valor de reinicialização", - "showRegister": "Mostrar Registo em Memória Inspector", - "viewVariable": "Mostrar Variable in Memory Inspector" - }, - "data": "Dados", - "decimal": "Decimal", - "diff": { - "label": "Dif: {0}" - }, - "diff-widget": { - "offset-label": "{0} Offset", - "offset-title": "Bytes para compensar a memória de {0}" - }, - "editable": { - "apply": "Aplicar alterações", - "clear": "Mudanças claras" - }, - "endianness": "Endianness", - "extraColumn": "Coluna Extra", - "groupsPerRow": "Grupos por fila", - "hexadecimal": "Hexadecimal", - "length": "Comprimento", - "lengthTooltip": "Número de bytes a buscar, em decimal ou hexadecimal", - "memory": { - "addressField": { - "memoryReadError": "Introduzir um endereço ou expressão no campo Localização." - }, - "freeze": "Vista de memória congelada", - "hideSettings": "Esconder Painel de Definições", - "readError": { - "bounds": "Limites de memória excedidos, o resultado será truncado.", - "noContents": "Não existe actualmente nenhum conteúdo de memória disponível." - }, - "readLength": { - "memoryReadError": "Introduza um comprimento (número decimal ou hexadecimal) no campo Comprimento." - }, - "showSettings": "Mostrar Painel de Ajustes", - "unfreeze": "Vista da memória de descongelamento", - "userError": "Havia uma memória de busca de erros." - }, - "memoryCategory": "Inspector de Memória", - "memoryInspector": "Inspector de Memória", - "memoryTitle": "Memória", - "octal": "Octal", - "offset": "Offset", - "offsetTooltip": "Offset a ser adicionado ao local da memória actual, quando se navega", - "provider": { - "localsError": "Não é possível ler variáveis locais. Não há sessão de depuração activa.", - "readError": "Não é possível ler a memória. Sem sessão de depuração activa.", - "writeError": "Não é possível escrever memória. Sem sessão de depuração activa." - }, - "register": "Registe-se", - "register-widget": { - "filter-placeholder": "Filtro (começa por)" - }, - "registerReadError": "Havia um registo de busca de erros.", - "registers": "Registos", - "toggleComparisonWidgetVisibility": "Visibilidade do widget de comparação de alternância", - "utils": { - "afterBytes": "Deve carregar memória em ambos os widgets que gostaria de comparar. {0} não tem memória carregada.", - "bytesMessage": "Deve carregar memória em ambos os widgets que gostaria de comparar. {0} não tem memória carregada." - } - }, - "messages": { - "notificationTimeout": "As notificações informativas serão ocultadas após este intervalo de tempo.", - "toggleNotifications": "Notificações de alternância" - }, - "mini-browser": { - "typeUrl": "Digite um URL" - }, - "monaco": { - "noSymbolsMatching": "Sem correspondência de símbolos", - "typeToSearchForSymbols": "Tipo para pesquisa de símbolos" - }, - "navigator": { - "autoReveal": "Auto Revelação", - "clipboardWarn": "O acesso à área de transferência foi negado. Verifique a permissão do seu browser.", - "clipboardWarnFirefox": "A API da área de transferência não está disponível. Pode ser activada através da preferência '{0}' na página '{1}'. Em seguida, recarregue o Theia. Note que isso permitirá ao FireFox obter acesso total à área de transferência do sistema.", - "refresh": "Actualizar no Explorer", - "reveal": "Revelar no Explorer", - "toggleHiddenFiles": "Alternar ficheiros escondidos" - }, - "output": { - "clearOutputChannel": "Canal de saída transparente...", - "closeOutputChannel": "Fechar canal de saída...", - "hiddenChannels": "Canais ocultos", - "hideOutputChannel": "Esconder canal de saída...", - "maxChannelHistory": "O número máximo de entradas num canal de saída.", - "outputChannels": "Canais de saída", - "showOutputChannel": "Mostrar canal de saída..." - }, - "plugin": { - "blockNewTab": "O seu navegador impediu a abertura de um novo separador" - }, - "plugin-dev": { - "alreadyRunning": "A instância anfitriã já está a funcionar.", - "debugInstance": "Tribunal de depuração", - "debugMode": "Usando inspeccionar ou inspeccionar-brk para Node.js debug", - "devHost": "Anfitrião do Desenvolvimento", - "failed": "Falha na execução de uma instância de plugin hospedado: {0}", - "hostedPlugin": "Plugin hospedado", - "hostedPluginRunning": "Plugin hospedado: A funcionar", - "hostedPluginStarting": "Plugin hospedado: Início", - "hostedPluginStopped": "Plugin hospedado: Parado", - "hostedPluginWatching": "Plugin hospedado: Observando", - "instanceTerminated": "{0} foi terminado", - "launchOutFiles": "O conjunto de padrões globais para localização de ficheiros JavaScript gerados (`${pluginPath}` será substituído pelo caminho real do plugin).", - "noValidPlugin": "A pasta especificada não contém um plugin válido.", - "notRunning": "A instância anfitriã não está a funcionar.", - "pluginFolder": "A pasta Plugin está definida para: {0}", - "preventedNewTab": "O seu navegador impediu a abertura de um novo separador", - "restartInstance": "Reinício da instância", - "running": "A instância anfitriã está a funcionar em:", - "select": "Seleccione", - "selectPath": "Seleccione o caminho", - "startInstance": "Instância inicial", - "starting": "Iniciar servidor de instância hospedado ...", - "stopInstance": "Instância de paragem", - "unknownTerminated": "A instância foi encerrada", - "watchMode": "Executar o watcher em plugin em desenvolvimento" - }, - "plugin-ext": { - "authentication-main": { - "loginTitle": "Iniciar sessão" - }, - "plugins": "Plugins", - "webviewTrace": "Controla o rastreio da comunicação com visualizações na web.", - "webviewWarnIfUnsecure": "Adverte os utilizadores de que as visualizações da web estão actualmente implantadas de forma insegura." - }, - "preferences": { - "hostedPlugin": "Plugin alojado", - "toolbar": "Barra de ferramentas" - }, - "preview": { - "openByDefault": "Abrir a pré-visualização em vez do editor por defeito." - }, - "property-view": { - "created": "Criado em", - "directory": "Directório", - "lastModified": "Última modificação", - "location": "Localização", - "noProperties": "Não existem propriedades disponíveis.", - "properties": "Imóveis", - "size": "Tamanho", - "symbolicLink": "Ligação simbólica" - }, - "scm": { - "amend": "Alterar", - "amendHeadCommit": "Compromisso HEAD", - "amendLastCommit": "Emendar o último compromisso", - "changeRepository": "Repositório de Mudanças...", - "config.untrackedChanges": "Controla o comportamento de mudanças não controladas.", - "config.untrackedChanges.hidden": "escondido", - "config.untrackedChanges.mixed": "misto", - "config.untrackedChanges.separate": "em separado", - "dirtyDiff": { - "close": "Fechar Alterar Vista panorâmica" - }, - "history": "História", - "noRepositoryFound": "Não foi encontrado nenhum repositório", - "unamend": "Unamend", - "unamendCommit": "Compromisso sem alterações" - }, - "search-in-workspace": { - "includeIgnoredFiles": "Incluir Ficheiros Ignorados", - "noFolderSpecified": "Não abriu nem especificou uma pasta. Apenas os ficheiros abertos são actualmente pesquisados.", - "resultSubset": "Este é apenas um subconjunto de todos os resultados. Use um termo de pesquisa mais específico para restringir a lista de resultados.", - "searchOnEditorModification": "Pesquisar o editor activo quando modificado." - }, - "secondary-window": { - "extract-widget": "Mover vista para a janela secundária" - }, - "shell-area": { - "secondary": "Janela Secundária" - }, - "task": { - "attachTask": "Anexar Tarefa...", - "clearHistory": "História clara", - "noTaskToRun": "Não foi encontrada nenhuma tarefa para executar. Configurar tarefas...", - "openUserTasks": "Tarefas de utilizador aberto" - }, - "terminal": { - "defaultProfile": "O perfil padrão utilizado em {0}", - "enableCopy": "Habilitar ctrl-c (cmd-c em macOS) para copiar o texto seleccionado", - "enablePaste": "Habilitar ctrl-v (cmd-v em macOS) para colar a partir da prancheta", - "profileArgs": "Os argumentos de concha que este perfil utiliza.", - "profileColor": "Um ID de cor de tema terminal a associar ao terminal.", - "profileDefault": "Escolher Perfil por Defeito...", - "profileIcon": "Um código de identificação para associar com o ícone do terminal.\nterminal-tmux: \"$(terminal-tmux)\".", - "profileNew": "Novo Terminal (Com Perfil)...", - "profilePath": "O caminho da concha que este perfil utiliza.", - "profiles": "Os perfis a apresentar aquando da criação de um novo terminal. Definir manualmente a propriedade do caminho com args opcionais.\nDefinir um perfil existente para `nulo` para ocultar o perfil da lista, por exemplo: `\"{0}\": nulo`.", - "rendererType": "Controla a forma como o terminal é renderizado.", - "rendererTypeDeprecationMessage": "O tipo de renderizador já não é suportado como uma opção.", - "selectProfile": "Seleccionar um perfil para o novo terminal", - "shell.deprecated": "Isto é depreciado, a nova forma recomendada para configurar a sua shell padrão é criando um perfil de terminal em 'terminal.integrated.profiles.{0}' e definindo o seu nome de perfil como o padrão em 'terminal.integrated.defaultProfile'.{0}.", - "shellArgsLinux": "Os argumentos de linha de comando a utilizar quando no terminal Linux.", - "shellArgsOsx": "Os argumentos de linha de comando a utilizar quando no terminal macOS.", - "shellArgsWindows": "Os argumentos de linha de comando a utilizar quando no terminal do Windows.", - "shellLinux": "O caminho da shell que o terminal utiliza no Linux (predefinição: '{0}'}).", - "shellOsx": "O caminho da concha que o terminal utiliza em macOS (predefinição: '{0}'}).", - "shellWindows": "O caminho da concha que o terminal utiliza no Windows. (predefinição: '{0}')." - }, - "test": { - "cancelAllTestRuns": "Cancelar todas as execuções de teste", - "testRunDefaultName": "{0} correr {1}", - "testRuns": "Testes" - }, - "toolbar": { - "addCommand": "Adicionar Comando à Barra de Ferramentas", - "addCommandPlaceholder": "Encontrar um comando para adicionar à barra de ferramentas", - "centerColumn": "Coluna Central", - "failedUpdate": "Falha em actualizar o valor de '{0}' em '{1}'.", - "filterIcons": "Ícones de filtro", - "iconSelectDialog": "Seleccione um Ícone para '{0}'.", - "iconSet": "Conjunto de Ícones", - "insertGroupLeft": "Inserir separador de grupo (Esquerda)", - "insertGroupRight": "Inserir separador de grupo (à direita)", - "leftColumn": "Coluna da Esquerda", - "openJSON": "Personalizar a barra de ferramentas (Abrir JSON)", - "removeCommand": "Remover Comando da Barra de Ferramentas", - "restoreDefaults": "Restaurar Padrões da Barra de Ferramentas", - "rightColumn": "Coluna da direita", - "selectIcon": "Seleccionar Ícone", - "toggleToolbar": "Barra de ferramentas Toggle", - "toolbarLocationPlaceholder": "Onde gostaria que o comando fosse adicionado?", - "useDefaultIcon": "Usar Ícone por defeito" - }, - "typehierarchy": { - "subtypeHierarchy": "Subtipo Hierarquia", - "supertypeHierarchy": "Hierarquia de Supertipo" - }, - "vsx-registry": { - "confirmDialogMessage": "A extensão \"{0}\" não foi verificada e pode representar um risco de segurança.", - "confirmDialogTitle": "Tem a certeza de que pretende prosseguir com a instalação?", - "downloadCount": "Contagem de downloads: {0}", - "errorFetching": "Extensões de erro de busca.", - "errorFetchingConfigurationHint": "Isto pode ser causado por problemas de configuração da rede.", - "failedInstallingVSIX": "Falha na instalação {0} da VSIX.", - "invalidVSIX": "O ficheiro seleccionado não é um plugin \"*.vsix\" válido.", - "license": "Licença: {0}", - "onlyShowVerifiedExtensionsDescription": "Isto permite que o {0} mostre apenas as extensões verificadas.", - "onlyShowVerifiedExtensionsTitle": "Mostrar apenas extensões verificadas", - "recommendedExtensions": "Uma lista dos nomes das extensões recomendadas para utilização neste espaço de trabalho.", - "searchPlaceholder": "Pesquisar extensões em {0}", - "showInstalled": "Mostrar extensões instaladas", - "showRecommendedExtensions": "Controla se as notificações são mostradas para recomendações de extensão.", - "vsx-extensions-contribution": { - "update-version-uninstall-error": "Erro ao retirar a extensão: {0}.", - "update-version-version-error": "Falha na instalação da versão {0} de {1}." - } - }, - "webview": { - "goToReadme": "Ir para LEIAME", - "messageWarning": " O padrão de alojamento do terminal {0} foi alterado para `{1}`; alterar o padrão pode levar a vulnerabilidades de segurança. Ver `{2}` para mais informações." - }, - "workspace": { - "compareWithEachOther": "Comparar uns com os outros", - "confirmDeletePermanently.description": "Falha em apagar \"{0}\" utilizando o Lixo. Pretende, em vez disso, apagar permanentemente?", - "confirmDeletePermanently.solution": "Pode desactivar a utilização de Lixo nas preferências.", - "confirmDeletePermanently.title": "Erro ao apagar ficheiro", - "confirmMessage.delete": "Quer mesmo apagar os seguintes ficheiros?", - "confirmMessage.dirtyMultiple": "Quer mesmo apagar ficheiros {0} com alterações não guardadas?", - "confirmMessage.dirtySingle": "Quer mesmo eliminar {0} com alterações não guardadas?", - "confirmMessage.uriMultiple": "Quer realmente apagar todos os ficheiros {0} seleccionados?", - "confirmMessage.uriSingle": "Quer mesmo apagar {0}?", - "duplicate": "Duplicado", - "failSaveAs": "Não é possível executar \"{0}\" para o widget actual.", - "newFilePlaceholder": "Nome do ficheiro", - "newFolderPlaceholder": "Nome da pasta", - "noErasure": "Nota: Nada será apagado do disco", - "openRecentPlaceholder": "Digite o nome do espaço de trabalho que pretende abrir", - "openRecentWorkspace": "Espaço de Trabalho Recente Aberto...", - "preserveWindow": "Activar a abertura de espaços de trabalho na janela actual.", - "removeFolder": "Tem a certeza de que quer remover a seguinte pasta do espaço de trabalho?", - "removeFolders": "Tem a certeza de que quer remover as seguintes pastas do espaço de trabalho?", - "trashTitle": "Mover {0} para o Lixo", - "trustEmptyWindow": "Controla se o espaço de trabalho vazio é ou não de confiança por defeito.", - "trustEnabled": "Controla se a confiança no espaço de trabalho está ou não activada. Se estiver desactivado, todos os espaços de trabalho são de confiança.", - "trustRequest": "Uma extensão pede confiança no espaço de trabalho, mas a API correspondente ainda não é totalmente suportada. Quer confiar neste espaço de trabalho?", - "untitled-cleanup": "Parece haver muitos ficheiros de espaço de trabalho sem título. Por favor verifique {0} e remova quaisquer ficheiros não utilizados.", - "workspaceFolderAdded": "Foi criado um espaço de trabalho com múltiplas raízes. Quer guardar a configuração do seu espaço de trabalho como um ficheiro?", - "workspaceFolderAddedTitle": "Pasta adicionada ao espaço de trabalho" - } - } -} diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index 08f7216c273a7..77d62935651d0 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -362,6 +362,9 @@ "alreadyRunning": "Хостируемый экземпляр уже запущен.", "debugInstance": "Отладочный экземпляр", "debugMode": "Использование inspect или inspect-brk для отладки Node.js", + "debugPorts": { + "debugPort": "Порт, используемый для отладки Node.js на этом сервере" + }, "devHost": "Ведущий разработки", "failed": "Не удалось запустить размещенный экземпляр плагина: {0}", "hostedPlugin": "Хостируемый плагин", diff --git a/packages/core/i18n/nls.tr.json b/packages/core/i18n/nls.tr.json new file mode 100644 index 0000000000000..e2337b14a427b --- /dev/null +++ b/packages/core/i18n/nls.tr.json @@ -0,0 +1,555 @@ +{ + "debug.breakpoint.editCondition": "Düzenleme Durumu...", + "notebook.cell.changeToCode": "Hücreyi Kod Olarak Değiştir", + "notebook.cell.changeToMarkdown": "Hücreyi Markdown Olarak Değiştirme", + "notebook.cell.insertMarkdownCellAbove": "Markdown Hücresini Yukarıya Ekleme", + "notebook.cell.insertMarkdownCellBelow": "Aşağıya Markdown Hücresi Ekleme", + "terminal:new:profile": "Profilden Yeni Entegre Terminal Oluşturma", + "terminal:profile:default": "Varsayılan Terminal Profilini seçin", + "theia": { + "callhierarchy": { + "noCallers": "Herhangi bir arayan tespit edilmedi.", + "open": "Açık Çağrı Hiyerarşisi" + }, + "core": { + "about": { + "compatibility": "{0} Uyumluluk", + "defaultApi": "Varsayılan {0} API", + "version": "Versiyon" + }, + "common": { + "closeAll": "Tüm Sekmeleri Kapat", + "closeAllTabMain": "Ana Alandaki Tüm Sekmeleri Kapat", + "closeOtherTabMain": "Ana Alandaki Diğer Sekmeleri Kapat", + "closeOthers": "Diğer Sekmeleri Kapat", + "closeRight": "Sekmeleri Sağa Kapat", + "closeTab": "Sekmeyi Kapat", + "closeTabMain": "Ana Alandaki Sekmeyi Kapat", + "collapseAllTabs": "Tüm Yan Panelleri Daralt", + "collapseBottomPanel": "Alt Paneli Değiştir", + "collapseTab": "Yan Paneli Daralt", + "showNextTabGroup": "Sonraki Sekme Grubuna Geç", + "showNextTabInGroup": "Gruptaki Sonraki Sekmeye Geç", + "showPreviousTabGroup": "Önceki Sekme Grubuna Geç", + "showPreviousTabInGroup": "Grupta Önceki Sekmeye Geç", + "toggleMaximized": "Maksimize Edilmiş Değiştir" + }, + "copyInfo": "Yolunu kopyalamak için önce bir dosya açın", + "copyWarn": "Lütfen tarayıcının kopyala komutunu veya kısayolunu kullanın.", + "cutWarn": "Lütfen tarayıcının kes komutunu veya kısayolunu kullanın.", + "enhancedPreview": { + "classic": "Temel bilgileri içeren sekmenin basit bir önizlemesini görüntüleyin.", + "enhanced": "Sekmenin ek bilgiler içeren gelişmiş bir önizlemesini görüntüleyin.", + "visual": "Sekmenin görsel bir önizlemesini görüntüleyin." + }, + "file": { + "browse": "Gözat" + }, + "highlightModifiedTabs": "Değiştirilmiş (kirli) düzenleyici sekmelerinde bir üst kenarlık çizilip çizilmeyeceğini kontrol eder.", + "keybindingStatus": "{0} basıldı, daha fazla tuş beklendi", + "keyboard": { + "choose": "Klavye Düzenini Seçin", + "chooseLayout": "Bir klavye düzeni seçin", + "current": "(güncel: {0})", + "currentLayout": " - mevcut düzen", + "mac": "Mac Klavyeleri", + "pc": "PC Klavyeleri", + "tryDetect": "Tarayıcı bilgilerinden ve basılan tuşlardan klavye düzenini tespit etmeye çalışın." + }, + "navigator": { + "clipboardWarn": "Panoya erişim reddedildi. Tarayıcınızın izinlerini kontrol edin.", + "clipboardWarnFirefox": "Pano API'si mevcut değildir. '{1}' sayfasındaki '{0}' tercihi ile etkinleştirilebilir. Ardından Theia'yı yeniden yükleyin. FireFox'un sistem panosuna tam erişim sağlamasına izin vereceğini unutmayın." + }, + "offline": "Çevrimdışı", + "pasteWarn": "Lütfen tarayıcının yapıştırma komutunu veya kısayolunu kullanın.", + "quitMessage": "Kaydedilmemiş hiçbir değişiklik kaydedilmeyecektir.", + "resetWorkbenchLayout": "Çalışma Tezgahı Düzenini Sıfırla", + "searchbox": { + "close": "Kapat (Kaçış)", + "next": "Sonraki (Aşağı)", + "previous": "Önceki (Yukarı)" + }, + "secondaryWindow": { + "alwaysOnTop": "Etkinleştirildiğinde, ikincil pencere farklı uygulamalarınkiler de dahil olmak üzere diğer tüm pencerelerin üzerinde kalır.", + "description": "Çıkarılan ikincil pencerenin ilk konumunu ve boyutunu ayarlar.", + "fullSize": "Çıkarılan widget'ın konumu ve boyutu, çalışan Theia uygulamasıyla aynı olacaktır.", + "halfWidth": "Çıkarılan widget'ın konumu ve boyutu, çalışan Theia uygulamasının genişliğinin yarısı kadar olacaktır.", + "originalSize": "Çıkarılan widget'ın konumu ve boyutu orijinal widget ile aynı olacaktır." + }, + "silentNotifications": "Bildirim açılır pencerelerinin bastırılıp bastırılmayacağını denetler.", + "tabDefaultSize": "Sekmeler için varsayılan boyutu belirtir.", + "tabMaximize": "Çift tıklamada sekmelerin büyütülüp büyütülmeyeceğini kontrol eder.", + "tabMinimumSize": "Sekmeler için minimum boyutu belirtir.", + "tabShrinkToFit": "Sekmeleri mevcut alana sığacak şekilde küçültün." + }, + "debug": { + "addConfigurationPlaceholder": "Yapılandırma eklemek için çalışma alanı kökünü seçin", + "breakpoint": "kesme noktası", + "compound-cycle": "Başlat yapılandırması '{0}' kendisiyle birlikte bir döngü içerir", + "continueAll": "Tümü Devam Ediyor", + "copyExpressionValue": "İfade Değerini Kopyala", + "dataBreakpoint": "veri kesme noktası", + "debugVariableInput": "{0} Değerini Ayarla", + "entry": "Giriş", + "exception": "istisna", + "functionBreakpoint": "fonksiyon kesme noktası", + "goto": "Goto", + "instruction-breakpoint": "Talimat Kesme Noktası", + "instructionBreakpoint": "talimat kesme noktası", + "missingConfiguration": "Dinamik yapılandırma '{0}:{1}' eksik veya uygulanabilir değil", + "pause": "duraklama", + "pauseAll": "Tümünü Duraklat", + "reveal": "Açığa Çıkar", + "step": "adım", + "threads": "İplikler", + "toggleTracing": "Hata ayıklama bağdaştırıcılarıyla izleme iletişimini etkinleştirme/devre dışı bırakma" + }, + "editor": { + "diffEditor.wordWrap2": "Satırlar `#editor.wordWrap#` ayarına göre sarılacaktır.", + "dirtyEncoding": "Dosya kirli. Lütfen başka bir kodlama ile yeniden açmadan önce kaydedin.", + "editor.accessibilitySupport0": "Bir Ekran Okuyucunun takılı olduğunu algılamak için platform API'lerini kullanma", + "editor.accessibilitySupport1": "Ekran Okuyucu ile kullanım için optimize edin", + "editor.accessibilitySupport2": "Bir ekran okuyucunun bağlı olmadığını varsayalım", + "editor.bracketPairColorization.enabled": "Ayraç çifti renklendirmesinin etkin olup olmadığını kontrol eder. Ayraç vurgu renklerini geçersiz kılmak için `#workbench.colorCustomizations#` kullanın.", + "editor.codeActionWidget.includeNearbyQuickfixes": "O anda bir tanılama üzerinde değilken bir hat içindeki en yakın hızlı düzeltmeyi göstermeyi etkinleştirin/devre dışı bırakın.", + "editor.cursorSurroundingLinesStyle": "Ne zaman `#cursorSurroundingLines#` uygulanacağını kontrol eder.", + "editor.detectIndentation": "Dosya içeriğine bağlı olarak bir dosya açıldığında `#editor.tabSize#` ve `#editor.insertSpaces#` öğelerinin otomatik olarak algılanıp algılanmayacağını kontrol eder.", + "editor.dropIntoEditor.enabled": "Bir dosyayı `shift` tuşunu basılı tutarak bir metin düzenleyicisine sürükleyip bırakıp bırakamayacağınızı kontrol eder (dosyayı bir düzenleyicide açmak yerine).", + "editor.formatOnSaveMode.modificationsIfAvailable": "Yalnızca değişiklikleri biçimlendirmeye çalışır (kaynak kontrolü gerektirir). Kaynak kontrolü kullanılamazsa, tüm dosya biçimlendirilecektir.", + "editor.hover.hidingDelay": "Üzerine gelinen öğenin gizlenmesinden sonraki gecikmeyi milisaniye cinsinden kontrol eder. Etkinleştirilmek için `editor.hover.sticky` gerektirir.", + "editor.inlayHints.enabled1": "Kakma ipuçları varsayılan olarak gösterilir ve Ctrl+Alt tuşları basılı tutulduğunda gizlenir", + "editor.inlayHints.enabled2": "Kakma ipuçları varsayılan olarak gizlidir ve Ctrl+Alt tuşları basılı tutulduğunda gösterilir", + "editor.inlayHints.fontFamily": "Düzenleyicideki yerleşik ipuçlarının yazı tipi ailesini kontrol eder. Boş olarak ayarlandığında, `#editor.fontFamily#` kullanılır.", + "editor.inlayHints.fontSize": "Düzenleyicideki yerleşik ipuçlarının yazı tipi boyutunu kontrol eder. Yapılandırılan değer `5`ten küçük veya düzenleyici yazı tipi boyutundan büyük olduğunda varsayılan olarak `#editor.fontSize#` kullanılır.", + "editor.insertSpaces": "Tab` tuşuna basıldığında boşluk ekler. Bu ayar, `#editor.detectIndentation#` açık olduğunda dosya içeriğine göre geçersiz kılınır.", + "editor.occurrencesHighlight": "Düzenleyicinin anlamsal sembol oluşumlarını vurgulayıp vurgulamayacağını kontrol eder.", + "editor.quickSuggestions": "Yazarken önerilerin otomatik olarak gösterilip gösterilmeyeceğini kontrol eder. Bu, yorumlar, dizeler ve diğer kodların yazılması için kontrol edilebilir. Hızlı öneri, hayalet metin olarak veya öneri widget'ı ile gösterilecek şekilde yapılandırılabilir. Ayrıca önerilerin özel karakterler tarafından tetiklenip tetiklenmeyeceğini kontrol eden '#editor.suggestOnTriggerCharacters#'ayarına da dikkat edin.", + "editor.stickyScroll.scrollWithEditor": "Yapışkan kaydırma widget'ının düzenleyicinin yatay kaydırma çubuğu ile kaydırılmasını etkinleştirin.", + "editor.suggestFontSize": "Suggest widget'ı için yazı tipi boyutu. 0` olarak ayarlandığında, `#editor.fontSize#` değeri kullanılır.", + "editor.suggestLineHeight": "Suggest widget'ı için satır yüksekliği. 0` olarak ayarlandığında, `#editor.lineHeight#` değeri kullanılır. Minimum değer 8`dir.", + "editor.tabSize": "Bir sekmenin eşit olduğu boşluk sayısı. Bu ayar, `#editor.detectIndentation#` açık olduğunda dosya içeriğine göre geçersiz kılınır.", + "editor.useTabStops": "Boşluk ekleme ve silme sekme duraklarını takip eder.", + "editor.wordBasedSuggestions": "Tamamlamaların belgedeki sözcüklere göre hesaplanıp hesaplanmayacağını denetler.", + "editor.wordBasedSuggestionsMode": "Kelime tabanlı tamamlamaların hangi belgelerden hesaplanacağını kontrol eder.", + "files.autoSave": "Kaydedilmemiş değişiklikleri olan editörlerin [otomatik kaydet] (https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) kontrollerini yapar.", + "files.autoSave.afterDelay": "Değişiklikler içeren bir düzenleyici, yapılandırılan `#files.autoSaveDelay#` değerinden sonra otomatik olarak kaydedilir.", + "files.autoSave.off": "Değişiklik yapılan bir düzenleyici asla otomatik olarak kaydedilmez.", + "files.autoSave.onFocusChange": "Düzenleyici odağı kaybettiğinde değişiklik yapılan bir düzenleyici otomatik olarak kaydedilir.", + "files.autoSave.onWindowChange": "Değişiklikler içeren bir düzenleyici, pencere odağını kaybettiğinde otomatik olarak kaydedilir.", + "formatOnSaveTimeout": "Dosya kaydedildiğinde çalıştırılan biçimlendirmenin iptal edileceği milisaniye cinsinden zaman aşımı.", + "persistClosedEditors": "Pencere yeniden yüklenirken çalışma alanı için kapalı düzenleyici geçmişinin devam ettirilip ettirilmeyeceğini kontrol eder.", + "showAllEditors": "Tüm Açık Editörleri Göster", + "splitHorizontal": "Bölme Düzenleyici Yatay", + "splitVertical": "Bölünmüş Editör Dikey", + "toggleStickyScroll": "Yapışkan Kaydırmayı Değiştir" + }, + "file-search": { + "toggleIgnoredFiles": " (Yok sayılan dosyaları göstermek/gizlemek için {0} adresine basın)" + }, + "fileDialog": { + "showHidden": "Gizli dosyaları göster" + }, + "fileSystem": { + "fileResource": { + "overWriteBody": "Dosya sisteminde '{0}' adresinde yapılan değişikliklerin üzerine yazmak istiyor musunuz?" + } + }, + "filesystem": { + "copiedToClipboard": "İndirme bağlantısını panoya kopyaladım.", + "copyDownloadLink": "İndirme Bağlantısını Kopyala", + "dialog": { + "initialLocation": "İlk Konuma Git", + "multipleItemMessage": "Yalnızca bir öğe seçebilirsiniz", + "name": "İsim:", + "navigateBack": "Geri Git", + "navigateForward": "İleriye Yönelin", + "navigateUp": "Bir Dizinde Gezinme" + }, + "fileResource": { + "binaryFileQuery": "Açmak biraz zaman alabilir ve IDE'nin yanıt vermemesine neden olabilir. Yine de '{0}' adresini açmak istiyor musunuz?", + "binaryTitle": "Dosya ya ikilidir ya da desteklenmeyen bir metin kodlaması kullanmaktadır.", + "largeFileTitle": "Dosya çok büyük ({0}).", + "overwriteTitle": "Dosya sisteminde '{0}' dosyası değiştirildi." + }, + "filesExclude": "Dosya ve klasörleri dışlamak için glob kalıplarını yapılandırın. Örneğin, dosya Gezgini bu ayara göre hangi dosya ve klasörlerin gösterileceğine veya gizleneceğine karar verir.", + "format": "Format:", + "maxConcurrentUploads": "Birden fazla dosya yüklerken yüklenecek maksimum eşzamanlı dosya sayısı. 0, tüm dosyaların eşzamanlı olarak yükleneceği anlamına gelir.", + "maxFileSizeMB": "Açılabilecek maksimum dosya boyutunu MB cinsinden kontrol eder.", + "prepareDownload": "İndirme hazırlanıyor...", + "prepareDownloadLink": "İndirme bağlantısı hazırlanıyor...", + "processedOutOf": "İşlenmiş {0} dışında {1}", + "replaceTitle": "Dosya Değiştir", + "uploadFiles": "Dosya Yükle...", + "uploadedOutOf": "{0} adresinden yüklendi {1}" + }, + "getting-started": { + "apiComparator": "{0} API Uyumluluğu", + "newExtension": "Yeni Bir Uzantı İnşa Etmek", + "newPlugin": "Yeni Bir Eklenti Oluşturma", + "startup-editor": { + "welcomePage": "{0} ve uzantılarını kullanmaya başlamanıza yardımcı olacak içeriğe sahip Hoş Geldiniz sayfasını açın." + } + }, + "git": { + "aFewSecondsAgo": "birkaç saniye önce", + "addSignedOff": "Signed-off-by ekleyin", + "amendReuseMessage": "Son taahhüt mesajını yeniden kullanmak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "amendRewrite": "Önceki commit mesajını yeniden yazın. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "checkoutCreateLocalBranchWithName": "Adı: {0} olan yeni bir yerel şube oluşturun. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "checkoutProvideBranchName": "Lütfen bir şube adı belirtiniz. ", + "checkoutSelectRef": "Ödeme yapmak veya yeni bir yerel şube oluşturmak için bir ref seçin:", + "cloneQuickInputLabel": "Lütfen bir Git deposu konumu girin. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "cloneRepository": "Git deposunu klonlayın: {0}. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "compareWith": "İle Karşılaştırın...", + "compareWithBranchOrTag": "O anda etkin olan {0} dalı ile karşılaştırmak için bir dal veya etiket seçin:", + "diff": "Diff", + "dirtyDiffLinesLimit": "Düzenleyicinin satır sayısı bu sınırı aşarsa, kirli fark süslemelerini gösterme.", + "dropStashMessage": "Zula başarıyla kaldırıldı.", + "editorDecorationsEnabled": "Düzenleyicide git süslemelerini göster.", + "fetchPickRemote": "Getirmek için bir uzaktan kumanda seçin:", + "gitDecorationsColors": "Navigatörde renk dekorasyonunu kullanın.", + "mergeQuickPickPlaceholder": "Şu anda etkin olan {0} dalıyla birleştirmek için bir dal seçin:", + "missingUserInfo": "git'te 'user.name' ve 'user.email' dosyalarınızı yapılandırdığınızdan emin olun.", + "noHistoryForError": "için mevcut bir geçmiş bulunmamaktadır. {0}", + "noPreviousCommit": "Değişiklik yapmak için önceden taahhüt yok", + "noRepositoriesSelected": "Hiçbir depo seçilmemiştir.", + "prepositionIn": "içinde", + "repositoryNotInitialized": "{0} deposu henüz başlatılmadı.", + "stashChanges": "Zula değişiklikleri. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "stashChangesWithMessage": "Zula mesajla değişir: {0}. Onaylamak için 'Enter' tuşuna veya iptal etmek için 'Escape' tuşuna basın.", + "tabTitleIndex": "{0} (Dizin)", + "tabTitleWorkingTree": "{0} (Çalışma ağacı)", + "toggleBlameAnnotations": "Suçlama Ek Açıklamalarını Değiştir" + }, + "keybinding-schema-updater": { + "deprecation": "Bunun yerine `when` cümlesini kullanın." + }, + "keymaps": { + "addKeybindingTitle": "Şunlar için Tuş Bağlama Ekle {0}", + "editKeybinding": "Tuş Bağlamasını Düzenle...", + "editKeybindingTitle": "için Tuş Bağlamasını Düzenle {0}", + "editWhenExpression": "İfade Ne Zaman Düzenlenir...", + "editWhenExpressionTitle": "İfade için Düzenle {0}", + "keybinding": { + "copy": "Tuş Bağlamayı Kopyala", + "copyCommandId": "Tuş Bağlama Komut Kimliğini Kopyala", + "copyCommandTitle": "Tuş Bağlantısını Kopyala Komut Başlığı", + "edit": "Tuş Bağlamasını Düzenle...", + "editWhenExpression": "İfade Olduğunda Tuş Bağlantısını Düzenle..." + }, + "keybindingCollidesValidation": "tuş bağlama şu anda çakışıyor", + "requiredKeybindingValidation": "tuş bağlama değeri gereklidir", + "resetKeybindingConfirmation": "Bu tuş bağlamayı gerçekten varsayılan değerine sıfırlamak istiyor musunuz?", + "resetKeybindingTitle": "için tuş bağlamayı sıfırla {0}", + "resetMultipleKeybindingsWarning": "Bu komut için birden fazla tuş ataması varsa, hepsi sıfırlanacaktır." + }, + "localize": { + "offlineTooltip": "Arka uca bağlanılamıyor." + }, + "markers": { + "clearAll": "Tümünü Temizle", + "noProblems": "Çalışma alanında şu ana kadar herhangi bir sorun tespit edilmedi.", + "tabbarDecorationsEnabled": "Sekme çubuklarında sorun dekoratörlerini (tanılama işaretleyicileri) gösterin." + }, + "memory-inspector": { + "addressTooltip": "Görüntülenecek bellek konumu, bir adres veya bir adrese göre değerlendirilen ifade", + "ascii": "ASCII", + "binary": "İkili", + "byteSize": "Bayt Boyutu", + "bytesPerGroup": "Grup Başına Bayt", + "closeSettings": "Ayarları Kapat", + "columns": "Sütunlar", + "command": { + "createNewMemory": "Yeni Bellek Denetçisi Oluşturun", + "createNewRegisterView": "Yeni Kayıt Görünümü Oluştur", + "followPointer": "Pointer'ı Takip Edin", + "followPointerMemory": "Bellek Denetçisinde İşaretçiyi Takip Etme", + "resetValue": "Sıfırlama Değeri", + "showRegister": "Bellek Denetçisinde Kaydı Göster", + "viewVariable": "Değişkeni Bellek Denetçisinde Göster" + }, + "data": "Veri", + "decimal": "Ondalık", + "diff": { + "label": "Diff: {0}" + }, + "diff-widget": { + "offset-label": "{0} Ofset", + "offset-title": "Belleğin kaydırılacağı baytlar {0}" + }, + "editable": { + "apply": "Değişiklikleri Uygula", + "clear": "Açık Değişiklikler" + }, + "endianness": "Endianness", + "extraColumn": "Ekstra Sütun", + "groupsPerRow": "Satır Başına Gruplar", + "hexadecimal": "Onaltılık", + "length": "Uzunluk", + "lengthTooltip": "Getirilecek bayt sayısı, ondalık veya onaltılık olarak", + "memory": { + "addressField": { + "memoryReadError": "Konum alanına bir adres veya ifade girin." + }, + "freeze": "Bellek Görünümünü Dondur", + "hideSettings": "Ayarlar Panelini Gizle", + "readError": { + "bounds": "Bellek sınırları aşıldı, sonuç kesilecektir.", + "noContents": "Şu anda kullanılabilir bellek içeriği yok." + }, + "readLength": { + "memoryReadError": "Uzunluk alanına bir uzunluk (ondalık veya onaltılık sayı) girin." + }, + "showSettings": "Ayarlar Panelini Göster", + "unfreeze": "Bellek Görünümünü Çöz", + "userError": "Bellek alınırken bir hata oluştu." + }, + "memoryCategory": "Bellek Denetçisi", + "memoryInspector": "Bellek Denetçisi", + "memoryTitle": "Hafıza", + "octal": "Sekizli", + "offset": "Ofset", + "offsetTooltip": "Gezinirken geçerli bellek konumuna eklenecek ofset", + "provider": { + "localsError": "Yerel değişkenler okunamıyor. Etkin hata ayıklama oturumu yok.", + "readError": "Bellek okunamıyor. Etkin hata ayıklama oturumu yok.", + "writeError": "Bellek yazılamıyor. Etkin hata ayıklama oturumu yok." + }, + "register": "Kayıt Olun", + "register-widget": { + "filter-placeholder": "Filtre (ile başlar)" + }, + "registerReadError": "Kayıtların alınmasında bir hata oluştu.", + "registers": "Kayıtlar", + "toggleComparisonWidgetVisibility": "Karşılaştırma Widget Görünürlüğünü Aç / Kapat", + "utils": { + "afterBytes": "Karşılaştırmak istediğiniz her iki widget'a da bellek yüklemeniz gerekir. {0} adresinde bellek yüklü değildir.", + "bytesMessage": "Karşılaştırmak istediğiniz her iki widget'a da bellek yüklemeniz gerekir. {0} adresinde bellek yüklü değildir." + } + }, + "messages": { + "notificationTimeout": "Bilgilendirici bildirimler bu zaman aşımından sonra gizlenecektir.", + "toggleNotifications": "Bildirimleri Aç / Kapat" + }, + "mini-browser": { + "typeUrl": "Bir URL yazın" + }, + "monaco": { + "noSymbolsMatching": "Eşleşen sembol yok", + "typeToSearchForSymbols": "Sembolleri aramak için yazın" + }, + "navigator": { + "autoReveal": "Otomatik Gösterge", + "clipboardWarn": "Panoya erişim reddedildi. Tarayıcınızın izinlerini kontrol edin.", + "clipboardWarnFirefox": "Pano API'si mevcut değildir. '{1}' sayfasındaki '{0}' tercihi ile etkinleştirilebilir. Ardından Theia'yı yeniden yükleyin. FireFox'un sistem panosuna tam erişim sağlamasına izin vereceğini unutmayın.", + "refresh": "Explorer'da Yenile", + "reveal": "Explorer'da Göster", + "toggleHiddenFiles": "Gizli Dosyaları Aç / Kapat" + }, + "output": { + "clearOutputChannel": "Çıkış Kanalını Temizle...", + "closeOutputChannel": "Çıkış Kanalını Kapat...", + "hiddenChannels": "Gizli Kanallar", + "hideOutputChannel": "Çıkış Kanalını Gizle...", + "maxChannelHistory": "Bir çıkış kanalındaki maksimum giriş sayısı.", + "outputChannels": "Çıkış Kanalları", + "showOutputChannel": "Çıkış Kanalını Göster..." + }, + "plugin": { + "blockNewTab": "Tarayıcınız yeni bir sekme açılmasını engelledi" + }, + "plugin-dev": { + "alreadyRunning": "Barındırılan örnek zaten çalışıyor.", + "debugInstance": "Hata Ayıklama Örneği", + "debugMode": "Node.js hata ayıklama için inspect veya inspect-brk kullanma", + "debugPorts": { + "debugPort": "Bu sunucunun Node.js hata ayıklaması için kullanılacak bağlantı noktası" + }, + "devHost": "Geliştirme Ev Sahibi", + "failed": "Barındırılan eklenti örneği çalıştırılamadı: {0}", + "hostedPlugin": "Barındırılan Eklenti", + "hostedPluginRunning": "Barındırılan Eklenti: Çalışıyor", + "hostedPluginStarting": "Barındırılan Eklenti: Başlıyor", + "hostedPluginStopped": "Barındırılan Eklenti: Durduruldu", + "hostedPluginWatching": "Barındırılan Eklenti: İzleme", + "instanceTerminated": "{0} sonlandırıldı", + "launchOutFiles": "Oluşturulan JavaScript dosyalarını bulmak için glob kalıpları dizisi (`${pluginPath}` eklentinin gerçek yolu ile değiştirilecektir).", + "noValidPlugin": "Belirtilen klasör geçerli bir eklenti içermiyor.", + "notRunning": "Barındırılan örnek çalışmıyor.", + "pluginFolder": "Eklenti klasörü olarak ayarlanmıştır: {0}", + "preventedNewTab": "Tarayıcınız yeni bir sekme açılmasını engelledi", + "restartInstance": "Örneği Yeniden Başlat", + "running": "Barındırılan örnek şu adreste çalışıyor:", + "select": "Seçiniz", + "selectPath": "Yol Seçin", + "startInstance": "Örneği Başlat", + "starting": "Barındırılan örnek sunucusu başlatılıyor ...", + "stopInstance": "Örneği Durdur", + "unknownTerminated": "Örnek sonlandırıldı", + "watchMode": "Geliştirme aşamasındaki eklenti üzerinde izleyiciyi çalıştırın" + }, + "plugin-ext": { + "authentication-main": { + "loginTitle": "Giriş" + }, + "plugins": "Eklentiler", + "webviewTrace": "Web görünümleri ile iletişim izlemeyi kontrol eder.", + "webviewWarnIfUnsecure": "Kullanıcıları web görünümlerinin şu anda güvenli olmayan bir şekilde dağıtıldığı konusunda uyarır." + }, + "preferences": { + "hostedPlugin": "Barındırılan Eklenti", + "toolbar": "Araç Çubuğu" + }, + "preview": { + "openByDefault": "Varsayılan olarak düzenleyici yerine önizlemeyi açın." + }, + "property-view": { + "created": "Oluşturuldu", + "directory": "Rehber", + "lastModified": "Son değişiklik", + "location": "Konum", + "noProperties": "Mevcut mülk yok.", + "properties": "Özellikler", + "size": "Boyut", + "symbolicLink": "Sembolik bağlantı" + }, + "scm": { + "amend": "Değiştirmek", + "amendHeadCommit": "HEAD Commit", + "amendLastCommit": "Son taahhüdü değiştirin", + "changeRepository": "Depoyu Değiştir...", + "config.untrackedChanges": "İzlenmeyen değişikliklerin nasıl davranacağını kontrol eder.", + "config.untrackedChanges.hidden": "gizli", + "config.untrackedChanges.mixed": "karışık", + "config.untrackedChanges.separate": "ayrı", + "dirtyDiff": { + "close": "Kapat Değişim Peek Görünümü" + }, + "history": "Tarih", + "noRepositoryFound": "Depo bulunamadı", + "unamend": "Unamend", + "unamendCommit": "Değişikliği kaldır" + }, + "search-in-workspace": { + "includeIgnoredFiles": "Yoksayılan Dosyaları Dahil Etme", + "noFolderSpecified": "Bir klasör açmadınız veya belirtmediniz. Şu anda yalnızca açık dosyalar aranmaktadır.", + "resultSubset": "Bu, tüm sonuçların yalnızca bir alt kümesidir. Sonuç listesini daraltmak için daha spesifik bir arama terimi kullanın.", + "searchOnEditorModification": "Değiştirildiğinde etkin düzenleyicide arama yapın." + }, + "secondary-window": { + "extract-widget": "Görünümü İkincil Pencereye Taşı" + }, + "shell-area": { + "secondary": "İkincil Pencere" + }, + "task": { + "attachTask": "Görev Ekle...", + "clearHistory": "Geçmişi Temizle", + "noTaskToRun": "Çalıştırılacak görev bulunamadı. Görevleri Yapılandır...", + "openUserTasks": "Kullanıcı Görevlerini Aç" + }, + "terminal": { + "defaultProfile": "üzerinde kullanılan varsayılan profil {0}", + "enableCopy": "Seçili metni kopyalamak için ctrl-c'yi (macOS'ta cmd-c) etkinleştirin", + "enablePaste": "Panodan yapıştırmak için ctrl-v'yi (macOS'ta cmd-v) etkinleştirin", + "profileArgs": "Bu profilin kullandığı kabuk argümanları.", + "profileColor": "Terminalle ilişkilendirilecek bir terminal tema rengi kimliği.", + "profileDefault": "Varsayılan Profil'i seçin...", + "profileIcon": "Terminal simgesiyle ilişkilendirilecek bir codicon kimliği.\nterminal-tmux:\"$(terminal-tmux)\"", + "profileNew": "Yeni Terminal (Profilli)...", + "profilePath": "Bu profilin kullandığı kabuğun yolu.", + "profiles": "Yeni bir terminal oluştururken sunulacak profiller. Yol özelliğini isteğe bağlı args ile manuel olarak ayarlayın.\nProfili listeden gizlemek için mevcut bir profili `null` olarak ayarlayın, örneğin: `\"{0}\": null`.", + "rendererType": "Terminalin nasıl oluşturulacağını kontrol eder.", + "rendererTypeDeprecationMessage": "Oluşturucu türü artık bir seçenek olarak desteklenmemektedir.", + "selectProfile": "Yeni terminal için bir profil seçin", + "shell.deprecated": "Bu kullanımdan kaldırılmıştır, varsayılan kabuğunuzu yapılandırmak için önerilen yeni yol 'terminal.integrated.profiles.{0}' içinde bir terminal profili oluşturmak ve profil adını 'terminal.integrated.defaultProfile.{0}' içinde varsayılan olarak ayarlamaktır.", + "shellArgsLinux": "Linux terminalindeyken kullanılacak komut satırı argümanları.", + "shellArgsOsx": "macOS terminalindeyken kullanılacak komut satırı argümanları.", + "shellArgsWindows": "Windows terminalindeyken kullanılacak komut satırı argümanları.", + "shellLinux": "Terminalin Linux üzerinde kullandığı kabuğun yolu (varsayılan: '{0}'}).", + "shellOsx": "Terminalin macOS üzerinde kullandığı kabuğun yolu (varsayılan: '{0}'}).", + "shellWindows": "Terminalin Windows üzerinde kullandığı kabuğun yolu. (varsayılan: '{0}')." + }, + "test": { + "cancelAllTestRuns": "Tüm Test Çalışmalarını İptal Et", + "testRunDefaultName": "{0} koşmak {1}", + "testRuns": "Test Çalışmaları" + }, + "toolbar": { + "addCommand": "Araç Çubuğuna Komut Ekleme", + "addCommandPlaceholder": "Araç çubuğuna eklemek için bir komut bulun", + "centerColumn": "Orta Kolon", + "failedUpdate": "'{1}' içindeki '{0}' değeri güncellenemedi.", + "filterIcons": "Filtre Simgeleri", + "iconSelectDialog": "'{0}' için bir Simge seçin", + "iconSet": "Simge Seti", + "insertGroupLeft": "Grup Ayırıcı Ekle (Sol)", + "insertGroupRight": "Grup Ayırıcı Ekle (Sağ)", + "leftColumn": "Sol Sütun", + "openJSON": "Araç Çubuğunu Özelleştir (JSON'u Aç)", + "removeCommand": "Command'ı Araç Çubuğundan Kaldır", + "restoreDefaults": "Araç Çubuğu Varsayılanlarını Geri Yükle", + "rightColumn": "Sağ Sütun", + "selectIcon": "Simge Seçin", + "toggleToolbar": "Araç Çubuğunu Değiştir", + "toolbarLocationPlaceholder": "Komutun nereye eklenmesini istersiniz?", + "useDefaultIcon": "Varsayılan Simgeyi Kullan" + }, + "typehierarchy": { + "subtypeHierarchy": "Alt Tip Hiyerarşisi", + "supertypeHierarchy": "Süper Tip Hiyerarşisi" + }, + "vsx-registry": { + "confirmDialogMessage": "\"{0}\" uzantısı doğrulanmamıştır ve güvenlik riski oluşturabilir.", + "confirmDialogTitle": "Kuruluma devam etmek istediğinizden emin misiniz?", + "downloadCount": "İndirme sayısı: {0}", + "errorFetching": "Uzantılar getirilirken hata oluştu.", + "errorFetchingConfigurationHint": "Bunun nedeni ağ yapılandırma sorunları olabilir.", + "failedInstallingVSIX": "VSIX'ten {0} yüklenemedi.", + "invalidVSIX": "Seçilen dosya geçerli bir \"*.vsix\" eklentisi değil.", + "license": "Ruhsat: {0}", + "onlyShowVerifiedExtensionsDescription": "Bu, {0} adresinin yalnızca doğrulanmış uzantıları göstermesini sağlar.", + "onlyShowVerifiedExtensionsTitle": "Yalnızca Doğrulanmış Uzantıları Göster", + "recommendedExtensions": "Bu depo için önerilen uzantıları yüklemek istiyor musunuz?", + "searchPlaceholder": "Uzantıları içinde ara {0}", + "showInstalled": "Yüklü Uzantıları Göster", + "showRecommendedExtensions": "Uzantı önerileri için bildirimlerin gösterilip gösterilmeyeceğini kontrol eder.", + "vsx-extensions-contribution": { + "update-version-uninstall-error": "Uzantı kaldırılırken hata oluştu: {0}.", + "update-version-version-error": "{1}'un {0} sürümü yüklenemedi." + } + }, + "webview": { + "goToReadme": "README'ye Git", + "messageWarning": " {0} uç noktasının ana bilgisayar kalıbı `{1}` olarak değiştirildi; kalıbın değiştirilmesi güvenlik açıklarına yol açabilir. Daha fazla bilgi için `{2}` adresine bakın." + }, + "workspace": { + "compareWithEachOther": "Birbirleriyle Karşılaştırın", + "confirmDeletePermanently.description": "Çöp Kutusu kullanılarak \"{0}\" silinemedi. Bunun yerine kalıcı olarak silmek mi istiyorsunuz?", + "confirmDeletePermanently.solution": "Tercihlerde Çöp Kutusu kullanımını devre dışı bırakabilirsiniz.", + "confirmDeletePermanently.title": "Dosya silinirken hata oluştu", + "confirmMessage.delete": "Aşağıdaki dosyaları gerçekten silmek istiyor musunuz?", + "confirmMessage.dirtyMultiple": "Kaydedilmemiş değişiklikler içeren {0} dosyalarını gerçekten silmek istiyor musunuz?", + "confirmMessage.dirtySingle": "Kaydedilmemiş değişikliklerle {0} adresini gerçekten silmek istiyor musunuz?", + "confirmMessage.uriMultiple": "Gerçekten tüm {0} seçili dosyaları silmek istiyor musunuz?", + "confirmMessage.uriSingle": "{0} adresini gerçekten silmek istiyor musunuz?", + "duplicate": "Yinelenen", + "failSaveAs": "Geçerli widget için \"{0}\" çalıştırılamıyor.", + "newFilePlaceholder": "Dosya Adı", + "newFolderPlaceholder": "Klasör adı", + "noErasure": "Not: Diskten hiçbir şey silinmeyecektir", + "openRecentPlaceholder": "Açmak istediğiniz çalışma alanının adını yazın", + "openRecentWorkspace": "Son Çalışma Alanını Aç...", + "preserveWindow": "Geçerli pencerede çalışma alanlarını açmayı etkinleştirir.", + "removeFolder": "Aşağıdaki klasörü çalışma alanından kaldırmak istediğinizden emin misiniz?", + "removeFolders": "Aşağıdaki klasörleri çalışma alanından kaldırmak istediğinizden emin misiniz?", + "trashTitle": "{0} adresini Çöp Kutusuna Taşı", + "trustEmptyWindow": "Varsayılan olarak boş çalışma alanına güvenilip güvenilmeyeceğini denetler.", + "trustEnabled": "Çalışma alanı güveninin etkin olup olmadığını denetler. Devre dışı bırakılırsa, tüm çalışma alanlarına güvenilir.", + "trustRequest": "Bir uzantı çalışma alanı güveni talep ediyor ancak ilgili API henüz tam olarak desteklenmiyor. Bu çalışma alanına güvenmek istiyor musunuz?", + "untitled-cleanup": "Çok sayıda başlıksız çalışma alanı dosyası var gibi görünüyor. Lütfen {0} adresini kontrol edin ve kullanılmayan dosyaları kaldırın.", + "workspaceFolderAdded": "Birden fazla kökü olan bir çalışma alanı oluşturuldu. Çalışma alanı yapılandırmanızı bir dosya olarak kaydetmek istiyor musunuz?", + "workspaceFolderAddedTitle": "Çalışma Alanına eklenen klasör" + } + } +} diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 426ec365dab33..5a2e4dafabf86 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -362,6 +362,9 @@ "alreadyRunning": "托管的实例已经在运行。", "debugInstance": "调试实例", "debugMode": "使用 inspect 或 inspect-brk 进行 Node.js 调试", + "debugPorts": { + "debugPort": "用于此服务器 Node.js 调试的端口" + }, "devHost": "发展的主人", "failed": "运行托管插件实例失败。{0}", "hostedPlugin": "托管的插件", diff --git a/packages/core/i18n/nls.zh-tw.json b/packages/core/i18n/nls.zh-tw.json new file mode 100644 index 0000000000000..dabde45b99629 --- /dev/null +++ b/packages/core/i18n/nls.zh-tw.json @@ -0,0 +1,555 @@ +{ + "debug.breakpoint.editCondition": "編輯條件...", + "notebook.cell.changeToCode": "變更儲存格為代碼", + "notebook.cell.changeToMarkdown": "變更儲存格為 Markdown", + "notebook.cell.insertMarkdownCellAbove": "在上方插入 Markdown 單元格", + "notebook.cell.insertMarkdownCellBelow": "在下方插入 Markdown 單元格", + "terminal:new:profile": "從設定檔建立新的整合式終端機", + "terminal:profile:default": "選擇預設終端設定檔", + "theia": { + "callhierarchy": { + "noCallers": "未偵測到來電者。", + "open": "開放式呼叫層級" + }, + "core": { + "about": { + "compatibility": "{0} 相容性", + "defaultApi": "預設{0} API", + "version": "版本" + }, + "common": { + "closeAll": "關閉所有標籤", + "closeAllTabMain": "關閉主區域中的所有標籤", + "closeOtherTabMain": "關閉主區中的其他標籤", + "closeOthers": "關閉其他標籤", + "closeRight": "向右關閉標籤", + "closeTab": "關閉標籤", + "closeTabMain": "關閉主區域中的標籤", + "collapseAllTabs": "折疊所有側板", + "collapseBottomPanel": "切換底部面板", + "collapseTab": "折疊側板", + "showNextTabGroup": "切換到下一個標籤組", + "showNextTabInGroup": "切換到群組中的下一個標籤", + "showPreviousTabGroup": "切換到上一個標籤組", + "showPreviousTabInGroup": "切換到群組中的上一個標籤", + "toggleMaximized": "切換最大化" + }, + "copyInfo": "先開啟檔案以複製其路徑", + "copyWarn": "請使用瀏覽器的複製指令或捷徑。", + "cutWarn": "請使用瀏覽器的剪下指令或捷徑。", + "enhancedPreview": { + "classic": "顯示標籤的簡單預覽及基本資訊。", + "enhanced": "顯示標籤的增強預覽,並提供其他資訊。", + "visual": "顯示標籤的視覺預覽。" + }, + "file": { + "browse": "瀏覽" + }, + "highlightModifiedTabs": "控制是否在已修改(髒)的編輯器標籤上畫上邊框。", + "keybindingStatus": "{0} 被按下,等待更多的按鍵", + "keyboard": { + "choose": "選擇鍵盤配置", + "chooseLayout": "選擇鍵盤配置", + "current": "(目前:{0})", + "currentLayout": "- 目前佈局", + "mac": "Mac 鍵盤", + "pc": "PC 鍵盤", + "tryDetect": "嘗試從瀏覽器資訊和按下的按鍵偵測鍵盤配置。" + }, + "navigator": { + "clipboardWarn": "拒絕存取剪貼板。檢查瀏覽器的權限。", + "clipboardWarnFirefox": "剪貼板 API 不可用。您可以在 '{1}' 頁面上的 '{0}' 偏好設定啟用。然後重新載入 Theia。請注意,這將允許 FireFox 完全存取系統剪貼板。" + }, + "offline": "離線", + "pasteWarn": "請使用瀏覽器的貼上指令或捷徑。", + "quitMessage": "任何未儲存的變更都不會儲存。", + "resetWorkbenchLayout": "重設工作台佈局", + "searchbox": { + "close": "關閉(逃生)", + "next": "下一頁 (向下)", + "previous": "上一頁 (上)" + }, + "secondaryWindow": { + "alwaysOnTop": "啟用時,次要視窗會停留在所有其他視窗之上,包括不同應用程式的視窗。", + "description": "設定抽取的次要視窗的初始位置和大小。", + "fullSize": "擷取的 widget 位置和大小將與執行中的 Theia 應用程式相同。", + "halfWidth": "擷取的 widget 位置和大小將是執行中的 Theia 應用程式寬度的一半。", + "originalSize": "提取出來的 widget 的位置和大小將與原始 widget 相同。" + }, + "silentNotifications": "控制是否抑制通知彈出。", + "tabDefaultSize": "指定標籤的預設大小。", + "tabMaximize": "控制是否在雙擊時將索引標籤最大化。", + "tabMinimumSize": "指定標籤頁的最小尺寸。", + "tabShrinkToFit": "縮小標籤以符合可用空間。" + }, + "debug": { + "addConfigurationPlaceholder": "選擇要新增組態的工作區根", + "breakpoint": "断点", + "compound-cycle": "啟動組態 '{0}' 包含與本身的循環", + "continueAll": "繼續全部", + "copyExpressionValue": "複製表達值", + "dataBreakpoint": "資料中斷點", + "debugVariableInput": "設定{0} 值", + "entry": "入口", + "exception": "例外", + "functionBreakpoint": "函數中斷點", + "goto": "到達", + "instruction-breakpoint": "指令中斷點", + "instructionBreakpoint": "指令中斷點", + "missingConfiguration": "動態設定 '{0}:{1}' 遺失或不適用", + "pause": "暫停", + "pauseAll": "暫停全部", + "reveal": "揭示", + "step": "步驟", + "threads": "線程", + "toggleTracing": "啟用/停用與除錯介面卡的追蹤通訊" + }, + "editor": { + "diffEditor.wordWrap2": "行會根據 `#editor.wordWrap#` 設定來換行。", + "dirtyEncoding": "檔案已損壞。請先儲存檔案,再以其他編碼重新開啟。", + "editor.accessibilitySupport0": "使用平台 API 來偵測是否已連接螢幕閱讀器", + "editor.accessibilitySupport1": "針對螢幕閱讀器的使用進行最佳化", + "editor.accessibilitySupport2": "假設沒有連接螢幕閱讀器", + "editor.bracketPairColorization.enabled": "控制是否啟用括號對著色。使用 `#workbench.colorCustomizations#` 來覆寫括弧高亮顏色。", + "editor.codeActionWidget.includeNearbyQuickfixes": "當目前未進行診斷時,啟用/停用在行內顯示最近的快速修復。", + "editor.cursorSurroundingLinesStyle": "控制何時執行 `#cursorSurroundingLines#`。", + "editor.detectIndentation": "控制在開啟檔案時,是否會根據檔案內容自動偵測 `#editor.tabSize#` 和 `#editor.insertSpaces#`。", + "editor.dropIntoEditor.enabled": "控制是否可以按住 `shift` 將檔案拖放到文字編輯器中 (而不是在編輯器中開啟檔案)。", + "editor.formatOnSaveMode.modificationsIfAvailable": "僅嘗試格式化修改 (需要來源控制)。如果無法使用原始碼控制,則會格式化整個檔案。", + "editor.hover.hidingDelay": "控制以毫秒為單位的延遲時間,之後懸浮會隱藏。需要啟用 `editor.hover.sticky`。", + "editor.inlayHints.enabled1": "鑲嵌提示依預設顯示,並在按住 Ctrl+Alt 時隱藏", + "editor.inlayHints.enabled2": "內嵌提示預設為隱藏,按住 Ctrl+Alt 時會顯示。", + "editor.inlayHints.fontFamily": "控制編輯器中內嵌提示的字型族。設定為空時,會使用 `#editor.fontFamily#`。", + "editor.inlayHints.fontSize": "控制編輯器中內嵌提示的字型大小。當設定值小於或大於編輯器字型大小時,預設會使用 `#editor.fontSize#`。", + "editor.insertSpaces": "按下 `Tab` 時插入空格。當 `#editor.detectIndentation#` 啟用時,此設定會根據檔案內容覆寫。", + "editor.occurrencesHighlight": "控制編輯器是否要高亮顯示語意符號的出現。", + "editor.quickSuggestions": "控制輸入時是否自動顯示建議。在輸入註解、字串和其他程式碼時,可以控制這一點。快速建議可設定為以 ghost text 或建議 widget 顯示。同時也要注意 `#editor.supplyOnTriggerCharacters#` 設定,它可以控制是否由特殊字符觸發建議。", + "editor.stickyScroll.scrollWithEditor": "使用編輯器的水平捲動條啟用黏貼捲動 widget 的捲動。", + "editor.suggestFontSize": "建議 Widget 的字型大小。設定為 `0` 時,會使用 `#editor.fontSize#` 的值。", + "editor.suggestLineHeight": "建議 widget 的行高。設定為 `0` 時,會使用 `#editor.lineHeight#` 的值。最小值為 8。", + "editor.tabSize": "制表符等於的空格數目。當 `#editor.detectIndentation#` 啟用時,此設定會根據檔案內容覆寫。", + "editor.useTabStops": "插入和刪除空白跟著制表符停止。", + "editor.wordBasedSuggestions": "控制是否應根據文件中的字詞計算完成度。", + "editor.wordBasedSuggestionsMode": "控制從哪些文件中計算出基於字詞的完成度。", + "files.autoSave": "控制有未儲存變更的編輯器 [自動儲存](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)。", + "files.autoSave.afterDelay": "有變更的編輯器會在設定的 `#files.autoSaveDelay#` 後自動儲存。", + "files.autoSave.off": "有變更的編輯器不會自動儲存。", + "files.autoSave.onFocusChange": "當編輯器失去焦點時,會自動儲存有變更的編輯器。", + "files.autoSave.onWindowChange": "當視窗失去焦點時,會自動儲存有變更的編輯器。", + "formatOnSaveTimeout": "以毫秒為單位的逾時,逾時後檔案儲存時執行的格式化會被取消。", + "persistClosedEditors": "控制是否在視窗重新載入時持續工作區的已關閉編輯歷史。", + "showAllEditors": "顯示所有開啟的編輯器", + "splitHorizontal": "水平分割編輯器", + "splitVertical": "垂直分割編輯器", + "toggleStickyScroll": "切換黏貼捲動" + }, + "file-search": { + "toggleIgnoredFiles": "(按{0} 顯示/隱藏忽略的檔案)" + }, + "fileDialog": { + "showHidden": "顯示隱藏的檔案" + }, + "fileSystem": { + "fileResource": { + "overWriteBody": "您要覆寫檔案系統上對 '{0}' 所做的變更?" + } + }, + "filesystem": { + "copiedToClipboard": "將下載連結複製到剪貼簿。", + "copyDownloadLink": "複製下載連結", + "dialog": { + "initialLocation": "前往初始位置", + "multipleItemMessage": "您只能選擇一個項目", + "name": "名稱:", + "navigateBack": "導航返回", + "navigateForward": "向前導航", + "navigateUp": "向上瀏覽一個目錄" + }, + "fileResource": { + "binaryFileQuery": "打開它可能需要一些時間,而且可能會導致 IDE 無反應。您到底要不要開啟 '{0}' 呢?", + "binaryTitle": "檔案為二進位或使用不支援的文字編碼。", + "largeFileTitle": "檔案太大 ({0})。", + "overwriteTitle": "檔案系統中的檔案 '{0}' 已變更。" + }, + "filesExclude": "設定 glob 模式以排除檔案和資料夾。例如,檔案總管會根據此設定決定顯示或隱藏哪些檔案和資料夾。", + "format": "格式:", + "maxConcurrentUploads": "上傳多個檔案時,同時上傳的最大檔案數量。0 表示所有檔案都會同時上傳。", + "maxFileSizeMB": "控制可開啟的最大檔案大小 (以 MB 為單位)。", + "prepareDownload": "準備下載...", + "prepareDownloadLink": "準備下載連結...", + "processedOutOf": "加工{0} 出{1}", + "replaceTitle": "取代檔案", + "uploadFiles": "上傳檔案...", + "uploadedOutOf": "上傳{0} 出{1}" + }, + "getting-started": { + "apiComparator": "{0} API 相容性", + "newExtension": "建立新擴展區", + "newPlugin": "建立新的外掛程式", + "startup-editor": { + "welcomePage": "開啟歡迎頁面,其內容可協助您開始使用{0} 和擴充套件。" + } + }, + "git": { + "aFewSecondsAgo": "數秒前", + "addSignedOff": "新增簽署人", + "amendReuseMessage": "若要重複使用上次的提交訊息,請按「Enter」或「Escape」取消。", + "amendRewrite": "重寫先前的提交訊息。按「Enter」確認或按「Escape」取消。", + "checkoutCreateLocalBranchWithName": "建立新的本地分支,名稱為{0}。按「Enter」確認或按「Escape」取消。", + "checkoutProvideBranchName": "請提供分行名稱。 ", + "checkoutSelectRef": "選擇要結帳的參考資料或建立新的本地分支:", + "cloneQuickInputLabel": "請提供 Git 儲存庫位置。按「Enter」確認或按「Escape」取消。", + "cloneRepository": "克隆 Git 倉庫:{0}。按「Enter」確認或按「Escape」取消。", + "compareWith": "比較...", + "compareWithBranchOrTag": "選取分支或標籤,與目前使用中的{0} 分支進行比較:", + "diff": "差異", + "dirtyDiffLinesLimit": "如果編輯器的行數超過此限制,則不顯示髒的差異裝飾。", + "dropStashMessage": "成功移除藏匿物。", + "editorDecorationsEnabled": "在編輯器中顯示 git 裝飾。", + "fetchPickRemote": "選取遠端擷取:", + "gitDecorationsColors": "在導覽器中使用顏色裝飾。", + "mergeQuickPickPlaceholder": "選取一個分支合併到目前使用中的{0} 分支:", + "missingUserInfo": "請確定您在 git 中設定了「user.name」和「user.email」。", + "noHistoryForError": "沒有關於{0}", + "noPreviousCommit": "之前沒有承諾修正", + "noRepositoriesSelected": "未選擇儲存庫。", + "prepositionIn": "於", + "repositoryNotInitialized": "儲存庫{0} 尚未初始化。", + "stashChanges": "儲存變更。按「Enter」確認,或按「Escape」取消。", + "stashChangesWithMessage": "儲存庫隨訊息變更:{0}.按「Enter」確認或按「Escape」取消。", + "tabTitleIndex": "{0} (索引)", + "tabTitleWorkingTree": "{0} (工作樹)", + "toggleBlameAnnotations": "切換指責註釋" + }, + "keybinding-schema-updater": { + "deprecation": "改用 `when` 子句。" + }, + "keymaps": { + "addKeybindingTitle": "新增鍵盤綁定{0}", + "editKeybinding": "編輯按鍵...", + "editKeybindingTitle": "編輯{0}", + "editWhenExpression": "編輯當表達...", + "editWhenExpressionTitle": "編輯{0}", + "keybinding": { + "copy": "複製鍵綁定", + "copyCommandId": "複製鍵綁定指令 ID", + "copyCommandTitle": "複製鍵綁定指令標題", + "edit": "編輯按鍵...", + "editWhenExpression": "編輯按鍵綁定當表達..." + }, + "keybindingCollidesValidation": "按鍵目前碰撞", + "requiredKeybindingValidation": "需要 keybinding 值", + "resetKeybindingConfirmation": "您真的要將此按鍵綁定重設為預設值?", + "resetKeybindingTitle": "重設{0}", + "resetMultipleKeybindingsWarning": "如果此命令存在多個按鍵綁定,則所有按鍵綁定都會被重設。" + }, + "localize": { + "offlineTooltip": "無法連線至後端。" + }, + "markers": { + "clearAll": "全部清除", + "noProblems": "到目前為止,工作區尚未偵測到任何問題。", + "tabbarDecorationsEnabled": "在標籤列中顯示問題裝飾符(診斷標記)。" + }, + "memory-inspector": { + "addressTooltip": "要顯示的記憶體位置、位址或演算至位址的表達式", + "ascii": "ASCII", + "binary": "二進制", + "byteSize": "位元組大小", + "bytesPerGroup": "每組位元組", + "closeSettings": "關閉設定", + "columns": "欄位", + "command": { + "createNewMemory": "建立新的記憶體檢查器", + "createNewRegisterView": "建立新的註冊檢視", + "followPointer": "追蹤指針", + "followPointerMemory": "在記憶體檢視器中追蹤指針", + "resetValue": "重設值", + "showRegister": "在記憶體檢查器中顯示暫存器", + "viewVariable": "在記憶體檢視器中顯示變數" + }, + "data": "資料", + "decimal": "十進制", + "diff": { + "label": "差異:{0}" + }, + "diff-widget": { + "offset-label": "{0} 偏移", + "offset-title": "偏移記憶體的位元組{0}" + }, + "editable": { + "apply": "應用變更", + "clear": "清楚的變更" + }, + "endianness": "尾數", + "extraColumn": "額外欄位", + "groupsPerRow": "每行的群組", + "hexadecimal": "十六進位", + "length": "長度", + "lengthTooltip": "要取得的位元組數量,以十進位或十六進位表示", + "memory": { + "addressField": { + "memoryReadError": "在 Location(位置)欄位中輸入地址或表達式。" + }, + "freeze": "凍結記憶體檢視", + "hideSettings": "隱藏設定面板", + "readError": { + "bounds": "超出記憶體範圍,結果會被截斷。", + "noContents": "目前沒有可用的記憶體內容。" + }, + "readLength": { + "memoryReadError": "在 Length(長度)欄位中輸入長度(十進制或十六進制數字)。" + }, + "showSettings": "顯示設定面板", + "unfreeze": "解除凍結記憶體檢視", + "userError": "取得記憶體時發生錯誤。" + }, + "memoryCategory": "記憶體檢查器", + "memoryInspector": "記憶體檢查器", + "memoryTitle": "記憶體", + "octal": "八進制", + "offset": "偏移", + "offsetTooltip": "當導航時,要加到目前記憶體位置的偏移量", + "provider": { + "localsError": "無法讀取本機變數。沒有啟動除錯階段。", + "readError": "無法讀取記憶體。沒有作用中的除錯會話。", + "writeError": "無法寫入記憶體。沒有作用中的除錯會話。" + }, + "register": "註冊", + "register-widget": { + "filter-placeholder": "過濾器 (以)" + }, + "registerReadError": "取得暫存器時發生錯誤。", + "registers": "註冊", + "toggleComparisonWidgetVisibility": "切換比較小工具的可見性", + "utils": { + "afterBytes": "您必須在要比較的兩個 widget 中都載入記憶體。{0} 沒有載入記憶體。", + "bytesMessage": "您必須在要比較的兩個 widget 中都載入記憶體。{0} 沒有載入記憶體。" + } + }, + "messages": { + "notificationTimeout": "超時後,資訊性通知將會隱藏。", + "toggleNotifications": "切換通知" + }, + "mini-browser": { + "typeUrl": "輸入 URL" + }, + "monaco": { + "noSymbolsMatching": "沒有符合的符號", + "typeToSearchForSymbols": "輸入以搜尋符號" + }, + "navigator": { + "autoReveal": "自動揭示", + "clipboardWarn": "拒絕存取剪貼板。檢查瀏覽器的權限。", + "clipboardWarnFirefox": "剪貼板 API 不可用。您可以在 '{1}' 頁面上的 '{0}' 偏好設定啟用。然後重新載入 Theia。請注意,這將允許 FireFox 完全存取系統剪貼板。", + "refresh": "在瀏覽器中重新整理", + "reveal": "在瀏覽器中顯示", + "toggleHiddenFiles": "切換隱藏檔案" + }, + "output": { + "clearOutputChannel": "清除輸出通道...", + "closeOutputChannel": "關閉輸出通道...", + "hiddenChannels": "隱藏通道", + "hideOutputChannel": "隱藏輸出通道...", + "maxChannelHistory": "輸出通道中的最大項目數量。", + "outputChannels": "輸出通道", + "showOutputChannel": "顯示輸出通道..." + }, + "plugin": { + "blockNewTab": "您的瀏覽器無法開啟新標籤頁" + }, + "plugin-dev": { + "alreadyRunning": "托管实例已在运行。", + "debugInstance": "除錯實例", + "debugMode": "使用 inspect 或 inspect-brk 進行 Node.js 除錯", + "debugPorts": { + "debugPort": "此伺服器的 Node.js 除錯要使用的連接埠" + }, + "devHost": "開發主機", + "failed": "執行虛擬外掛實例失敗:{0}", + "hostedPlugin": "託管外掛程式", + "hostedPluginRunning": "託管外掛程式: 執行中", + "hostedPluginStarting": "託管外掛程式:啟動", + "hostedPluginStopped": "託管外掛程式:已停止", + "hostedPluginWatching": "託管外掛程式:觀看", + "instanceTerminated": "{0} 已終止", + "launchOutFiles": "用於定位產生的 JavaScript 檔案的 glob 模式陣列 (`${pluginPath}`將被 plugin 的實際路徑取代)。", + "noValidPlugin": "指定的資料夾不包含有效的外掛程式。", + "notRunning": "托管实例未运行。", + "pluginFolder": "外掛程式資料夾設定為:{0}", + "preventedNewTab": "您的瀏覽器無法開啟新標籤頁", + "restartInstance": "重新啟動實體", + "running": "主機實例執行於:", + "select": "選擇", + "selectPath": "選擇路徑", + "startInstance": "啟動實例", + "starting": "啟動託管實例伺服器 ...", + "stopInstance": "停止實例", + "unknownTerminated": "實例已終止", + "watchMode": "在開發中的外掛程式上執行觀察程式" + }, + "plugin-ext": { + "authentication-main": { + "loginTitle": "登入" + }, + "plugins": "外掛程式", + "webviewTrace": "控制 webviews 的通訊追蹤。", + "webviewWarnIfUnsecure": "警告使用者目前以不安全的方式部署 webview。" + }, + "preferences": { + "hostedPlugin": "託管外掛程式", + "toolbar": "工具列" + }, + "preview": { + "openByDefault": "預設開啟預覽而非編輯器。" + }, + "property-view": { + "created": "創建", + "directory": "目錄", + "lastModified": "最後修改", + "location": "地點", + "noProperties": "無房產可供選擇。", + "properties": "屬性", + "size": "尺寸", + "symbolicLink": "符號連結" + }, + "scm": { + "amend": "修正", + "amendHeadCommit": "HEAD 承諾", + "amendLastCommit": "修正上次提交", + "changeRepository": "變更儲存庫...", + "config.untrackedChanges": "控制未追蹤變更的行為。", + "config.untrackedChanges.hidden": "隱藏的", + "config.untrackedChanges.mixed": "混合", + "config.untrackedChanges.separate": "獨立", + "dirtyDiff": { + "close": "關閉變更窺視" + }, + "history": "歷史", + "noRepositoryFound": "未找到儲存庫", + "unamend": "Unamend", + "unamendCommit": "取消修正提交" + }, + "search-in-workspace": { + "includeIgnoredFiles": "包含忽略的檔案", + "noFolderSpecified": "您尚未開啟或指定資料夾。目前只搜尋開啟的檔案。", + "resultSubset": "這只是所有結果的子集。請使用更明確的搜尋字詞縮小結果清單的範圍。", + "searchOnEditorModification": "修改時搜尋使用中的編輯器。" + }, + "secondary-window": { + "extract-widget": "將檢視移至次要視窗" + }, + "shell-area": { + "secondary": "輔助窗口" + }, + "task": { + "attachTask": "附加任務...", + "clearHistory": "清除歷史", + "noTaskToRun": "未找到要執行的任務。配置任務...", + "openUserTasks": "開啟使用者任務" + }, + "terminal": { + "defaultProfile": "上使用的預設設定檔{0}", + "enableCopy": "啟用 ctrl-c(macOS 上為 cmd-c)複製選取的文字", + "enablePaste": "啟用 ctrl-v(macOS 上為 cmd-v)從剪貼簿貼上", + "profileArgs": "此設定檔使用的 shell 參數。", + "profileColor": "與終端關聯的終端主題顏色 ID。", + "profileDefault": "選擇預設設定檔...", + "profileIcon": "要與終端圖示關聯的 codicon ID。\nterminal-tmux:\"$(terminal-tmux)\"", + "profileNew": "新終端機 (含簡介)...", + "profilePath": "此設定檔使用的 shell 路徑。", + "profiles": "建立新終端時要顯示的設定檔。使用可選的 args 手動設定路徑屬性。\n將現有的設定檔設定為「null」,以從清單中隱藏設定檔,例如: `\"{0}\": null`。", + "rendererType": "控制終端機的呈現方式。", + "rendererTypeDeprecationMessage": "渲染器類型不再支援為選項。", + "selectProfile": "為新終端選擇設定檔", + "shell.deprecated": "這已經被廢棄,新的建議配置預設 shell 的方式是在 'terminal.integrated.profiles.{0}' 中建立終端設定檔,並在 'terminal.integrated.defaultProfile.{0}.' 中將其設定檔名稱設定為預設值。", + "shellArgsLinux": "在 Linux 終端時要使用的命令列參數。", + "shellArgsOsx": "在 macOS 終端時要使用的命令列參數。", + "shellArgsWindows": "在 Windows 終端時要使用的命令列參數。", + "shellLinux": "終端在 Linux 上使用的 shell 路徑 (預設值: '{0}'})。", + "shellOsx": "終端在 macOS 上使用的 shell 路徑 (預設值: '{0}'})。", + "shellWindows": "終端在 Windows 上使用的 shell 路徑。(預設值:'{0}' )。" + }, + "test": { + "cancelAllTestRuns": "取消所有測試執行", + "testRunDefaultName": "{0} 跑{1}", + "testRuns": "測試運行" + }, + "toolbar": { + "addCommand": "新增指令至工具列", + "addCommandPlaceholder": "尋找要加入工具列的指令", + "centerColumn": "中柱", + "failedUpdate": "更新 '{1}' 中 '{0}' 的值失敗。", + "filterIcons": "篩選器圖示", + "iconSelectDialog": "為 '{0}' 選擇圖示", + "iconSet": "圖示集", + "insertGroupLeft": "插入群組分隔線(左)", + "insertGroupRight": "插入群組分隔線(右)", + "leftColumn": "左欄", + "openJSON": "自訂工具列 (開啟 JSON)", + "removeCommand": "從工具列移除指令", + "restoreDefaults": "還原工具列預設值", + "rightColumn": "右欄", + "selectIcon": "選擇圖示", + "toggleToolbar": "切換工具列", + "toolbarLocationPlaceholder": "您希望在哪裡加入指令?", + "useDefaultIcon": "使用預設圖示" + }, + "typehierarchy": { + "subtypeHierarchy": "子類別階層", + "supertypeHierarchy": "超類型層級" + }, + "vsx-registry": { + "confirmDialogMessage": "副檔名 \"{0}\" 未經驗證,可能會構成安全風險。", + "confirmDialogTitle": "您確定要繼續安裝嗎?", + "downloadCount": "下載計數:{0}", + "errorFetching": "取得擴充套件時出錯。", + "errorFetchingConfigurationHint": "這可能是網路組態問題造成的。", + "failedInstallingVSIX": "從 VSIX 安裝{0} 失敗。", + "invalidVSIX": "選取的檔案不是有效的「*.vsix」外掛程式。", + "license": "許可證:{0}", + "onlyShowVerifiedExtensionsDescription": "這可讓{0} 只顯示已驗證的擴充套件。", + "onlyShowVerifiedExtensionsTitle": "只顯示已驗證的擴充套件", + "recommendedExtensions": "您要為此套件庫安裝建議的擴充套件嗎?", + "searchPlaceholder": "搜尋擴展名稱{0}", + "showInstalled": "顯示已安裝的擴充套件", + "showRecommendedExtensions": "控制是否顯示擴展建議的通知。", + "vsx-extensions-contribution": { + "update-version-uninstall-error": "移除副檔名時發生錯誤:{0}。", + "update-version-version-error": "{1} 的{0} 版本安裝失敗。" + } + }, + "webview": { + "goToReadme": "前往 README", + "messageWarning": " {0} 端點的主機模式已變更為 `{1}`;變更模式可能會導致安全漏洞。 詳情請參閱 `{2}`。" + }, + "workspace": { + "compareWithEachOther": "相互比較", + "confirmDeletePermanently.description": "使用垃圾桶刪除 \"{0}\" 失敗。您想要永久刪除嗎?", + "confirmDeletePermanently.solution": "您可以在喜好設定中停用垃圾桶的使用。", + "confirmDeletePermanently.title": "刪除檔案出錯", + "confirmMessage.delete": "您真的要刪除下列檔案嗎?", + "confirmMessage.dirtyMultiple": "您真的想要刪除{0} 檔案中未儲存的變更嗎?", + "confirmMessage.dirtySingle": "您真的要刪除{0} 未儲存的變更嗎?", + "confirmMessage.uriMultiple": "您真的要刪除所有{0} 選取的檔案?", + "confirmMessage.uriSingle": "您真的要刪除{0}?", + "duplicate": "重複", + "failSaveAs": "無法為目前的 widget 執行 \"{0}\"。", + "newFilePlaceholder": "檔案名稱", + "newFolderPlaceholder": "資料夾名稱", + "noErasure": "注意:磁碟上的任何內容都不會被刪除", + "openRecentPlaceholder": "輸入您要開啟的工作區名稱", + "openRecentWorkspace": "開啟最近工作區...", + "preserveWindow": "啟用在目前視窗中開啟工作區。", + "removeFolder": "您確定要從工作區移除下列資料夾?", + "removeFolders": "您確定要從工作區移除下列資料夾?", + "trashTitle": "移動{0} 到垃圾桶", + "trustEmptyWindow": "控制預設是否信任空工作區。", + "trustEnabled": "控制是否啟用工作區信任。如果停用,則信任所有工作區。", + "trustRequest": "擴充套件要求信任工作區,但對應的 API 尚未完全支援。您要信任此工作區嗎?", + "untitled-cleanup": "似乎有許多無標題的工作區檔案。請檢查{0} 並移除任何未使用的檔案。", + "workspaceFolderAdded": "已建立具有多個根的工作區。您是否要將工作區設定儲存為檔案?", + "workspaceFolderAddedTitle": "資料夾新增至工作區" + } + } +} diff --git a/packages/core/src/node/i18n/theia-localization-contribution.ts b/packages/core/src/node/i18n/theia-localization-contribution.ts index 4f6ad11db6a9c..e02eacac442de 100644 --- a/packages/core/src/node/i18n/theia-localization-contribution.ts +++ b/packages/core/src/node/i18n/theia-localization-contribution.ts @@ -20,17 +20,21 @@ import { LocalizationContribution, LocalizationRegistry } from './localization-c @injectable() export class TheiaLocalizationContribution implements LocalizationContribution { async registerLocalizations(registry: LocalizationRegistry): Promise { - registry.registerLocalizationFromRequire('cs', require('../../../i18n/nls.cs.json')); - registry.registerLocalizationFromRequire('de', require('../../../i18n/nls.de.json')); - registry.registerLocalizationFromRequire('es', require('../../../i18n/nls.es.json')); + // Attempt to use the same languages as VS Code + // See https://code.visualstudio.com/docs/getstarted/locales#_available-locales + registry.registerLocalizationFromRequire('zh-cn', require('../../../i18n/nls.zh-cn.json')); + registry.registerLocalizationFromRequire('zh-tw', require('../../../i18n/nls.zh-tw.json')); registry.registerLocalizationFromRequire('fr', require('../../../i18n/nls.fr.json')); - registry.registerLocalizationFromRequire('hu', require('../../../i18n/nls.hu.json')); + registry.registerLocalizationFromRequire('de', require('../../../i18n/nls.de.json')); registry.registerLocalizationFromRequire('it', require('../../../i18n/nls.it.json')); + registry.registerLocalizationFromRequire('es', require('../../../i18n/nls.es.json')); registry.registerLocalizationFromRequire('ja', require('../../../i18n/nls.ja.json')); - registry.registerLocalizationFromRequire('pl', require('../../../i18n/nls.pl.json')); - registry.registerLocalizationFromRequire('pt-br', require('../../../i18n/nls.pt-br.json')); - registry.registerLocalizationFromRequire('pt-pt', require('../../../i18n/nls.pt-pt.json')); + registry.registerLocalizationFromRequire('ko', require('../../../i18n/nls.ko.json')); registry.registerLocalizationFromRequire('ru', require('../../../i18n/nls.ru.json')); - registry.registerLocalizationFromRequire('zh-cn', require('../../../i18n/nls.zh-cn.json')); + registry.registerLocalizationFromRequire('pt-br', require('../../../i18n/nls.pt-br.json')); + registry.registerLocalizationFromRequire('tr', require('../../../i18n/nls.tr.json')); + registry.registerLocalizationFromRequire('pl', require('../../../i18n/nls.pl.json')); + registry.registerLocalizationFromRequire('cs', require('../../../i18n/nls.cs.json')); + registry.registerLocalizationFromRequire('hu', require('../../../i18n/nls.hu.json')); } } diff --git a/scripts/translation-update.js b/scripts/translation-update.js index caec3cbaca59b..9209af82eb86f 100644 --- a/scripts/translation-update.js +++ b/scripts/translation-update.js @@ -23,7 +23,8 @@ function performNlsExtract() { '-e', 'vscode', '-f', './packages/**/browser/**/*.{ts,tsx}' ], { - shell: true + shell: true, + stdio: 'inherit' }); } @@ -40,9 +41,9 @@ function performDeepLTranslation(token) { cp.spawnSync('yarn', [ 'theia', 'nls-localize', '-f', './packages/core/i18n/nls.json', - '--free-api', '-k', token, - 'cs', 'de', 'es', 'fr', 'hu', 'it', 'ja', 'pl', 'pt-br', 'pt-pt', 'ru', 'zh-cn' + '--free-api', '-k', token ], { - shell: true + shell: true, + stdio: 'inherit' }); } From ad3dd29e2d2fee08b93aa7cdf326b9b5bcbf3cc0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 10:56:08 +0200 Subject: [PATCH 354/441] Translation update for version 1.53.0 (#14089) Co-authored-by: jfaltermeier --- packages/core/i18n/nls.cs.json | 1 + packages/core/i18n/nls.de.json | 1 + packages/core/i18n/nls.es.json | 1 + packages/core/i18n/nls.fr.json | 1 + packages/core/i18n/nls.hu.json | 1 + packages/core/i18n/nls.it.json | 1 + packages/core/i18n/nls.ja.json | 1 + packages/core/i18n/nls.json | 27 +++++++++++++++++++++++++++ packages/core/i18n/nls.ko.json | 1 + packages/core/i18n/nls.pl.json | 1 + packages/core/i18n/nls.pt-br.json | 1 + packages/core/i18n/nls.ru.json | 1 + packages/core/i18n/nls.tr.json | 1 + packages/core/i18n/nls.zh-cn.json | 1 + packages/core/i18n/nls.zh-tw.json | 1 + 15 files changed, 41 insertions(+) diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index c72543a040b4b..31e51b0bfe70c 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -11,6 +11,7 @@ "noCallers": "Nebyl zjištěn žádný volající.", "open": "Hierarchie otevřených výzev" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Kompatibilita", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index dc15c4127beee..21ab06785c954 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -11,6 +11,7 @@ "noCallers": "Es wurden keine Anrufer entdeckt.", "open": "Öffne Aufruf-Hierarchie" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Kompatibilität", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index 04ec8f9efe3da..b412a52253c1f 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -11,6 +11,7 @@ "noCallers": "No se ha detectado ninguna llamada.", "open": "Jerarquía de la convocatoria abierta" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Compatibilidad", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index b1811a82b60a2..e788409daae31 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -11,6 +11,7 @@ "noCallers": "Aucun appelant n'a été détecté.", "open": "Hiérarchie des appels ouverts" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Compatibilité", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index 0d29584db5731..b636cd04403c0 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -11,6 +11,7 @@ "noCallers": "Nem észleltek hívókat.", "open": "Nyílt felhívás hierarchia" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Kompatibilitás", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index a160d7d08a2d0..0fc479a65e66f 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -11,6 +11,7 @@ "noCallers": "Non sono stati rilevati chiamanti.", "open": "Gerarchia delle chiamate aperte" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Compatibilità", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 58d37b01bed79..34871ca325a1d 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -11,6 +11,7 @@ "noCallers": "発信者は検出されていません。", "open": "オープンコールヒエラルキー" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} 互換性", diff --git a/packages/core/i18n/nls.json b/packages/core/i18n/nls.json index 174bc39c6f65c..e797ed9a60e8a 100644 --- a/packages/core/i18n/nls.json +++ b/packages/core/i18n/nls.json @@ -11,6 +11,33 @@ "noCallers": "No callers have been detected.", "open": "Open Call Hierarchy" }, + "collaboration": { + "collaborate": "Collaborate", + "collaboration": "Collaboration", + "collaborationWorkspace": "Collaboration Workspace", + "connected": "Connected", + "connectedSession": "Connected to a collaboration session", + "copiedInvitation": "Invitation code copied to clipboard.", + "copyAgain": "Copy Again", + "createRoom": "Create New Collaboration Session", + "creatingRoom": "Creating Session", + "end": "End Collaboration Session", + "endDetail": "Terminate the session, cease content sharing, and revoke access for others.", + "enterCode": "Enter collaboration session code", + "failedCreate": "Failed to create room: {0}", + "failedJoin": "Failed to join room: {0}", + "invite": "Invite Others", + "inviteDetail": "Copy the invitation code for sharing it with others to join the session.", + "joinRoom": "Join Collaboration Session", + "joiningRoom": "Joining Session", + "leave": "Leave Collaboration Session", + "leaveDetail": "Disconnect from the current collaboration session and close the workspace.", + "selectCollaboration": "Select collaboration option", + "sharedSession": "Shared a collaboration session", + "startSession": "Start or join collaboration session", + "userWantsToJoin": "User '{0}' wants to join the collaboration room", + "whatToDo": "What would you like to do with other collaborators?" + }, "core": { "about": { "compatibility": "{0} Compatibility", diff --git a/packages/core/i18n/nls.ko.json b/packages/core/i18n/nls.ko.json index 09f20c64dad05..8e9682548446c 100644 --- a/packages/core/i18n/nls.ko.json +++ b/packages/core/i18n/nls.ko.json @@ -11,6 +11,7 @@ "noCallers": "발신자가 감지되지 않았습니다.", "open": "오픈 콜 계층 구조" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} 호환성", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index 02fe620f30810..ef9a8677dd57a 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -11,6 +11,7 @@ "noCallers": "Nie wykryto żadnych rozmówców.", "open": "Hierarchia zaproszeń otwartych" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Zgodność", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index e186c31779bf0..04706f612398f 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -11,6 +11,7 @@ "noCallers": "Nenhum chamador foi detectado.", "open": "Hierarquia de Chamadas Abertas" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Compatibilidade", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index 77d62935651d0..d823d61975293 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -11,6 +11,7 @@ "noCallers": "Вызывающих не обнаружено.", "open": "Иерархия открытых звонков" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Совместимость", diff --git a/packages/core/i18n/nls.tr.json b/packages/core/i18n/nls.tr.json index e2337b14a427b..8cfec2522e698 100644 --- a/packages/core/i18n/nls.tr.json +++ b/packages/core/i18n/nls.tr.json @@ -11,6 +11,7 @@ "noCallers": "Herhangi bir arayan tespit edilmedi.", "open": "Açık Çağrı Hiyerarşisi" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} Uyumluluk", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 5a2e4dafabf86..871d9b07dccce 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -11,6 +11,7 @@ "noCallers": "没有发现调用者。", "open": "打开调用层次结构" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} 兼容性", diff --git a/packages/core/i18n/nls.zh-tw.json b/packages/core/i18n/nls.zh-tw.json index dabde45b99629..6c06fb3edd79e 100644 --- a/packages/core/i18n/nls.zh-tw.json +++ b/packages/core/i18n/nls.zh-tw.json @@ -11,6 +11,7 @@ "noCallers": "未偵測到來電者。", "open": "開放式呼叫層級" }, + "collaboration": {}, "core": { "about": { "compatibility": "{0} 相容性", From ed84a105e4c6256b9d718a903ee574a53bef7120 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 29 Aug 2024 12:37:49 +0200 Subject: [PATCH 355/441] Support notebook selection VS Code API (#14087) --- .../notebook-documents-and-editors-main.ts | 2 +- .../browser/notebooks/notebook-editors-main.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index b41159f8e2f0f..46c73e5a2fec4 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -242,7 +242,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain return { id: notebookEditor.id, documentUri: uri.toComponents(), - selections: [], + selections: [{ start: 0, end: 0 }], visibleRanges: [] }; } diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts index 1a479206da286..27f15e8d45aad 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-editors-main.ts @@ -54,12 +54,25 @@ export class NotebookEditorsMainImpl implements NotebookEditorsMain { throw new Error('Method not implemented.'); } $trySetSelections(id: string, range: CellRange[]): void { - throw new Error('Method not implemented.'); + if (!this.mainThreadEditors.has(id)) { + throw new Error('Editor not found'); + } + const editor = this.mainThreadEditors.get(id); + editor?.model?.setSelectedCell(editor.model.cells[range[0].start]); } - handleEditorsAdded(editors: readonly NotebookEditorWidget[]): void { + async handleEditorsAdded(editors: readonly NotebookEditorWidget[]): Promise { for (const editor of editors) { this.mainThreadEditors.set(editor.id, editor); + const model = await editor.ready; + model.onDidChangeSelectedCell(e => { + const newCellIndex = e.cell ? model.cells.indexOf(e.cell) : -1; + this.proxy.$acceptEditorPropertiesChanged(editor.id, { + selections: { + selections: newCellIndex >= 0 ? [{ start: newCellIndex, end: newCellIndex }] : [] + } + }); + }); } } From 82b4ae2e9c1ccc952925750b45fb07bac304a74f Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 29 Aug 2024 12:38:21 +0200 Subject: [PATCH 356/441] Add 3 vscode command implementations (#14093) --- .../plugin-vscode-commands-contribution.ts | 36 +++++++++++++++++++ .../plugin-ext/src/plugin/known-commands.ts | 1 + 2 files changed, 37 insertions(+) diff --git a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts index ba9790c4f051a..6972ffbf60193 100755 --- a/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts +++ b/packages/plugin-ext-vscode/src/browser/plugin-vscode-commands-contribution.ts @@ -80,6 +80,8 @@ import * as monaco from '@theia/monaco-editor-core'; import { VSCodeExtensionUri } from '../common/plugin-vscode-uri'; import { CodeEditorWidgetUtil } from '@theia/plugin-ext/lib/main/browser/menus/vscode-theia-menu-mappings'; import { OutlineViewContribution } from '@theia/outline-view/lib/browser/outline-view-contribution'; +import { Range } from '@theia/plugin'; +import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages'; export namespace VscodeCommands { @@ -189,6 +191,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution { protected readonly messageService: MessageService; @inject(OutlineViewContribution) protected outlineViewContribution: OutlineViewContribution; + @inject(MonacoLanguages) + protected monacoLanguages: MonacoLanguages; private async openWith(commandId: string, resource: URI, columnOrOptions?: ViewColumn | TextDocumentShowOptions, openerId?: string): Promise { if (!resource) { @@ -655,6 +659,38 @@ export class PluginVscodeCommandsContribution implements CommandContribution { commands.executeCommand('_executeFormatOnTypeProvider', monaco.Uri.from(resource), position, ch, options)) } ); + commands.registerCommand( + { + id: 'vscode.executeFoldingRangeProvider' + }, + { + execute: ((resource: URI, position: Position) => + commands.executeCommand('_executeFoldingRangeProvider', monaco.Uri.from(resource), position)) + } + ); + commands.registerCommand( + { + id: 'vscode.executeCodeActionProvider' + }, + { + execute: ((resource: URI, range: Range, kind?: string, itemResolveCount?: number) => + commands.executeCommand('_executeCodeActionProvider', monaco.Uri.from(resource), range, kind, itemResolveCount)) + } + ); + commands.registerCommand( + { + id: 'vscode.executeWorkspaceSymbolProvider' + }, + { + execute: async (queryString: string) => + (await Promise.all( + this.monacoLanguages.workspaceSymbolProviders + .map(async provider => provider.provideWorkspaceSymbols({ query: queryString }, new CancellationTokenSource().token)))) + .flatMap(symbols => symbols) + .filter(symbols => !!symbols) + } + ); + commands.registerCommand( { id: 'vscode.prepareCallHierarchy' diff --git a/packages/plugin-ext/src/plugin/known-commands.ts b/packages/plugin-ext/src/plugin/known-commands.ts index e8bf476056d14..7b63db385f364 100755 --- a/packages/plugin-ext/src/plugin/known-commands.ts +++ b/packages/plugin-ext/src/plugin/known-commands.ts @@ -297,6 +297,7 @@ export namespace KnownCommands { mappings['vscode.executeFormatDocumentProvider'] = ['vscode.executeFormatDocumentProvider', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.executeFormatRangeProvider'] = ['vscode.executeFormatRangeProvider', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.executeFormatOnTypeProvider'] = ['vscode.executeFormatOnTypeProvider', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; + mappings['vscode.executeCodeActionProvider'] = ['vscode.executeCodeActionProvider', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.prepareCallHierarchy'] = ['vscode.prepareCallHierarchy', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.provideIncomingCalls'] = ['vscode.provideIncomingCalls', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; mappings['vscode.provideOutgoingCalls'] = ['vscode.provideOutgoingCalls', CONVERT_VSCODE_TO_MONACO, CONVERT_MONACO_TO_VSCODE]; From f444b21fe2894d373bc41eb2df3c1015b756b3c1 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 29 Aug 2024 13:18:52 +0200 Subject: [PATCH 357/441] Produce failure on translation error (#14092) --- dev-packages/cli/src/theia.ts | 5 +++- .../src/localization-manager.ts | 12 ++++++-- packages/core/i18n/nls.cs.json | 28 ++++++++++++++++++- packages/core/i18n/nls.de.json | 28 ++++++++++++++++++- packages/core/i18n/nls.es.json | 28 ++++++++++++++++++- packages/core/i18n/nls.fr.json | 28 ++++++++++++++++++- packages/core/i18n/nls.hu.json | 28 ++++++++++++++++++- packages/core/i18n/nls.it.json | 28 ++++++++++++++++++- packages/core/i18n/nls.ja.json | 28 ++++++++++++++++++- packages/core/i18n/nls.ko.json | 28 ++++++++++++++++++- packages/core/i18n/nls.pl.json | 28 ++++++++++++++++++- packages/core/i18n/nls.pt-br.json | 28 ++++++++++++++++++- packages/core/i18n/nls.ru.json | 28 ++++++++++++++++++- packages/core/i18n/nls.tr.json | 28 ++++++++++++++++++- packages/core/i18n/nls.zh-cn.json | 28 ++++++++++++++++++- packages/core/i18n/nls.zh-tw.json | 28 ++++++++++++++++++- scripts/translation-update.js | 6 +++- 17 files changed, 396 insertions(+), 19 deletions(-) diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index 6da5e2c498b1f..0a57af1895cbf 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -466,13 +466,16 @@ async function theiaCli(): Promise { } }, handler: async ({ freeApi, deeplKey, file, sourceLanguage, languages = [] }) => { - await localizationManager.localize({ + const success = await localizationManager.localize({ sourceFile: file, freeApi: freeApi ?? true, authKey: deeplKey, targetLanguages: languages, sourceLanguage }); + if (!success) { + process.exit(1); + } } }) .command<{ diff --git a/dev-packages/localization-manager/src/localization-manager.ts b/dev-packages/localization-manager/src/localization-manager.ts index f8468d0057abc..34081a0d3d9f9 100644 --- a/dev-packages/localization-manager/src/localization-manager.ts +++ b/dev-packages/localization-manager/src/localization-manager.ts @@ -34,7 +34,7 @@ export class LocalizationManager { constructor(private localizationFn = deepl) { } - async localize(options: LocalizationOptions): Promise { + async localize(options: LocalizationOptions): Promise { let source: Localization = {}; const cwd = process.env.INIT_CWD || process.cwd(); const sourceFile = path.resolve(cwd, options.sourceFile); @@ -66,7 +66,8 @@ export class LocalizationManager { existingTranslations.set(targetLanguage, {}); } } - await Promise.all(languages.map(language => this.translateLanguage(source, existingTranslations.get(language)!, language, options))); + const results = await Promise.all(languages.map(language => this.translateLanguage(source, existingTranslations.get(language)!, language, options))); + let result = results.reduce((acc, val) => acc && val, true); for (const targetLanguage of languages) { const targetPath = this.translationFileName(sourceFile, targetLanguage); @@ -75,8 +76,10 @@ export class LocalizationManager { await fs.writeJson(targetPath, sortLocalization(translation), { spaces: 2 }); } catch { console.error(chalk.red(`Error writing translated file to '${targetPath}'`)); + result = false; } } + return result; } protected translationFileName(original: string, language: string): string { @@ -85,7 +88,7 @@ export class LocalizationManager { return path.join(directory, `${fileName}.${language.toLowerCase()}.json`); } - async translateLanguage(source: Localization, target: Localization, targetLanguage: string, options: LocalizationOptions): Promise { + async translateLanguage(source: Localization, target: Localization, targetLanguage: string, options: LocalizationOptions): Promise { const map = this.buildLocalizationMap(source, target); if (map.text.length > 0) { try { @@ -102,11 +105,14 @@ export class LocalizationManager { map.localize(i, this.removeIgnoreTags(text)); }); console.log(chalk.green(`Successfully translated ${map.text.length} value${map.text.length > 1 ? 's' : ''} for language "${targetLanguage}"`)); + return true; } catch (e) { console.log(chalk.red(`Could not translate into language "${targetLanguage}"`), e); + return false; } } else { console.log(`No translation necessary for language "${targetLanguage}"`); + return true; } } diff --git a/packages/core/i18n/nls.cs.json b/packages/core/i18n/nls.cs.json index 31e51b0bfe70c..5168d1dd92a9a 100644 --- a/packages/core/i18n/nls.cs.json +++ b/packages/core/i18n/nls.cs.json @@ -11,7 +11,33 @@ "noCallers": "Nebyl zjištěn žádný volající.", "open": "Hierarchie otevřených výzev" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Spolupracujte", + "collaboration": "Spolupráce", + "collaborationWorkspace": "Pracovní prostor pro spolupráci", + "connected": "Připojeno", + "connectedSession": "Připojení k relaci spolupráce", + "copiedInvitation": "Kód pozvánky zkopírovaný do schránky.", + "copyAgain": "Znovu kopírovat", + "createRoom": "Vytvoření nové relace spolupráce", + "creatingRoom": "Vytvoření relace", + "end": "Ukončení relace spolupráce", + "endDetail": "Ukončit relaci, ukončit sdílení obsahu a zrušit přístup ostatním uživatelům.", + "enterCode": "Zadejte kód relace spolupráce", + "failedCreate": "Nepodařilo se vytvořit prostor: {0}", + "failedJoin": "Nepodařilo se připojit k místnosti: {0}", + "invite": "Pozvat ostatní", + "inviteDetail": "Zkopírujte si kód pozvánky, abyste ji mohli sdílet s ostatními a připojit se k sezení.", + "joinRoom": "Připojte se k zasedání o spolupráci", + "joiningRoom": "Připojení k relaci", + "leave": "Opustit zasedání pro spolupráci", + "leaveDetail": "Odpojte se od aktuální relace spolupráce a zavřete pracovní prostor.", + "selectCollaboration": "Vyberte možnost spolupráce", + "sharedSession": "Společná relace spolupráce", + "startSession": "Zahájení relace spolupráce nebo připojení se k ní", + "userWantsToJoin": "Uživatel '{0}' se chce připojit k místnosti pro spolupráci", + "whatToDo": "Co byste chtěli dělat s dalšími spolupracovníky?" + }, "core": { "about": { "compatibility": "{0} Kompatibilita", diff --git a/packages/core/i18n/nls.de.json b/packages/core/i18n/nls.de.json index 21ab06785c954..c032b6c735f39 100644 --- a/packages/core/i18n/nls.de.json +++ b/packages/core/i18n/nls.de.json @@ -11,7 +11,33 @@ "noCallers": "Es wurden keine Anrufer entdeckt.", "open": "Öffne Aufruf-Hierarchie" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Zusammenarbeiten", + "collaboration": "Zusammenarbeit", + "collaborationWorkspace": "Arbeitsbereich für Zusammenarbeit", + "connected": "Verbunden", + "connectedSession": "Verbunden mit einer Sitzung zur Zusammenarbeit", + "copiedInvitation": "Einladungscode in die Zwischenablage kopiert.", + "copyAgain": "Erneut kopieren", + "createRoom": "Neue Collaboration-Sitzung erstellen", + "creatingRoom": "Sitzung erstellen", + "end": "Kollaborationssitzung beenden", + "endDetail": "Beenden Sie die Sitzung, beenden Sie die Freigabe von Inhalten und sperren Sie den Zugang für andere.", + "enterCode": "Enter collaboration session code", + "failedCreate": "Es konnte kein Raum erstellt werden: {0}", + "failedJoin": "Verbindung zum Raum fehlgeschlagen: {0}", + "invite": "Andere einladen", + "inviteDetail": "Kopieren Sie den Einladungscode, um ihn mit anderen zu teilen und an der Sitzung teilzunehmen.", + "joinRoom": "An der Collaboration-Sitzung teilnehmen", + "joiningRoom": "Beitrittssitzung", + "leave": "Kollaborationssitzung verlassen", + "leaveDetail": "Trennen Sie die Verbindung zur aktuellen Zusammenarbeitssitzung und schließen Sie den Arbeitsbereich.", + "selectCollaboration": "Wählen Sie die Option Zusammenarbeit", + "sharedSession": "Gemeinsame Sitzung zur Zusammenarbeit", + "startSession": "Zusammenarbeitssitzung starten oder beitreten", + "userWantsToJoin": "Benutzer '{0}' möchte dem Collaboration Room beitreten", + "whatToDo": "Was würden Sie gerne mit anderen Mitarbeitern machen?" + }, "core": { "about": { "compatibility": "{0} Kompatibilität", diff --git a/packages/core/i18n/nls.es.json b/packages/core/i18n/nls.es.json index b412a52253c1f..8931ed3df2226 100644 --- a/packages/core/i18n/nls.es.json +++ b/packages/core/i18n/nls.es.json @@ -11,7 +11,33 @@ "noCallers": "No se ha detectado ninguna llamada.", "open": "Jerarquía de la convocatoria abierta" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Colabore", + "collaboration": "Colaboración", + "collaborationWorkspace": "Espacio de trabajo colaborativo", + "connected": "Conectado", + "connectedSession": "Conectado a una sesión de colaboración", + "copiedInvitation": "Código de invitación copiado en el portapapeles.", + "copyAgain": "Copiar de nuevo", + "createRoom": "Crear una nueva sesión de colaboración", + "creatingRoom": "Crear sesión", + "end": "Fin de la sesión de colaboración", + "endDetail": "Finalizar la sesión, dejar de compartir contenidos y revocar el acceso a otras personas.", + "enterCode": "Introduzca el código de la sesión de colaboración", + "failedCreate": "No se ha podido crear la sala: {0}", + "failedJoin": "No se ha podido entrar en la sala: {0}", + "invite": "Invitar a otros", + "inviteDetail": "Copie el código de invitación para compartirlo con otras personas y unirse a la sesión.", + "joinRoom": "Unirse a la sesión de colaboración", + "joiningRoom": "Sesión inaugural", + "leave": "Abandonar la sesión de colaboración", + "leaveDetail": "Desconectar de la sesión de colaboración actual y cerrar el espacio de trabajo.", + "selectCollaboration": "Seleccione la opción de colaboración", + "sharedSession": "Compartió una sesión de colaboración", + "startSession": "Iniciar o unirse a una sesión de colaboración", + "userWantsToJoin": "Usuario '{0}' quiere unirse a la sala de colaboración", + "whatToDo": "¿Qué le gustaría hacer con otros colaboradores?" + }, "core": { "about": { "compatibility": "{0} Compatibilidad", diff --git a/packages/core/i18n/nls.fr.json b/packages/core/i18n/nls.fr.json index e788409daae31..45accd898c9b0 100644 --- a/packages/core/i18n/nls.fr.json +++ b/packages/core/i18n/nls.fr.json @@ -11,7 +11,33 @@ "noCallers": "Aucun appelant n'a été détecté.", "open": "Hiérarchie des appels ouverts" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Collaborer", + "collaboration": "Collaboration", + "collaborationWorkspace": "Espace de travail collaboratif", + "connected": "Connecté", + "connectedSession": "Connecté à une session de collaboration", + "copiedInvitation": "Code d'invitation copié dans le presse-papiers.", + "copyAgain": "Copier à nouveau", + "createRoom": "Créer une nouvelle session de collaboration", + "creatingRoom": "Création d'une session", + "end": "Fin de la session de collaboration", + "endDetail": "Mettre fin à la session, cesser le partage de contenu et révoquer l'accès pour d'autres personnes.", + "enterCode": "Enter collaboration session code", + "failedCreate": "Échec de la création d'une salle : {0}", + "failedJoin": "N'a pas réussi à rejoindre la salle : {0}", + "invite": "Inviter d'autres personnes", + "inviteDetail": "Copiez le code d'invitation pour le partager avec d'autres personnes afin de participer à la session.", + "joinRoom": "Participer à une session de collaboration", + "joiningRoom": "Session d'adhésion", + "leave": "Quitter la session de collaboration", + "leaveDetail": "Se déconnecter de la session de collaboration en cours et fermer l'espace de travail.", + "selectCollaboration": "Sélectionner l'option de collaboration", + "sharedSession": "Partager une session de collaboration", + "startSession": "Démarrer ou rejoindre une session de collaboration", + "userWantsToJoin": "User '{0}' wants to join the collaboration room", + "whatToDo": "Qu'aimeriez-vous faire avec d'autres collaborateurs ?" + }, "core": { "about": { "compatibility": "{0} Compatibilité", diff --git a/packages/core/i18n/nls.hu.json b/packages/core/i18n/nls.hu.json index b636cd04403c0..c059404ecd184 100644 --- a/packages/core/i18n/nls.hu.json +++ b/packages/core/i18n/nls.hu.json @@ -11,7 +11,33 @@ "noCallers": "Nem észleltek hívókat.", "open": "Nyílt felhívás hierarchia" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Együttműködés", + "collaboration": "Együttműködés", + "collaborationWorkspace": "Együttműködési munkaterület", + "connected": "Csatlakoztatva", + "connectedSession": "Együttműködési munkamenethez csatlakoztatva", + "copiedInvitation": "Meghívó kódja a vágólapra másolva.", + "copyAgain": "Ismétlemásolás", + "createRoom": "Új együttműködési munkamenet létrehozása", + "creatingRoom": "Munkamenet létrehozása", + "end": "Együttműködési ülés befejezése", + "endDetail": "A munkamenet megszüntetése, a tartalom megosztásának megszüntetése és a hozzáférés visszavonása mások számára.", + "enterCode": "Adja meg az együttműködési munkamenet kódját", + "failedCreate": "Nem sikerült helyet létrehozni: {0}", + "failedJoin": "Nem sikerült csatlakozni a szobához: {0}", + "invite": "Mások meghívása", + "inviteDetail": "Másolja ki a meghívó kódját, hogy másokkal is megoszthassa, és csatlakozhasson az üléshez.", + "joinRoom": "Csatlakozzon az együttműködési üléshez", + "joiningRoom": "Csatlakozási munkamenet", + "leave": "Hagyja el az együttműködési ülést", + "leaveDetail": "Szakítsa meg a kapcsolatot az aktuális együttműködési munkamenetből, és zárja be a munkaterületet.", + "selectCollaboration": "Együttműködési lehetőség kiválasztása", + "sharedSession": "Közös együttműködési munkamenet", + "startSession": "Együttműködési munkamenet indítása vagy ahhoz való csatlakozás", + "userWantsToJoin": "Felhasználó '{0}' csatlakozni szeretne a kollaborációs szobához", + "whatToDo": "Mit szeretnél csinálni más munkatársakkal?" + }, "core": { "about": { "compatibility": "{0} Kompatibilitás", diff --git a/packages/core/i18n/nls.it.json b/packages/core/i18n/nls.it.json index 0fc479a65e66f..46ff1f2ae77fb 100644 --- a/packages/core/i18n/nls.it.json +++ b/packages/core/i18n/nls.it.json @@ -11,7 +11,33 @@ "noCallers": "Non sono stati rilevati chiamanti.", "open": "Gerarchia delle chiamate aperte" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Collaborate", + "collaboration": "Collaborazione", + "collaborationWorkspace": "Spazio di lavoro per la collaborazione", + "connected": "Collegato", + "connectedSession": "Collegato a una sessione di collaborazione", + "copiedInvitation": "Codice invito copiato negli appunti.", + "copyAgain": "Copia di nuovo", + "createRoom": "Creare una nuova sessione di collaborazione", + "creatingRoom": "Creazione di una sessione", + "end": "Fine della sessione di collaborazione", + "endDetail": "Terminare la sessione, interrompere la condivisione dei contenuti e revocare l'accesso ad altri.", + "enterCode": "Inserire il codice della sessione di collaborazione", + "failedCreate": "Impossibile creare una stanza: {0}", + "failedJoin": "Impossibile unirsi alla stanza: {0}", + "invite": "Invitare altri", + "inviteDetail": "Copiare il codice di invito per condividerlo con altri e partecipare alla sessione.", + "joinRoom": "Partecipa alla sessione di collaborazione", + "joiningRoom": "Sessione di adesione", + "leave": "Lasciare la sessione di collaborazione", + "leaveDetail": "Disconnettersi dalla sessione di collaborazione in corso e chiudere l'area di lavoro.", + "selectCollaboration": "Selezionare l'opzione di collaborazione", + "sharedSession": "Condivisione di una sessione di collaborazione", + "startSession": "Avviare o partecipare a una sessione di collaborazione", + "userWantsToJoin": "L'utente '{0}' vuole unirsi alla sala di collaborazione", + "whatToDo": "Cosa le piacerebbe fare con altri collaboratori?" + }, "core": { "about": { "compatibility": "{0} Compatibilità", diff --git a/packages/core/i18n/nls.ja.json b/packages/core/i18n/nls.ja.json index 34871ca325a1d..1d823f74c0c86 100644 --- a/packages/core/i18n/nls.ja.json +++ b/packages/core/i18n/nls.ja.json @@ -11,7 +11,33 @@ "noCallers": "発信者は検出されていません。", "open": "オープンコールヒエラルキー" }, - "collaboration": {}, + "collaboration": { + "collaborate": "コラボレーション", + "collaboration": "コラボレーション", + "collaborationWorkspace": "コラボレーション・ワークスペース", + "connected": "接続済み", + "connectedSession": "コラボレーション・セッションに接続", + "copiedInvitation": "招待状コードがクリップボードにコピーされました。", + "copyAgain": "コピー・アゲイン", + "createRoom": "新しいコラボレーション・セッションの作成", + "creatingRoom": "セッションの作成", + "end": "コラボレーション・セッション終了", + "endDetail": "セッションを終了し、コンテンツの共有を停止し、他の人のアクセスを取り消す。", + "enterCode": "コラボレーションセッションコードを入力", + "failedCreate": "部屋の作成に失敗しました:{0}", + "failedJoin": "入室に失敗:{0}", + "invite": "他の人を招待する", + "inviteDetail": "招待コードをコピーして他の人と共有し、セッションに参加してください。", + "joinRoom": "コラボレーション・セッションに参加", + "joiningRoom": "セッションへの参加", + "leave": "コラボレーション・セッション", + "leaveDetail": "現在のコラボレーションセッションから切断し、ワークスペースを閉じます。", + "selectCollaboration": "コラボレーション・オプションを選択", + "sharedSession": "コラボレーション・セッションを共有", + "startSession": "コラボレーションセッションの開始または参加", + "userWantsToJoin": "ユーザー '{0}' がコラボレーションルームへの参加を希望しています。", + "whatToDo": "他の協力者とどんなことをしたいですか?" + }, "core": { "about": { "compatibility": "{0} 互換性", diff --git a/packages/core/i18n/nls.ko.json b/packages/core/i18n/nls.ko.json index 8e9682548446c..c35b5f4e7a0d9 100644 --- a/packages/core/i18n/nls.ko.json +++ b/packages/core/i18n/nls.ko.json @@ -11,7 +11,33 @@ "noCallers": "발신자가 감지되지 않았습니다.", "open": "오픈 콜 계층 구조" }, - "collaboration": {}, + "collaboration": { + "collaborate": "협업", + "collaboration": "협업", + "collaborationWorkspace": "협업 작업 공간", + "connected": "연결됨", + "connectedSession": "공동 작업 세션에 연결됨", + "copiedInvitation": "초대 코드가 클립보드에 복사되었습니다.", + "copyAgain": "다시 복사", + "createRoom": "새 공동 작업 세션 만들기", + "creatingRoom": "세션 만들기", + "end": "협업 세션 종료", + "endDetail": "세션을 종료하고, 콘텐츠 공유를 중단하고, 다른 사용자의 액세스 권한을 취소합니다.", + "enterCode": "공동 작업 세션 코드 입력", + "failedCreate": "공간을 만들지 못했습니다: {0}", + "failedJoin": "방에 참여하지 못했습니다: {0}", + "invite": "다른 사람 초대", + "inviteDetail": "초대 코드를 복사하여 다른 사람들과 공유하여 세션에 참여합니다.", + "joinRoom": "협업 세션 참여", + "joiningRoom": "세션 참여", + "leave": "공동 작업 세션 나가기", + "leaveDetail": "현재 공동 작업 세션에서 연결을 끊고 워크스페이스를 닫습니다.", + "selectCollaboration": "협업 옵션 선택", + "sharedSession": "공동 작업 세션 공유", + "startSession": "공동 작업 세션 시작 또는 참여", + "userWantsToJoin": "사용자 '{0}' 가 공동 작업실에 참여하려고 합니다.", + "whatToDo": "다른 공동 작업자들과 함께 무엇을 하고 싶으신가요?" + }, "core": { "about": { "compatibility": "{0} 호환성", diff --git a/packages/core/i18n/nls.pl.json b/packages/core/i18n/nls.pl.json index ef9a8677dd57a..8955829d6df56 100644 --- a/packages/core/i18n/nls.pl.json +++ b/packages/core/i18n/nls.pl.json @@ -11,7 +11,33 @@ "noCallers": "Nie wykryto żadnych rozmówców.", "open": "Hierarchia zaproszeń otwartych" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Współpraca", + "collaboration": "Współpraca", + "collaborationWorkspace": "Przestrzeń robocza do współpracy", + "connected": "Połączony", + "connectedSession": "Połączenie z sesją współpracy", + "copiedInvitation": "Kod zaproszenia skopiowany do schowka.", + "copyAgain": "Kopiuj ponownie", + "createRoom": "Utwórz nową sesję współpracy", + "creatingRoom": "Tworzenie sesji", + "end": "Zakończenie sesji współpracy", + "endDetail": "Zakończenie sesji, zaprzestanie udostępniania treści i odebranie dostępu innym osobom.", + "enterCode": "Wprowadź kod sesji współpracy", + "failedCreate": "Nie udało się utworzyć miejsca: {0}", + "failedJoin": "Nie udało się dołączyć do pokoju: {0}", + "invite": "Zaproś innych", + "inviteDetail": "Skopiuj kod zaproszenia, aby udostępnić go innym osobom i dołączyć do sesji.", + "joinRoom": "Dołącz do sesji współpracy", + "joiningRoom": "Sesja dołączania", + "leave": "Zostaw sesję współpracy", + "leaveDetail": "Rozłączenie się z bieżącą sesją współpracy i zamknięcie obszaru roboczego.", + "selectCollaboration": "Wybierz opcję współpracy", + "sharedSession": "Wspólna sesja współpracy", + "startSession": "Rozpoczęcie lub dołączenie do sesji współpracy", + "userWantsToJoin": "Użytkownik '{0}' chce dołączyć do pokoju współpracy", + "whatToDo": "Co chciałbyś zrobić z innymi współpracownikami?" + }, "core": { "about": { "compatibility": "{0} Zgodność", diff --git a/packages/core/i18n/nls.pt-br.json b/packages/core/i18n/nls.pt-br.json index 04706f612398f..10793ca773fd0 100644 --- a/packages/core/i18n/nls.pt-br.json +++ b/packages/core/i18n/nls.pt-br.json @@ -11,7 +11,33 @@ "noCallers": "Nenhum chamador foi detectado.", "open": "Hierarquia de Chamadas Abertas" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Colaborar", + "collaboration": "Colaboração", + "collaborationWorkspace": "Espaço de trabalho de colaboração", + "connected": "Conectado", + "connectedSession": "Conectado a uma sessão de colaboração", + "copiedInvitation": "Código do convite copiado para a área de transferência.", + "copyAgain": "Copiar novamente", + "createRoom": "Criar nova sessão de colaboração", + "creatingRoom": "Criação de sessão", + "end": "Encerrar a sessão de colaboração", + "endDetail": "Encerrar a sessão, interromper o compartilhamento de conteúdo e revogar o acesso de outras pessoas.", + "enterCode": "Digite o código da sessão de colaboração", + "failedCreate": "Falha ao criar a sala: {0}", + "failedJoin": "Falha ao entrar na sala: {0}", + "invite": "Convidar outras pessoas", + "inviteDetail": "Copie o código do convite para compartilhá-lo com outras pessoas e participar da sessão.", + "joinRoom": "Participe da sessão de colaboração", + "joiningRoom": "Sessão de ingresso", + "leave": "Sair da sessão de colaboração", + "leaveDetail": "Desconecte-se da sessão de colaboração atual e feche o espaço de trabalho.", + "selectCollaboration": "Selecione a opção de colaboração", + "sharedSession": "Compartilhou uma sessão de colaboração", + "startSession": "Iniciar ou participar de uma sessão de colaboração", + "userWantsToJoin": "O usuário '{0}' deseja participar da sala de colaboração", + "whatToDo": "O que você gostaria de fazer com outros colaboradores?" + }, "core": { "about": { "compatibility": "{0} Compatibilidade", diff --git a/packages/core/i18n/nls.ru.json b/packages/core/i18n/nls.ru.json index d823d61975293..18e1d8d01d3fa 100644 --- a/packages/core/i18n/nls.ru.json +++ b/packages/core/i18n/nls.ru.json @@ -11,7 +11,33 @@ "noCallers": "Вызывающих не обнаружено.", "open": "Иерархия открытых звонков" }, - "collaboration": {}, + "collaboration": { + "collaborate": "Сотрудничайте", + "collaboration": "Сотрудничество", + "collaborationWorkspace": "Рабочее пространство для совместной работы", + "connected": "Подключено", + "connectedSession": "Подключение к сеансу совместной работы", + "copiedInvitation": "Код приглашения скопирован в буфер обмена.", + "copyAgain": "Повторная копия", + "createRoom": "Создать новый сеанс совместной работы", + "creatingRoom": "Создание сессии", + "end": "Завершение сеанса совместной работы", + "endDetail": "Прервите сеанс, прекратите обмен содержимым и отмените доступ для других.", + "enterCode": "Введите код сеанса совместной работы", + "failedCreate": "Не удалось создать комнату: {0}", + "failedJoin": "Не удалось присоединиться к комнате: {0}", + "invite": "Пригласите других", + "inviteDetail": "Скопируйте код приглашения, чтобы поделиться им с другими людьми и присоединиться к сеансу.", + "joinRoom": "Присоединяйтесь к сессии совместной работы", + "joiningRoom": "Присоединение к сессии", + "leave": "Оставьте сессию совместной работы", + "leaveDetail": "Отключитесь от текущего сеанса совместной работы и закройте рабочую область.", + "selectCollaboration": "Выберите вариант сотрудничества", + "sharedSession": "Совместная сессия сотрудничества", + "startSession": "Начать или присоединиться к сеансу совместной работы", + "userWantsToJoin": "Пользователь '{0}' хочет присоединиться к комнате для совместной работы", + "whatToDo": "Что бы вы хотели сделать с другими соавторами?" + }, "core": { "about": { "compatibility": "{0} Совместимость", diff --git a/packages/core/i18n/nls.tr.json b/packages/core/i18n/nls.tr.json index 8cfec2522e698..87ebc516eaeed 100644 --- a/packages/core/i18n/nls.tr.json +++ b/packages/core/i18n/nls.tr.json @@ -11,7 +11,33 @@ "noCallers": "Herhangi bir arayan tespit edilmedi.", "open": "Açık Çağrı Hiyerarşisi" }, - "collaboration": {}, + "collaboration": { + "collaborate": "İşbirliği yapın", + "collaboration": "İşbirliği", + "collaborationWorkspace": "İşbirliği Çalışma Alanı", + "connected": "Bağlı", + "connectedSession": "Bir işbirliği oturumuna bağlandı", + "copiedInvitation": "Davetiye kodu panoya kopyalandı.", + "copyAgain": "Tekrar Kopyala", + "createRoom": "Yeni İşbirliği Oturumu Oluştur", + "creatingRoom": "Oturum Oluşturma", + "end": "İşbirliği Oturumunu Sonlandırın", + "endDetail": "Oturumu sonlandırın, içerik paylaşımını durdurun ve başkalarının erişimini iptal edin.", + "enterCode": "İşbirliği oturum kodunu girin", + "failedCreate": "Oda oluşturulamadı: {0}", + "failedJoin": "Odaya katılamadı: {0}", + "invite": "Başkalarını Davet Edin", + "inviteDetail": "Oturuma katılmak üzere başkalarıyla paylaşmak için davet kodunu kopyalayın.", + "joinRoom": "İşbirliği Oturumuna Katılın", + "joiningRoom": "Katılma Oturumu", + "leave": "İşbirliği Oturumundan Ayrılın", + "leaveDetail": "Geçerli işbirliği oturumunun bağlantısını kesin ve çalışma alanını kapatın.", + "selectCollaboration": "İşbirliği seçeneğini belirleyin", + "sharedSession": "Bir işbirliği oturumu paylaştı", + "startSession": "İşbirliği oturumunu başlatın veya oturuma katılın", + "userWantsToJoin": "Kullanıcı '{0}' işbirliği odasına katılmak istiyor", + "whatToDo": "Diğer işbirlikçilerle neler yapmak istersiniz?" + }, "core": { "about": { "compatibility": "{0} Uyumluluk", diff --git a/packages/core/i18n/nls.zh-cn.json b/packages/core/i18n/nls.zh-cn.json index 871d9b07dccce..3c356b419855d 100644 --- a/packages/core/i18n/nls.zh-cn.json +++ b/packages/core/i18n/nls.zh-cn.json @@ -11,7 +11,33 @@ "noCallers": "没有发现调用者。", "open": "打开调用层次结构" }, - "collaboration": {}, + "collaboration": { + "collaborate": "合作", + "collaboration": "合作", + "collaborationWorkspace": "协作工作区", + "connected": "已连接", + "connectedSession": "连接到协作会议", + "copiedInvitation": "邀请函代码已复制到剪贴板。", + "copyAgain": "再次复制", + "createRoom": "创建新的协作会话", + "creatingRoom": "创建会话", + "end": "结束合作会议", + "endDetail": "终止会话,停止内容共享,并取消其他人的访问权限。", + "enterCode": "输入协作会话代码", + "failedCreate": "创建房间失败:{0}", + "failedJoin": "未能加入房间:{0}", + "invite": "邀请他人", + "inviteDetail": "复制邀请代码,与他人分享,参加会议。", + "joinRoom": "参加协作会议", + "joiningRoom": "加入会议", + "leave": "离开合作会议", + "leaveDetail": "断开当前协作会话并关闭工作区。", + "selectCollaboration": "选择协作选项", + "sharedSession": "共享合作会议", + "startSession": "开始或加入协作会议", + "userWantsToJoin": "用户 '{0}' 希望加入协作室", + "whatToDo": "您想与其他合作者做些什么?" + }, "core": { "about": { "compatibility": "{0} 兼容性", diff --git a/packages/core/i18n/nls.zh-tw.json b/packages/core/i18n/nls.zh-tw.json index 6c06fb3edd79e..b3f4b4f67c936 100644 --- a/packages/core/i18n/nls.zh-tw.json +++ b/packages/core/i18n/nls.zh-tw.json @@ -11,7 +11,33 @@ "noCallers": "未偵測到來電者。", "open": "開放式呼叫層級" }, - "collaboration": {}, + "collaboration": { + "collaborate": "合作", + "collaboration": "合作", + "collaborationWorkspace": "協同工作區", + "connected": "連接", + "connectedSession": "連接至協作會議", + "copiedInvitation": "邀請函代碼已複製到剪貼簿。", + "copyAgain": "再次複製", + "createRoom": "建立新的協作會議", + "creatingRoom": "創建會話", + "end": "結束協作會議", + "endDetail": "終止會話、停止內容共用,並取消其他人的存取權限。", + "enterCode": "輸入協作會議代碼", + "failedCreate": "建立空間失敗:{0}", + "failedJoin": "未能加入房間:{0}", + "invite": "邀請他人", + "inviteDetail": "複製邀請代碼,以便與他人分享,參加會議。", + "joinRoom": "加入協作會議", + "joiningRoom": "加入會議", + "leave": "休假協作會議", + "leaveDetail": "中斷目前的協作工作階段,並關閉工作區。", + "selectCollaboration": "選擇合作選項", + "sharedSession": "分享協作會議", + "startSession": "開始或加入協作會議", + "userWantsToJoin": "使用者 '{0}' 想要加入協作室", + "whatToDo": "您想與其他合作者做什麼?" + }, "core": { "about": { "compatibility": "{0} 相容性", diff --git a/scripts/translation-update.js b/scripts/translation-update.js index 9209af82eb86f..c90cce7d9bcfc 100644 --- a/scripts/translation-update.js +++ b/scripts/translation-update.js @@ -38,7 +38,7 @@ function getDeepLToken() { } function performDeepLTranslation(token) { - cp.spawnSync('yarn', [ + const childProcess = cp.spawnSync('yarn', [ 'theia', 'nls-localize', '-f', './packages/core/i18n/nls.json', '--free-api', '-k', token @@ -46,4 +46,8 @@ function performDeepLTranslation(token) { shell: true, stdio: 'inherit' }); + if (childProcess.status !== 0) { + console.error('DeepL translation failed'); + process.exit(1); + } } From e094481839bc0920b91f726f81cb759a4cba9cc5 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 29 Aug 2024 11:13:39 +0200 Subject: [PATCH 358/441] docs: updated changelog for 1.53.0 Contributed on behalf of STMicroelectronics --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 950e4dc037a9f..49cd5cf7bab25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,48 @@ - [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/) -## Unreleased -- [plugin] implement stubbed API window registerUriHandler() [#13306](https://github.com/eclipse-theia/theia/pull/13306) - contributed on behalf of STMicroelectronics -- [core] Download json schema catalog at build-time - [#14065](https://github.com/eclipse-theia/theia/pull/14065/) - Contributed on behalf of STMicroelectronics + + +## 1.53.0 - 08/29/2024 + +- [application-package] bumpped API version to 1.92.2 [#14076](https://github.com/eclipse-theia/theia/pull/14076) - Contributed on behalf of STMicroelectronics +- [collaboration] added support for collaboration feature [#13309](https://github.com/eclipse-theia/theia/pull/13309) +- [core] added `testing/profiles/context` menu contribution [#14028](https://github.com/eclipse-theia/theia/pull/14028) - Contributed on behalf of STMicroelectronics +- [core] added support for reverting a composite saveable [#14079](https://github.com/eclipse-theia/theia/pull/14079) +- [core] aligned available locales to VS Code [#14039](https://github.com/eclipse-theia/theia/pull/14039) +- [core] dropped support for Node 16.x [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics +- [core] refactored undo-redo action for editors [#13963](https://github.com/eclipse-theia/theia/pull/13963) +- [core] updated logic to correctly revert saveable on widget close [#14062](https://github.com/eclipse-theia/theia/pull/14062) +- [core] updated logic to download json schema catalog at build-time [#14065](https://github.com/eclipse-theia/theia/pull/14065) - Contributed on behalf of STMicroelectronics +- [electron] updated electron to version 30.1.2 [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics +- [monaco] updated logic to rely on `IConfigurationService` change event to update model options [#13994](https://github.com/eclipse-theia/theia/pull/13994) - Contributed on behalf of STMicroelectronics +- [notebook] added aliases for `list.focusUp` and `list.focusDown` for notebooks [#14042](https://github.com/eclipse-theia/theia/pull/14042) +- [notebook] added logic to support Alt+Enter in notebooks - run the current cell and insert a new below [#14022](https://github.com/eclipse-theia/theia/pull/14022) +- [notebook] added notebook selected cell status bar item and center selected cell command [#14046](https://github.com/eclipse-theia/theia/pull/14046) +- [notebook] added support to find widget in notebooks [#13982](https://github.com/eclipse-theia/theia/pull/13982) +- [notebook] enhanced notebook cell divider [#14081](https://github.com/eclipse-theia/theia/pull/14081) +- [notebook] fixed notebook output scrolling and text rendering [#14016](https://github.com/eclipse-theia/theia/pull/14016) +- [notebook] fixed vscode api notebook selection property [#14087](https://github.com/eclipse-theia/theia/pull/14087) +- [notebook] updated logic to make sure notebook model created when calling `openNotebookDocument` [#14029](https://github.com/eclipse-theia/theia/pull/14029) +- [notebook] updated logic to use correct cell type for selected language [#13983](https://github.com/eclipse-theia/theia/pull/13983) +- [playwright] fixed flaky playwright Theia Main Menu test [#13951](https://github.com/eclipse-theia/theia/pull/13951) - Contributed on behalf of STMicroelectronics +- [plugin] added `executeFoldingRangeProvider`, `executeCodeActionProvider`, and `executeWorkspaceSymbolProvider` command implementations [#14093](https://github.com/eclipse-theia/theia/pull/14093) +- [plugin] added support for `--headless-hosted-plugin-inspect` cmd argument [#13918](https://github.com/eclipse-theia/theia/pull/13918) +- [plugin] fixed issue when creating new untitled notebook doesn't work [#14031](https://github.com/eclipse-theia/theia/pull/14031) +- [plugin] implemented previously stubbed API `window.registerUriHandler()` [#13306](https://github.com/eclipse-theia/theia/pull/13306) - Contributed on behalf of STMicroelectronics +- [plugin] stubbed Terminal Shell Integration VS Code API [#14058](https://github.com/eclipse-theia/theia/pull/14058) +- [plugin] updated logic to allow opening changes for files associated with custom editors [#13916](https://github.com/eclipse-theia/theia/pull/13916) +- [plugin] upated code to not use `ChannelMultiplexer` in `RPCProtocol` [#13980](https://github.com/eclipse-theia/theia/pull/13980) - Contributed on behalf of STMicroelectronics +- [preferences] fixed preference tree for plugins [#14036](https://github.com/eclipse-theia/theia/pull/14036) +- [vsx-registry] fixed `429` errors on OVSX requests [#14030](https://github.com/eclipse-theia/theia/pull/14030) [Breaking Changes:](#breaking_changes_1.53.0) + - [dependencies] Updated electron to version 30.1.2 - [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics - [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics - ## 1.52.0 - 07/25/2024 - [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#13955](https://github.com/eclipse-theia/theia/pull/13955) - Contributed on behalf of STMicroelectronics From 81fc43d476fb7eed519ce71cc5de865a48d6ac04 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 29 Aug 2024 13:39:16 +0200 Subject: [PATCH 359/441] core: update re-exports for 1.53.0 Contributed on behalf of STMicroelectronics --- packages/core/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index db504560046e6..2483bb3abbaa4 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -84,12 +84,12 @@ export class SomeClass { - `@phosphor/signaling` (from [`@phosphor/signaling@1`](https://www.npmjs.com/package/@phosphor/signaling)) - `@phosphor/virtualdom` (from [`@phosphor/virtualdom@1`](https://www.npmjs.com/package/@phosphor/virtualdom)) - `@phosphor/widgets` (from [`@phosphor/widgets@1`](https://www.npmjs.com/package/@phosphor/widgets)) - - `@theia/application-package` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) - - `@theia/application-package/lib/api` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) - - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.52.0`](https://www.npmjs.com/package/@theia/application-package/v/1.52.0)) - - `@theia/request` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) - - `@theia/request/lib/proxy` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) - - `@theia/request/lib/node-request-service` (from [`@theia/request@1.52.0`](https://www.npmjs.com/package/@theia/request/v/1.52.0)) + - `@theia/application-package` (from [`@theia/application-package@1.53.0`](https://www.npmjs.com/package/@theia/application-package/v/1.53.0)) + - `@theia/application-package/lib/api` (from [`@theia/application-package@1.53.0`](https://www.npmjs.com/package/@theia/application-package/v/1.53.0)) + - `@theia/application-package/lib/environment` (from [`@theia/application-package@1.53.0`](https://www.npmjs.com/package/@theia/application-package/v/1.53.0)) + - `@theia/request` (from [`@theia/request@1.53.0`](https://www.npmjs.com/package/@theia/request/v/1.53.0)) + - `@theia/request/lib/proxy` (from [`@theia/request@1.53.0`](https://www.npmjs.com/package/@theia/request/v/1.53.0)) + - `@theia/request/lib/node-request-service` (from [`@theia/request@1.53.0`](https://www.npmjs.com/package/@theia/request/v/1.53.0)) - `fs-extra` (from [`fs-extra@^4.0.2`](https://www.npmjs.com/package/fs-extra)) - `fuzzy` (from [`fuzzy@^0.1.3`](https://www.npmjs.com/package/fuzzy)) - `inversify` (from [`inversify@^6.0.1`](https://www.npmjs.com/package/inversify)) From 4c7ffb632b46f52841f09e6fdc88e9c23448b572 Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 29 Aug 2024 13:39:30 +0200 Subject: [PATCH 360/441] v1.53.0 --- dev-packages/application-manager/package.json | 10 +- dev-packages/application-package/package.json | 6 +- dev-packages/cli/package.json | 14 +-- dev-packages/ffmpeg/package.json | 2 +- .../localization-manager/package.json | 4 +- .../native-webpack-plugin/package.json | 2 +- dev-packages/ovsx-client/package.json | 4 +- .../private-eslint-plugin/package.json | 8 +- dev-packages/private-ext-scripts/package.json | 2 +- dev-packages/private-re-exports/package.json | 2 +- dev-packages/request/package.json | 2 +- examples/api-provider-sample/package.json | 10 +- examples/api-samples/package.json | 26 ++--- examples/api-tests/package.json | 4 +- examples/browser-only/package.json | 92 ++++++++-------- examples/browser/package.json | 102 +++++++++--------- examples/electron/package.json | 100 ++++++++--------- examples/playwright/package.json | 2 +- lerna.json | 2 +- packages/bulk-edit/package.json | 14 +-- packages/callhierarchy/package.json | 8 +- packages/collaboration/package.json | 14 +-- packages/console/package.json | 8 +- packages/core/package.json | 10 +- packages/debug/package.json | 30 +++--- packages/dev-container/package.json | 12 +-- packages/editor-preview/package.json | 10 +- packages/editor/package.json | 8 +- packages/electron/package.json | 6 +- packages/external-terminal/package.json | 10 +- packages/file-search/package.json | 14 +-- packages/filesystem/package.json | 6 +- packages/getting-started/package.json | 16 +-- packages/git/package.json | 18 ++-- packages/keymaps/package.json | 12 +-- packages/markers/package.json | 10 +- packages/memory-inspector/package.json | 6 +- packages/messages/package.json | 6 +- packages/metrics/package.json | 6 +- packages/mini-browser/package.json | 8 +- packages/monaco/package.json | 16 +-- packages/navigator/package.json | 10 +- packages/notebook/package.json | 14 +-- packages/outline-view/package.json | 6 +- packages/output/package.json | 10 +- packages/plugin-dev/package.json | 16 +-- packages/plugin-ext-headless/package.json | 10 +- packages/plugin-ext-vscode/package.json | 30 +++--- packages/plugin-ext/package.json | 56 +++++----- packages/plugin-metrics/package.json | 12 +-- packages/plugin/package.json | 4 +- packages/preferences/package.json | 16 +-- packages/preview/package.json | 12 +-- packages/process/package.json | 6 +- packages/property-view/package.json | 8 +- packages/remote/package.json | 8 +- packages/scm-extra/package.json | 14 +-- packages/scm/package.json | 12 +-- packages/search-in-workspace/package.json | 16 +-- packages/secondary-window/package.json | 6 +- packages/task/package.json | 24 ++--- packages/terminal/package.json | 18 ++-- packages/test/package.json | 14 +-- packages/timeline/package.json | 8 +- packages/toolbar/package.json | 18 ++-- packages/typehierarchy/package.json | 8 +- packages/userstorage/package.json | 8 +- packages/variable-resolver/package.json | 6 +- packages/vsx-registry/package.json | 20 ++-- packages/workspace/package.json | 10 +- .../sample-namespace/plugin-a/package.json | 2 +- .../sample-namespace/plugin-b/package.json | 2 +- .../sample-namespace/plugin-gotd/package.json | 4 +- 73 files changed, 525 insertions(+), 525 deletions(-) diff --git a/dev-packages/application-manager/package.json b/dev-packages/application-manager/package.json index 10c58467c38f5..253333063f3d8 100644 --- a/dev-packages/application-manager/package.json +++ b/dev-packages/application-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-manager", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia application manager API.", "publishConfig": { "access": "public" @@ -33,9 +33,9 @@ "@babel/plugin-transform-classes": "^7.10.0", "@babel/plugin-transform-runtime": "^7.10.0", "@babel/preset-env": "^7.10.0", - "@theia/application-package": "1.52.0", - "@theia/ffmpeg": "1.52.0", - "@theia/native-webpack-plugin": "1.52.0", + "@theia/application-package": "1.53.0", + "@theia/ffmpeg": "1.53.0", + "@theia/native-webpack-plugin": "1.53.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "babel-loader": "^8.2.2", @@ -74,7 +74,7 @@ } }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/node-abi": "*" }, "nyc": { diff --git a/dev-packages/application-package/package.json b/dev-packages/application-package/package.json index c448d1ec3df4d..5b9d6fdb4b895 100644 --- a/dev-packages/application-package/package.json +++ b/dev-packages/application-package/package.json @@ -1,6 +1,6 @@ { "name": "@theia/application-package", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia application package API.", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.52.0", + "@theia/request": "1.53.0", "@types/fs-extra": "^4.0.2", "@types/semver": "^7.5.0", "@types/write-json-file": "^2.2.1", @@ -43,7 +43,7 @@ "write-json-file": "^2.2.0" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 0fb56c018e2bb..585de9466de36 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@theia/cli", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia CLI.", "publishConfig": { "access": "public" @@ -32,12 +32,12 @@ "clean": "theiaext clean" }, "dependencies": { - "@theia/application-manager": "1.52.0", - "@theia/application-package": "1.52.0", - "@theia/ffmpeg": "1.52.0", - "@theia/localization-manager": "1.52.0", - "@theia/ovsx-client": "1.52.0", - "@theia/request": "1.52.0", + "@theia/application-manager": "1.53.0", + "@theia/application-package": "1.53.0", + "@theia/ffmpeg": "1.53.0", + "@theia/localization-manager": "1.53.0", + "@theia/ovsx-client": "1.53.0", + "@theia/request": "1.53.0", "@types/chai": "^4.2.7", "@types/mocha": "^10.0.0", "@types/node-fetch": "^2.5.7", diff --git a/dev-packages/ffmpeg/package.json b/dev-packages/ffmpeg/package.json index 6d04dec267ac9..04b6c254c6995 100644 --- a/dev-packages/ffmpeg/package.json +++ b/dev-packages/ffmpeg/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ffmpeg", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia FFMPEG reader utility.", "publishConfig": { "access": "public" diff --git a/dev-packages/localization-manager/package.json b/dev-packages/localization-manager/package.json index c316b4d2c0344..0b17fa8efce24 100644 --- a/dev-packages/localization-manager/package.json +++ b/dev-packages/localization-manager/package.json @@ -1,6 +1,6 @@ { "name": "@theia/localization-manager", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia localization manager API.", "publishConfig": { "access": "public" @@ -40,7 +40,7 @@ "typescript": "~5.4.5" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index face0ca0563de..47de634969c89 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/native-webpack-plugin", - "version": "1.52.0", + "version": "1.53.0", "description": "Webpack Plugin for native dependencies of Theia.", "publishConfig": { "access": "public" diff --git a/dev-packages/ovsx-client/package.json b/dev-packages/ovsx-client/package.json index 3ef7f91b6d1bd..160fe418dc6b3 100644 --- a/dev-packages/ovsx-client/package.json +++ b/dev-packages/ovsx-client/package.json @@ -1,6 +1,6 @@ { "name": "@theia/ovsx-client", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia Open-VSX Client", "publishConfig": { "access": "public" @@ -29,7 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/request": "1.52.0", + "@theia/request": "1.53.0", "limiter": "^2.1.0", "semver": "^7.5.4", "tslib": "^2.6.2" diff --git a/dev-packages/private-eslint-plugin/package.json b/dev-packages/private-eslint-plugin/package.json index 99d5704dde653..ef8aa9cb183a1 100644 --- a/dev-packages/private-eslint-plugin/package.json +++ b/dev-packages/private-eslint-plugin/package.json @@ -1,16 +1,16 @@ { "private": true, "name": "@theia/eslint-plugin", - "version": "1.52.0", + "version": "1.53.0", "description": "Custom ESLint rules for developing Theia extensions and applications", "main": "index.js", "scripts": { "prepare": "tsc -b" }, "dependencies": { - "@theia/core": "1.52.0", - "@theia/ext-scripts": "1.52.0", - "@theia/re-exports": "1.52.0", + "@theia/core": "1.53.0", + "@theia/ext-scripts": "1.53.0", + "@theia/re-exports": "1.53.0", "js-levenshtein": "^1.1.6" } } diff --git a/dev-packages/private-ext-scripts/package.json b/dev-packages/private-ext-scripts/package.json index 3bcf0da0a0565..1ab8efa73e4b6 100644 --- a/dev-packages/private-ext-scripts/package.json +++ b/dev-packages/private-ext-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/ext-scripts", - "version": "1.52.0", + "version": "1.53.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "description": "NPM scripts for Theia packages.", "bin": { diff --git a/dev-packages/private-re-exports/package.json b/dev-packages/private-re-exports/package.json index aec9cadd287e2..e91f290766f93 100644 --- a/dev-packages/private-re-exports/package.json +++ b/dev-packages/private-re-exports/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/re-exports", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia re-export helper functions and scripts.", "main": "lib/index.js", "engines": { diff --git a/dev-packages/request/package.json b/dev-packages/request/package.json index 9011b84a4dce2..58f272d50880c 100644 --- a/dev-packages/request/package.json +++ b/dev-packages/request/package.json @@ -1,6 +1,6 @@ { "name": "@theia/request", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia Proxy-Aware Request Service", "publishConfig": { "access": "public" diff --git a/examples/api-provider-sample/package.json b/examples/api-provider-sample/package.json index c8553f9c0caf6..33d76b3bf09ce 100644 --- a/examples/api-provider-sample/package.json +++ b/examples/api-provider-sample/package.json @@ -1,12 +1,12 @@ { "private": true, "name": "@theia/api-provider-sample", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Example code to demonstrate Theia API Provider Extensions", "dependencies": { - "@theia/core": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/plugin-ext-headless": "1.52.0" + "@theia/core": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/plugin-ext-headless": "1.53.0" }, "theiaExtensions": [ { @@ -37,6 +37,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" } } diff --git a/examples/api-samples/package.json b/examples/api-samples/package.json index f5a4b84142086..959f47f940556 100644 --- a/examples/api-samples/package.json +++ b/examples/api-samples/package.json @@ -1,21 +1,21 @@ { "private": true, "name": "@theia/api-samples", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Example code to demonstrate Theia API", "dependencies": { - "@theia/core": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.52.0", - "@theia/ovsx-client": "1.52.0", - "@theia/search-in-workspace": "1.52.0", - "@theia/test": "1.52.0", - "@theia/toolbar": "1.52.0", - "@theia/vsx-registry": "1.52.0", - "@theia/workspace": "1.52.0" + "@theia/output": "1.53.0", + "@theia/ovsx-client": "1.53.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/test": "1.53.0", + "@theia/toolbar": "1.53.0", + "@theia/vsx-registry": "1.53.0", + "@theia/workspace": "1.53.0" }, "theiaExtensions": [ { @@ -57,6 +57,6 @@ "clean": "theiaext clean" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" } } diff --git a/examples/api-tests/package.json b/examples/api-tests/package.json index 947cd74ef3332..32a8954956517 100644 --- a/examples/api-tests/package.json +++ b/examples/api-tests/package.json @@ -1,9 +1,9 @@ { "name": "@theia/api-tests", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia API tests", "dependencies": { - "@theia/core": "1.52.0" + "@theia/core": "1.53.0" }, "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index 2913b38d8658e..caf6e3b12f115 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser-only", - "version": "1.52.0", + "version": "1.53.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "target": "browser-only", @@ -15,50 +15,50 @@ } }, "dependencies": { - "@theia/api-samples": "1.52.0", - "@theia/bulk-edit": "1.52.0", - "@theia/callhierarchy": "1.52.0", - "@theia/collaboration": "1.52.0", - "@theia/console": "1.52.0", - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/editor-preview": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/getting-started": "1.52.0", - "@theia/git": "1.52.0", - "@theia/keymaps": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/memory-inspector": "1.52.0", - "@theia/messages": "1.52.0", - "@theia/metrics": "1.52.0", - "@theia/mini-browser": "1.52.0", - "@theia/monaco": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/outline-view": "1.52.0", - "@theia/output": "1.52.0", - "@theia/plugin-dev": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/plugin-ext-vscode": "1.52.0", - "@theia/plugin-metrics": "1.52.0", - "@theia/preferences": "1.52.0", - "@theia/preview": "1.52.0", - "@theia/process": "1.52.0", - "@theia/property-view": "1.52.0", - "@theia/scm": "1.52.0", - "@theia/scm-extra": "1.52.0", - "@theia/search-in-workspace": "1.52.0", - "@theia/secondary-window": "1.52.0", - "@theia/task": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/timeline": "1.52.0", - "@theia/toolbar": "1.52.0", - "@theia/typehierarchy": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/vsx-registry": "1.52.0", - "@theia/workspace": "1.52.0" + "@theia/api-samples": "1.53.0", + "@theia/bulk-edit": "1.53.0", + "@theia/callhierarchy": "1.53.0", + "@theia/collaboration": "1.53.0", + "@theia/console": "1.53.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/editor-preview": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/getting-started": "1.53.0", + "@theia/git": "1.53.0", + "@theia/keymaps": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/memory-inspector": "1.53.0", + "@theia/messages": "1.53.0", + "@theia/metrics": "1.53.0", + "@theia/mini-browser": "1.53.0", + "@theia/monaco": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/outline-view": "1.53.0", + "@theia/output": "1.53.0", + "@theia/plugin-dev": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/plugin-ext-vscode": "1.53.0", + "@theia/plugin-metrics": "1.53.0", + "@theia/preferences": "1.53.0", + "@theia/preview": "1.53.0", + "@theia/process": "1.53.0", + "@theia/property-view": "1.53.0", + "@theia/scm": "1.53.0", + "@theia/scm-extra": "1.53.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/secondary-window": "1.53.0", + "@theia/task": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/timeline": "1.53.0", + "@theia/toolbar": "1.53.0", + "@theia/typehierarchy": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/vsx-registry": "1.53.0", + "@theia/workspace": "1.53.0" }, "scripts": { "prepare:no-native": "lerna run prepare --scope=\"@theia/re-exports\" && lerna run generate-theia-re-exports --scope=\"@theia/core\"", @@ -74,6 +74,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.52.0" + "@theia/cli": "1.53.0" } } diff --git a/examples/browser/package.json b/examples/browser/package.json index 0e2e714ea1756..ab928d6b29f2e 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@theia/example-browser", - "version": "1.52.0", + "version": "1.53.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { "frontend": { @@ -20,55 +20,55 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.52.0", - "@theia/api-samples": "1.52.0", - "@theia/bulk-edit": "1.52.0", - "@theia/callhierarchy": "1.52.0", - "@theia/collaboration": "1.52.0", - "@theia/console": "1.52.0", - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", - "@theia/dev-container": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/editor-preview": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/getting-started": "1.52.0", - "@theia/keymaps": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/memory-inspector": "1.52.0", - "@theia/messages": "1.52.0", - "@theia/metrics": "1.52.0", - "@theia/mini-browser": "1.52.0", - "@theia/monaco": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/notebook": "1.52.0", - "@theia/outline-view": "1.52.0", - "@theia/output": "1.52.0", - "@theia/plugin-dev": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/plugin-ext-headless": "1.52.0", - "@theia/plugin-ext-vscode": "1.52.0", - "@theia/plugin-metrics": "1.52.0", - "@theia/preferences": "1.52.0", - "@theia/preview": "1.52.0", - "@theia/process": "1.52.0", - "@theia/property-view": "1.52.0", - "@theia/remote": "1.52.0", - "@theia/scm": "1.52.0", - "@theia/scm-extra": "1.52.0", - "@theia/search-in-workspace": "1.52.0", - "@theia/secondary-window": "1.52.0", - "@theia/task": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/test": "1.52.0", - "@theia/timeline": "1.52.0", - "@theia/toolbar": "1.52.0", - "@theia/typehierarchy": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/vsx-registry": "1.52.0", - "@theia/workspace": "1.52.0" + "@theia/api-provider-sample": "1.53.0", + "@theia/api-samples": "1.53.0", + "@theia/bulk-edit": "1.53.0", + "@theia/callhierarchy": "1.53.0", + "@theia/collaboration": "1.53.0", + "@theia/console": "1.53.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", + "@theia/dev-container": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/editor-preview": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/getting-started": "1.53.0", + "@theia/keymaps": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/memory-inspector": "1.53.0", + "@theia/messages": "1.53.0", + "@theia/metrics": "1.53.0", + "@theia/mini-browser": "1.53.0", + "@theia/monaco": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/notebook": "1.53.0", + "@theia/outline-view": "1.53.0", + "@theia/output": "1.53.0", + "@theia/plugin-dev": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/plugin-ext-headless": "1.53.0", + "@theia/plugin-ext-vscode": "1.53.0", + "@theia/plugin-metrics": "1.53.0", + "@theia/preferences": "1.53.0", + "@theia/preview": "1.53.0", + "@theia/process": "1.53.0", + "@theia/property-view": "1.53.0", + "@theia/remote": "1.53.0", + "@theia/scm": "1.53.0", + "@theia/scm-extra": "1.53.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/secondary-window": "1.53.0", + "@theia/task": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/test": "1.53.0", + "@theia/timeline": "1.53.0", + "@theia/toolbar": "1.53.0", + "@theia/typehierarchy": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/vsx-registry": "1.53.0", + "@theia/workspace": "1.53.0" }, "scripts": { "clean": "theia clean", @@ -91,6 +91,6 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.52.0" + "@theia/cli": "1.53.0" } } diff --git a/examples/electron/package.json b/examples/electron/package.json index 52eceabf186bd..16386e54eb76f 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@theia/example-electron", "productName": "Theia Electron Example", - "version": "1.52.0", + "version": "1.53.0", "main": "lib/backend/electron-main.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "theia": { @@ -26,54 +26,54 @@ } }, "dependencies": { - "@theia/api-provider-sample": "1.52.0", - "@theia/api-samples": "1.52.0", - "@theia/bulk-edit": "1.52.0", - "@theia/callhierarchy": "1.52.0", - "@theia/collaboration": "1.52.0", - "@theia/console": "1.52.0", - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", - "@theia/dev-container": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/editor-preview": "1.52.0", - "@theia/electron": "1.52.0", - "@theia/external-terminal": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/getting-started": "1.52.0", - "@theia/keymaps": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/memory-inspector": "1.52.0", - "@theia/messages": "1.52.0", - "@theia/metrics": "1.52.0", - "@theia/mini-browser": "1.52.0", - "@theia/monaco": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/outline-view": "1.52.0", - "@theia/output": "1.52.0", - "@theia/plugin-dev": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/plugin-ext-headless": "1.52.0", - "@theia/plugin-ext-vscode": "1.52.0", - "@theia/preferences": "1.52.0", - "@theia/preview": "1.52.0", - "@theia/process": "1.52.0", - "@theia/property-view": "1.52.0", - "@theia/remote": "1.52.0", - "@theia/scm": "1.52.0", - "@theia/scm-extra": "1.52.0", - "@theia/search-in-workspace": "1.52.0", - "@theia/secondary-window": "1.52.0", - "@theia/task": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/timeline": "1.52.0", - "@theia/toolbar": "1.52.0", - "@theia/typehierarchy": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/vsx-registry": "1.52.0", - "@theia/workspace": "1.52.0" + "@theia/api-provider-sample": "1.53.0", + "@theia/api-samples": "1.53.0", + "@theia/bulk-edit": "1.53.0", + "@theia/callhierarchy": "1.53.0", + "@theia/collaboration": "1.53.0", + "@theia/console": "1.53.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", + "@theia/dev-container": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/editor-preview": "1.53.0", + "@theia/electron": "1.53.0", + "@theia/external-terminal": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/getting-started": "1.53.0", + "@theia/keymaps": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/memory-inspector": "1.53.0", + "@theia/messages": "1.53.0", + "@theia/metrics": "1.53.0", + "@theia/mini-browser": "1.53.0", + "@theia/monaco": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/outline-view": "1.53.0", + "@theia/output": "1.53.0", + "@theia/plugin-dev": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/plugin-ext-headless": "1.53.0", + "@theia/plugin-ext-vscode": "1.53.0", + "@theia/preferences": "1.53.0", + "@theia/preview": "1.53.0", + "@theia/process": "1.53.0", + "@theia/property-view": "1.53.0", + "@theia/remote": "1.53.0", + "@theia/scm": "1.53.0", + "@theia/scm-extra": "1.53.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/secondary-window": "1.53.0", + "@theia/task": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/timeline": "1.53.0", + "@theia/toolbar": "1.53.0", + "@theia/typehierarchy": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/vsx-registry": "1.53.0", + "@theia/workspace": "1.53.0" }, "scripts": { "build": "yarn -s compile && yarn -s bundle", @@ -91,7 +91,7 @@ "watch:compile": "tsc -b -w" }, "devDependencies": { - "@theia/cli": "1.52.0", + "@theia/cli": "1.53.0", "electron": "^30.1.2" } } diff --git a/examples/playwright/package.json b/examples/playwright/package.json index d9dd3f72aabb5..0d0b175d74bf6 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -1,6 +1,6 @@ { "name": "@theia/playwright", - "version": "1.52.0", + "version": "1.53.0", "description": "System tests for Theia", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/lerna.json b/lerna.json index fedeabfeeb7d2..2221326102421 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/lerna.json", "npmClient": "yarn", - "version": "1.52.0", + "version": "1.53.0", "command": { "run": { "stream": true diff --git a/packages/bulk-edit/package.json b/packages/bulk-edit/package.json index 0278d46c71001..312ef80cc23a8 100644 --- a/packages/bulk-edit/package.json +++ b/packages/bulk-edit/package.json @@ -1,14 +1,14 @@ { "name": "@theia/bulk-edit", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Bulk Edit Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.52.0", + "@theia/workspace": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/callhierarchy/package.json b/packages/callhierarchy/package.json index 5caaaee70073d..cde8b9af18d1d 100644 --- a/packages/callhierarchy/package.json +++ b/packages/callhierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/callhierarchy", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Call Hierarchy Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", "ts-md5": "^1.2.2", "tslib": "^2.6.2" }, @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/collaboration/package.json b/packages/collaboration/package.json index 130c2b6718399..ef39af5fad751 100644 --- a/packages/collaboration/package.json +++ b/packages/collaboration/package.json @@ -1,14 +1,14 @@ { "name": "@theia/collaboration", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Collaboration Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/workspace": "1.52.0", + "@theia/workspace": "1.53.0", "open-collaboration-protocol": "0.2.0", "open-collaboration-yjs": "0.2.0", "socket.io-client": "^4.5.3", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/console/package.json b/packages/console/package.json index 5f5f209d0a16b..6b0055c2b2fb5 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,10 +1,10 @@ { "name": "@theia/console", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Console Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", "anser": "^2.0.1", "tslib": "^2.6.2" @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/core/package.json b/packages/core/package.json index e5349a14818d8..b941db18927e3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@theia/core", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia is a cloud & desktop IDE framework implemented in TypeScript.", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", @@ -16,8 +16,8 @@ "@phosphor/signaling": "1", "@phosphor/virtualdom": "1", "@phosphor/widgets": "1", - "@theia/application-package": "1.52.0", - "@theia/request": "1.52.0", + "@theia/application-package": "1.53.0", + "@theia/request": "1.53.0", "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", @@ -210,8 +210,8 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", - "@theia/re-exports": "1.52.0", + "@theia/ext-scripts": "1.53.0", + "@theia/re-exports": "1.53.0", "minimist": "^1.2.0", "nodejs-file-downloader": "4.13.0" }, diff --git a/packages/debug/package.json b/packages/debug/package.json index ae1a8e14c2dd4..20c5f05cf0f2a 100644 --- a/packages/debug/package.json +++ b/packages/debug/package.json @@ -1,22 +1,22 @@ { "name": "@theia/debug", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Debug Extension", "dependencies": { - "@theia/console": "1.52.0", - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/console": "1.53.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/output": "1.52.0", - "@theia/process": "1.52.0", - "@theia/task": "1.52.0", - "@theia/test": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/output": "1.53.0", + "@theia/process": "1.53.0", + "@theia/task": "1.53.0", + "@theia/test": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/workspace": "1.53.0", "@vscode/debugprotocol": "^1.51.0", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -59,7 +59,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/dev-container/package.json b/packages/dev-container/package.json index dde7c2c865464..32f233826ae62 100644 --- a/packages/dev-container/package.json +++ b/packages/dev-container/package.json @@ -1,12 +1,12 @@ { "name": "@theia/dev-container", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/output": "1.52.0", - "@theia/remote": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/output": "1.53.0", + "@theia/remote": "1.53.0", + "@theia/workspace": "1.53.0", "dockerode": "^4.0.2", "jsonc-parser": "^2.2.0", "uuid": "^8.0.0" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/dockerode": "^3.3.23" }, "nyc": { diff --git a/packages/editor-preview/package.json b/packages/editor-preview/package.json index 7c1e4566da0ab..7f55c86cd94fc 100644 --- a/packages/editor-preview/package.json +++ b/packages/editor-preview/package.json @@ -1,11 +1,11 @@ { "name": "@theia/editor-preview", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Editor Preview Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/navigator": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/navigator": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/editor/package.json b/packages/editor/package.json index 0556960495382..c0a330246a020 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,10 +1,10 @@ { "name": "@theia/editor", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Editor Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/variable-resolver": "1.52.0", + "@theia/core": "1.53.0", + "@theia/variable-resolver": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/electron/package.json b/packages/electron/package.json index 2a947ac02c81a..e6ec7699f5883 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -1,6 +1,6 @@ { "name": "@theia/electron", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Electron utility package", "dependencies": { "electron-store": "^8.0.0", @@ -8,8 +8,8 @@ "native-keymap": "^2.2.1" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", - "@theia/re-exports": "1.52.0" + "@theia/ext-scripts": "1.53.0", + "@theia/re-exports": "1.53.0" }, "peerDependencies": { "electron": "^30.1.2" diff --git a/packages/external-terminal/package.json b/packages/external-terminal/package.json index f125bcc1283c8..e0348b0654fed 100644 --- a/packages/external-terminal/package.json +++ b/packages/external-terminal/package.json @@ -1,11 +1,11 @@ { "name": "@theia/external-terminal", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - External Terminal Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/workspace": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -42,7 +42,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/file-search/package.json b/packages/file-search/package.json index 48a4598b2782c..4c5ba9e5d522e 100644 --- a/packages/file-search/package.json +++ b/packages/file-search/package.json @@ -1,13 +1,13 @@ { "name": "@theia/file-search", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - File Search Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/process": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/process": "1.53.0", + "@theia/workspace": "1.53.0", "@vscode/ripgrep": "^1.14.2", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index cf7ea16b482f5..7c1a60322c673 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -1,9 +1,9 @@ { "name": "@theia/filesystem", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - FileSystem Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", "@types/rimraf": "^2.0.2", @@ -73,7 +73,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/getting-started/package.json b/packages/getting-started/package.json index 0877622695314..644cfca695224 100644 --- a/packages/getting-started/package.json +++ b/packages/getting-started/package.json @@ -1,14 +1,14 @@ { "name": "@theia/getting-started", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - GettingStarted Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/keymaps": "1.52.0", - "@theia/preview": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/keymaps": "1.53.0", + "@theia/preview": "1.53.0", + "@theia/workspace": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/git/package.json b/packages/git/package.json index 6af6d23eca82c..eb69eba9b6383 100644 --- a/packages/git/package.json +++ b/packages/git/package.json @@ -1,16 +1,16 @@ { "name": "@theia/git", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Git Integration", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.52.0", - "@theia/scm": "1.52.0", - "@theia/scm-extra": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/navigator": "1.53.0", + "@theia/scm": "1.53.0", + "@theia/scm-extra": "1.53.0", + "@theia/workspace": "1.53.0", "@types/diff": "^5.2.1", "@types/p-queue": "^2.3.1", "diff": "^5.2.0", @@ -67,7 +67,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/luxon": "^2.3.2", "upath": "^1.0.2" }, diff --git a/packages/keymaps/package.json b/packages/keymaps/package.json index 8df3ca28c183c..425fe663b24fd 100644 --- a/packages/keymaps/package.json +++ b/packages/keymaps/package.json @@ -1,18 +1,18 @@ { "name": "@theia/keymaps", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Custom Keymaps Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/preferences": "1.52.0", - "@theia/userstorage": "1.52.0", + "@theia/preferences": "1.53.0", + "@theia/userstorage": "1.53.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "publishConfig": { "access": "public" diff --git a/packages/markers/package.json b/packages/markers/package.json index a4ae243d95287..97d1a36c62c8a 100644 --- a/packages/markers/package.json +++ b/packages/markers/package.json @@ -1,11 +1,11 @@ { "name": "@theia/markers", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Markers Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/memory-inspector/package.json b/packages/memory-inspector/package.json index 2510402629ac8..ff3b247c27495 100644 --- a/packages/memory-inspector/package.json +++ b/packages/memory-inspector/package.json @@ -1,6 +1,6 @@ { "name": "@theia/memory-inspector", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Memory Inspector", "keywords": [ "theia-extension" @@ -27,8 +27,8 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", "@vscode/debugprotocol": "^1.51.0", "long": "^4.0.0", "tslib": "^2.6.2" diff --git a/packages/messages/package.json b/packages/messages/package.json index 8ea723c887ccf..56cfe7f700f30 100644 --- a/packages/messages/package.json +++ b/packages/messages/package.json @@ -1,9 +1,9 @@ { "name": "@theia/messages", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Messages Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "react-perfect-scrollbar": "^1.5.3", "ts-md5": "^1.2.2", "tslib": "^2.6.2" @@ -41,7 +41,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/metrics/package.json b/packages/metrics/package.json index cc53ef925b854..422ede4ef0702 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -1,9 +1,9 @@ { "name": "@theia/metrics", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Metrics Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "prom-client": "^10.2.0", "tslib": "^2.6.2" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/mini-browser/package.json b/packages/mini-browser/package.json index 863c2f399e72f..405aab53fc71c 100644 --- a/packages/mini-browser/package.json +++ b/packages/mini-browser/package.json @@ -1,10 +1,10 @@ { "name": "@theia/mini-browser", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Mini-Browser Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", "@types/mime-types": "^2.1.0", "mime-types": "^2.1.18", "pdfobject": "^2.0.201604172", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/monaco/package.json b/packages/monaco/package.json index 93417d193a7bc..626ab8cb50145 100644 --- a/packages/monaco/package.json +++ b/packages/monaco/package.json @@ -1,15 +1,15 @@ { "name": "@theia/monaco", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Monaco Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/markers": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/markers": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/outline-view": "1.53.0", + "@theia/workspace": "1.53.0", "fast-plist": "^0.1.2", "idb": "^4.0.5", "jsonc-parser": "^2.2.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/navigator/package.json b/packages/navigator/package.json index 92480dfdf2906..e20b6b9692b2a 100644 --- a/packages/navigator/package.json +++ b/packages/navigator/package.json @@ -1,11 +1,11 @@ { "name": "@theia/navigator", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Navigator Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/notebook/package.json b/packages/notebook/package.json index 9a2f6bb8b8e9a..c62ef82bb0701 100644 --- a/packages/notebook/package.json +++ b/packages/notebook/package.json @@ -1,14 +1,14 @@ { "name": "@theia/notebook", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Notebook Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/outline-view": "1.52.0", + "@theia/outline-view": "1.53.0", "advanced-mark.js": "^2.6.0", "react-perfect-scrollbar": "^1.5.8", "tslib": "^2.6.2" @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/markdown-it": "^12.2.3", "@types/vscode-notebook-renderer": "^1.72.0" }, diff --git a/packages/outline-view/package.json b/packages/outline-view/package.json index 1d0935e68949b..b843df4db85ab 100644 --- a/packages/outline-view/package.json +++ b/packages/outline-view/package.json @@ -1,9 +1,9 @@ { "name": "@theia/outline-view", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Outline View Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,7 +39,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/output/package.json b/packages/output/package.json index 6202626d4b471..3844098b6f9fc 100644 --- a/packages/output/package.json +++ b/packages/output/package.json @@ -1,11 +1,11 @@ { "name": "@theia/output", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Output Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", "@types/p-queue": "^2.3.1", "p-queue": "^2.4.2", @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-dev/package.json b/packages/plugin-dev/package.json index 53dbfea468f0c..64e3d429d2b0c 100644 --- a/packages/plugin-dev/package.json +++ b/packages/plugin-dev/package.json @@ -1,16 +1,16 @@ { "name": "@theia/plugin-dev", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Plugin Development Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/output": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/output": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/workspace": "1.53.0", "ps-tree": "^1.2.0", "tslib": "^2.6.2" }, @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext-headless/package.json b/packages/plugin-ext-headless/package.json index 964d6cbd0704c..0a37cd93a205d 100644 --- a/packages/plugin-ext-headless/package.json +++ b/packages/plugin-ext-headless/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-ext-headless", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Headless (Backend-only) Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/core": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/terminal": "1.52.0", + "@theia/core": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/terminal": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-ext-vscode/package.json b/packages/plugin-ext-vscode/package.json index 75d95b98bb1c7..2e8ce00a71ea7 100644 --- a/packages/plugin-ext-vscode/package.json +++ b/packages/plugin-ext-vscode/package.json @@ -1,22 +1,22 @@ { "name": "@theia/plugin-ext-vscode", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Plugin Extension for VsCode", "dependencies": { - "@theia/callhierarchy": "1.52.0", - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/callhierarchy": "1.53.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.52.0", - "@theia/outline-view": "1.52.0", - "@theia/plugin": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/typehierarchy": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/navigator": "1.53.0", + "@theia/outline-view": "1.53.0", + "@theia/plugin": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/typehierarchy": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/workspace": "1.53.0", "decompress": "^4.2.1", "filenamify": "^4.1.0", "tslib": "^2.6.2" @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin-ext/package.json b/packages/plugin-ext/package.json index 43fa009a22eca..129124f12c7d1 100644 --- a/packages/plugin-ext/package.json +++ b/packages/plugin-ext/package.json @@ -1,37 +1,37 @@ { "name": "@theia/plugin-ext", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Plugin Extension", "main": "lib/common/index.js", "typings": "lib/common/index.d.ts", "dependencies": { - "@theia/bulk-edit": "1.52.0", - "@theia/callhierarchy": "1.52.0", - "@theia/console": "1.52.0", - "@theia/core": "1.52.0", - "@theia/debug": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/editor-preview": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/messages": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/bulk-edit": "1.53.0", + "@theia/callhierarchy": "1.53.0", + "@theia/console": "1.53.0", + "@theia/core": "1.53.0", + "@theia/debug": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/editor-preview": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/messages": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/navigator": "1.52.0", - "@theia/notebook": "1.52.0", - "@theia/output": "1.52.0", - "@theia/plugin": "1.52.0", - "@theia/preferences": "1.52.0", - "@theia/scm": "1.52.0", - "@theia/search-in-workspace": "1.52.0", - "@theia/task": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/test": "1.52.0", - "@theia/timeline": "1.52.0", - "@theia/typehierarchy": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/navigator": "1.53.0", + "@theia/notebook": "1.53.0", + "@theia/output": "1.53.0", + "@theia/plugin": "1.53.0", + "@theia/preferences": "1.53.0", + "@theia/scm": "1.53.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/task": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/test": "1.53.0", + "@theia/timeline": "1.53.0", + "@theia/typehierarchy": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/workspace": "1.53.0", "@types/mime": "^2.0.1", "@vscode/debugprotocol": "^1.51.0", "@vscode/proxy-agent": "^0.13.2", @@ -88,7 +88,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/decompress": "^4.2.2", "@types/escape-html": "^0.0.20", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/plugin-metrics/package.json b/packages/plugin-metrics/package.json index 3f54efcc0f54d..e94657de4a0d5 100644 --- a/packages/plugin-metrics/package.json +++ b/packages/plugin-metrics/package.json @@ -1,13 +1,13 @@ { "name": "@theia/plugin-metrics", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Plugin Metrics", "dependencies": { - "@theia/core": "1.52.0", - "@theia/metrics": "1.52.0", + "@theia/core": "1.53.0", + "@theia/metrics": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/plugin": "1.52.0", - "@theia/plugin-ext": "1.52.0", + "@theia/plugin": "1.53.0", + "@theia/plugin-ext": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 2ecf9c5d15bf8..7acbf0e09fd9e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@theia/plugin", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Plugin API", "types": "./src/theia.d.ts", "publishConfig": { @@ -27,7 +27,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 8b0513b9e860a..7963265e81868 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -1,15 +1,15 @@ { "name": "@theia/preferences", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Preferences Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/userstorage": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/userstorage": "1.53.0", + "@theia/workspace": "1.53.0", "async-mutex": "^0.3.1", "fast-deep-equal": "^3.1.3", "jsonc-parser": "^2.2.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/preview/package.json b/packages/preview/package.json index c0a876920461f..3b346f27ec6ce 100644 --- a/packages/preview/package.json +++ b/packages/preview/package.json @@ -1,12 +1,12 @@ { "name": "@theia/preview", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Preview Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/mini-browser": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/mini-browser": "1.53.0", + "@theia/monaco": "1.53.0", "@types/highlight.js": "^10.1.0", "@types/markdown-it-anchor": "^4.0.1", "highlight.js": "10.4.1", @@ -46,7 +46,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/process/package.json b/packages/process/package.json index 3b0b8d4158ac9..e1626aa212009 100644 --- a/packages/process/package.json +++ b/packages/process/package.json @@ -1,9 +1,9 @@ { "name": "@theia/process", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia process support.", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "node-pty": "0.11.0-beta24", "string-argv": "^0.1.1", "tslib": "^2.6.2" @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/property-view/package.json b/packages/property-view/package.json index 74880056d9d3e..46cc02f53cd1a 100644 --- a/packages/property-view/package.json +++ b/packages/property-view/package.json @@ -1,10 +1,10 @@ { "name": "@theia/property-view", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Property View Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/remote/package.json b/packages/remote/package.json index 246c914c5a54f..4993914bbbd03 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -1,10 +1,10 @@ { "name": "@theia/remote", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Remote", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", "archiver": "^5.3.1", "decompress": "^4.2.1", "decompress-tar": "^4.0.0", @@ -52,7 +52,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", "@types/express-http-proxy": "^1.6.3", diff --git a/packages/scm-extra/package.json b/packages/scm-extra/package.json index 675d0ef784dd6..0a83d3c53fa75 100644 --- a/packages/scm-extra/package.json +++ b/packages/scm-extra/package.json @@ -1,13 +1,13 @@ { "name": "@theia/scm-extra", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Source control extras Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/scm": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/scm": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -43,7 +43,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/scm/package.json b/packages/scm/package.json index 06a55db61c073..4a5bfd979221a 100644 --- a/packages/scm/package.json +++ b/packages/scm/package.json @@ -1,12 +1,12 @@ { "name": "@theia/scm", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Source control Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", "@types/diff": "^5.2.1", "diff": "^5.2.0", @@ -49,7 +49,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/search-in-workspace/package.json b/packages/search-in-workspace/package.json index 73facb28b55bd..ba4cde4e6bfc6 100644 --- a/packages/search-in-workspace/package.json +++ b/packages/search-in-workspace/package.json @@ -1,14 +1,14 @@ { "name": "@theia/search-in-workspace", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Search in workspace", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/process": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/process": "1.53.0", + "@theia/workspace": "1.53.0", "@vscode/ripgrep": "^1.14.2", "minimatch": "^5.1.0", "react-autosize-textarea": "^7.0.0", @@ -48,6 +48,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" } } diff --git a/packages/secondary-window/package.json b/packages/secondary-window/package.json index ad8ba480d9be1..237203d85b302 100644 --- a/packages/secondary-window/package.json +++ b/packages/secondary-window/package.json @@ -1,9 +1,9 @@ { "name": "@theia/secondary-window", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Secondary Window Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -39,6 +39,6 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" } } diff --git a/packages/task/package.json b/packages/task/package.json index 6b136daa5492e..507ff9d0724b3 100644 --- a/packages/task/package.json +++ b/packages/task/package.json @@ -1,19 +1,19 @@ { "name": "@theia/task", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Task extension. This extension adds support for executing raw or terminal processes in the backend.", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/markers": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/markers": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/process": "1.52.0", - "@theia/terminal": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/process": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/workspace": "1.53.0", "async-mutex": "^0.3.1", "jsonc-parser": "^2.2.0", "p-debounce": "^2.1.0", @@ -53,7 +53,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/terminal/package.json b/packages/terminal/package.json index 439290b6e72aa..bf9c8a435a54d 100644 --- a/packages/terminal/package.json +++ b/packages/terminal/package.json @@ -1,15 +1,15 @@ { "name": "@theia/terminal", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Terminal Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/process": "1.52.0", - "@theia/variable-resolver": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/process": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/workspace": "1.53.0", "tslib": "^2.6.2", "xterm": "^5.3.0", "xterm-addon-fit": "^0.8.0", @@ -50,7 +50,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/test/package.json b/packages/test/package.json index d1e601bf53328..e30f799f7ac30 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,13 +1,13 @@ { "name": "@theia/test", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Test Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/terminal": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/terminal": "1.53.0", "xterm": "^4.16.0", "xterm-addon-fit": "^0.5.0" }, @@ -44,7 +44,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/timeline/package.json b/packages/timeline/package.json index 760112f5bbbd9..d508da7c10aaf 100644 --- a/packages/timeline/package.json +++ b/packages/timeline/package.json @@ -1,10 +1,10 @@ { "name": "@theia/timeline", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Timeline Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/navigator": "1.52.0", + "@theia/core": "1.53.0", + "@theia/navigator": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/toolbar/package.json b/packages/toolbar/package.json index e75806cf534de..da728cf0315f3 100644 --- a/packages/toolbar/package.json +++ b/packages/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@theia/toolbar", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Toolbar", "keywords": [ "theia-extension" @@ -27,15 +27,15 @@ "watch": "theiaext watch" }, "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", - "@theia/file-search": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/monaco": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/file-search": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", "@theia/monaco-editor-core": "1.83.101", - "@theia/search-in-workspace": "1.52.0", - "@theia/userstorage": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/search-in-workspace": "1.53.0", + "@theia/userstorage": "1.53.0", + "@theia/workspace": "1.53.0", "ajv": "^6.5.3", "jsonc-parser": "^2.2.0", "perfect-scrollbar": "^1.3.0", diff --git a/packages/typehierarchy/package.json b/packages/typehierarchy/package.json index ef90ebaa08de3..d9965c61e42d0 100644 --- a/packages/typehierarchy/package.json +++ b/packages/typehierarchy/package.json @@ -1,10 +1,10 @@ { "name": "@theia/typehierarchy", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Type Hierarchy Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/editor": "1.52.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/userstorage/package.json b/packages/userstorage/package.json index 3eb1a5c484d6e..0b7cee4c41d76 100644 --- a/packages/userstorage/package.json +++ b/packages/userstorage/package.json @@ -1,10 +1,10 @@ { "name": "@theia/userstorage", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - User Storage Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -40,7 +40,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/variable-resolver/package.json b/packages/variable-resolver/package.json index 0e27cc4599295..ba8fbdafbd7cb 100644 --- a/packages/variable-resolver/package.json +++ b/packages/variable-resolver/package.json @@ -1,9 +1,9 @@ { "name": "@theia/variable-resolver", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Variable Resolver Extension", "dependencies": { - "@theia/core": "1.52.0", + "@theia/core": "1.53.0", "tslib": "^2.6.2" }, "publishConfig": { @@ -45,7 +45,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/packages/vsx-registry/package.json b/packages/vsx-registry/package.json index fa9adcd5c5f2b..86656ef7f8dac 100644 --- a/packages/vsx-registry/package.json +++ b/packages/vsx-registry/package.json @@ -1,16 +1,16 @@ { "name": "@theia/vsx-registry", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - VSX Registry", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/navigator": "1.52.0", - "@theia/ovsx-client": "1.52.0", - "@theia/plugin-ext": "1.52.0", - "@theia/plugin-ext-vscode": "1.52.0", - "@theia/preferences": "1.52.0", - "@theia/workspace": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/ovsx-client": "1.53.0", + "@theia/plugin-ext": "1.53.0", + "@theia/plugin-ext-vscode": "1.53.0", + "@theia/preferences": "1.53.0", + "@theia/workspace": "1.53.0", "limiter": "^2.1.0", "luxon": "^2.4.0", "p-debounce": "^2.1.0", @@ -55,7 +55,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0", + "@theia/ext-scripts": "1.53.0", "@types/luxon": "^2.3.2" }, "nyc": { diff --git a/packages/workspace/package.json b/packages/workspace/package.json index f361e8e65400f..85340e1e18638 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -1,11 +1,11 @@ { "name": "@theia/workspace", - "version": "1.52.0", + "version": "1.53.0", "description": "Theia - Workspace Extension", "dependencies": { - "@theia/core": "1.52.0", - "@theia/filesystem": "1.52.0", - "@theia/variable-resolver": "1.52.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/variable-resolver": "1.53.0", "jsonc-parser": "^2.2.0", "tslib": "^2.6.2", "valid-filename": "^2.0.1" @@ -47,7 +47,7 @@ "watch": "theiaext watch" }, "devDependencies": { - "@theia/ext-scripts": "1.52.0" + "@theia/ext-scripts": "1.53.0" }, "nyc": { "extends": "../../configs/nyc.json" diff --git a/sample-plugins/sample-namespace/plugin-a/package.json b/sample-plugins/sample-namespace/plugin-a/package.json index 1ab56e9efe0bb..6979f04fb5433 100644 --- a/sample-plugins/sample-namespace/plugin-a/package.json +++ b/sample-plugins/sample-namespace/plugin-a/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-a", - "version": "1.52.0", + "version": "1.53.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-b/package.json b/sample-plugins/sample-namespace/plugin-b/package.json index 89315d68ce7d2..4cb8ea0bd7577 100644 --- a/sample-plugins/sample-namespace/plugin-b/package.json +++ b/sample-plugins/sample-namespace/plugin-b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-b", - "version": "1.52.0", + "version": "1.53.0", "main": "extension.js", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { diff --git a/sample-plugins/sample-namespace/plugin-gotd/package.json b/sample-plugins/sample-namespace/plugin-gotd/package.json index 637a229bc13ef..eee86aed9b23a 100644 --- a/sample-plugins/sample-namespace/plugin-gotd/package.json +++ b/sample-plugins/sample-namespace/plugin-gotd/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "plugin-gotd", - "version": "1.52.0", + "version": "1.53.0", "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", "repository": { "type": "git", @@ -15,7 +15,7 @@ "*" ], "devDependencies": { - "@theia/api-provider-sample": "1.52.0" + "@theia/api-provider-sample": "1.53.0" }, "scripts": { "prepare": "yarn -s package", From e891c9be687dce3645614b7b75ec0dcd4c2057f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 4 Sep 2024 09:11:26 +0200 Subject: [PATCH 361/441] Remove the timeout handler when an request is handled. (#14118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14117 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- dev-packages/request/src/node-request-service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dev-packages/request/src/node-request-service.ts b/dev-packages/request/src/node-request-service.ts index e31a022bc1ef6..2a5e491ec37e8 100644 --- a/dev-packages/request/src/node-request-service.ts +++ b/dev-packages/request/src/node-request-service.ts @@ -31,7 +31,6 @@ export interface NodeRequestOptions extends RequestOptions { }; export class NodeRequestService implements RequestService { - protected proxyUrl?: string; protected strictSSL?: boolean; protected authorization?: string; @@ -107,9 +106,14 @@ export class NodeRequestService implements RequestService { opts.auth = options.user + ':' + options.password; } + const timeoutHandler = () => { + reject('timeout'); + }; + const req = rawRequest(opts, async res => { const followRedirects = options.followRedirects ?? 3; if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers.location) { + req.off('timeout', timeoutHandler); this.request({ ...options, url: res.headers.location, @@ -125,6 +129,7 @@ export class NodeRequestService implements RequestService { }); stream.on('end', () => { + req.off('timeout', timeoutHandler); const buffer = Buffer.concat(chunks); resolve({ url: options.url, @@ -146,9 +151,7 @@ export class NodeRequestService implements RequestService { reject(err); }); - req.on('timeout', () => { - reject('timeout'); - }); + req.on('timeout', timeoutHandler); if (options.timeout) { req.setTimeout(options.timeout); From 638c071b8515982af084240a93a94f3b2e0e07e3 Mon Sep 17 00:00:00 2001 From: Hanksha Date: Wed, 4 Sep 2024 09:52:16 +0200 Subject: [PATCH 362/441] Fix FileResource sometimes sending contents change (#14043) Add a lock to the write operation so that checking if the file is synced must wait for the write operation to be done. Fixes #14021 Contributed on behalf of Toro Cloud Signed-off-by: Vivien Jovet --- .../src/browser/file-resource.spec.ts | 255 ++++++++++++++++++ .../filesystem/src/browser/file-resource.ts | 12 + 2 files changed, 267 insertions(+) create mode 100644 packages/filesystem/src/browser/file-resource.spec.ts diff --git a/packages/filesystem/src/browser/file-resource.spec.ts b/packages/filesystem/src/browser/file-resource.spec.ts new file mode 100644 index 0000000000000..5a02884695185 --- /dev/null +++ b/packages/filesystem/src/browser/file-resource.spec.ts @@ -0,0 +1,255 @@ +// ***************************************************************************** +// Copyright (C) 2024 Toro Cloud Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; +let disableJSDOM = enableJSDOM(); + +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +FrontendApplicationConfigProvider.set({}); + +import { Disposable, Emitter, URI } from '@theia/core'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { FileChangesEvent, FileChangeType, FileStatWithMetadata } from '../common/files'; +import { FileResource } from './file-resource'; +import { FileService } from './file-service'; + +disableJSDOM(); + +describe.only('file-resource', () => { + const sandbox = sinon.createSandbox(); + const mockEmitter = new Emitter(); + const mockOnChangeEmitter = new Emitter(); + const mockFileService = new FileService(); + + before(() => { + disableJSDOM = enableJSDOM(); + }); + + beforeEach(() => { + sandbox.restore(); + + sandbox.stub(mockFileService, 'onDidFilesChange').get(() => + mockOnChangeEmitter.event + ); + sandbox.stub(mockFileService, 'onDidRunOperation').returns(Disposable.NULL); + sandbox.stub(mockFileService, 'watch').get(() => + mockEmitter.event + ); + sandbox.stub(mockFileService, 'onDidChangeFileSystemProviderCapabilities').get(() => + mockEmitter.event + ); + sandbox.stub(mockFileService, 'onDidChangeFileSystemProviderReadOnlyMessage').get(() => + mockEmitter.event + ); + }); + + after(() => { + disableJSDOM(); + }); + + it('should save contents and not trigger change event', async () => { + const resource = new FileResource(new URI('file://test/file.txt'), + mockFileService, { readOnly: false, shouldOpenAsText: () => Promise.resolve(true), shouldOverwrite: () => Promise.resolve(true) }); + + const onChangeSpy = sandbox.spy(); + resource.onDidChangeContents(onChangeSpy); + + const deferred = new Deferred(); + + sandbox.stub(mockFileService, 'write') + .callsFake(() => + deferred.promise + ); + + sandbox.stub(mockFileService, 'resolve') + .resolves({ + mtime: 1, + ctime: 0, + size: 0, + etag: '', + isFile: true, + isDirectory: false, + isSymbolicLink: false, + isReadonly: false, + name: 'file.txt', + resource: new URI('file://test/file.txt') + }); + + resource.saveContents!('test'); + + await new Promise(resolve => setTimeout(resolve, 0)); + + mockOnChangeEmitter.fire(new FileChangesEvent( + [{ + resource: new URI('file://test/file.txt'), + type: FileChangeType.UPDATED + }] + )); + + await new Promise(resolve => setImmediate(resolve)); + + expect(onChangeSpy.called).to.be.false; + + deferred.resolve({ + mtime: 0, + ctime: 0, + size: 0, + etag: '', + encoding: 'utf-8', + isFile: true, + isDirectory: false, + isSymbolicLink: false, + isReadonly: false, + name: 'file.txt', + resource: new URI('file://test/file.txt') + }); + + await new Promise(resolve => setImmediate(resolve)); + + expect(resource.version).to.deep.equal({ etag: '', mtime: 0, encoding: 'utf-8' }); + }); + + it('should save content changes and not trigger change event', async () => { + sandbox.stub(mockFileService, 'hasCapability').returns(true); + + const resource = new FileResource(new URI('file://test/file.txt'), + mockFileService, { readOnly: false, shouldOpenAsText: () => Promise.resolve(true), shouldOverwrite: () => Promise.resolve(true) }); + + const onChangeSpy = sandbox.spy(); + resource.onDidChangeContents(onChangeSpy); + + sandbox.stub(mockFileService, 'read') + .resolves({ + mtime: 1, + ctime: 0, + size: 0, + etag: '', + name: 'file.txt', + resource: new URI('file://test/file.txt'), + value: 'test', + encoding: 'utf-8' + }); + + await resource.readContents!(); + + const deferred = new Deferred(); + + sandbox.stub(mockFileService, 'update') + .callsFake(() => + deferred.promise + ); + + sandbox.stub(mockFileService, 'resolve') + .resolves({ + mtime: 1, + ctime: 0, + size: 0, + etag: '', + isFile: true, + isDirectory: false, + isSymbolicLink: false, + isReadonly: false, + name: 'file.txt', + resource: new URI('file://test/file.txt') + }); + + resource.saveContentChanges!([{ + range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }, + rangeLength: 0, + text: 'test' + }]); + + await new Promise(resolve => setTimeout(resolve, 0)); + + mockOnChangeEmitter.fire(new FileChangesEvent( + [{ + resource: new URI('file://test/file.txt'), + type: FileChangeType.UPDATED + }] + )); + + await new Promise(resolve => setImmediate(resolve)); + + expect(onChangeSpy.called).to.be.false; + + deferred.resolve({ + mtime: 0, + ctime: 0, + size: 0, + etag: '', + encoding: 'utf-8', + isFile: true, + isDirectory: false, + isSymbolicLink: false, + isReadonly: false, + name: 'file.txt', + resource: new URI('file://test/file.txt') + }); + + await new Promise(resolve => setImmediate(resolve)); + + expect(resource.version).to.deep.equal({ etag: '', mtime: 0, encoding: 'utf-8' }); + }); + + it('should trigger change event if file is updated and not in sync', async () => { + const resource = new FileResource(new URI('file://test/file.txt'), + mockFileService, { readOnly: false, shouldOpenAsText: () => Promise.resolve(true), shouldOverwrite: () => Promise.resolve(true) }); + + const onChangeSpy = sandbox.spy(); + resource.onDidChangeContents(onChangeSpy); + + sandbox.stub(mockFileService, 'read') + .resolves({ + mtime: 1, + ctime: 0, + size: 0, + etag: '', + name: 'file.txt', + resource: new URI('file://test/file.txt'), + value: 'test', + encoding: 'utf-8' + }); + + await resource.readContents!(); + + sandbox.stub(mockFileService, 'resolve') + .resolves({ + mtime: 2, + ctime: 0, + size: 0, + etag: '', + isFile: true, + isDirectory: false, + isSymbolicLink: false, + isReadonly: false, + name: 'file.txt', + resource: new URI('file://test/file.txt') + }); + + mockOnChangeEmitter.fire(new FileChangesEvent( + [{ + resource: new URI('file://test/file.txt'), + type: FileChangeType.UPDATED + }] + )); + + await new Promise(resolve => setImmediate(resolve)); + + expect(onChangeSpy.called).to.be.true; + }); +}); diff --git a/packages/filesystem/src/browser/file-resource.ts b/packages/filesystem/src/browser/file-resource.ts index 9cf675ce1cf64..0d148fde3688d 100644 --- a/packages/filesystem/src/browser/file-resource.ts +++ b/packages/filesystem/src/browser/file-resource.ts @@ -28,6 +28,7 @@ import { GENERAL_MAX_FILE_SIZE_MB } from './filesystem-preferences'; import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; import { nls } from '@theia/core'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; +import { Mutex } from 'async-mutex'; export interface FileResourceVersion extends ResourceVersion { readonly encoding: string; @@ -69,6 +70,8 @@ export class FileResource implements Resource { return this.options.readOnly; } + protected writingLock = new Mutex(); + constructor( readonly uri: URI, protected readonly fileService: FileService, @@ -216,6 +219,8 @@ export class FileResource implements Resource { const version = options?.version || this._version; const current = FileResourceVersion.is(version) ? version : undefined; const etag = current?.etag; + const releaseLock = await this.writingLock.acquire(); + try { const stat = await this.fileService.write(this.uri, content, { encoding: options?.encoding, @@ -237,6 +242,8 @@ export class FileResource implements Resource { throw ResourceError.OutOfSync({ message, stack, data: { uri: this.uri } }); } throw e; + } finally { + releaseLock(); } }; @@ -263,6 +270,8 @@ export class FileResource implements Resource { throw ResourceError.NotFound({ message: 'has not been read yet', data: { uri: this.uri } }); } const etag = current?.etag; + const releaseLock = await this.writingLock.acquire(); + try { const stat = await this.fileService.update(this.uri, changes, { readEncoding: current.encoding, @@ -286,6 +295,8 @@ export class FileResource implements Resource { throw ResourceError.OutOfSync({ message, stack, data: { uri: this.uri } }); } throw e; + } finally { + releaseLock(); } }; @@ -303,6 +314,7 @@ export class FileResource implements Resource { } protected async isInSync(): Promise { try { + await this.writingLock.waitForUnlock(); const stat = await this.fileService.resolve(this.uri, { resolveMetadata: true }); return !!this.version && this.version.mtime >= stat.mtime; } catch { From 0a1c1742c23de6cd07217cfd0138c4f734d21b80 Mon Sep 17 00:00:00 2001 From: pchuong <1699061+pchuong@users.noreply.github.com> Date: Thu, 5 Sep 2024 05:45:21 -0400 Subject: [PATCH 363/441] Support proxy env variables for schema catalog download (#14130) Co-authored-by: Mark Sujew --- packages/core/scripts/download-catalog.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/core/scripts/download-catalog.js b/packages/core/scripts/download-catalog.js index 11c9fb1be7a1a..50fc84a8d6f2d 100644 --- a/packages/core/scripts/download-catalog.js +++ b/packages/core/scripts/download-catalog.js @@ -21,6 +21,11 @@ new Downloader({ directory: './lib/browser', fileName: 'catalog.json', timeout: 60000, + proxy: process.env.http_proxy + || process.env.HTTP_PROXY + || process.env.https_proxy + || process.env.HTTPS_PROXY + || '', cloneFiles: false }).download(); From 19556f4d90c1b661ba53caea9b6a035a714e112d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 5 Sep 2024 14:58:15 +0200 Subject: [PATCH 364/441] Fix selection of contributed menu action argument adapters (#14132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14072 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder om> --- .../tab-bar-toolbar-menu-adapters.ts | 2 +- .../tab-bar-toolbar-registry.ts | 5 +++-- .../tab-bar-toolbar/tab-bar-toolbar-types.ts | 4 ++++ .../shell/tab-bar-toolbar/tab-bar-toolbar.tsx | 4 ++-- .../menus/plugin-menu-command-adapter.ts | 22 ++++++++++++++++++- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts index 76edc12e0a1f9..261fbd4bbf9f5 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.ts @@ -20,7 +20,7 @@ import { NAVIGATION, RenderedToolbarItem } from './tab-bar-toolbar-types'; export const TOOLBAR_WRAPPER_ID_SUFFIX = '-as-tabbar-toolbar-item'; export class ToolbarMenuNodeWrapper implements RenderedToolbarItem { - constructor(protected readonly menuNode: MenuNode, readonly group?: string, readonly menuPath?: MenuPath) { } + constructor(protected readonly menuNode: MenuNode, readonly group: string | undefined, readonly delegateMenuPath: MenuPath, readonly menuPath?: MenuPath) { } get id(): string { return this.menuNode.id + TOOLBAR_WRAPPER_ID_SUFFIX; } get command(): string { return this.menuNode.command ?? ''; }; get icon(): string | undefined { return this.menuNode.icon; } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts index 5851830895b23..e10afb4a0c09e 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts @@ -116,11 +116,12 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { for (const grandchild of child.children) { if (!grandchild.when || this.contextKeyService.match(grandchild.when, widget.node)) { const menuPath = this.menuRegistry.getPath(grandchild); - result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, menuPath)); + result.push(new ToolbarMenuNodeWrapper(grandchild, child.id, delegate.menuPath, menuPath)); } } } else if (child.command) { - result.push(new ToolbarMenuNodeWrapper(child, '')); + const menuPath = this.menuRegistry.getPath(child); + result.push(new ToolbarMenuNodeWrapper(child, undefined, delegate.menuPath, menuPath)); } } } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts index e59f9f63c2384..c9db6e3b18027 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts @@ -73,6 +73,10 @@ export interface TabBarToolbarItemBase { * If no command is present, this menu will be opened. */ menuPath?: MenuPath; + /** + * The path of the menu delegate that contributed this toolbar item + */ + delegateMenuPath?: MenuPath; contextKeyOverlays?: Record; /** * Optional ordering string for placing the item within its group diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx index 6d4f21b3d3264..e5c65095477b0 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx @@ -405,8 +405,8 @@ export class TabBarToolbar extends ReactWidget { return; } - if (item.command && item.menuPath) { - this.menuCommandExecutor.executeCommand(item.menuPath, item.command, this.current); + if (item.command && item.delegateMenuPath) { + this.menuCommandExecutor.executeCommand(item.delegateMenuPath, item.command, this.current); } else if (item.command) { this.commands.executeCommand(item.command, this.current); } else if (item.menuPath) { diff --git a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts index ec91bc9ef7c95..9e79ae892cd96 100644 --- a/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts +++ b/packages/plugin-ext/src/main/browser/menus/plugin-menu-command-adapter.ts @@ -169,7 +169,27 @@ export class PluginMenuCommandAdapter implements MenuCommandAdapter { } protected getArgumentAdapterForMenu(menuPath: MenuPath): ArgumentAdapter | undefined { - return this.argumentAdapters.get(menuPath.join(this.separator)); + let result; + let length = 0; + for (const [key, value] of this.argumentAdapters.entries()) { + const candidate = key.split(this.separator); + if (this.isPrefixOf(candidate, menuPath) && candidate.length > length) { + result = value; + length = candidate.length; + } + } + return result; + } + isPrefixOf(candidate: string[], menuPath: MenuPath): boolean { + if (candidate.length > menuPath.length) { + return false; + } + for (let i = 0; i < candidate.length; i++) { + if (candidate[i] !== menuPath[i]) { + return false; + } + } + return true; } protected addArgumentAdapter(menuPath: MenuPath, adapter: ArgumentAdapter): void { From a55af20f988e4bd212ef61d4ca53e4b24bfe53a3 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Tue, 13 Aug 2024 16:57:47 +0200 Subject: [PATCH 365/441] Theia AI LLM Support [Experimental] Implements AI LLM support via optionally consumable Theia extensions. The base functionality is provided by the following extensions: - @theia/ai-core - @theia/ai-chat - @theia/ai-chat-ui 'ai-core' contains the basic LLM integration and defines the core concepts for interacting with LLM via agents, prompts and variables. 'ai-chat' builts on top to define a model for chat like conversations. 'ai-chat-ui' provides the actual Chat UI. The AI integration was built from the ground up to be flexible, inspectible, customizable and configurable. This feature is still highly experimental. Therefore, even when the AI extensions are included in a Theia based application, they are turned off by default and need to be enabled in the preferences. The preferences include a convenient "Turn all AI features on/off" setting. Additional features and integrations are offered by the remaining extensions: - @theia/ai-history - @theia/ai-code-completion - @theia/ai-terminal - @theia/ai-workspace-agent - @theia/ai-openai 'ai-history' offers a service to record requests and responses. The recordings can be inspected via the 'AI History View'. 'ai-code-completion' offers AI based code completion via completion items and inline suggestions. 'ai-terminal' offers a specialized AI for the Theia terminal which will suggest commands to execute. 'ai-workspace-agent' is a specialized agent which is able to inspect the current workspace content for context specific questions. 'ai-openai' integrates the LLM offerings of Open AI into Theia. Co-authored-by: Alexandra Muntean Co-authored-by: Camille Letavernier Co-authored-by: Christian W. Damus Co-authored-by: Eugen Neufeld Co-authored-by: Haydar Metin Co-authored-by: Johannes Faltermeier Co-authored-by: Jonas Helming Co-authored-by: Lucas Koehler Co-authored-by: Martin Fleck Co-authored-by: Maximilian Koegel Co-authored-by: Nina Doschek Co-authored-by: Olaf Lessenich Co-authored-by: Philip Langer Co-authored-by: Remi Schnekenburger Co-authored-by: Simon Graband Co-authored-by: Tobias Ortmayr --- examples/browser-only/package.json | 6 + examples/browser-only/tsconfig.json | 18 + examples/browser/package.json | 8 + examples/browser/tsconfig.json | 24 + examples/electron/package.json | 8 + examples/electron/tsconfig.json | 24 + packages/ai-chat-ui/.eslintrc.js | 10 + packages/ai-chat-ui/README.md | 32 + packages/ai-chat-ui/package.json | 58 ++ .../src/browser/ai-chat-ui-contribution.ts | 171 ++++ .../src/browser/ai-chat-ui-frontend-module.ts | 99 +++ .../src/browser/chat-input-widget.tsx | 247 ++++++ .../browser/chat-response-part-renderer.ts | 25 + .../ai-editor-manager.ts | 183 +++++ .../code-part-renderer.tsx | 208 +++++ .../command-part-renderer.tsx | 60 ++ .../error-part-renderer.tsx | 35 + .../horizontal-layout-part-renderer.tsx | 59 ++ .../browser/chat-response-renderer/index.ts | 23 + .../markdown-part-renderer.tsx | 71 ++ .../text-part-renderer.spec.ts | 50 ++ .../text-part-renderer.tsx | 35 + .../toolcall-part-renderer.tsx | 49 ++ .../chat-view-tree-container.ts | 32 + .../chat-tree-view/chat-view-tree-widget.tsx | 368 +++++++++ .../src/browser/chat-tree-view/index.ts | 18 + .../src/browser/chat-view-commands.ts | 45 + .../src/browser/chat-view-contribution.ts | 154 ++++ .../chat-view-language-contribution.ts | 141 ++++ .../chat-view-widget-toolbar-contribution.tsx | 54 ++ .../src/browser/chat-view-widget.tsx | 194 +++++ .../ai-chat-ui/src/browser/style/index.css | 309 +++++++ packages/ai-chat-ui/tsconfig.json | 37 + packages/ai-chat/.eslintrc.js | 10 + packages/ai-chat/README.md | 30 + packages/ai-chat/package.json | 53 ++ .../src/browser/ai-chat-frontend-module.ts | 66 ++ .../src/browser/ai-chat-preferences.ts | 31 + .../src/browser/frontend-chat-service.ts | 66 ++ .../ai-chat/src/common/chat-agent-service.ts | 85 ++ .../chat-agents-variable-contribution.ts | 81 ++ packages/ai-chat/src/common/chat-agents.ts | 383 +++++++++ packages/ai-chat/src/common/chat-model.ts | 769 ++++++++++++++++++ .../src/common/chat-request-parser.spec.ts | 120 +++ .../ai-chat/src/common/chat-request-parser.ts | 220 +++++ packages/ai-chat/src/common/chat-service.ts | 225 +++++ .../ai-chat/src/common/command-chat-agents.ts | 343 ++++++++ packages/ai-chat/src/common/index.ts | 24 + .../src/common/orchestrator-chat-agent.ts | 146 ++++ .../ai-chat/src/common/parsed-chat-request.ts | 112 +++ .../src/common/universal-chat-agent.ts | 104 +++ packages/ai-chat/tsconfig.json | 28 + packages/ai-code-completion/.eslintrc.js | 10 + packages/ai-code-completion/README.md | 30 + packages/ai-code-completion/package.json | 54 ++ .../ai-code-completion-frontend-module.ts | 39 + .../browser/ai-code-completion-preference.ts | 46 ++ .../browser/ai-code-completion-provider.ts | 84 ++ ...-code-frontend-application-contribution.ts | 73 ++ .../ai-code-inline-completion-provider.ts | 43 + .../ai-code-completion/src/browser/index.ts | 18 + .../src/common/code-completion-agent.ts | 154 ++++ .../ai-code-completion/src/package.spec.ts | 28 + packages/ai-code-completion/tsconfig.json | 28 + packages/ai-core/.eslintrc.js | 10 + packages/ai-core/README.md | 30 + .../data/prompttemplate.tmLanguage.json | 52 ++ packages/ai-core/package.json | 58 ++ .../src/browser/ai-activation-service.ts | 52 ++ .../src/browser/ai-command-handler-factory.ts | 20 + .../agent-configuration-widget.tsx | 154 ++++ .../ai-configuration-service.ts | 43 + .../ai-configuration-view-contribution.ts | 54 ++ .../ai-configuration-widget.tsx | 80 ++ .../language-model-renderer.tsx | 113 +++ .../template-settings-renderer.tsx | 39 + .../variable-configuration-widget.tsx | 110 +++ ...-core-frontend-application-contribution.ts | 40 + .../src/browser/ai-core-frontend-module.ts | 160 ++++ .../src/browser/ai-core-preferences.ts | 74 ++ .../src/browser/ai-settings-service.ts | 56 ++ .../src/browser/ai-view-contribution.ts | 77 ++ .../frontend-language-model-registry.ts | 405 +++++++++ .../frontend-prompt-customization-service.ts | 191 +++++ .../src/browser/frontend-variable-service.ts | 26 + packages/ai-core/src/browser/index.ts | 26 + .../browser/prompttemplate-contribution.ts | 250 ++++++ packages/ai-core/src/browser/style/index.css | 80 ++ .../browser/theia-variable-contribution.ts | 58 ++ packages/ai-core/src/common/agent-service.ts | 92 +++ packages/ai-core/src/common/agent.ts | 48 ++ .../common/agents-variable-contribution.ts | 64 ++ .../common/communication-recording-service.ts | 44 + packages/ai-core/src/common/index.ts | 29 + .../src/common/language-model-delegate.ts | 45 + .../ai-core/src/common/language-model-util.ts | 67 ++ .../ai-core/src/common/language-model.spec.ts | 86 ++ packages/ai-core/src/common/language-model.ts | 238 ++++++ .../ai-core/src/common/prompt-service-util.ts | 21 + .../ai-core/src/common/prompt-service.spec.ts | 98 +++ packages/ai-core/src/common/prompt-service.ts | 208 +++++ packages/ai-core/src/common/protocol.ts | 23 + .../src/common/today-variable-contribution.ts | 67 ++ .../common/tomorrow-variable-contribution.ts | 66 ++ .../src/common/tool-invocation-registry.ts | 79 ++ .../ai-core/src/common/variable-service.ts | 177 ++++ .../src/node/ai-core-backend-module.ts | 83 ++ .../node/backend-language-model-registry.ts | 60 ++ .../node/language-model-frontend-delegate.ts | 116 +++ packages/ai-core/tsconfig.json | 34 + packages/ai-history/.eslintrc.js | 10 + packages/ai-history/README.md | 31 + packages/ai-history/package.json | 53 ++ .../browser/ai-history-communication-card.tsx | 48 ++ .../src/browser/ai-history-contribution.ts | 52 ++ .../src/browser/ai-history-frontend-module.ts | 41 + .../src/browser/ai-history-widget.tsx | 96 +++ .../src/browser/style/ai-history.css | 75 ++ .../communication-recording-service.spec.ts | 37 + .../common/communication-recording-service.ts | 63 ++ packages/ai-history/src/common/index.ts | 17 + packages/ai-history/tsconfig.json | 28 + packages/ai-openai/.eslintrc.js | 10 + packages/ai-openai/README.md | 31 + packages/ai-openai/package.json | 53 ++ ...penai-frontend-application-contribution.ts | 60 ++ .../src/browser/openai-frontend-module.ts | 31 + .../src/browser/openai-preferences.ts | 40 + packages/ai-openai/src/common/index.ts | 16 + .../common/openai-language-models-manager.ts | 23 + .../src/node/openai-backend-module.ts | 30 + .../src/node/openai-language-model.ts | 185 +++++ .../openai-language-models-manager-impl.ts | 58 ++ packages/ai-openai/src/package.spec.ts | 28 + packages/ai-openai/tsconfig.json | 25 + packages/ai-terminal/.eslintrc.js | 10 + packages/ai-terminal/README.md | 31 + packages/ai-terminal/package.json | 51 ++ .../src/browser/ai-terminal-agent.ts | 196 +++++ .../src/browser/ai-terminal-contribution.ts | 193 +++++ .../browser/ai-terminal-frontend-module.ts | 34 + .../src/browser/style/ai-terminal.css | 94 +++ packages/ai-terminal/src/package.spec.ts | 28 + packages/ai-terminal/tsconfig.json | 25 + packages/ai-workspace-agent/.eslintrc.js | 10 + packages/ai-workspace-agent/README.md | 30 + packages/ai-workspace-agent/package.json | 53 ++ .../src/browser/frontend-module.ts | 28 + .../src/browser/functions.ts | 134 +++ .../src/browser/workspace-agent.ts | 48 ++ .../src/common/functions.ts | 17 + .../ai-workspace-agent/src/common/template.ts | 29 + .../ai-workspace-agent/src/package.spec.ts | 28 + packages/ai-workspace-agent/tsconfig.json | 40 + .../src/browser/widgets/extractable-widget.ts | 2 +- packages/editor/src/browser/editor-manager.ts | 2 +- .../browser/editor-variable-contribution.ts | 10 +- .../src/browser/getting-started-widget.tsx | 96 ++- .../src/browser/style/index.css | 20 + .../src/browser/util/preference-layout.ts | 4 + .../src/browser/terminal-link-provider.ts | 2 +- tsconfig.json | 24 + yarn.lock | 220 +++-- 163 files changed, 12826 insertions(+), 135 deletions(-) create mode 100644 packages/ai-chat-ui/.eslintrc.js create mode 100644 packages/ai-chat-ui/README.md create mode 100644 packages/ai-chat-ui/package.json create mode 100644 packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts create mode 100644 packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-input-widget.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-part-renderer.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/ai-editor-manager.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/command-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/error-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/horizontal-layout-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/index.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.spec.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-container.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-tree-view/index.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-view-commands.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-view-contribution.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-view-language-contribution.ts create mode 100644 packages/ai-chat-ui/src/browser/chat-view-widget-toolbar-contribution.tsx create mode 100644 packages/ai-chat-ui/src/browser/chat-view-widget.tsx create mode 100644 packages/ai-chat-ui/src/browser/style/index.css create mode 100644 packages/ai-chat-ui/tsconfig.json create mode 100644 packages/ai-chat/.eslintrc.js create mode 100644 packages/ai-chat/README.md create mode 100644 packages/ai-chat/package.json create mode 100644 packages/ai-chat/src/browser/ai-chat-frontend-module.ts create mode 100644 packages/ai-chat/src/browser/ai-chat-preferences.ts create mode 100644 packages/ai-chat/src/browser/frontend-chat-service.ts create mode 100644 packages/ai-chat/src/common/chat-agent-service.ts create mode 100644 packages/ai-chat/src/common/chat-agents-variable-contribution.ts create mode 100644 packages/ai-chat/src/common/chat-agents.ts create mode 100644 packages/ai-chat/src/common/chat-model.ts create mode 100644 packages/ai-chat/src/common/chat-request-parser.spec.ts create mode 100644 packages/ai-chat/src/common/chat-request-parser.ts create mode 100644 packages/ai-chat/src/common/chat-service.ts create mode 100644 packages/ai-chat/src/common/command-chat-agents.ts create mode 100644 packages/ai-chat/src/common/index.ts create mode 100644 packages/ai-chat/src/common/orchestrator-chat-agent.ts create mode 100644 packages/ai-chat/src/common/parsed-chat-request.ts create mode 100644 packages/ai-chat/src/common/universal-chat-agent.ts create mode 100644 packages/ai-chat/tsconfig.json create mode 100644 packages/ai-code-completion/.eslintrc.js create mode 100644 packages/ai-code-completion/README.md create mode 100644 packages/ai-code-completion/package.json create mode 100644 packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts create mode 100644 packages/ai-code-completion/src/browser/ai-code-completion-preference.ts create mode 100644 packages/ai-code-completion/src/browser/ai-code-completion-provider.ts create mode 100644 packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts create mode 100644 packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts create mode 100644 packages/ai-code-completion/src/browser/index.ts create mode 100644 packages/ai-code-completion/src/common/code-completion-agent.ts create mode 100644 packages/ai-code-completion/src/package.spec.ts create mode 100644 packages/ai-code-completion/tsconfig.json create mode 100644 packages/ai-core/.eslintrc.js create mode 100644 packages/ai-core/README.md create mode 100644 packages/ai-core/data/prompttemplate.tmLanguage.json create mode 100644 packages/ai-core/package.json create mode 100644 packages/ai-core/src/browser/ai-activation-service.ts create mode 100644 packages/ai-core/src/browser/ai-command-handler-factory.ts create mode 100644 packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx create mode 100644 packages/ai-core/src/browser/ai-configuration/ai-configuration-service.ts create mode 100644 packages/ai-core/src/browser/ai-configuration/ai-configuration-view-contribution.ts create mode 100644 packages/ai-core/src/browser/ai-configuration/ai-configuration-widget.tsx create mode 100644 packages/ai-core/src/browser/ai-configuration/language-model-renderer.tsx create mode 100644 packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx create mode 100644 packages/ai-core/src/browser/ai-configuration/variable-configuration-widget.tsx create mode 100644 packages/ai-core/src/browser/ai-core-frontend-application-contribution.ts create mode 100644 packages/ai-core/src/browser/ai-core-frontend-module.ts create mode 100644 packages/ai-core/src/browser/ai-core-preferences.ts create mode 100644 packages/ai-core/src/browser/ai-settings-service.ts create mode 100644 packages/ai-core/src/browser/ai-view-contribution.ts create mode 100644 packages/ai-core/src/browser/frontend-language-model-registry.ts create mode 100644 packages/ai-core/src/browser/frontend-prompt-customization-service.ts create mode 100644 packages/ai-core/src/browser/frontend-variable-service.ts create mode 100644 packages/ai-core/src/browser/index.ts create mode 100644 packages/ai-core/src/browser/prompttemplate-contribution.ts create mode 100644 packages/ai-core/src/browser/style/index.css create mode 100644 packages/ai-core/src/browser/theia-variable-contribution.ts create mode 100644 packages/ai-core/src/common/agent-service.ts create mode 100644 packages/ai-core/src/common/agent.ts create mode 100644 packages/ai-core/src/common/agents-variable-contribution.ts create mode 100644 packages/ai-core/src/common/communication-recording-service.ts create mode 100644 packages/ai-core/src/common/index.ts create mode 100644 packages/ai-core/src/common/language-model-delegate.ts create mode 100644 packages/ai-core/src/common/language-model-util.ts create mode 100644 packages/ai-core/src/common/language-model.spec.ts create mode 100644 packages/ai-core/src/common/language-model.ts create mode 100644 packages/ai-core/src/common/prompt-service-util.ts create mode 100644 packages/ai-core/src/common/prompt-service.spec.ts create mode 100644 packages/ai-core/src/common/prompt-service.ts create mode 100644 packages/ai-core/src/common/protocol.ts create mode 100644 packages/ai-core/src/common/today-variable-contribution.ts create mode 100644 packages/ai-core/src/common/tomorrow-variable-contribution.ts create mode 100644 packages/ai-core/src/common/tool-invocation-registry.ts create mode 100644 packages/ai-core/src/common/variable-service.ts create mode 100644 packages/ai-core/src/node/ai-core-backend-module.ts create mode 100644 packages/ai-core/src/node/backend-language-model-registry.ts create mode 100644 packages/ai-core/src/node/language-model-frontend-delegate.ts create mode 100644 packages/ai-core/tsconfig.json create mode 100644 packages/ai-history/.eslintrc.js create mode 100644 packages/ai-history/README.md create mode 100644 packages/ai-history/package.json create mode 100644 packages/ai-history/src/browser/ai-history-communication-card.tsx create mode 100644 packages/ai-history/src/browser/ai-history-contribution.ts create mode 100644 packages/ai-history/src/browser/ai-history-frontend-module.ts create mode 100644 packages/ai-history/src/browser/ai-history-widget.tsx create mode 100644 packages/ai-history/src/browser/style/ai-history.css create mode 100644 packages/ai-history/src/common/communication-recording-service.spec.ts create mode 100644 packages/ai-history/src/common/communication-recording-service.ts create mode 100644 packages/ai-history/src/common/index.ts create mode 100644 packages/ai-history/tsconfig.json create mode 100644 packages/ai-openai/.eslintrc.js create mode 100644 packages/ai-openai/README.md create mode 100644 packages/ai-openai/package.json create mode 100644 packages/ai-openai/src/browser/openai-frontend-application-contribution.ts create mode 100644 packages/ai-openai/src/browser/openai-frontend-module.ts create mode 100644 packages/ai-openai/src/browser/openai-preferences.ts create mode 100644 packages/ai-openai/src/common/index.ts create mode 100644 packages/ai-openai/src/common/openai-language-models-manager.ts create mode 100644 packages/ai-openai/src/node/openai-backend-module.ts create mode 100644 packages/ai-openai/src/node/openai-language-model.ts create mode 100644 packages/ai-openai/src/node/openai-language-models-manager-impl.ts create mode 100644 packages/ai-openai/src/package.spec.ts create mode 100644 packages/ai-openai/tsconfig.json create mode 100644 packages/ai-terminal/.eslintrc.js create mode 100644 packages/ai-terminal/README.md create mode 100644 packages/ai-terminal/package.json create mode 100644 packages/ai-terminal/src/browser/ai-terminal-agent.ts create mode 100644 packages/ai-terminal/src/browser/ai-terminal-contribution.ts create mode 100644 packages/ai-terminal/src/browser/ai-terminal-frontend-module.ts create mode 100644 packages/ai-terminal/src/browser/style/ai-terminal.css create mode 100644 packages/ai-terminal/src/package.spec.ts create mode 100644 packages/ai-terminal/tsconfig.json create mode 100644 packages/ai-workspace-agent/.eslintrc.js create mode 100644 packages/ai-workspace-agent/README.md create mode 100644 packages/ai-workspace-agent/package.json create mode 100644 packages/ai-workspace-agent/src/browser/frontend-module.ts create mode 100644 packages/ai-workspace-agent/src/browser/functions.ts create mode 100644 packages/ai-workspace-agent/src/browser/workspace-agent.ts create mode 100644 packages/ai-workspace-agent/src/common/functions.ts create mode 100644 packages/ai-workspace-agent/src/common/template.ts create mode 100644 packages/ai-workspace-agent/src/package.spec.ts create mode 100644 packages/ai-workspace-agent/tsconfig.json diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index caf6e3b12f115..e1ed7399bd982 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -15,6 +15,12 @@ } }, "dependencies": { + "@theia/ai-chat": "1.53.0", + "@theia/ai-chat-ui": "1.53.0", + "@theia/ai-code-completion": "1.53.0", + "@theia/ai-core": "1.53.0", + "@theia/ai-history": "1.53.0", + "@theia/ai-openai": "1.53.0", "@theia/api-samples": "1.53.0", "@theia/bulk-edit": "1.53.0", "@theia/callhierarchy": "1.53.0", diff --git a/examples/browser-only/tsconfig.json b/examples/browser-only/tsconfig.json index eef920de150af..2d8d29a0d1fa1 100644 --- a/examples/browser-only/tsconfig.json +++ b/examples/browser-only/tsconfig.json @@ -8,6 +8,24 @@ { "path": "../../dev-packages/cli" }, + { + "path": "../../packages/ai-chat" + }, + { + "path": "../../packages/ai-chat-ui" + }, + { + "path": "../../packages/ai-code-completion" + }, + { + "path": "../../packages/ai-core" + }, + { + "path": "../../packages/ai-history" + }, + { + "path": "../../packages/ai-openai" + }, { "path": "../../packages/bulk-edit" }, diff --git a/examples/browser/package.json b/examples/browser/package.json index ab928d6b29f2e..e6b387674d0b7 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -20,6 +20,14 @@ } }, "dependencies": { + "@theia/ai-chat": "1.53.0", + "@theia/ai-chat-ui": "1.53.0", + "@theia/ai-code-completion": "1.53.0", + "@theia/ai-core": "1.53.0", + "@theia/ai-history": "1.53.0", + "@theia/ai-openai": "1.53.0", + "@theia/ai-terminal": "1.53.0", + "@theia/ai-workspace-agent": "1.53.0", "@theia/api-provider-sample": "1.53.0", "@theia/api-samples": "1.53.0", "@theia/bulk-edit": "1.53.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index 60de2a565c967..ed5f00cd84c77 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -8,6 +8,30 @@ { "path": "../../dev-packages/cli" }, + { + "path": "../../packages/ai-chat" + }, + { + "path": "../../packages/ai-chat-ui" + }, + { + "path": "../../packages/ai-code-completion" + }, + { + "path": "../../packages/ai-core" + }, + { + "path": "../../packages/ai-history" + }, + { + "path": "../../packages/ai-openai" + }, + { + "path": "../../packages/ai-terminal" + }, + { + "path": "../../packages/ai-workspace-agent" + }, { "path": "../../packages/bulk-edit" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index 16386e54eb76f..16c72e673eef9 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -26,6 +26,14 @@ } }, "dependencies": { + "@theia/ai-chat": "1.53.0", + "@theia/ai-chat-ui": "1.53.0", + "@theia/ai-code-completion": "1.53.0", + "@theia/ai-core": "1.53.0", + "@theia/ai-history": "1.53.0", + "@theia/ai-openai": "1.53.0", + "@theia/ai-terminal": "1.53.0", + "@theia/ai-workspace-agent": "1.53.0", "@theia/api-provider-sample": "1.53.0", "@theia/api-samples": "1.53.0", "@theia/bulk-edit": "1.53.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index 8e8e306fc9c1a..b67a624574045 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -11,6 +11,30 @@ { "path": "../../dev-packages/cli" }, + { + "path": "../../packages/ai-chat" + }, + { + "path": "../../packages/ai-chat-ui" + }, + { + "path": "../../packages/ai-code-completion" + }, + { + "path": "../../packages/ai-core" + }, + { + "path": "../../packages/ai-history" + }, + { + "path": "../../packages/ai-openai" + }, + { + "path": "../../packages/ai-terminal" + }, + { + "path": "../../packages/ai-workspace-agent" + }, { "path": "../../packages/bulk-edit" }, diff --git a/packages/ai-chat-ui/.eslintrc.js b/packages/ai-chat-ui/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-chat-ui/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-chat-ui/README.md b/packages/ai-chat-ui/README.md new file mode 100644 index 0000000000000..3638d69df5491 --- /dev/null +++ b/packages/ai-chat-ui/README.md @@ -0,0 +1,32 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Chat UI EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-chat-ui` extension contributes the `AI Chat` view.\ +The `AI Chat view` can be used to easily communicate with a language model. + +It is based on `@theia/ai-chat`. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-chat-ui/package.json b/packages/ai-chat-ui/package.json new file mode 100644 index 0000000000000..895049bd2d70e --- /dev/null +++ b/packages/ai-chat-ui/package.json @@ -0,0 +1,58 @@ +{ + "name": "@theia/ai-chat-ui", + "version": "1.53.0", + "description": "Theia - AI Chat UI Extension", + "dependencies": { + "@theia/ai-core": "1.53.0", + "@theia/ai-chat": "1.53.0", + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", + "@theia/monaco-editor-core": "1.83.101", + "@theia/editor-preview": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-chat-ui-frontend-module", + "secondaryWindow": "lib/browser/ai-chat-ui-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts b/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts new file mode 100644 index 0000000000000..3c7c25eac7472 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/ai-chat-ui-contribution.ts @@ -0,0 +1,171 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CommandRegistry, QuickInputButton, QuickInputService, QuickPickItem } from '@theia/core'; +import { Widget } from '@theia/core/lib/browser'; +import { AI_CHAT_NEW_CHAT_WINDOW_COMMAND, AI_CHAT_SHOW_CHATS_COMMAND, ChatCommands } from './chat-view-commands'; +import { ChatAgentLocation, ChatService } from '@theia/ai-chat'; +import { AbstractViewContribution } from '@theia/core/lib/browser/shell/view-contribution'; +import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { ChatViewWidget } from './chat-view-widget'; +import { Deferred } from '@theia/core/lib/common/promise-util'; +import { SecondaryWindowHandler } from '@theia/core/lib/browser/secondary-window-handler'; + +export const AI_CHAT_TOGGLE_COMMAND_ID = 'aiChat:toggle'; + +@injectable() +export class AIChatContribution extends AbstractViewContribution implements TabBarToolbarContribution { + + @inject(ChatService) + protected readonly chatService: ChatService; + @inject(QuickInputService) + protected readonly quickInputService: QuickInputService; + + protected static readonly REMOVE_CHAT_BUTTON: QuickInputButton = { + iconClass: 'codicon-remove-close', + tooltip: 'Remove Chat', + }; + + @inject(SecondaryWindowHandler) + protected readonly secondaryWindowHandler: SecondaryWindowHandler; + + constructor() { + super({ + widgetId: ChatViewWidget.ID, + widgetName: ChatViewWidget.LABEL, + defaultWidgetOptions: { + area: 'left', + rank: 100 + }, + toggleCommandId: AI_CHAT_TOGGLE_COMMAND_ID, + toggleKeybinding: 'ctrlcmd+shift+e' + }); + } + + override registerCommands(registry: CommandRegistry): void { + super.registerCommands(registry); + registry.registerCommand(ChatCommands.SCROLL_LOCK_WIDGET, { + isEnabled: widget => this.withWidget(widget, chatWidget => !chatWidget.isLocked), + isVisible: widget => this.withWidget(widget, chatWidget => !chatWidget.isLocked), + execute: widget => this.withWidget(widget, chatWidget => { + chatWidget.lock(); + return true; + }) + }); + registry.registerCommand(ChatCommands.SCROLL_UNLOCK_WIDGET, { + isEnabled: widget => this.withWidget(widget, chatWidget => chatWidget.isLocked), + isVisible: widget => this.withWidget(widget, chatWidget => chatWidget.isLocked), + execute: widget => this.withWidget(widget, chatWidget => { + chatWidget.unlock(); + return true; + }) + }); + registry.registerCommand(AI_CHAT_NEW_CHAT_WINDOW_COMMAND, { + execute: () => this.chatService.createSession(ChatAgentLocation.Panel, { focus: true }), + isEnabled: widget => this.withWidget(widget, () => true), + isVisible: widget => this.withWidget(widget, () => true), + }); + registry.registerCommand(AI_CHAT_SHOW_CHATS_COMMAND, { + execute: () => this.selectChat(), + isEnabled: widget => this.withWidget(widget, () => true) && this.chatService.getSessions().length > 1, + isVisible: widget => this.withWidget(widget, () => true) + }); + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem({ + id: AI_CHAT_NEW_CHAT_WINDOW_COMMAND.id, + command: AI_CHAT_NEW_CHAT_WINDOW_COMMAND.id, + tooltip: 'New Chat', + isVisible: widget => this.isChatViewWidget(widget) + }); + registry.registerItem({ + id: AI_CHAT_SHOW_CHATS_COMMAND.id, + command: AI_CHAT_SHOW_CHATS_COMMAND.id, + tooltip: 'Show Chats...', + isVisible: widget => this.isChatViewWidget(widget), + }); + } + + protected isChatViewWidget(widget?: Widget): boolean { + return !!widget && ChatViewWidget.ID === widget.id; + } + + protected async selectChat(sessionId?: string): Promise { + let activeSessionId = sessionId; + + if (!activeSessionId) { + const item = await this.askForChatSession(); + if (item === undefined) { + return; + } + activeSessionId = item.id; + } + + this.chatService.setActiveSession(activeSessionId!, { focus: true }); + } + + protected askForChatSession(): Promise { + const getItems = () => + this.chatService.getSessions().filter(session => !session.isActive).map(session => ({ + label: session.title ?? 'New Chat', + id: session.id, + buttons: [AIChatContribution.REMOVE_CHAT_BUTTON] + })).reverse(); + + const defer = new Deferred(); + const quickPick = this.quickInputService.createQuickPick(); + quickPick.placeholder = 'Select chat'; + quickPick.canSelectMany = false; + quickPick.items = getItems(); + + quickPick.onDidTriggerItemButton(async context => { + this.chatService.deleteSession(context.item.id!); + quickPick.items = getItems(); + if (this.chatService.getSessions().length <= 1) { + quickPick.hide(); + } + }); + + quickPick.onDidAccept(() => { + const selectedItem = quickPick.selectedItems[0]; + defer.resolve(selectedItem); + quickPick.hide(); + }); + + quickPick.onDidHide(() => defer.resolve(undefined)); + + quickPick.show(); + + return defer.promise; + } + + protected withWidget( + widget: Widget | undefined = this.tryGetWidget(), + predicate: (output: ChatViewWidget) => boolean = () => true + ): boolean | false { + return widget instanceof ChatViewWidget ? predicate(widget) : false; + } + + protected extractChatView(chatView: ChatViewWidget): void { + this.secondaryWindowHandler.moveWidgetToSecondaryWindow(chatView); + } + + canExtractChatView(chatView: ChatViewWidget): boolean { + return !chatView.secondaryWindow; + } +} diff --git a/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts new file mode 100644 index 0000000000000..7c2c81f6a3e18 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts @@ -0,0 +1,99 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { bindContributionProvider, CommandContribution, MenuContribution } from '@theia/core'; +import { bindViewContribution, FrontendApplicationContribution, WidgetFactory, } from '@theia/core/lib/browser'; +import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; +import { EditorManager } from '@theia/editor/lib/browser'; +import '../../src/browser/style/index.css'; +import { AIChatContribution } from './ai-chat-ui-contribution'; +import { AIChatInputWidget } from './chat-input-widget'; +import { CodePartRenderer, CommandPartRenderer, HorizontalLayoutPartRenderer, MarkdownPartRenderer, ErrorPartRenderer, ToolCallPartRenderer } from './chat-response-renderer'; +import { + AIEditorManager, AIEditorSelectionResolver, + GitHubSelectionResolver, TextFragmentSelectionResolver, TypeDocSymbolSelectionResolver +} from './chat-response-renderer/ai-editor-manager'; +import { createChatViewTreeWidget } from './chat-tree-view'; +import { ChatViewTreeWidget } from './chat-tree-view/chat-view-tree-widget'; +import { ChatViewLanguageContribution } from './chat-view-language-contribution'; +import { ChatViewMenuContribution } from './chat-view-contribution'; +import { ChatViewWidget } from './chat-view-widget'; +import { ChatViewWidgetToolbarContribution } from './chat-view-widget-toolbar-contribution'; +import { ChatResponsePartRenderer } from './chat-response-part-renderer'; + +export default new ContainerModule((bind, _ubind, _isBound, rebind) => { + bindViewContribution(bind, AIChatContribution); + bind(TabBarToolbarContribution).toService(AIChatContribution); + + bindContributionProvider(bind, ChatResponsePartRenderer); + + bindChatViewWidget(bind); + + bind(AIChatInputWidget).toSelf(); + bind(WidgetFactory).toDynamicValue(({ container }) => ({ + id: AIChatInputWidget.ID, + createWidget: () => container.get(AIChatInputWidget) + })).inSingletonScope(); + + bind(ChatViewTreeWidget).toDynamicValue(ctx => + createChatViewTreeWidget(ctx.container) + ); + bind(WidgetFactory).toDynamicValue(({ container }) => ({ + id: ChatViewTreeWidget.ID, + createWidget: () => container.get(ChatViewTreeWidget) + })).inSingletonScope(); + + bind(ChatResponsePartRenderer).to(HorizontalLayoutPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(ErrorPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(MarkdownPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(CodePartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(CommandPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(ToolCallPartRenderer).inSingletonScope(); + bind(ChatResponsePartRenderer).to(ErrorPartRenderer).inSingletonScope(); + [CommandContribution, MenuContribution].forEach(serviceIdentifier => + bind(serviceIdentifier).to(ChatViewMenuContribution).inSingletonScope() + ); + + bind(AIEditorManager).toSelf().inSingletonScope(); + rebind(EditorManager).toService(AIEditorManager); + + bindContributionProvider(bind, AIEditorSelectionResolver); + bind(AIEditorSelectionResolver).to(GitHubSelectionResolver).inSingletonScope(); + bind(AIEditorSelectionResolver).to(TypeDocSymbolSelectionResolver).inSingletonScope(); + bind(AIEditorSelectionResolver).to(TextFragmentSelectionResolver).inSingletonScope(); + + bind(ChatViewWidgetToolbarContribution).toSelf().inSingletonScope(); + bind(TabBarToolbarContribution).toService(ChatViewWidgetToolbarContribution); + + bind(FrontendApplicationContribution).to(ChatViewLanguageContribution).inSingletonScope(); + +}); + +function bindChatViewWidget(bind: interfaces.Bind): void { + let chatViewWidget: ChatViewWidget | undefined; + bind(ChatViewWidget).toSelf(); + + bind(WidgetFactory).toDynamicValue(context => ({ + id: ChatViewWidget.ID, + createWidget: () => { + if (chatViewWidget?.isDisposed !== false) { + chatViewWidget = context.container.get(ChatViewWidget); + } + return chatViewWidget; + } + })).inSingletonScope(); +} diff --git a/packages/ai-chat-ui/src/browser/chat-input-widget.tsx b/packages/ai-chat-ui/src/browser/chat-input-widget.tsx new file mode 100644 index 0000000000000..332c5b3b04232 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-input-widget.tsx @@ -0,0 +1,247 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ChatAgent, ChatAgentService, ChatModel, ChatRequestModel } from '@theia/ai-chat'; +import { UntitledResourceResolver } from '@theia/core'; +import { ContextMenuRenderer, Message, ReactWidget } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import * as React from '@theia/core/shared/react'; +import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider'; +import { CHAT_VIEW_LANGUAGE_EXTENSION } from './chat-view-language-contribution'; +import { IMouseEvent } from '@theia/monaco-editor-core'; + +type Query = (query: string) => Promise; +type Cancel = (requestModel: ChatRequestModel) => void; + +@injectable() +export class AIChatInputWidget extends ReactWidget { + public static ID = 'chat-input-widget'; + static readonly CONTEXT_MENU = ['chat-input-context-menu']; + + @inject(ChatAgentService) + protected readonly agentService: ChatAgentService; + + @inject(MonacoEditorProvider) + protected readonly editorProvider: MonacoEditorProvider; + + @inject(UntitledResourceResolver) + protected readonly untitledResourceResolver: UntitledResourceResolver; + + @inject(ContextMenuRenderer) + protected readonly contextMenuRenderer: ContextMenuRenderer; + + protected isEnabled = false; + + private _onQuery: Query; + set onQuery(query: Query) { + this._onQuery = query; + } + private _onCancel: Cancel; + set onCancel(cancel: Cancel) { + this._onCancel = cancel; + } + private _chatModel: ChatModel; + set chatModel(chatModel: ChatModel) { + this._chatModel = chatModel; + this.update(); + } + + @postConstruct() + protected init(): void { + this.id = AIChatInputWidget.ID; + this.title.closable = false; + this.update(); + } + protected override onActivateRequest(msg: Message): void { + super.onActivateRequest(msg); + this.node.focus({ preventScroll: true }); + } + + protected getChatAgents(): ChatAgent[] { + return this.agentService.getAgents(); + } + + protected render(): React.ReactNode { + return ( + + ); + } + + public setEnabled(enabled: boolean): void { + this.isEnabled = enabled; + this.update(); + } + + protected handleContextMenu(event: IMouseEvent): void { + this.contextMenuRenderer.render({ + menuPath: AIChatInputWidget.CONTEXT_MENU, + anchor: { x: event.posx, y: event.posy }, + }); + event.preventDefault(); + } + +} + +interface ChatInputProperties { + onCancel: (requestModel: ChatRequestModel) => void; + onQuery: (query: string) => void; + isEnabled?: boolean; + chatModel: ChatModel; + getChatAgents: () => ChatAgent[]; + editorProvider: MonacoEditorProvider; + untitledResourceResolver: UntitledResourceResolver; + contextMenuCallback: (event: IMouseEvent) => void; +} +const ChatInput: React.FunctionComponent = (props: ChatInputProperties) => { + + const [inProgress, setInProgress] = React.useState(false); + // eslint-disable-next-line no-null/no-null + const editorContainerRef = React.useRef(null); + // eslint-disable-next-line no-null/no-null + const placeholderRef = React.useRef(null); + const editorRef = React.useRef(undefined); + const allRequests = props.chatModel.getRequests(); + const lastRequest = allRequests.length === 0 ? undefined : allRequests[allRequests.length - 1]; + + const createInputElement = async () => { + const resource = await props.untitledResourceResolver.createUntitledResource('', CHAT_VIEW_LANGUAGE_EXTENSION); + const editor = await props.editorProvider.createInline(resource.uri, editorContainerRef.current!, { + language: CHAT_VIEW_LANGUAGE_EXTENSION, + // Disable code lens, inlay hints and hover support to avoid console errors from other contributions + codeLens: false, + inlayHints: { enabled: 'off' }, + hover: { enabled: false }, + autoSizing: true, + scrollBeyondLastLine: false, + scrollBeyondLastColumn: 0, + minHeight: 1, + fontFamily: 'var(--theia-ui-font-family)', + fontSize: 13, + cursorWidth: 1, + maxHeight: -1, + scrollbar: { horizontal: 'hidden' }, + automaticLayout: true, + lineNumbers: 'off', + lineHeight: 20, + padding: { top: 8 }, + suggest: { + showIcons: true, + showSnippets: false, + showWords: false, + showStatusBar: false, + insertMode: 'replace', + }, + bracketPairColorization: { enabled: false }, + wrappingStrategy: 'advanced', + stickyScroll: { enabled: false }, + }); + + editor.getControl().onDidChangeModelContent(() => { + layout(); + }); + + editor.getControl().onContextMenu(e => + props.contextMenuCallback(e.event) + ); + + editorRef.current = editor; + }; + + React.useEffect(() => { + createInputElement(); + return () => { + if (editorRef.current) { + editorRef.current.dispose(); + } + }; + }, []); + + React.useEffect(() => { + const listener = lastRequest?.response.onDidChange(() => { + if (lastRequest.response.isCanceled || lastRequest.response.isComplete || lastRequest.response.isError) { + setInProgress(false); + } + }); + return () => listener?.dispose(); + }, [lastRequest]); + + function submit(value: string): void { + setInProgress(true); + props.onQuery(value); + if (editorRef.current) { + editorRef.current.document.textEditorModel.setValue(''); + } + }; + + function layout(): void { + if (editorRef.current === undefined) { + return; + } + const hiddenClass = 'hidden'; + const editor = editorRef.current; + if (editor.document.textEditorModel.getValue().length > 0) { + placeholderRef.current?.classList.add(hiddenClass); + } else { + placeholderRef.current?.classList.remove(hiddenClass); + } + } + + const onKeyDown = React.useCallback((event: React.KeyboardEvent) => { + if (!props.isEnabled) { + return; + } + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + submit(editorRef.current?.document.textEditorModel.getValue() || ''); + } + }, [props.isEnabled]); + + return
      +
      +
      +
      Enter your question
      +
      +
      +
      + { + inProgress ? { + if (lastRequest) { + props.onCancel(lastRequest); + } + setInProgress(false); + }} /> : + submit(editorRef.current?.document.textEditorModel.getValue() || '')} + style={{ cursor: !props.isEnabled ? 'default' : 'pointer', opacity: !props.isEnabled ? 0.5 : 1 }} + /> + } +
      +
      ; +}; diff --git a/packages/ai-chat-ui/src/browser/chat-response-part-renderer.ts b/packages/ai-chat-ui/src/browser/chat-response-part-renderer.ts new file mode 100644 index 0000000000000..17743805aa59d --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-part-renderer.ts @@ -0,0 +1,25 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponseContent } from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import { ResponseNode } from './chat-tree-view/chat-view-tree-widget'; + +export const ChatResponsePartRenderer = Symbol('ChatResponsePartRenderer'); +export interface ChatResponsePartRenderer { + canHandle(response: ChatResponseContent): number; + render(response: T, parentNode: ResponseNode): ReactNode; +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/ai-editor-manager.ts b/packages/ai-chat-ui/src/browser/chat-response-renderer/ai-editor-manager.ts new file mode 100644 index 0000000000000..85f0fbeb95824 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/ai-editor-manager.ts @@ -0,0 +1,183 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CancellationToken, ContributionProvider, Prioritizeable, RecursivePartial, URI } from '@theia/core'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { EditorOpenerOptions, EditorWidget, Range } from '@theia/editor/lib/browser'; + +import { EditorPreviewManager } from '@theia/editor-preview/lib/browser/editor-preview-manager'; +import { DocumentSymbol } from '@theia/monaco-editor-core/esm/vs/editor/common/languages'; +import { TextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model/textModel'; +import { ILanguageFeaturesService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/languageFeatures'; +import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter'; + +/** Regex to match GitHub-style position and range declaration with line (L) and column (C) */ +export const LOCATION_REGEX = /#L(\d+)?(?:C(\d+))?(?:-L(\d+)?(?:C(\d+))?)?$/; + +export const AIEditorSelectionResolver = Symbol('AIEditorSelectionResolver'); +export interface AIEditorSelectionResolver { + /** + * The priority of the resolver. A higher value resolver will be called before others. + */ + priority?: number; + resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise | undefined> +} + +@injectable() +export class GitHubSelectionResolver implements AIEditorSelectionResolver { + priority = 100; + + async resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise | undefined> { + if (!uri) { + return; + } + // We allow the GitHub syntax of selecting a range in markdown 'L1', 'L1-L2' 'L1-C1_L2-C2' (starting at line 1 and column 1) + const match = uri?.toString().match(LOCATION_REGEX); + if (!match) { + return; + } + // we need to adapt the position information from one-based (in GitHub) to zero-based (in Theia) + const startLine = match[1] ? parseInt(match[1], 10) - 1 : undefined; + // if no start column is given, we assume the start of the line + const startColumn = match[2] ? parseInt(match[2], 10) - 1 : 0; + const endLine = match[3] ? parseInt(match[3], 10) - 1 : undefined; + // if no end column is given, we assume the end of the line + const endColumn = match[4] ? parseInt(match[4], 10) - 1 : endLine ? widget.editor.document.getLineMaxColumn(endLine) : undefined; + + return { + start: { line: startLine, character: startColumn }, + end: { line: endLine, character: endColumn } + }; + } +} + +@injectable() +export class TypeDocSymbolSelectionResolver implements AIEditorSelectionResolver { + priority = 50; + + @inject(MonacoToProtocolConverter) protected readonly m2p: MonacoToProtocolConverter; + + async resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise | undefined> { + if (!uri) { + return; + } + const editor = MonacoEditor.get(widget); + const monacoEditor = editor?.getControl(); + if (!monacoEditor) { + return; + } + const symbolPath = this.findSymbolPath(uri); + if (!symbolPath) { + return; + } + const textModel = monacoEditor.getModel() as unknown as TextModel; + if (!textModel) { + return; + } + + // try to find the symbol through the document symbol provider + // support referencing nested symbols by separating a dot path similar to TypeDoc + for (const provider of StandaloneServices.get(ILanguageFeaturesService).documentSymbolProvider.ordered(textModel)) { + const symbols = await provider.provideDocumentSymbols(textModel, CancellationToken.None); + const match = this.findSymbolByPath(symbols ?? [], symbolPath); + if (match) { + return this.m2p.asRange(match.selectionRange); + } + } + } + + protected findSymbolPath(uri: URI): string[] | undefined { + return uri.fragment.split('.'); + } + + protected findSymbolByPath(symbols: DocumentSymbol[], symbolPath: string[]): DocumentSymbol | undefined { + if (!symbols || symbolPath.length === 0) { + return undefined; + } + let matchedSymbol: DocumentSymbol | undefined = undefined; + let currentSymbols = symbols; + for (const part of symbolPath) { + matchedSymbol = currentSymbols.find(symbol => symbol.name === part); + if (!matchedSymbol) { + return undefined; + } + currentSymbols = matchedSymbol.children || []; + } + return matchedSymbol; + } +} + +@injectable() +export class TextFragmentSelectionResolver implements AIEditorSelectionResolver { + async resolveSelection(widget: EditorWidget, options: EditorOpenerOptions, uri?: URI): Promise | undefined> { + if (!uri) { + return; + } + const fragment = this.findFragment(uri); + if (!fragment) { + return; + } + const matches = widget.editor.document.findMatches?.({ isRegex: false, matchCase: false, matchWholeWord: false, searchString: fragment }) ?? []; + if (matches.length > 0) { + return { + start: { + line: matches[0].range.start.line - 1, + character: matches[0].range.start.character - 1 + }, + end: { + line: matches[0].range.end.line - 1, + character: matches[0].range.end.character - 1 + } + }; + } + } + + protected findFragment(uri: URI): string | undefined { + return uri.fragment; + } +} + +@injectable() +export class AIEditorManager extends EditorPreviewManager { + @inject(ContributionProvider) @named(AIEditorSelectionResolver) + protected readonly resolvers: ContributionProvider; + + protected override async revealSelection(widget: EditorWidget, options: EditorOpenerOptions = {}, uri?: URI): Promise { + if (!options.selection) { + options.selection = await this.resolveSelection(options, widget, uri); + } + super.revealSelection(widget, options, uri); + } + + protected async resolveSelection(options: EditorOpenerOptions, widget: EditorWidget, uri: URI | undefined): Promise | undefined> { + if (!options.selection) { + const orderedResolvers = Prioritizeable.prioritizeAllSync(this.resolvers.getContributions(), resolver => resolver.priority ?? 1); + for (const linkResolver of orderedResolvers) { + try { + const selection = await linkResolver.value.resolveSelection(widget, options, uri); + if (selection) { + return selection; + } + } catch (error) { + console.error(error); + } + } + } + return undefined; + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx new file mode 100644 index 0000000000000..55647ead854d3 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx @@ -0,0 +1,208 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + ChatResponseContent, + CodeChatResponseContent, +} from '@theia/ai-chat/lib/common'; +import { UntitledResourceResolver, URI } from '@theia/core'; +import { ContextMenuRenderer, TreeNode } from '@theia/core/lib/browser'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { ReactNode } from '@theia/core/shared/react'; +import { Position } from '@theia/core/shared/vscode-languageserver-protocol'; +import { EditorManager, EditorWidget } from '@theia/editor/lib/browser'; +import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor'; +import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider'; +import { MonacoLanguages } from '@theia/monaco/lib/browser/monaco-languages'; +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { ChatViewTreeWidget, ResponseNode } from '../chat-tree-view/chat-view-tree-widget'; +import { IMouseEvent } from '@theia/monaco-editor-core'; + +@injectable() +export class CodePartRenderer + implements ChatResponsePartRenderer { + + @inject(ClipboardService) + protected readonly clipboardService: ClipboardService; + @inject(EditorManager) + protected readonly editorManager: EditorManager; + @inject(UntitledResourceResolver) + protected readonly untitledResourceResolver: UntitledResourceResolver; + @inject(MonacoEditorProvider) + protected readonly editorProvider: MonacoEditorProvider; + @inject(MonacoLanguages) + protected readonly languageService: MonacoLanguages; + @inject(ContextMenuRenderer) + protected readonly contextMenuRenderer: ContextMenuRenderer; + + canHandle(response: ChatResponseContent): number { + if (CodeChatResponseContent.is(response)) { + return 10; + } + return -1; + } + + render(response: CodeChatResponseContent, parentNode: ResponseNode): ReactNode { + const language = response.language ? this.languageService.getExtension(response.language) : undefined; + + return ( +
      +
      +
      {this.renderTitle(response)}
      +
      + + +
      +
      +
      +
      + this.handleContextMenuEvent(parentNode, e, response.code)}> +
      +
      + ); + } + + protected renderTitle(response: CodeChatResponseContent): ReactNode { + const uri = response.location?.uri; + const position = response.location?.position; + if (uri && position) { + return {this.getTitle(response.location?.uri, response.language)}; + } + return this.getTitle(response.location?.uri, response.language); + } + + private getTitle(uri: URI | undefined, language: string | undefined): string { + // If there is a URI, use the file name as the title. Otherwise, use the language as the title. + // If there is no language, use a generic fallback title. + return uri?.path?.toString().split('/').pop() ?? language ?? 'Generated Code'; + } + + /** + * Opens a file and moves the cursor to the specified position. + * + * @param uri - The URI of the file to open. + * @param position - The position to move the cursor to, specified as {line, character}. + */ + async openFileAtPosition(uri: URI, position: Position): Promise { + const editorWidget = await this.editorManager.open(uri) as EditorWidget; + if (editorWidget) { + const editor = editorWidget.editor; + editor.revealPosition(position); + editor.focus(); + editor.cursor = position; + } + } + + protected handleContextMenuEvent(node: TreeNode | undefined, event: IMouseEvent, code: string): void { + this.contextMenuRenderer.render({ + menuPath: ChatViewTreeWidget.CONTEXT_MENU, + anchor: { x: event.posx, y: event.posy }, + args: [node, { code }] + }); + event.preventDefault(); + } +} + +const CopyToClipboardButton = (props: { code: string, clipboardService: ClipboardService }) => { + const { code, clipboardService } = props; + const copyCodeToClipboard = React.useCallback(() => { + clipboardService.writeText(code); + }, [code, clipboardService]); + return ; +}; + +const InsertCodeAtCursorButton = (props: { code: string, editorManager: EditorManager }) => { + const { code, editorManager } = props; + const insertCode = React.useCallback(() => { + const editor = editorManager.currentEditor; + if (editor) { + const currentEditor = editor.editor; + const selection = currentEditor.selection; + + // Insert the text at the current cursor position + // If there is a selection, replace the selection with the text + currentEditor.executeEdits([{ + range: { + start: selection.start, + end: selection.end + }, + newText: code + }]); + } + }, [code, editorManager]); + return ; +}; + +/** + * Renders the given code within a Monaco Editor + */ +export const CodeWrapper = (props: { + content: string, + language?: string, + untitledResourceResolver: UntitledResourceResolver, + editorProvider: MonacoEditorProvider, + contextMenuCallback: (e: IMouseEvent) => void +}) => { + // eslint-disable-next-line no-null/no-null + const ref = React.useRef(null); + const editorRef = React.useRef(undefined); + + const createInputElement = async () => { + const resource = await props.untitledResourceResolver.createUntitledResource(undefined, props.language); + const editor = await props.editorProvider.createInline(resource.uri, ref.current!, { + readOnly: true, + autoSizing: true, + scrollBeyondLastLine: false, + scrollBeyondLastColumn: 0, + renderFinalNewline: 'on', + maxHeight: -1, + scrollbar: { vertical: 'hidden', horizontal: 'hidden' }, + codeLens: false, + inlayHints: { enabled: 'off' }, + hover: { enabled: false } + }); + editor.document.textEditorModel.setValue(props.content); + editor.getControl().onContextMenu(e => props.contextMenuCallback(e.event)); + editorRef.current = editor; + }; + + React.useEffect(() => { + createInputElement(); + return () => { + if (editorRef.current) { + editorRef.current.dispose(); + } + }; + }, []); + + React.useEffect(() => { + if (editorRef.current) { + editorRef.current.document.textEditorModel.setValue(props.content); + } + }, [props.content]); + + editorRef.current?.resizeToFit(); + + return
      ; +}; + diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/command-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/command-part-renderer.tsx new file mode 100644 index 0000000000000..8494742fbe659 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/command-part-renderer.tsx @@ -0,0 +1,60 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatResponseContent, CommandChatResponseContent } from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; +import { CommandRegistry, CommandService } from '@theia/core'; + +@injectable() +export class CommandPartRenderer implements ChatResponsePartRenderer { + @inject(CommandService) private commandService: CommandService; + @inject(CommandRegistry) private commandRegistry: CommandRegistry; + canHandle(response: ChatResponseContent): number { + if (CommandChatResponseContent.is(response)) { + return 10; + } + return -1; + } + render(response: CommandChatResponseContent): ReactNode { + const label = + response.customCallback?.label ?? + response.command?.label ?? + response.command?.id + .split('-') + .map(s => s[0].toUpperCase() + s.substring(1)) + .join(' ') ?? 'Execute'; + if (!response.customCallback && response.command) { + const isCommandEnabled = this.commandRegistry.isEnabled(response.command.id); + if (!isCommandEnabled) { + return
      The command has the id "{response.command.id}" but it is not executable from the Chat window.
      ; + + } + } + return ; + } + private onCommand(arg: CommandChatResponseContent): void { + if (arg.customCallback) { + arg.customCallback.callback().catch(e => { console.error(e); }); + } else if (arg.command) { + this.commandService.executeCommand(arg.command.id, ...(arg.arguments ?? [])).catch(e => { console.error(e); }); + } else { + console.warn('No command or custom callback provided in command chat response content'); + } + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/error-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/error-part-renderer.tsx new file mode 100644 index 0000000000000..4a8fc593d9b50 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/error-part-renderer.tsx @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { injectable } from '@theia/core/shared/inversify'; +import { ChatResponseContent, ErrorChatResponseContent } from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; + +@injectable() +export class ErrorPartRenderer implements ChatResponsePartRenderer { + canHandle(response: ChatResponseContent): number { + if (ErrorChatResponseContent.is(response)) { + return 10; + } + return -1; + } + render(response: ErrorChatResponseContent): ReactNode { + return
      {response.error.message}
      ; + } + +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/horizontal-layout-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/horizontal-layout-part-renderer.tsx new file mode 100644 index 0000000000000..79ef8d1b9e483 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/horizontal-layout-part-renderer.tsx @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { + ChatResponseContent, + HorizontalLayoutChatResponseContent, +} from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; +import { ContributionProvider } from '@theia/core'; +import { ResponseNode } from '../chat-tree-view/chat-view-tree-widget'; + +@injectable() +export class HorizontalLayoutPartRenderer + implements ChatResponsePartRenderer { + @inject(ContributionProvider) + @named(ChatResponsePartRenderer) + protected readonly chatResponsePartRenderers: ContributionProvider< + ChatResponsePartRenderer + >; + + canHandle(response: ChatResponseContent): number { + if (HorizontalLayoutChatResponseContent.is(response)) { + return 10; + } + return -1; + } + render(response: HorizontalLayoutChatResponseContent, parentNode: ResponseNode): ReactNode { + const contributions = this.chatResponsePartRenderers.getContributions(); + return ( +
      + {response.content.map(content => { + const renderer = contributions + .map(c => ({ + prio: c.canHandle(content), + renderer: c, + })) + .sort((a, b) => b.prio - a.prio)[0].renderer; + return renderer.render(content, parentNode); + })} +
      + ); + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/index.ts b/packages/ai-chat-ui/src/browser/chat-response-renderer/index.ts new file mode 100644 index 0000000000000..b0846e52fe1c2 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/index.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export * from './ai-editor-manager'; +export * from './code-part-renderer'; +export * from './command-part-renderer'; +export * from './error-part-renderer'; +export * from './horizontal-layout-part-renderer'; +export * from './markdown-part-renderer'; +export * from './text-part-renderer'; +export * from './toolcall-part-renderer'; diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx new file mode 100644 index 0000000000000..a2fcc7329d53b --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx @@ -0,0 +1,71 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { + ChatResponseContent, + InformationalChatResponseContent, + MarkdownChatResponseContent, +} from '@theia/ai-chat/lib/common'; +import { ReactNode, useEffect, useRef } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; +import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; + +@injectable() +export class MarkdownPartRenderer implements ChatResponsePartRenderer { + @inject(MarkdownRenderer) private renderer: MarkdownRenderer; + canHandle(response: ChatResponseContent): number { + if (MarkdownChatResponseContent.is(response)) { + return 10; + } + if (InformationalChatResponseContent.is(response)) { + return 10; + } + return -1; + } + private renderMarkdown(md: MarkdownString): HTMLElement { + return this.renderer.render(md).element; + } + render(response: MarkdownChatResponseContent | InformationalChatResponseContent): ReactNode { + // TODO let the user configure whether they want to see informational content + if (InformationalChatResponseContent.is(response)) { + // null is valid in React + // eslint-disable-next-line no-null/no-null + return null; + } + return ; + } + +} + +export const MarkdownWrapper = (props: { data: MarkdownString, renderCallback: (md: MarkdownString) => HTMLElement }) => { + // eslint-disable-next-line no-null/no-null + const ref: React.MutableRefObject = useRef(null); + + useEffect(() => { + const myDomElement = props.renderCallback(props.data); + + while (ref?.current?.firstChild) { + ref.current.removeChild(ref.current.firstChild); + } + + ref?.current?.appendChild(myDomElement); + }, [props.data.value]); + + return
      ; +}; diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.spec.ts b/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.spec.ts new file mode 100644 index 0000000000000..e67b0fe0b122a --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.spec.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { TextPartRenderer } from './text-part-renderer'; +import { expect } from 'chai'; +import { ChatResponseContent } from '@theia/ai-chat'; + +describe('TextPartRenderer', () => { + + it('accepts all parts', () => { + const renderer = new TextPartRenderer(); + expect(renderer.canHandle({ kind: 'text' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'code' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'command' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'error' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'horizontal' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'informational' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'markdownContent' })).to.be.greaterThan(0); + expect(renderer.canHandle({ kind: 'toolCall' })).to.be.greaterThan(0); + expect(renderer.canHandle(undefined as unknown as ChatResponseContent)).to.be.greaterThan(0); + }); + + it('renders text correctly', () => { + const renderer = new TextPartRenderer(); + const part = { kind: 'text', asString: () => 'Hello, World!' }; + const node = renderer.render(part); + expect(JSON.stringify(node)).to.contain('Hello, World!'); + }); + + it('handles undefined content gracefully', () => { + const renderer = new TextPartRenderer(); + const part = undefined as unknown as ChatResponseContent; + const node = renderer.render(part); + expect(node).to.exist; + }); + +}); diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.tsx new file mode 100644 index 0000000000000..61ba2ed813e41 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/text-part-renderer.tsx @@ -0,0 +1,35 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { injectable } from '@theia/core/shared/inversify'; +import { ChatResponseContent } from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; + +@injectable() +export class TextPartRenderer implements ChatResponsePartRenderer { + canHandle(_reponse: ChatResponseContent): number { + // this is the fallback renderer + return 1; + } + render(response: ChatResponseContent): ReactNode { + if (response && ChatResponseContent.hasAsString(response)) { + return {response.asString()}; + } + return Can't display response, please check your ChatResponsePartRenderers! {JSON.stringify(response)}; + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx new file mode 100644 index 0000000000000..e568f50386bf4 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx @@ -0,0 +1,49 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; +import { injectable } from '@theia/core/shared/inversify'; +import { ChatResponseContent, ToolCallChatResponseContent } from '@theia/ai-chat/lib/common'; +import { ReactNode } from '@theia/core/shared/react'; +import * as React from '@theia/core/shared/react'; + +@injectable() +export class ToolCallPartRenderer implements ChatResponsePartRenderer { + + canHandle(response: ChatResponseContent): number { + if (ToolCallChatResponseContent.is(response)) { + return 10; + } + return -1; + } + render(response: ToolCallChatResponseContent): ReactNode { + return

      + {response.finished ? +
      + Ran {response.name} +

      {response.result}

      +
      + : Running [{response.name}] + } +

      ; + + } + +} + +const Spinner = () => ( + +); diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-container.ts b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-container.ts new file mode 100644 index 0000000000000..9550de109ec0a --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-container.ts @@ -0,0 +1,32 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { createTreeContainer, TreeProps } from '@theia/core/lib/browser'; +import { interfaces } from '@theia/core/shared/inversify'; +import { ChatViewTreeWidget } from './chat-view-tree-widget'; + +const CHAT_VIEW_TREE_PROPS = { + multiSelect: false, + search: false, +} as TreeProps; + +export function createChatViewTreeWidget(parent: interfaces.Container): ChatViewTreeWidget { + const child = createTreeContainer(parent, { + props: CHAT_VIEW_TREE_PROPS, + widget: ChatViewTreeWidget, + }); + return child.get(ChatViewTreeWidget); +} diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx new file mode 100644 index 0000000000000..cdd9ec870436f --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx @@ -0,0 +1,368 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { + ChatResponseContent, + ChatAgentService, + ChatModel, + ChatProgressMessage, + ChatRequestModel, + ChatResponseModel, +} from '@theia/ai-chat'; +import { CommandRegistry, ContributionProvider } from '@theia/core'; +import { + codicon, + CommonCommands, + CompositeTreeNode, + ContextMenuRenderer, + Key, + KeyCode, + NodeProps, + TreeModel, + TreeNode, + TreeProps, + TreeWidget, +} from '@theia/core/lib/browser'; +import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; +import { + inject, + injectable, + named, + postConstruct, +} from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; + +import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; +import { MarkdownWrapper } from '../chat-response-renderer/markdown-part-renderer'; +import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; + +// TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model +export interface RequestNode extends TreeNode { + request: ChatRequestModel +} +export const isRequestNode = (node: TreeNode): node is RequestNode => 'request' in node; + +// TODO Instead of directly operating on the ChatResponseModel we could use an intermediate view model +export interface ResponseNode extends TreeNode { + response: ChatResponseModel +} +export const isResponseNode = (node: TreeNode): node is ResponseNode => 'response' in node; + +function isEnterKey(e: React.KeyboardEvent): boolean { + return Key.ENTER.keyCode === KeyCode.createKeyCode(e.nativeEvent).key?.keyCode; +} + +@injectable() +export class ChatViewTreeWidget extends TreeWidget { + static readonly ID = 'chat-tree-widget'; + static readonly CONTEXT_MENU = ['chat-tree-context-menu']; + + @inject(ContributionProvider) @named(ChatResponsePartRenderer) + protected readonly chatResponsePartRenderers: ContributionProvider>; + + @inject(MarkdownRenderer) + private renderer: MarkdownRenderer; + + @inject(ChatAgentService) + protected chatAgentService: ChatAgentService; + + @inject(CommandRegistry) + private commandRegistry: CommandRegistry; + + protected _shouldScrollToEnd = true; + + protected isEnabled = false; + + set shouldScrollToEnd(shouldScrollToEnd: boolean) { + this._shouldScrollToEnd = shouldScrollToEnd; + this.shouldScrollToRow = this._shouldScrollToEnd; + } + + get shouldScrollToEnd(): boolean { + return this._shouldScrollToEnd; + } + + constructor( + @inject(TreeProps) props: TreeProps, + @inject(TreeModel) model: TreeModel, + @inject(ContextMenuRenderer) contextMenuRenderer: ContextMenuRenderer + ) { + super(props, model, contextMenuRenderer); + + this.id = ChatViewTreeWidget.ID; + this.title.closable = false; + + model.root = { + id: 'ChatTree', + name: 'ChatRootNode', + parent: undefined, + visible: false, + children: [], + } as CompositeTreeNode; + } + + @postConstruct() + protected override init(): void { + super.init(); + + this.id = ChatViewTreeWidget.ID + '-treeContainer'; + this.addClass('treeContainer'); + } + + public setEnabled(enabled: boolean): void { + this.isEnabled = enabled; + this.update(); + } + + protected override renderTree(model: TreeModel): React.ReactNode { + if (this.isEnabled) { + return super.renderTree(model); + } + return this.renderDisabledMessage(); + } + + private renderDisabledMessage(): React.ReactNode { + return
      +
      +
      + 🚀 Experimental AI Feature Available! +
      +

      Currently, all AI Features are disabled!

      +
      +
      +

      How to Enable Experimental AI Features:

      +
      +
      +

      To enable the experimental AI features, please go to   + {this.renderLinkButton('the settings menu', CommonCommands.OPEN_PREFERENCES.id)} +  and locate the Extensions > ✨ AI Features [Experimental] section.

      +
        +
      1. Toggle the switch for 'Ai-features: Enable'.
      2. +
      3. Provide an OpenAI API Key through the 'OpenAI: API Key' setting or by + setting the OPENAI_API_KEY environment variable.
      4. +
      +

      This will activate the new AI capabilities in the app. Please remember, these features are still in development, so they may change or be unstable. 🚧

      +
      + +
      +

      Currently Supported Views and Features:

      +
      +
      +

      Once the experimental AI features are enabled, you can access the following views and features:

      +
        +
      • Code Completion
      • +
      • Quick Fixes
      • +
      • Terminal Assistance
      • +
      • {this.renderLinkButton('AI History View', 'aiHistory:open')}
      • +
      • {this.renderLinkButton('AI Configuration View', 'aiConfiguration:open')}
      • +
      +
      +
      +
      +
      ; + } + + private renderLinkButton(title: string, openCommandId: string): React.ReactNode { + return this.commandRegistry.executeCommand(openCommandId)} + onKeyDown={e => isEnterKey(e) && this.commandRegistry.executeCommand(openCommandId)}> + {title} + ; + } + + private mapRequestToNode(request: ChatRequestModel): RequestNode { + return { + id: request.id, + parent: this.model.root as CompositeTreeNode, + request + }; + } + + private mapResponseToNode(response: ChatResponseModel): ResponseNode { + return { + id: response.id, + parent: this.model.root as CompositeTreeNode, + response + }; + } + + /** + * Tracks the ChatModel handed over. + * Tracking multiple chat models will result in a weird UI + */ + public trackChatModel(chatModel: ChatModel): void { + this.recreateModelTree(chatModel); + chatModel.getRequests().forEach(request => { + if (!request.response.isComplete) { + request.response.onDidChange(() => this.scheduleUpdateScrollToRow()); + } + }); + this.toDispose.push( + chatModel.onDidChange(event => { + this.recreateModelTree(chatModel); + if (event.kind === 'addRequest' && !event.request.response.isComplete) { + event.request.response.onDidChange(() => this.scheduleUpdateScrollToRow()); + } + }) + ); + } + + protected override getScrollToRow(): number | undefined { + if (this.shouldScrollToEnd) { + return this.rows.size; + } + return super.getScrollToRow(); + } + + private async recreateModelTree(chatModel: ChatModel): Promise { + if (CompositeTreeNode.is(this.model.root)) { + const nodes: TreeNode[] = []; + chatModel.getRequests().forEach(request => { + nodes.push(this.mapRequestToNode(request)); + nodes.push(this.mapResponseToNode(request.response)); + }); + this.model.root.children = nodes; + this.model.refresh(); + } + } + + protected override renderNode( + node: TreeNode, + props: NodeProps + ): React.ReactNode { + if (!TreeNode.isVisible(node)) { + return undefined; + } + if (!(isRequestNode(node) || isResponseNode(node))) { + return super.renderNode(node, props); + } + return +
      this.handleContextMenu(node, e)}> + {this.renderAgent(node)} + {this.renderDetail(node)} +
      +
      ; + } + private renderAgent(node: RequestNode | ResponseNode): React.ReactNode { + const inProgress = isResponseNode(node) && !node.response.isComplete && !node.response.isCanceled && !node.response.isError; + return +
      +
      +

      {this.getAgentLabel(node)}

      + {inProgress && Generating} +
      +
      ; + } + private getAgentLabel(node: RequestNode | ResponseNode): string { + if (isRequestNode(node)) { + // TODO find user name + return 'You'; + } + const agent = node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined; + return agent?.name ?? 'AI'; + } + private getAgentIconClassName(node: RequestNode | ResponseNode): string | undefined { + if (isRequestNode(node)) { + return codicon('account'); + } + + const agent = node.response.agentId ? this.chatAgentService.getAgent(node.response.agentId) : undefined; + return agent?.iconClass ?? codicon('copilot'); + } + + private renderDetail(node: RequestNode | ResponseNode): React.ReactNode { + if (isRequestNode(node)) { + return this.renderChatRequest(node); + } + if (isResponseNode(node)) { + return this.renderChatResponse(node); + }; + } + + private renderChatRequest(node: RequestNode): React.ReactNode { + const text = node.request.request.displayText ?? node.request.request.text; + const markdownString = new MarkdownStringImpl(text, { supportHtml: true, isTrusted: true }); + return ( +
      + { this.renderer.render(markdownString).element} + >} +
      + ); + } + + private renderChatResponse(node: ResponseNode): React.ReactNode { + return ( +
      + {!node.response.isComplete + && node.response.response.content.length === 0 + && node.response.progressMessages.map((c, i) => + + )} + {node.response.response.content.map((c, i) => +
      {this.getChatResponsePartRenderer(c, node)}
      + )} +
      + ); + } + + private getChatResponsePartRenderer(content: ChatResponseContent, node: ResponseNode): React.ReactNode { + const renderer = this.chatResponsePartRenderers.getContributions().reduce<[number, ChatResponsePartRenderer | undefined]>( + (prev, current) => { + const prio = current.canHandle(content); + if (prio > prev[0]) { + return [prio, current]; + } return prev; + }, + [-1, undefined])[1]; + if (!renderer) { + console.error('No renderer found for content', content); + return
      Error: No renderer found
      ; + } + return renderer.render(content, node); + } + + protected handleContextMenu(node: TreeNode | undefined, event: React.MouseEvent): void { + this.contextMenuRenderer.render({ + menuPath: ChatViewTreeWidget.CONTEXT_MENU, + anchor: { x: event.clientX, y: event.clientY }, + args: [node] + }); + event.preventDefault(); + } +} + +const ProgressMessage = (c: ChatProgressMessage) => ( +
      + {c.content} +
      +); + +const Indicator = (progressMessage: ChatProgressMessage) => ( + + {progressMessage.status === 'inProgress' && + + } + {progressMessage.status === 'completed' && + + } + {progressMessage.status === 'failed' && + + } + +); diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/index.ts b/packages/ai-chat-ui/src/browser/chat-tree-view/index.ts new file mode 100644 index 0000000000000..b3a2fd606e01a --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/index.ts @@ -0,0 +1,18 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './chat-view-tree-container'; +export * from './chat-view-tree-widget'; diff --git a/packages/ai-chat-ui/src/browser/chat-view-commands.ts b/packages/ai-chat-ui/src/browser/chat-view-commands.ts new file mode 100644 index 0000000000000..f513e32690444 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-view-commands.ts @@ -0,0 +1,45 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Command, nls } from '@theia/core'; +import { codicon } from '@theia/core/lib/browser'; + +export namespace ChatCommands { + const CHAT_CATEGORY = 'Chat'; + const CHAT_CATEGORY_KEY = nls.getDefaultKey(CHAT_CATEGORY); + + export const SCROLL_LOCK_WIDGET = Command.toLocalizedCommand({ + id: 'chat:widget:lock', + category: CHAT_CATEGORY, + iconClass: codicon('unlock') + }, '', CHAT_CATEGORY_KEY); + + export const SCROLL_UNLOCK_WIDGET = Command.toLocalizedCommand({ + id: 'chat:widget:unlock', + category: CHAT_CATEGORY, + iconClass: codicon('lock') + }, '', CHAT_CATEGORY_KEY); +} + +export const AI_CHAT_NEW_CHAT_WINDOW_COMMAND: Command = { + id: 'ai-chat-ui.new-chat', + iconClass: codicon('add') +}; + +export const AI_CHAT_SHOW_CHATS_COMMAND: Command = { + id: 'ai-chat-ui.show-chats', + iconClass: codicon('history') +}; diff --git a/packages/ai-chat-ui/src/browser/chat-view-contribution.ts b/packages/ai-chat-ui/src/browser/chat-view-contribution.ts new file mode 100644 index 0000000000000..a94c2d2eeeadd --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-view-contribution.ts @@ -0,0 +1,154 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { Command, CommandContribution, CommandRegistry, CommandService, isObject, MenuContribution, MenuModelRegistry } from '@theia/core'; +import { CommonCommands, TreeNode } from '@theia/core/lib/browser'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatViewTreeWidget, isRequestNode, isResponseNode, RequestNode, ResponseNode } from './chat-tree-view/chat-view-tree-widget'; +import { AIChatInputWidget } from './chat-input-widget'; + +export namespace ChatViewCommands { + export const COPY_MESSAGE = Command.toDefaultLocalizedCommand({ + id: 'chat.copy.message', + label: 'Copy Message' + }); + export const COPY_ALL = Command.toDefaultLocalizedCommand({ + id: 'chat.copy.all', + label: 'Copy All' + }); + export const COPY_CODE = Command.toDefaultLocalizedCommand({ + id: 'chat.copy.code', + label: 'Copy Code Block' + }); +} + +@injectable() +export class ChatViewMenuContribution implements MenuContribution, CommandContribution { + + @inject(ClipboardService) + protected readonly clipboardService: ClipboardService; + + @inject(CommandService) + protected readonly commandService: CommandService; + + registerCommands(commands: CommandRegistry): void { + commands.registerHandler(CommonCommands.COPY.id, { + execute: (...args: unknown[]) => { + if (window.getSelection()?.type !== 'Range' && containsRequestOrResponseNode(args)) { + this.copyMessage(extractRequestOrResponseNodes(args)); + } else { + this.commandService.executeCommand(CommonCommands.COPY.id); + } + }, + isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args) + }); + commands.registerCommand(ChatViewCommands.COPY_MESSAGE, { + execute: (...args: unknown[]) => { + if (containsRequestOrResponseNode(args)) { + this.copyMessage(extractRequestOrResponseNodes(args)); + } + }, + isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args) + }); + commands.registerCommand(ChatViewCommands.COPY_ALL, { + execute: (...args: unknown[]) => { + if (containsRequestOrResponseNode(args)) { + const parent = extractRequestOrResponseNodes(args).find(arg => arg.parent)?.parent; + const text = parent?.children + .filter(isRequestOrResponseNode) + .map(child => this.getText(child)) + .join('\n\n---\n\n'); + if (text) { + this.clipboardService.writeText(text); + } + } + }, + isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args) + }); + commands.registerCommand(ChatViewCommands.COPY_CODE, { + execute: (...args: unknown[]) => { + if (containsCode(args)) { + const code = args + .filter(isCodeArg) + .map(arg => arg.code) + .join(); + this.clipboardService.writeText(code); + } + }, + isEnabled: (...args: unknown[]) => containsRequestOrResponseNode(args) && containsCode(args) + }); + } + + protected copyMessage(args: (RequestNode | ResponseNode)[]): void { + const text = this.getTextAndJoin(args); + this.clipboardService.writeText(text); + } + + protected getTextAndJoin(args: (RequestNode | ResponseNode)[] | undefined): string { + return args !== undefined ? args.map(arg => this.getText(arg)).join() : ''; + } + + protected getText(arg: RequestNode | ResponseNode): string { + if (isRequestNode(arg)) { + return arg.request.request.text; + } else if (isResponseNode(arg)) { + return arg.response.response.asString(); + } + return ''; + } + + registerMenus(menus: MenuModelRegistry): void { + menus.registerMenuAction([...ChatViewTreeWidget.CONTEXT_MENU, '_1'], { + commandId: CommonCommands.COPY.id + }); + menus.registerMenuAction([...ChatViewTreeWidget.CONTEXT_MENU, '_1'], { + commandId: ChatViewCommands.COPY_MESSAGE.id + }); + menus.registerMenuAction([...ChatViewTreeWidget.CONTEXT_MENU, '_1'], { + commandId: ChatViewCommands.COPY_ALL.id + }); + menus.registerMenuAction([...ChatViewTreeWidget.CONTEXT_MENU, '_1'], { + commandId: ChatViewCommands.COPY_CODE.id + }); + menus.registerMenuAction([...AIChatInputWidget.CONTEXT_MENU, '_1'], { + commandId: CommonCommands.COPY.id + }); + menus.registerMenuAction([...AIChatInputWidget.CONTEXT_MENU, '_1'], { + commandId: CommonCommands.PASTE.id + }); + } + +} + +function extractRequestOrResponseNodes(args: unknown[]): (RequestNode | ResponseNode)[] { + return args.filter(arg => isRequestOrResponseNode(arg)) as (RequestNode | ResponseNode)[]; +} + +function containsRequestOrResponseNode(args: unknown[]): args is (unknown | RequestNode | ResponseNode)[] { + return extractRequestOrResponseNodes(args).length > 0; +} + +function isRequestOrResponseNode(arg: unknown): arg is RequestNode | ResponseNode { + return TreeNode.is(arg) && (isRequestNode(arg) || isResponseNode(arg)); +} + +function containsCode(args: unknown[]): args is (unknown | { code: string })[] { + return args.filter(arg => isCodeArg(arg)).length > 0; +} + +function isCodeArg(arg: unknown): arg is { code: string } { + return isObject(arg) && 'code' in arg; +} diff --git a/packages/ai-chat-ui/src/browser/chat-view-language-contribution.ts b/packages/ai-chat-ui/src/browser/chat-view-language-contribution.ts new file mode 100644 index 0000000000000..1a9f0dcc8138d --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-view-language-contribution.ts @@ -0,0 +1,141 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { FrontendApplication, FrontendApplicationContribution } from '@theia/core/lib/browser'; +import * as monaco from '@theia/monaco-editor-core'; +import { ContributionProvider, MaybePromise } from '@theia/core'; +import { ProviderResult } from '@theia/monaco-editor-core/esm/vs/editor/common/languages'; +import { ChatAgentService } from '@theia/ai-chat'; +import { AIVariableService } from '@theia/ai-core/lib/common'; +import { ToolProvider } from '@theia/ai-core/lib/common/tool-invocation-registry'; + +export const CHAT_VIEW_LANGUAGE_ID = 'theia-ai-chat-view-language'; +export const CHAT_VIEW_LANGUAGE_EXTENSION = 'aichatviewlanguage'; + +@injectable() +export class ChatViewLanguageContribution implements FrontendApplicationContribution { + + @inject(ChatAgentService) + protected readonly agentService: ChatAgentService; + + @inject(AIVariableService) + protected readonly variableService: AIVariableService; + + @inject(ContributionProvider) + @named(ToolProvider) + private providers: ContributionProvider; + + onStart(_app: FrontendApplication): MaybePromise { + console.log('ChatViewLanguageContribution started'); + monaco.languages.register({ id: CHAT_VIEW_LANGUAGE_ID, extensions: [CHAT_VIEW_LANGUAGE_EXTENSION] }); + + monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, { + triggerCharacters: ['@'], + provideCompletionItems: (model, position, _context, _token): ProviderResult => this.provideAgentCompletions(model, position), + }); + monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, { + triggerCharacters: ['#'], + provideCompletionItems: (model, position, _context, _token): ProviderResult => this.provideVariableCompletions(model, position), + }); + monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, { + triggerCharacters: ['~'], + provideCompletionItems: (model, position, _context, _token): ProviderResult => this.provideToolCompletions(model, position), + }); + } + + getCompletionRange(model: monaco.editor.ITextModel, position: monaco.Position, triggerCharacter: string): monaco.Range | undefined { + // Check if the character before the current position is the trigger character + const lineContent = model.getLineContent(position.lineNumber); + const characterBefore = lineContent[position.column - 2]; // Get the character before the current position + + if (characterBefore !== triggerCharacter) { + // Do not return agent suggestions if the user didn't just type the trigger character + return undefined; + } + + // Calculate the range from the position of the '@' character + const wordInfo = model.getWordUntilPosition(position); + return new monaco.Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + position.column + ); + } + + private getSuggestions( + model: monaco.editor.ITextModel, + position: monaco.Position, + triggerChar: string, + items: T[], + kind: monaco.languages.CompletionItemKind, + getId: (item: T) => string, + getName: (item: T) => string, + getDescription: (item: T) => string + ): ProviderResult { + const completionRange = this.getCompletionRange(model, position, triggerChar); + if (completionRange === undefined) { + return { suggestions: [] }; + } + const suggestions = items.map(item => ({ + insertText: getId(item), + kind: kind, + label: getName(item), + range: completionRange, + detail: getDescription(item), + })); + return { suggestions }; + } + + provideAgentCompletions(model: monaco.editor.ITextModel, position: monaco.Position): ProviderResult { + return this.getSuggestions( + model, + position, + '@', + this.agentService.getAgents(), + monaco.languages.CompletionItemKind.Value, + agent => agent.id, + agent => agent.name, + agent => agent.description + ); + } + + provideVariableCompletions(model: monaco.editor.ITextModel, position: monaco.Position): ProviderResult { + return this.getSuggestions( + model, + position, + '#', + this.variableService.getVariables(), + monaco.languages.CompletionItemKind.Variable, + variable => variable.name, + variable => variable.name, + variable => variable.description + ); + } + + provideToolCompletions(model: monaco.editor.ITextModel, position: monaco.Position): ProviderResult { + return this.getSuggestions( + model, + position, + '~', + this.providers.getContributions().map(provider => provider.getTool()), + monaco.languages.CompletionItemKind.Function, + tool => tool.id, + tool => tool.name, + tool => tool.description ?? '' + ); + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-view-widget-toolbar-contribution.tsx b/packages/ai-chat-ui/src/browser/chat-view-widget-toolbar-contribution.tsx new file mode 100644 index 0000000000000..85846de3bed08 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-view-widget-toolbar-contribution.tsx @@ -0,0 +1,54 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { AIChatContribution } from './ai-chat-ui-contribution'; +import { Emitter, nls } from '@theia/core'; +import { ChatCommands } from './chat-view-commands'; + +@injectable() +export class ChatViewWidgetToolbarContribution implements TabBarToolbarContribution { + @inject(AIChatContribution) + protected readonly chatContribution: AIChatContribution; + + protected readonly onChatWidgetStateChangedEmitter = new Emitter(); + protected readonly onChatWidgetStateChanged = this.onChatWidgetStateChangedEmitter.event; + + @postConstruct() + protected init(): void { + this.chatContribution.widget.then(widget => { + widget.onStateChanged(() => this.onChatWidgetStateChangedEmitter.fire()); + }); + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem({ + id: ChatCommands.SCROLL_LOCK_WIDGET.id, + command: ChatCommands.SCROLL_LOCK_WIDGET.id, + tooltip: nls.localizeByDefault('Turn Auto Scrolling Off'), + onDidChange: this.onChatWidgetStateChanged, + priority: 2 + }); + registry.registerItem({ + id: ChatCommands.SCROLL_UNLOCK_WIDGET.id, + command: ChatCommands.SCROLL_UNLOCK_WIDGET.id, + tooltip: nls.localizeByDefault('Turn Auto Scrolling On'), + onDidChange: this.onChatWidgetStateChanged, + priority: 2 + }); + } +} diff --git a/packages/ai-chat-ui/src/browser/chat-view-widget.tsx b/packages/ai-chat-ui/src/browser/chat-view-widget.tsx new file mode 100644 index 0000000000000..7763b2a8ec8c3 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/chat-view-widget.tsx @@ -0,0 +1,194 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommandService, deepClone, Emitter, Event, MessageService } from '@theia/core'; +import { ChatRequest, ChatRequestModel, ChatRequestModelImpl, ChatService, ChatSession } from '@theia/ai-chat'; +import { BaseWidget, codicon, ExtractableWidget, PanelLayout, PreferenceService, StatefulWidget } from '@theia/core/lib/browser'; +import { nls } from '@theia/core/lib/common/nls'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { AIChatInputWidget } from './chat-input-widget'; +import { ChatViewTreeWidget } from './chat-tree-view/chat-view-tree-widget'; +import { AIActivationService } from '@theia/ai-core/lib/browser/ai-activation-service'; + +export namespace ChatViewWidget { + export interface State { + locked?: boolean; + } +} + +@injectable() +export class ChatViewWidget extends BaseWidget implements ExtractableWidget, StatefulWidget { + + public static ID = 'chat-view-widget'; + static LABEL = `✨ ${nls.localizeByDefault('Chat')} [Experimental]`; + + @inject(ChatService) + protected chatService: ChatService; + + @inject(MessageService) + protected messageService: MessageService; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + @inject(CommandService) + protected readonly commandService: CommandService; + + @inject(AIActivationService) + protected readonly activationService: AIActivationService; + + protected chatSession: ChatSession; + + protected _state: ChatViewWidget.State = { locked: false }; + protected readonly onStateChangedEmitter = new Emitter(); + + secondaryWindow: Window | undefined; + + constructor( + @inject(ChatViewTreeWidget) + readonly treeWidget: ChatViewTreeWidget, + @inject(AIChatInputWidget) + readonly inputWidget: AIChatInputWidget + ) { + super(); + this.id = ChatViewWidget.ID; + this.title.label = ChatViewWidget.LABEL; + this.title.caption = ChatViewWidget.LABEL; + this.title.iconClass = codicon('comment-discussion'); + this.title.closable = true; + this.node.classList.add('chat-view-widget'); + this.update(); + } + + @postConstruct() + protected init(): void { + this.toDispose.pushAll([ + this.treeWidget, + this.inputWidget, + this.onStateChanged(newState => { + this.treeWidget.shouldScrollToEnd = !newState.locked; + this.update(); + }) + ]); + const layout = this.layout = new PanelLayout(); + + this.treeWidget.node.classList.add('chat-tree-view-widget'); + layout.addWidget(this.treeWidget); + this.inputWidget.node.classList.add('chat-input-widget'); + layout.addWidget(this.inputWidget); + this.chatSession = this.chatService.createSession(); + + this.inputWidget.onQuery = this.onQuery.bind(this); + this.inputWidget.onCancel = this.onCancel.bind(this); + this.inputWidget.chatModel = this.chatSession.model; + this.treeWidget.trackChatModel(this.chatSession.model); + + this.initListeners(); + + this.inputWidget.setEnabled(this.activationService.isActive); + this.treeWidget.setEnabled(this.activationService.isActive); + + this.activationService.onDidChangeActiveStatus(change => { + this.treeWidget.setEnabled(change); + this.inputWidget.setEnabled(change); + this.update(); + }); + } + + protected initListeners(): void { + this.toDispose.push( + this.chatService.onActiveSessionChanged(event => { + const session = event.sessionId ? this.chatService.getSession(event.sessionId) : this.chatService.createSession(); + if (session) { + this.chatSession = session; + this.treeWidget.trackChatModel(this.chatSession.model); + this.inputWidget.chatModel = this.chatSession.model; + if (event.focus) { + this.show(); + } + } else { + console.warn(`Session with ${event.sessionId} not found.`); + } + }) + ); + } + + storeState(): object { + return this.state; + } + + restoreState(oldState: object & Partial): void { + const copy = deepClone(this.state); + if (oldState.locked) { + copy.locked = oldState.locked; + } + this.state = copy; + } + + protected get state(): ChatViewWidget.State { + return this._state; + } + + protected set state(state: ChatViewWidget.State) { + this._state = state; + this.onStateChangedEmitter.fire(this._state); + } + + get onStateChanged(): Event { + return this.onStateChangedEmitter.event; + } + + protected async onQuery(query: string): Promise { + if (query.length === 0) { return; } + + const chatRequest: ChatRequest = { + text: query + }; + + const requestProgress = await this.chatService.sendRequest(this.chatSession.id, chatRequest); + requestProgress?.responseCompleted.then(responseModel => { + if (responseModel.isError) { + this.messageService.error(responseModel.errorObject?.message ?? 'An error occurred druring chat service invocation.'); + } + }); + if (!requestProgress) { + this.messageService.error(`Was not able to send request "${chatRequest.text}" to session ${this.chatSession.id}`); + return; + } + // Tree Widget currently tracks the ChatModel itself. Therefore no notification necessary. + } + + protected onCancel(requestModel: ChatRequestModel): void { + // TODO we should pass a cancellation token with the request (or retrieve one from the request invocation) so we can cleanly cancel here + // For now we cancel manually via casting + (requestModel as ChatRequestModelImpl).response.cancel(); + } + + lock(): void { + this.state = { ...deepClone(this.state), locked: true }; + } + + unlock(): void { + this.state = { ...deepClone(this.state), locked: false }; + } + + get isLocked(): boolean { + return !!this.state.locked; + } + + get isExtractable(): boolean { + return this.secondaryWindow === undefined; + } +} diff --git a/packages/ai-chat-ui/src/browser/style/index.css b/packages/ai-chat-ui/src/browser/style/index.css new file mode 100644 index 0000000000000..3f014cb6e9a26 --- /dev/null +++ b/packages/ai-chat-ui/src/browser/style/index.css @@ -0,0 +1,309 @@ +.chat-view-widget { + display: flex; + flex-direction: column; +} + +.chat-tree-view-widget { + flex: 1; +} + +.chat-input-widget > .ps__rail-x, +.chat-input-widget > .ps__rail-y { + display: none !important; +} + +.theia-ChatNode { + cursor: default; + display: flex; + flex-direction: column; + gap: 8px; + padding: 16px 20px; + user-select: text; + -webkit-user-select: text; + border-bottom: 1px solid var(--theia-sideBarSectionHeader-border); + overflow-wrap: break-word; +} + +div:last-child > .theia-ChatNode { + border: none; +} + +.theia-ChatNodeHeader { + align-items: center; + display: flex; + gap: 8px; + width: 100%; +} + +.theia-ChatNodeHeader .theia-AgentAvatar { + display: flex; + pointer-events: none; + user-select: none; + font-size: 20px; +} + +.theia-ChatNodeHeader .theia-AgentLabel { + font-size: 13px; + font-weight: 600; + margin: 0; +} + +.theia-ChatNodeHeader .theia-ChatContentInProgress { + color: var(--theia-disabledForeground); +} + +.theia-ChatNodeHeader .theia-ChatContentInProgress-Cancel { + position: absolute; + z-index: 999; + right: 20px; +} + +@keyframes dots { + 0%, + 20% { + content: ""; + } + + 40% { + content: "."; + } + + 60% { + content: ".."; + } + + 80%, + 100% { + content: "..."; + } +} + +.theia-ChatNodeHeader .theia-ChatContentInProgress::after { + content: ""; + animation: dots 1s steps(1, end) infinite; +} + +.theia-ChatNode .codicon { + text-align: left; +} + +.theia-AgentLabel { + font-weight: 600; +} + +.theia-ChatNode .rendered-markdown p { + margin: 0 0 16px; +} + +.theia-ChatNode:last-child .rendered-markdown > :last-child { + margin-bottom: 0; +} + +.theia-ChatNode .rendered-markdown { + line-height: 1.3rem; +} + +.chat-input-widget { + align-items: flex-end; + display: flex; + flex-direction: column; +} + +.theia-ChatInput { + position: relative; + width: 100%; + box-sizing: border-box; + gap: 4px; +} + +.theia-ChatInput-Editor-Box { + margin-bottom: 2px; + padding: 10px; + height: auto; + display: flex; + flex-direction: column; + justify-content: flex-end; + overflow: hidden; +} + +.theia-ChatInput-Editor { + width: 100%; + height: auto; + border: var(--theia-border-width) solid var(--theia-dropdown-border); + border-radius: 4px; + display: flex; + flex-direction: column-reverse; + overflow: hidden; +} + +.theia-ChatInput-Editor:has(.monaco-editor.focused) { + border-color: var(--theia-focusBorder); +} + +.theia-ChatInput-Editor .monaco-editor { + display: flex; + width: 100%; + height: 100%; + overflow: hidden; + position: relative; +} + +.theia-ChatInput-Editor-Placeholder { + position: absolute; + top: -3px; + left: 19px; + right: 0; + bottom: 0; + display: flex; + align-items: center; + color: var(--theia-descriptionForeground); + pointer-events: none; + z-index: 10; + text-align: left; +} +.theia-ChatInput-Editor-Placeholder.hidden { + display: none; +} + +.theia-ChatInput-Editor .monaco-editor .margin, +.theia-ChatInput-Editor .monaco-editor .monaco-editor-background, +.theia-ChatInput-Editor .monaco-editor .inputarea.ime-input { + padding-left: 8px !important; +} + +.theia-ChatInputOptions { + position: absolute; + bottom: 31px; + right: 26px; + width: 10px; + height: 10px; +} + +.theia-ChatInputOptions .option { + width: 21px; + height: 21px; + margin-top: 2px; + display: inline-block; + box-sizing: border-box; + user-select: none; + background-repeat: no-repeat; + background-position: center; + border: var(--theia-border-width) solid transparent; + opacity: 0.7; + cursor: pointer; +} + +.theia-ChatInputOptions .option:hover { + opacity: 1; +} + +.theia-CodePartRenderer-root { + display: flex; + flex-direction: column; + gap: 4px; + border: 1px solid var(--theia-input-border); + border-radius: 4px; +} + +.theia-CodePartRenderer-left { + flex-grow: 1; +} + +.theia-CodePartRenderer-top { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 4px; +} + +.theia-CodePartRenderer-right button { + margin-left: 4px; +} + +.theia-CodePartRenderer-separator { + width: 100%; + height: 1px; + background-color: var(--theia-input-border); +} + +.theia-toolCall { + font-weight: normal; + color: var(--theia-descriptionForeground); + line-height: 20px; + margin-bottom: 6px; + cursor: pointer; +} + +.theia-toolCall .fa, +.theia-toolCall details summary::marker { + color: var(--theia-button-background); +} + +.theia-ResponseNode-ProgressMessage { + font-weight: normal; + color: var(--theia-descriptionForeground); + line-height: 20px; + margin-bottom: 6px; +} + +.theia-ResponseNode-ProgressMessage .inProgress { + color: var(--theia-progressBar-background); +} +.theia-ResponseNode-ProgressMessage .completed { + color: var(--theia-successBackground); +} +.theia-ResponseNode-ProgressMessage .failed { + color: var(--theia-errorForeground); +} + +.spinner { + display: inline-block; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +.theia-ChatPart-Error { + display: flex; + flex-direction: row; + gap: 0.5em; + color: var(--theia-errorForeground); +} + +.section-header { + font-weight: bold; + font-size: 16px; + margin-bottom: 10px; +} + +.section-title { + font-weight: bold; + font-size: 14px; + margin: 20px 0px; +} + +.disable-message { + font-size: 12px; + line-height: 1.6; + padding: 15px; +} + +.section-content p { + margin: 10px 0; +} + +.section-content a { + cursor: pointer; +} + +.section-content strong { + font-weight: bold; +} diff --git a/packages/ai-chat-ui/tsconfig.json b/packages/ai-chat-ui/tsconfig.json new file mode 100644 index 0000000000000..13d585dc94ad5 --- /dev/null +++ b/packages/ai-chat-ui/tsconfig.json @@ -0,0 +1,37 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-chat" + }, + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../editor" + }, + { + "path": "../editor-preview" + }, + { + "path": "../filesystem" + }, + { + "path": "../monaco" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-chat/.eslintrc.js b/packages/ai-chat/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-chat/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-chat/README.md b/packages/ai-chat/README.md new file mode 100644 index 0000000000000..6f394ce95cc55 --- /dev/null +++ b/packages/ai-chat/README.md @@ -0,0 +1,30 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Chat EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-chat` extension provides the concept of a language model chat to Theia. +It serves as the basis for `@theia/ai-chat-ui` to provide the Chat UI. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-chat/package.json b/packages/ai-chat/package.json new file mode 100644 index 0000000000000..907a6b05712c6 --- /dev/null +++ b/packages/ai-chat/package.json @@ -0,0 +1,53 @@ +{ + "name": "@theia/ai-chat", + "version": "1.53.0", + "description": "Theia - AI Chat Extension", + "dependencies": { + "@theia/ai-core": "1.53.0", + "@theia/ai-history": "1.53.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2" + }, + "publishConfig": { + "access": "public" + }, + "main": "lib/common", + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-chat-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-chat/src/browser/ai-chat-frontend-module.ts b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts new file mode 100644 index 0000000000000..d4ed71579b514 --- /dev/null +++ b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts @@ -0,0 +1,66 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Agent, AIVariableContribution } from '@theia/ai-core/lib/common'; +import { bindContributionProvider } from '@theia/core'; +import { PreferenceContribution } from '@theia/core/lib/browser'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { + ChatAgent, + ChatAgentService, + ChatAgentServiceImpl, + ChatRequestParser, + ChatRequestParserImpl, + ChatService, + DefaultChatAgentId +} from '../common'; +import { CommandChatAgent } from '../common/command-chat-agents'; +import { OrchestratorChatAgent, OrchestratorChatAgentId } from '../common/orchestrator-chat-agent'; +import { UniversalChatAgent } from '../common/universal-chat-agent'; +import { aiChatPreferences } from './ai-chat-preferences'; +import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution'; +import { FrontendChatServiceImpl } from './frontend-chat-service'; + +export default new ContainerModule(bind => { + bindContributionProvider(bind, Agent); + bindContributionProvider(bind, ChatAgent); + + bind(ChatAgentServiceImpl).toSelf().inSingletonScope(); + bind(ChatAgentService).toService(ChatAgentServiceImpl); + bind(DefaultChatAgentId).toConstantValue({ id: OrchestratorChatAgentId }); + + bind(AIVariableContribution).to(ChatAgentsVariableContribution).inSingletonScope(); + + bind(ChatRequestParserImpl).toSelf().inSingletonScope(); + bind(ChatRequestParser).toService(ChatRequestParserImpl); + + bind(FrontendChatServiceImpl).toSelf().inSingletonScope(); + bind(ChatService).toService(FrontendChatServiceImpl); + + bind(OrchestratorChatAgent).toSelf().inSingletonScope(); + bind(Agent).toService(OrchestratorChatAgent); + bind(ChatAgent).toService(OrchestratorChatAgent); + + bind(UniversalChatAgent).toSelf().inSingletonScope(); + bind(Agent).toService(UniversalChatAgent); + bind(ChatAgent).toService(UniversalChatAgent); + + bind(CommandChatAgent).toSelf().inSingletonScope(); + bind(Agent).toService(CommandChatAgent); + bind(ChatAgent).toService(CommandChatAgent); + + bind(PreferenceContribution).toConstantValue({ schema: aiChatPreferences }); +}); diff --git a/packages/ai-chat/src/browser/ai-chat-preferences.ts b/packages/ai-chat/src/browser/ai-chat-preferences.ts new file mode 100644 index 0000000000000..2e676f040b4ae --- /dev/null +++ b/packages/ai-chat/src/browser/ai-chat-preferences.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; +import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution'; + +export const DEFAULT_CHAT_AGENT_PREF = 'ai-features.chat.default-chat-agent'; + +export const aiChatPreferences: PreferenceSchema = { + type: 'object', + properties: { + [DEFAULT_CHAT_AGENT_PREF]: { + type: 'string', + description: ' of the Chat Agent that shall be invoked, if no agent is explicitly mentioned with @ in the user query.', + title: AI_CORE_PREFERENCES_TITLE, + } + } +}; diff --git a/packages/ai-chat/src/browser/frontend-chat-service.ts b/packages/ai-chat/src/browser/frontend-chat-service.ts new file mode 100644 index 0000000000000..10e7dde4a1d46 --- /dev/null +++ b/packages/ai-chat/src/browser/frontend-chat-service.ts @@ -0,0 +1,66 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatAgent, ChatServiceImpl, ParsedChatRequest } from '../common'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { DEFAULT_CHAT_AGENT_PREF } from './ai-chat-preferences'; + +@injectable() +export class FrontendChatServiceImpl extends ChatServiceImpl { + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + protected override getAgent(parsedRequest: ParsedChatRequest): ChatAgent | undefined { + const agentPart = this.getMentionedAgent(parsedRequest); + if (agentPart) { + return this.chatAgentService.getAgent(agentPart.agentId); + } + + const configuredDefaultChatAgent = this.getConfiguredDefaultChatAgent(); + if (configuredDefaultChatAgent) { + return configuredDefaultChatAgent; + } + + if (this.defaultChatAgentId) { + const defaultAgent = this.chatAgentService.getAgent(this.defaultChatAgentId.id); + // the default agent could be disabled + if (defaultAgent) { + return defaultAgent; + } + } + + // check whether "Universal" is available + const universalAgent = this.chatAgentService.getAgent('Universal'); + if (universalAgent) { + return universalAgent; + } + + this.logger.warn('No default chat agent is configured or available and the "Universal" Chat Agent is unavailable too. Falling back to first registered agent.'); + + return this.chatAgentService.getAgents()[0] ?? undefined; + } + + protected getConfiguredDefaultChatAgent(): ChatAgent | undefined { + const configuredDefaultChatAgentId = this.preferenceService.get(DEFAULT_CHAT_AGENT_PREF, undefined); + const configuredDefaultChatAgent = configuredDefaultChatAgentId ? this.chatAgentService.getAgent(configuredDefaultChatAgentId) : undefined; + if (configuredDefaultChatAgentId && !configuredDefaultChatAgent) { + this.logger.warn(`The configured default chat agent with id '${configuredDefaultChatAgentId}' does not exist.`); + } + return configuredDefaultChatAgent; + } +} diff --git a/packages/ai-chat/src/common/chat-agent-service.ts b/packages/ai-chat/src/common/chat-agent-service.ts new file mode 100644 index 0000000000000..53704d427cfd0 --- /dev/null +++ b/packages/ai-chat/src/common/chat-agent-service.ts @@ -0,0 +1,85 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatAgents.ts + +import { ContributionProvider, ILogger } from '@theia/core'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { ChatAgent } from './chat-agents'; +import { AgentService } from '@theia/ai-core'; + +export const ChatAgentService = Symbol('ChatAgentService'); +/** + * The ChatAgentService provides access to the available chat agents. + */ +export interface ChatAgentService { + /** + * Returns all available agents. + */ + getAgents(): ChatAgent[]; + /** + * Returns the specified agent, if available + */ + getAgent(id: string): ChatAgent | undefined; + /** + * Returns all agents, including disabled ones. + */ + getAllAgents(): ChatAgent[]; +} +@injectable() +export class ChatAgentServiceImpl implements ChatAgentService { + + @inject(ContributionProvider) @named(ChatAgent) + protected readonly agentContributions: ContributionProvider; + + @inject(ILogger) + protected logger: ILogger; + + @inject(AgentService) + protected agentService: AgentService; + + protected _agents: ChatAgent[] = []; + + protected get agents(): ChatAgent[] { + // We can't collect the contributions at @postConstruct because this will lead to a circular dependency + // with chat agents reusing the chat agent service (e.g. orchestrator) + return [...this.agentContributions.getContributions(), ...this._agents]; + } + + registerChatAgent(agent: ChatAgent): void { + this._agents.push(agent); + } + + getAgent(id: string): ChatAgent | undefined { + if (!this._agentIsEnabled(id)) { + return undefined; + } + return this.getAgents().find(agent => agent.id === id); + } + getAgents(): ChatAgent[] { + return this.agents.filter(a => this._agentIsEnabled(a.id)); + } + getAllAgents(): ChatAgent[] { + return this.agents; + } + + private _agentIsEnabled(id: string): boolean { + return this.agentService.isEnabled(id); + } +} diff --git a/packages/ai-chat/src/common/chat-agents-variable-contribution.ts b/packages/ai-chat/src/common/chat-agents-variable-contribution.ts new file mode 100644 index 0000000000000..968a41f9af33e --- /dev/null +++ b/packages/ai-chat/src/common/chat-agents-variable-contribution.ts @@ -0,0 +1,81 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { MaybePromise } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { + AIVariable, + AIVariableContext, + AIVariableContribution, + AIVariableResolutionRequest, + AIVariableResolver, + AIVariableService, + ResolvedAIVariable +} from '../../../ai-core/src/common/variable-service'; +import { ChatAgentService } from './chat-agent-service'; + +export const CHAT_AGENTS_VARIABLE: AIVariable = { + id: 'chatAgents', + name: 'chatAgents', + description: 'Returns the list of chat agents available in the system' +}; + +export interface ChatAgentDescriptor { + id: string; + name: string; + description: string; +} + +@injectable() +export class ChatAgentsVariableContribution implements AIVariableContribution, AIVariableResolver { + + @inject(ChatAgentService) + protected readonly agents: ChatAgentService; + + registerVariables(service: AIVariableService): void { + service.registerResolver(CHAT_AGENTS_VARIABLE, this); + } + + canResolve(request: AIVariableResolutionRequest, _context: AIVariableContext): MaybePromise { + if (request.variable.name === CHAT_AGENTS_VARIABLE.name) { + return 1; + } + return -1; + } + + async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + if (request.variable.name === CHAT_AGENTS_VARIABLE.name) { + return this.resolveAgentsVariable(request); + } + } + + resolveAgentsVariable(_request: AIVariableResolutionRequest): ResolvedAIVariable { + const agents = this.agents.getAgents().map(agent => ({ + id: agent.id, + name: agent.name, + description: agent.description + })); + const value = agents.map(agent => prettyPrintInMd(agent)).join('\n'); + return { variable: CHAT_AGENTS_VARIABLE, value }; + } +} + +function prettyPrintInMd(agent: { id: string; name: string; description: string; }): string { + return `- ${agent.id} + - *ID*: ${agent.id} + - *Name*: ${agent.name} + - *Description*: ${agent.description.replace(/\n/g, ' ')}`; +} + diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts new file mode 100644 index 0000000000000..fd101832c9bb3 --- /dev/null +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -0,0 +1,383 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatAgents.ts + +import { + CommunicationRecordingService, + getTextOfResponse, + LanguageModel, + LanguageModelRequirement, + LanguageModelResponse, + PromptService, + ResolvedPromptTemplate, + ToolRequest, +} from '@theia/ai-core'; +import { + Agent, + isLanguageModelStreamResponse, + isLanguageModelTextResponse, + LanguageModelRegistry, + LanguageModelStreamResponsePart, + MessageActor, +} from '@theia/ai-core/lib/common'; +import { CancellationToken, CancellationTokenSource, ILogger, isArray } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatAgentService } from './chat-agent-service'; +import { + ChatModel, + ChatRequestModel, + ChatRequestModelImpl, + ChatResponseContent, + CodeChatResponseContentImpl, + ErrorChatResponseContentImpl, + MarkdownChatResponseContentImpl, + ToolCallChatResponseContentImpl +} from './chat-model'; + +/** + * A conversation consists of a sequence of ChatMessages. + * Each ChatMessage is either a user message, AI message or a system message. + * + * For now we only support text based messages. + */ +export interface ChatMessage { + actor: MessageActor; + type: 'text'; + query: string; +} + +/** + * System message content, enriched with function descriptions. + */ +export interface SystemMessageDescription { + text: string; + /** All functions references in the system message. */ + functionDescriptions?: Map; +} +export namespace SystemMessageDescription { + export function fromResolvedPromptTemplate(resolvedPrompt: ResolvedPromptTemplate): SystemMessageDescription { + return { + text: resolvedPrompt.text, + functionDescriptions: resolvedPrompt.functionDescriptions + }; + } +} + +/** + * The location from where an chat agent may be invoked. + * Based on the location, a different context may be available. + */ +export enum ChatAgentLocation { + Panel = 'panel', + Terminal = 'terminal', + Notebook = 'notebook', + Editor = 'editor' +} + +export namespace ChatAgentLocation { + export const ALL: ChatAgentLocation[] = [ChatAgentLocation.Panel, ChatAgentLocation.Terminal, ChatAgentLocation.Notebook, ChatAgentLocation.Editor]; + + export function fromRaw(value: string): ChatAgentLocation { + switch (value) { + case 'panel': return ChatAgentLocation.Panel; + case 'terminal': return ChatAgentLocation.Terminal; + case 'notebook': return ChatAgentLocation.Notebook; + case 'editor': return ChatAgentLocation.Editor; + } + return ChatAgentLocation.Panel; + } +} + +export const ChatAgent = Symbol('ChatAgent'); +/** + * A chat agent is a specialized agent with a common interface for its invocation. + */ +export interface ChatAgent extends Agent { + locations: ChatAgentLocation[]; + iconClass?: string; + invoke(request: ChatRequestModelImpl, chatAgentService?: ChatAgentService): Promise; +} + +@injectable() +export abstract class AbstractChatAgent { + @inject(LanguageModelRegistry) protected languageModelRegistry: LanguageModelRegistry; + @inject(ILogger) protected logger: ILogger; + @inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService; + @inject(PromptService) protected promptService: PromptService; + constructor( + public id: string, + public languageModelRequirements: LanguageModelRequirement[], + protected defaultLanguageModelPurpose: string, + public iconClass: string = 'codicon codicon-copilot', + public locations: ChatAgentLocation[] = ChatAgentLocation.ALL) { + } + + async invoke(request: ChatRequestModelImpl): Promise { + try { + const languageModel = await this.getLanguageModel(this.defaultLanguageModelPurpose); + if (!languageModel) { + throw new Error('Couldn\'t find a matching language model. Please check your setup!'); + } + const messages = await this.getMessages(request.session); + this.recordingService.recordRequest({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.id, + request: request.request.text, + messages + }); + + const systemMessageDescription = await this.getSystemMessageDescription(); + const tools: Map = new Map(); + if (systemMessageDescription) { + const systemMsg: ChatMessage = { + actor: 'system', + type: 'text', + query: systemMessageDescription.text + }; + // insert system message at the beginning of the request messages + messages.unshift(systemMsg); + systemMessageDescription.functionDescriptions?.forEach((tool, id) => { + tools.set(id, tool); + }); + } + this.getTools(request)?.forEach(tool => tools.set(tool.id, tool)); + + const cancellationToken = new CancellationTokenSource(); + request.response.onDidChange(() => { + if (request.response.isCanceled) { + cancellationToken.cancel(); + } + }); + + const languageModelResponse = await this.callLlm( + languageModel, + messages, + tools.size > 0 ? Array.from(tools.values()) : undefined, + cancellationToken.token + ); + await this.addContentsToResponse(languageModelResponse, request); + request.response.complete(); + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + }); + } catch (e) { + this.handleError(request, e); + } + } + + protected handleError(request: ChatRequestModelImpl, error: Error): void { + request.response.response.addContent(new ErrorChatResponseContentImpl(error)); + request.response.error(error); + } + + protected getLanguageModelSelector(languageModelPurpose: string): LanguageModelRequirement { + return this.languageModelRequirements.find(req => req.purpose === languageModelPurpose)!; + } + + protected async getLanguageModel(languageModelPurpose: string): Promise { + return this.selectLanguageModel(this.getLanguageModelSelector(languageModelPurpose)); + } + + protected async selectLanguageModel(selector: LanguageModelRequirement): Promise { + const languageModel = await this.languageModelRegistry.selectLanguageModel({ agent: this.id, ...selector }); + if (!languageModel) { + throw new Error('Couldn\'t find a language model. Please check your setup!'); + } + return languageModel; + } + + protected abstract getSystemMessageDescription(): Promise; + + protected async getMessages( + model: ChatModel, includeResponseInProgress = false + ): Promise { + const requestMessages = model.getRequests().flatMap(request => { + const messages: ChatMessage[] = []; + const text = request.message.parts.map(part => part.promptText).join(''); + messages.push({ + actor: 'user', + type: 'text', + query: text, + }); + if (request.response.isComplete || includeResponseInProgress) { + messages.push({ + actor: 'ai', + type: 'text', + query: request.response.response.asString(), + }); + } + return messages; + }); + + return requestMessages; + } + + /** + * @returns the list of tools used by this agent, or undefined if none is needed. + */ + protected getTools(request: ChatRequestModel): ToolRequest[] | undefined { + return request.message.toolRequests.size > 0 + ? [...request.message.toolRequests.values()] + : undefined; + } + + protected async callLlm( + languageModel: LanguageModel, + messages: ChatMessage[], + tools: ToolRequest[] | undefined, + token: CancellationToken + ): Promise { + const languageModelResponse = languageModel.request({ + messages, + tools, + }, token); + return languageModelResponse; + } + + protected abstract addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise; +} + +@injectable() +export abstract class AbstractTextToModelParsingChatAgent extends AbstractChatAgent { + + protected async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise { + const responseAsText = await getTextOfResponse(languageModelResponse); + const parsedCommand = await this.parseTextResponse(responseAsText); + const content = this.createResponseContent(parsedCommand, request); + request.response.response.addContent(content); + } + + protected abstract parseTextResponse(text: string): Promise; + + protected abstract createResponseContent(parsedModel: T, request: ChatRequestModelImpl): ChatResponseContent; +} + +@injectable() +export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { + + protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise { + if (isLanguageModelTextResponse(languageModelResponse)) { + request.response.response.addContent( + new MarkdownChatResponseContentImpl(languageModelResponse.text) + ); + request.response.complete(); + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + }); + return; + } + if (isLanguageModelStreamResponse(languageModelResponse)) { + for await (const token of languageModelResponse.stream) { + const newContents = this.parse(token, request.response.response.content); + if (isArray(newContents)) { + newContents.forEach(newContent => request.response.response.addContent(newContent)); + } else { + request.response.response.addContent(newContents); + } + + const lastContent = request.response.response.content.pop(); + if (lastContent === undefined) { + return; + } + const text = lastContent.asString?.(); + if (text === undefined) { + return; + } + let curSearchIndex = 0; + const result: ChatResponseContent[] = []; + while (curSearchIndex < text.length) { + // find start of code block: ```[language]\n[\n]``` + const codeStartIndex = text.indexOf('```', curSearchIndex); + if (codeStartIndex === -1) { + break; + } + + // find language specifier if present + const newLineIndex = text.indexOf('\n', codeStartIndex + 3); + const language = codeStartIndex + 3 < newLineIndex ? text.substring(codeStartIndex + 3, newLineIndex) : undefined; + + // find end of code block + const codeEndIndex = text.indexOf('```', codeStartIndex + 3); + if (codeEndIndex === -1) { + break; + } + + // add text before code block as markdown content + result.push(new MarkdownChatResponseContentImpl(text.substring(curSearchIndex, codeStartIndex))); + // add code block as code content + const codeText = text.substring(newLineIndex + 1, codeEndIndex).trimEnd(); + result.push(new CodeChatResponseContentImpl(codeText, language)); + curSearchIndex = codeEndIndex + 3; + } + + if (result.length > 0) { + result.forEach(r => { + request.response.response.addContent(r); + }); + } else { + request.response.response.addContent(lastContent); + } + } + request.response.complete(); + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + }); + return; + } + this.logger.error( + 'Received unknown response in agent. Return response as text' + ); + request.response.response.addContent( + new MarkdownChatResponseContentImpl( + JSON.stringify(languageModelResponse) + ) + ); + } + + private parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] { + const content = token.content; + // eslint-disable-next-line no-null/no-null + if (content !== undefined && content !== null) { + return new MarkdownChatResponseContentImpl(content); + } + const toolCalls = token.tool_calls; + if (toolCalls !== undefined) { + const toolCallContents = toolCalls.map(toolCall => + new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result)); + return toolCallContents; + } + return new MarkdownChatResponseContentImpl(''); + } + +} diff --git a/packages/ai-chat/src/common/chat-model.ts b/packages/ai-chat/src/common/chat-model.ts new file mode 100644 index 0000000000000..a7b192f820af9 --- /dev/null +++ b/packages/ai-chat/src/common/chat-model.ts @@ -0,0 +1,769 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts + +import { Command, Emitter, Event, generateUuid, URI } from '@theia/core'; +import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering'; +import { Position } from '@theia/core/shared/vscode-languageserver-protocol'; +import { ChatAgentLocation } from './chat-agents'; +import { ParsedChatRequest } from './parsed-chat-request'; + +/********************** + * INTERFACES AND TYPE GUARDS + **********************/ + +export type ChatChangeEvent = + | ChatAddRequestEvent + | ChatAddResponseEvent + | ChatRemoveRequestEvent; + +export interface ChatAddRequestEvent { + kind: 'addRequest'; + request: ChatRequestModel; +} + +export interface ChatAddResponseEvent { + kind: 'addResponse'; + response: ChatResponseModel; +} + +export type ChatRequestRemovalReason = 'removal' | 'resend' | 'adoption'; + +export interface ChatRemoveRequestEvent { + kind: 'removeRequest'; + requestId: string; + responseId?: string; + reason: ChatRequestRemovalReason; +} + +export interface ChatModel { + readonly onDidChange: Event; + readonly id: string; + readonly location: ChatAgentLocation; + getRequests(): ChatRequestModel[]; + isEmpty(): boolean; +} + +export interface ChatRequest { + readonly text: string; + readonly displayText?: string; +} + +export interface ChatRequestModel { + readonly id: string; + readonly session: ChatModel; + readonly request: ChatRequest; + readonly response: ChatResponseModel; + readonly message: ParsedChatRequest; + readonly agentId?: string; +} + +export interface ChatProgressMessage { + kind: 'progressMessage'; + id: string; + status: 'inProgress' | 'completed' | 'failed'; + content: string; +} + +export interface ChatResponseContent { + kind: string; + /** + * Represents the content as a string. Returns `undefined` if the content + * is purely informational and/or visual and should not be included in the overall + * representation of the response. + */ + asString?(): string | undefined; + merge?(nextChatResponseContent: ChatResponseContent): boolean; +} + +export namespace ChatResponseContent { + export function is(obj: unknown): obj is ChatResponseContent { + return !!( + obj && + typeof obj === 'object' && + 'kind' in obj && + typeof (obj as { kind: unknown }).kind === 'string' + ); + } + export function hasAsString( + obj: ChatResponseContent + ): obj is Required> & ChatResponseContent { + return typeof obj.asString === 'function'; + } + export function hasMerge( + obj: ChatResponseContent + ): obj is Required> & ChatResponseContent { + return typeof obj.merge === 'function'; + } +} + +export interface TextChatResponseContent + extends Required { + kind: 'text'; + content: string; +} + +export interface ErrorChatResponseContent extends ChatResponseContent { + kind: 'error'; + error: Error; +} + +export interface MarkdownChatResponseContent + extends Required { + kind: 'markdownContent'; + content: MarkdownString; +} + +export interface CodeChatResponseContent + extends ChatResponseContent { + kind: 'code'; + code: string; + language?: string; + location?: Location; +} + +export interface HorizontalLayoutChatResponseContent extends Required { + kind: 'horizontal'; + content: ChatResponseContent[]; +} + +export interface ToolCallChatResponseContent extends Required { + kind: 'toolCall'; + id?: string; + name?: string; + arguments?: string; + finished: boolean; + result?: string; +} + +export interface Location { + uri: URI; + position: Position; +} +export namespace Location { + export function is(obj: unknown): obj is Location { + return !!obj && typeof obj === 'object' && + 'uri' in obj && (obj as { uri: unknown }).uri instanceof URI && + 'position' in obj && Position.is((obj as { position: unknown }).position); + } +} + +export interface CustomCallback { + label: string; + callback: () => Promise; +} + +/** + * A command chat response content represents a command that is offered to the user for execution. + * It either refers to an already registered Theia command or provides a custom callback. + * If both are given, the custom callback will be preferred. + */ +export interface CommandChatResponseContent extends ChatResponseContent { + kind: 'command'; + command?: Command; + customCallback?: CustomCallback; + arguments?: unknown[]; +} + +/** + * An informational chat response content represents a message that is purely informational and should not be included in the overall representation of the response. + */ +export interface InformationalChatResponseContent extends ChatResponseContent { + kind: 'informational'; + content: MarkdownString; +} + +export namespace TextChatResponseContent { + export function is(obj: unknown): obj is TextChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'text' && + 'content' in obj && + typeof (obj as { content: unknown }).content === 'string' + ); + } +} + +export namespace MarkdownChatResponseContent { + export function is(obj: unknown): obj is MarkdownChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'markdownContent' && + 'content' in obj && + MarkdownString.is((obj as { content: unknown }).content) + ); + } +} + +export namespace InformationalChatResponseContent { + export function is(obj: unknown): obj is InformationalChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'informational' && + 'content' in obj && + MarkdownString.is((obj as { content: unknown }).content) + ); + } +} + +export namespace CommandChatResponseContent { + export function is(obj: unknown): obj is CommandChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'command' && + 'command' in obj && + Command.is((obj as { command: unknown }).command) + ); + } +} + +export namespace CodeChatResponseContent { + export function is(obj: unknown): obj is CodeChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'code' && + 'code' in obj && + typeof (obj as { code: unknown }).code === 'string' + ); + } +} + +export namespace HorizontalLayoutChatResponseContent { + export function is( + obj: unknown + ): obj is HorizontalLayoutChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'horizontal' && + 'content' in obj && + Array.isArray((obj as { content: unknown }).content) && + (obj as { content: unknown[] }).content.every( + ChatResponseContent.is + ) + ); + } +} + +export namespace ToolCallChatResponseContent { + export function is(obj: unknown): obj is ToolCallChatResponseContent { + return ChatResponseContent.is(obj) && obj.kind === 'toolCall'; + } +} + +export namespace ErrorChatResponseContent { + export function is(obj: unknown): obj is ErrorChatResponseContent { + return ( + ChatResponseContent.is(obj) && + obj.kind === 'error' && + 'error' in obj && + obj.error instanceof Error + ); + } +} + +export interface ChatResponse { + readonly content: ChatResponseContent[]; + asString(): string; +} + +export interface ChatResponseModel { + readonly onDidChange: Event; + readonly id: string; + readonly requestId: string; + readonly progressMessages: ChatProgressMessage[]; + readonly response: ChatResponse; + readonly isComplete: boolean; + readonly isCanceled: boolean; + readonly isError: boolean; + readonly agentId?: string + readonly errorObject?: Error; +} + +/********************** + * Implementations + **********************/ + +export class ChatModelImpl implements ChatModel { + protected readonly _onDidChangeEmitter = new Emitter(); + onDidChange: Event = this._onDidChangeEmitter.event; + + protected _requests: ChatRequestModelImpl[]; + protected _id: string; + + constructor(public readonly location = ChatAgentLocation.Panel) { + // TODO accept serialized data as a parameter to restore a previously saved ChatModel + this._requests = []; + this._id = generateUuid(); + } + + getRequests(): ChatRequestModelImpl[] { + return this._requests; + } + + get id(): string { + return this._id; + } + + addRequest(parsedChatRequest: ParsedChatRequest, agentId?: string): ChatRequestModelImpl { + const requestModel = new ChatRequestModelImpl(this, parsedChatRequest, agentId); + this._requests.push(requestModel); + this._onDidChangeEmitter.fire({ + kind: 'addRequest', + request: requestModel, + }); + return requestModel; + } + + isEmpty(): boolean { + return this._requests.length === 0; + } +} + +export class ChatRequestModelImpl implements ChatRequestModel { + protected readonly _id: string; + protected _session: ChatModel; + protected _request: ChatRequest; + protected _response: ChatResponseModelImpl; + protected _agentId?: string; + + constructor(session: ChatModel, public readonly message: ParsedChatRequest, agentId?: string) { + // TODO accept serialized data as a parameter to restore a previously saved ChatRequestModel + this._request = message.request; + this._id = generateUuid(); + this._session = session; + this._response = new ChatResponseModelImpl(this._id, agentId); + this._agentId = agentId; + } + + get id(): string { + return this._id; + } + + get session(): ChatModel { + return this._session; + } + + get request(): ChatRequest { + return this._request; + } + + get response(): ChatResponseModelImpl { + return this._response; + } + + get agentId(): string | undefined { + return this._agentId; + } +} + +export class ErrorChatResponseContentImpl implements ErrorChatResponseContent { + kind: 'error' = 'error'; + protected _error: Error; + constructor(error: Error) { + this._error = error; + } + get error(): Error { + return this._error; + } + asString(): string | undefined { + return undefined; + } +} + +export class TextChatResponseContentImpl implements TextChatResponseContent { + kind: 'text' = 'text'; + protected _content: string; + + constructor(content: string) { + this._content = content; + } + + get content(): string { + return this._content; + } + + asString(): string { + return this._content; + } + + merge(nextChatResponseContent: TextChatResponseContent): boolean { + this._content += nextChatResponseContent.content; + return true; + } +} + +export class MarkdownChatResponseContentImpl implements MarkdownChatResponseContent { + kind: 'markdownContent' = 'markdownContent'; + protected _content: MarkdownStringImpl = new MarkdownStringImpl(); + + constructor(content: string) { + this._content.appendMarkdown(content); + } + + get content(): MarkdownString { + return this._content; + } + + asString(): string { + return this._content.value; + } + + merge(nextChatResponseContent: MarkdownChatResponseContent): boolean { + this._content.appendMarkdown(nextChatResponseContent.content.value); + return true; + } +} + +export class InformationalChatResponseContentImpl implements InformationalChatResponseContent { + kind: 'informational' = 'informational'; + protected _content: MarkdownStringImpl; + + constructor(content: string) { + this._content = new MarkdownStringImpl(content); + } + + get content(): MarkdownString { + return this._content; + } + + asString(): string | undefined { + return undefined; + } + + merge(nextChatResponseContent: InformationalChatResponseContent): boolean { + this._content.appendMarkdown(nextChatResponseContent.content.value); + return true; + } +} + +export class CodeChatResponseContentImpl implements CodeChatResponseContent { + kind: 'code' = 'code'; + protected _code: string; + protected _language?: string; + protected _location?: Location; + + constructor(code: string, language?: string, location?: Location) { + this._code = code; + this._language = language; + this._location = location; + } + + get code(): string { + return this._code; + } + + get language(): string | undefined { + return this._language; + } + + get location(): Location | undefined { + return this._location; + } + + asString(): string { + return `\`\`\`${this._language ?? ''}\n${this._code}\n\`\`\``; + } + + merge(nextChatResponseContent: CodeChatResponseContent): boolean { + this._code += `${nextChatResponseContent.code}`; + return true; + } +} + +export class ToolCallChatResponseContentImpl implements ToolCallChatResponseContent { + kind: 'toolCall' = 'toolCall'; + protected _id?: string; + protected _name?: string; + protected _arguments?: string; + protected _finished?: boolean; + protected _result?: string; + + constructor(id?: string, name?: string, arg_string?: string, finished?: boolean, result?: string) { + this._id = id; + this._name = name; + this._arguments = arg_string; + this._finished = finished; + this._result = result; + } + + get id(): string | undefined { + return this._id; + } + + get name(): string | undefined { + return this._name; + } + + get arguments(): string | undefined { + return this._arguments; + } + + get finished(): boolean { + return this._finished === undefined ? false : this._finished; + } + get result(): string | undefined { + return this._result; + } + + asString(): string { + return `Tool call: ${this._name}(${this._arguments ?? ''})`; + } + merge(nextChatResponseContent: ToolCallChatResponseContent): boolean { + if (nextChatResponseContent.id === this.id) { + this._finished = nextChatResponseContent.finished; + this._result = nextChatResponseContent.result; + return true; + } + if (nextChatResponseContent.name !== undefined) { + return false; + } + if (nextChatResponseContent.arguments === undefined) { + return false; + } + this._arguments += `${nextChatResponseContent.arguments}`; + return true; + } +} + +export const COMMAND_CHAT_RESPONSE_COMMAND: Command = { + id: 'ai-chat.command-chat-response.generic' +}; +export class CommandChatResponseContentImpl implements CommandChatResponseContent { + kind: 'command' = 'command'; + + constructor(public command?: Command, public customCallback?: CustomCallback, protected args?: unknown[]) { + } + + get arguments(): unknown[] { + return this.args ?? []; + } + + asString(): string { + return this.command?.id || this.customCallback?.label || 'command'; + } +} + +export class HorizontalLayoutChatResponseContentImpl implements HorizontalLayoutChatResponseContent { + kind: 'horizontal' = 'horizontal'; + protected _content: ChatResponseContent[]; + + constructor(content: ChatResponseContent[] = []) { + this._content = content; + } + + get content(): ChatResponseContent[] { + return this._content; + } + + asString(): string { + return this._content.map(child => child.asString && child.asString()).join(' '); + } + + merge(nextChatResponseContent: ChatResponseContent): boolean { + if (HorizontalLayoutChatResponseContent.is(nextChatResponseContent)) { + this._content.push(...nextChatResponseContent.content); + } else { + this._content.push(nextChatResponseContent); + } + return true; + } +} + +class ChatResponseImpl implements ChatResponse { + protected readonly _onDidChangeEmitter = new Emitter(); + onDidChange: Event = this._onDidChangeEmitter.event; + protected _content: ChatResponseContent[]; + protected _responseRepresentation: string; + + constructor() { + // TODO accept serialized data as a parameter to restore a previously saved ChatResponse + this._content = []; + } + + get content(): ChatResponseContent[] { + return this._content; + } + + addContent(nextContent: ChatResponseContent): void { + // TODO: Support more complex merges affecting different content than the last, e.g. via some kind of ProcessorRegistry + // TODO: Support more of the built-in VS Code behavior, see + // https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts#L188-L244 + if (ToolCallChatResponseContent.is(nextContent) && nextContent.id !== undefined) { + const fittingTool = this._content.find(c => ToolCallChatResponseContent.is(c) && c.id === nextContent.id); + if (fittingTool !== undefined) { + fittingTool.merge?.(nextContent); + } else { + this._content.push(nextContent); + } + } else { + const lastElement = + this._content.length > 0 + ? this._content[this._content.length - 1] + : undefined; + if (lastElement?.kind === nextContent.kind && ChatResponseContent.hasMerge(lastElement)) { + const mergeSuccess = lastElement.merge(nextContent); + if (!mergeSuccess) { + this._content.push(nextContent); + } + } else { + this._content.push(nextContent); + } + } + this._updateResponseRepresentation(); + this._onDidChangeEmitter.fire(); + } + + protected _updateResponseRepresentation(): void { + this._responseRepresentation = this._content + .map(responseContent => { + if (ChatResponseContent.hasAsString(responseContent)) { + return responseContent.asString(); + } + if (TextChatResponseContent.is(responseContent)) { + return responseContent.content; + } + console.warn( + 'Was not able to map responseContent to a string', + responseContent + ); + return undefined; + }) + .filter(text => text !== undefined) + .join('\n\n'); + } + + asString(): string { + return this._responseRepresentation; + } +} + +class ChatResponseModelImpl implements ChatResponseModel { + protected readonly _onDidChangeEmitter = new Emitter(); + onDidChange: Event = this._onDidChangeEmitter.event; + + protected _id: string; + protected _requestId: string; + protected _progressMessages: ChatProgressMessage[]; + protected _response: ChatResponseImpl; + protected _isComplete: boolean; + protected _isCanceled: boolean; + protected _agentId?: string; + protected _isError: boolean; + protected _errorObject: Error | undefined; + + constructor(requestId: string, agentId?: string) { + // TODO accept serialized data as a parameter to restore a previously saved ChatResponseModel + this._requestId = requestId; + this._id = generateUuid(); + this._progressMessages = []; + const response = new ChatResponseImpl(); + response.onDidChange(() => this._onDidChangeEmitter.fire()); + this._response = response; + this._isComplete = false; + this._isCanceled = false; + this._agentId = agentId; + } + + get id(): string { + return this._id; + } + + get requestId(): string { + return this._requestId; + } + + get progressMessages(): ChatProgressMessage[] { + return this._progressMessages; + } + + addProgressMessage(message: { content: string } & Partial>): ChatProgressMessage { + const id = message.id ?? generateUuid(); + const existingMessage = this.getProgressMessage(id); + if (existingMessage) { + this.updateProgressMessage({ id, ...message }); + return existingMessage; + } + const newMessage: ChatProgressMessage = { + kind: 'progressMessage', + id, + status: message.status ?? 'inProgress', + ...message, + }; + this._progressMessages.push(newMessage); + this._onDidChangeEmitter.fire(); + return newMessage; + } + + getProgressMessage(id: string): ChatProgressMessage | undefined { + return this._progressMessages.find(message => message.id === id); + } + + updateProgressMessage(message: { id: string } & Partial>): void { + const progressMessage = this.getProgressMessage(message.id); + if (progressMessage) { + Object.assign(progressMessage, message); + this._onDidChangeEmitter.fire(); + } + } + + get response(): ChatResponseImpl { + return this._response; + } + + get isComplete(): boolean { + return this._isComplete; + } + + get isCanceled(): boolean { + return this._isCanceled; + } + + get agentId(): string | undefined { + return this._agentId; + } + + overrideAgentId(agentId: string): void { + this._agentId = agentId; + } + + complete(): void { + this._isComplete = true; + this._onDidChangeEmitter.fire(); + } + + cancel(): void { + this._isComplete = true; + this._isCanceled = true; + this._onDidChangeEmitter.fire(); + } + error(error: Error): void { + this._isComplete = true; + this._isCanceled = false; + this._isError = true; + this._errorObject = error; + this._onDidChangeEmitter.fire(); + } + get errorObject(): Error | undefined { + return this._errorObject; + } + get isError(): boolean { + return this._isError; + } +} diff --git a/packages/ai-chat/src/common/chat-request-parser.spec.ts b/packages/ai-chat/src/common/chat-request-parser.spec.ts new file mode 100644 index 0000000000000..711c85d36bf68 --- /dev/null +++ b/packages/ai-chat/src/common/chat-request-parser.spec.ts @@ -0,0 +1,120 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as sinon from 'sinon'; +import { ChatAgentServiceImpl } from './chat-agent-service'; +import { ChatRequestParserImpl } from './chat-request-parser'; +import { ChatAgentLocation } from './chat-agents'; +import { ChatRequest } from './chat-model'; +import { expect } from 'chai'; +import { DefaultAIVariableService, ToolInvocationRegistry, ToolInvocationRegistryImpl } from '@theia/ai-core'; + +describe('ChatRequestParserImpl', () => { + const chatAgentService = sinon.createStubInstance(ChatAgentServiceImpl); + const variableService = sinon.createStubInstance(DefaultAIVariableService); + const toolInvocationRegistry: ToolInvocationRegistry = sinon.createStubInstance(ToolInvocationRegistryImpl); + const parser = new ChatRequestParserImpl(chatAgentService, variableService, toolInvocationRegistry); + + it('parses simple text', () => { + const req: ChatRequest = { + text: 'What is the best pizza topping?' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result.parts).to.deep.contain({ + text: 'What is the best pizza topping?', + range: { start: 0, endExclusive: 31 } + }); + }); + + it('parses text with variable name', () => { + const req: ChatRequest = { + text: 'What is the #best pizza topping?' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result).to.deep.contain({ + parts: [{ + text: 'What is the ', + range: { start: 0, endExclusive: 12 } + }, { + variableName: 'best', + variableArg: undefined, + range: { start: 12, endExclusive: 17 } + }, { + text: ' pizza topping?', + range: { start: 17, endExclusive: 32 } + }] + }); + }); + + it('parses text with variable name with argument', () => { + const req: ChatRequest = { + text: 'What is the #best:by-poll pizza topping?' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result).to.deep.contain({ + parts: [{ + text: 'What is the ', + range: { start: 0, endExclusive: 12 } + }, { + variableName: 'best', + variableArg: 'by-poll', + range: { start: 12, endExclusive: 25 } + }, { + text: ' pizza topping?', + range: { start: 25, endExclusive: 40 } + }] + }); + }); + + it('parses text with variable name with numeric argument', () => { + const req: ChatRequest = { + text: '#size-class:2' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result.parts[0]).to.contain( + { + variableName: 'size-class', + variableArg: '2' + } + ); + }); + + it('parses text with variable name with POSIX path argument', () => { + const req: ChatRequest = { + text: '#file:/path/to/file.ext' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result.parts[0]).to.contain( + { + variableName: 'file', + variableArg: '/path/to/file.ext' + } + ); + }); + + it('parses text with variable name with Win32 path argument', () => { + const req: ChatRequest = { + text: '#file:c:\\path\\to\\file.ext' + }; + const result = parser.parseChatRequest(req, ChatAgentLocation.Panel); + expect(result.parts[0]).to.contain( + { + variableName: 'file', + variableArg: 'c:\\path\\to\\file.ext' + } + ); + }); +}); diff --git a/packages/ai-chat/src/common/chat-request-parser.ts b/packages/ai-chat/src/common/chat-request-parser.ts new file mode 100644 index 0000000000000..8c21aaa4304c7 --- /dev/null +++ b/packages/ai-chat/src/common/chat-request-parser.ts @@ -0,0 +1,220 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatRequestParser.ts + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatAgentService } from './chat-agent-service'; +import { ChatAgentLocation } from './chat-agents'; +import { ChatRequest } from './chat-model'; +import { + chatAgentLeader, + chatFunctionLeader, + ParsedChatRequestAgentPart, + ParsedChatRequestFunctionPart, + ParsedChatRequestTextPart, + ParsedChatRequestVariablePart, + chatVariableLeader, + OffsetRange, + ParsedChatRequest, + ParsedChatRequestPart, +} from './parsed-chat-request'; +import { AIVariable, AIVariableService, ToolInvocationRegistry, ToolRequest } from '@theia/ai-core'; + +const agentReg = /^@([\w_\-\.]+)(?=(\s|$|\b))/i; // An @-agent +const functionReg = /^~([\w_\-\.]+)(?=(\s|$|\b))/i; // A ~ tool function +const variableReg = /^#([\w_\-]+)(?::([\w_\-_\/\\.:]+))?(?=(\s|$|\b))/i; // A #-variable with an optional : arg (#file:workspace/path/name.ext) + +export const ChatRequestParser = Symbol('ChatRequestParser'); +export interface ChatRequestParser { + parseChatRequest(request: ChatRequest, location: ChatAgentLocation): ParsedChatRequest; +} + +function offsetRange(start: number, endExclusive: number): OffsetRange { + if (start > endExclusive) { + throw new Error(`Invalid range: start=${start} endExclusive=${endExclusive}`); + } + return { start, endExclusive }; +} +@injectable() +export class ChatRequestParserImpl { + constructor( + @inject(ChatAgentService) private readonly agentService: ChatAgentService, + @inject(AIVariableService) private readonly variableService: AIVariableService, + @inject(ToolInvocationRegistry) private readonly toolInvocationRegistry: ToolInvocationRegistry + ) { } + + parseChatRequest(request: ChatRequest, location: ChatAgentLocation): ParsedChatRequest { + const parts: ParsedChatRequestPart[] = []; + const variables = new Map(); + const toolRequests = new Map(); + const message = request.text; + for (let i = 0; i < message.length; i++) { + const previousChar = message.charAt(i - 1); + const char = message.charAt(i); + let newPart: ParsedChatRequestPart | undefined; + + if (previousChar.match(/\s/) || i === 0) { + if (char === chatFunctionLeader) { + const functionPart = this.tryParseFunction( + message.slice(i), + i + ); + newPart = functionPart; + if (functionPart) { + toolRequests.set(functionPart.toolRequest.id, functionPart.toolRequest); + } + } else if (char === chatVariableLeader) { + const variablePart = this.tryToParseVariable( + message.slice(i), + i, + parts + ); + newPart = variablePart; + if (variablePart) { + const variable = this.variableService.getVariable(variablePart.variableName); + if (variable) { + variables.set(variable.name, variable); + } + } + } else if (char === chatAgentLeader) { + newPart = this.tryToParseAgent( + message.slice(i), + i, + parts, + location + ); + } + } + + if (newPart) { + if (i !== 0) { + // Insert a part for all the text we passed over, then insert the new parsed part + const previousPart = parts.at(-1); + const previousPartEnd = + previousPart?.range.endExclusive ?? 0; + parts.push( + new ParsedChatRequestTextPart( + offsetRange(previousPartEnd, i), + message.slice(previousPartEnd, i) + ) + ); + } + + parts.push(newPart); + } + } + + const lastPart = parts.at(-1); + const lastPartEnd = lastPart?.range.endExclusive ?? 0; + if (lastPartEnd < message.length) { + parts.push( + new ParsedChatRequestTextPart( + offsetRange(lastPartEnd, message.length), + message.slice(lastPartEnd, message.length) + ) + ); + } + + return { request, parts, toolRequests, variables }; + } + + private tryToParseAgent( + message: string, + offset: number, + parts: ReadonlyArray, + location: ChatAgentLocation + ): ParsedChatRequestAgentPart | ParsedChatRequestVariablePart | undefined { + const nextAgentMatch = message.match(agentReg); + if (!nextAgentMatch) { + return; + } + + const [full, name] = nextAgentMatch; + const agentRange = offsetRange(offset, offset + full.length); + + let agents = this.agentService.getAgents().filter(a => a.name === name); + if (!agents.length) { + const fqAgent = this.agentService.getAgent(name); + if (fqAgent) { + agents = [fqAgent]; + } + } + + // If there is more than one agent with this name, and the user picked it from the suggest widget, then the selected agent should be in the + // context and we use that one. Otherwise just pick the first. + const agent = agents[0]; + if (!agent || !agent.locations.includes(location)) { + return; + } + + if (parts.some(p => p instanceof ParsedChatRequestAgentPart)) { + // Only one agent allowed + return; + } + + // The agent must come first + if ( + parts.some( + p => + (p instanceof ParsedChatRequestTextPart && + p.text.trim() !== '') || + !(p instanceof ParsedChatRequestAgentPart) + ) + ) { + return; + } + + return new ParsedChatRequestAgentPart(agentRange, agent.id, agent.name); + } + + private tryToParseVariable( + message: string, + offset: number, + _parts: ReadonlyArray + ): ParsedChatRequestVariablePart | undefined { + const nextVariableMatch = message.match(variableReg); + if (!nextVariableMatch) { + return; + } + + const [full, name] = nextVariableMatch; + const variableArg = nextVariableMatch[2]; + const varRange = offsetRange(offset, offset + full.length); + + return new ParsedChatRequestVariablePart(varRange, name, variableArg); + } + + private tryParseFunction(message: string, offset: number): ParsedChatRequestFunctionPart | undefined { + const nextFunctionMatch = message.match(functionReg); + if (!nextFunctionMatch) { + return; + } + + const [full, id] = nextFunctionMatch; + + const maybeToolRequest = this.toolInvocationRegistry.getFunction(id); + if (!maybeToolRequest) { + return; + } + + const functionRange = offsetRange(offset, offset + full.length); + return new ParsedChatRequestFunctionPart(functionRange, maybeToolRequest); + } +} diff --git a/packages/ai-chat/src/common/chat-service.ts b/packages/ai-chat/src/common/chat-service.ts new file mode 100644 index 0000000000000..99c167e005bc9 --- /dev/null +++ b/packages/ai-chat/src/common/chat-service.ts @@ -0,0 +1,225 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatService.ts + +import { inject, injectable, optional } from '@theia/core/shared/inversify'; +import { + ChatModel, + ChatModelImpl, + ChatRequest, + ChatRequestModel, + ChatResponseModel, +} from './chat-model'; +import { ChatAgentService } from './chat-agent-service'; +import { Emitter, ILogger } from '@theia/core'; +import { ChatRequestParser } from './chat-request-parser'; +import { ChatAgent, ChatAgentLocation } from './chat-agents'; +import { ParsedChatRequestAgentPart, ParsedChatRequestVariablePart, ParsedChatRequest } from './parsed-chat-request'; +import { AIVariableService } from '@theia/ai-core'; +import { Event } from '@theia/core/shared/vscode-languageserver-protocol'; + +export interface ChatRequestInvocation { + /** + * Promise which completes once the request preprocessing is complete. + */ + requestCompleted: Promise; + /** + * Promise which completes once a response is expected to arrive. + */ + responseCreated: Promise; + /** + * Promise which completes once the response is complete. + */ + responseCompleted: Promise; +} + +export interface ChatSession { + id: string; + title?: string; + model: ChatModel; + isActive: boolean; +} + +export interface ActiveSessionChangedEvent { + sessionId: string | undefined; + focus?: boolean; +} + +export interface SessionOptions { + focus?: boolean; +} + +export const DefaultChatAgentId = Symbol('DefaultChatAgentId'); +export interface DefaultChatAgentId { + id: string; +} + +export const ChatService = Symbol('ChatService'); +export interface ChatService { + onActiveSessionChanged: Event + + getSession(id: string): ChatSession | undefined; + getSessions(): ChatSession[]; + createSession(location?: ChatAgentLocation, options?: SessionOptions): ChatSession; + deleteSession(sessionId: string): void; + setActiveSession(sessionId: string, options?: SessionOptions): void; + + sendRequest( + sessionId: string, + request: ChatRequest + ): Promise; +} + +interface ChatSessionInternal extends ChatSession { + model: ChatModelImpl; +} + +@injectable() +export class ChatServiceImpl implements ChatService { + protected readonly onActiveSessionChangedEmitter = new Emitter(); + onActiveSessionChanged = this.onActiveSessionChangedEmitter.event; + + @inject(ChatAgentService) + protected chatAgentService: ChatAgentService; + + @inject(DefaultChatAgentId) @optional() + protected defaultChatAgentId: DefaultChatAgentId | undefined; + + @inject(ChatRequestParser) + protected chatRequestParser: ChatRequestParser; + + @inject(AIVariableService) + protected variableService: AIVariableService; + + @inject(ILogger) + protected logger: ILogger; + + protected _sessions: ChatSessionInternal[] = []; + + getSessions(): ChatSessionInternal[] { + return [...this._sessions]; + } + + getSession(id: string): ChatSessionInternal | undefined { + return this._sessions.find(session => session.id === id); + } + + createSession(location = ChatAgentLocation.Panel, options?: SessionOptions): ChatSession { + const model = new ChatModelImpl(location); + const session: ChatSessionInternal = { + id: model.id, + model, + isActive: true + }; + this._sessions.push(session); + this.setActiveSession(session.id, options); + return session; + } + + deleteSession(sessionId: string): void { + // If the removed session is the active one, set the newest one as active + if (this.getSession(sessionId)?.isActive) { + this.setActiveSession(this._sessions[this._sessions.length - 1]?.id); + } + this._sessions = this._sessions.filter(item => item.id !== sessionId); + } + + setActiveSession(sessionId: string | undefined, options?: SessionOptions): void { + this._sessions.forEach(session => { + session.isActive = session.id === sessionId; + }); + this.onActiveSessionChangedEmitter.fire({ sessionId: sessionId, ...options }); + } + + async sendRequest( + sessionId: string, + request: ChatRequest + ): Promise { + const session = this.getSession(sessionId); + if (!session) { + return undefined; + } + session.title = request.text; + + const parsedRequest = this.chatRequestParser.parseChatRequest(request, session.model.location); + + const agent = this.getAgent(parsedRequest); + const requestModel = session.model.addRequest(parsedRequest, agent?.id); + + for (const part of parsedRequest.parts) { + if (part instanceof ParsedChatRequestVariablePart) { + const resolvedVariable = await this.variableService.resolveVariable( + { variable: part.variableName, arg: part.variableArg }, + { request, model: session } + ); + if (resolvedVariable) { + part.resolution = resolvedVariable; + } else { + this.logger.warn(`Failed to resolve variable ${part.variableName} for ${session.model.location}`); + } + } + } + + let resolveResponseCreated: (responseModel: ChatResponseModel) => void; + let resolveResponseCompleted: (responseModel: ChatResponseModel) => void; + const invocation: ChatRequestInvocation = { + requestCompleted: Promise.resolve(requestModel), + responseCreated: new Promise(resolve => { + resolveResponseCreated = resolve; + }), + responseCompleted: new Promise(resolve => { + resolveResponseCompleted = resolve; + }), + }; + + resolveResponseCreated!(requestModel.response); + requestModel.response.onDidChange(() => { + if (requestModel.response.isComplete) { + resolveResponseCompleted!(requestModel.response); + } + if (requestModel.response.isError) { + resolveResponseCompleted!(requestModel.response); + } + }); + + if (agent) { + agent.invoke(requestModel).catch(error => requestModel.response.error(error)); + } else { + this.logger.error('No ChatAgents available to handle request!', requestModel); + } + + return invocation; + } + + protected getAgent(parsedRequest: ParsedChatRequest): ChatAgent | undefined { + const agentPart = this.getMentionedAgent(parsedRequest); + if (agentPart) { + return this.chatAgentService.getAgent(agentPart.agentId); + } + if (this.defaultChatAgentId) { + return this.chatAgentService.getAgent(this.defaultChatAgentId.id); + } + return this.chatAgentService.getAgents()[0] ?? undefined; + } + + protected getMentionedAgent(parsedRequest: ParsedChatRequest): ParsedChatRequestAgentPart | undefined { + return parsedRequest.parts.find(p => p instanceof ParsedChatRequestAgentPart) as ParsedChatRequestAgentPart | undefined; + } +} diff --git a/packages/ai-chat/src/common/command-chat-agents.ts b/packages/ai-chat/src/common/command-chat-agents.ts new file mode 100644 index 0000000000000..0aac25e178f12 --- /dev/null +++ b/packages/ai-chat/src/common/command-chat-agents.ts @@ -0,0 +1,343 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { AbstractTextToModelParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; +import { + PromptTemplate, +} from '@theia/ai-core'; +import { + ChatRequestModelImpl, + ChatResponseContent, + CommandChatResponseContentImpl, + CustomCallback, + HorizontalLayoutChatResponseContentImpl, + MarkdownChatResponseContentImpl, +} from './chat-model'; +import { + CommandRegistry, + MessageService, + generateUuid, +} from '@theia/core'; + +export const commandChatAgentSystemPromptTemplate: PromptTemplate = { + id: 'command-chat-agent-system-prompt-template', + template: `# System Prompt + +You are a service that helps users find commands to execute in an IDE. +You reply with stringified JSON Objects that tell the user which command to execute and its arguments, if any. + +# Examples + +The examples start with a short explanation of the return object. +The response can be found within the markdown \`\`\`json and \`\`\` markers. +Please include these markers in the reply. + +Never under any circumstances may you reply with just the command-id! + +## Example 1 + +This reply is to tell the user to execute the \`theia-ai-prompt-template:show-prompts-command\` command that is available in the Theia command registry. + +\`\`\`json +{ + "type": "theia-command", + "commandId": "theia-ai-prompt-template:show-prompts-command" +} +\`\`\` + +## Example 2 + +This reply is to tell the user to execute the \`theia-ai-prompt-template:show-prompts-command\` command that is available in the theia command registry, +when the user want to pass arguments to the command. + +\`\`\`json +{ + "type": "theia-command", + "commandId": "theia-ai-prompt-template:show-prompts-command", + "arguments": ["foo"] +} +\`\`\` + +## Example 3 + +This reply is for custom commands that are not registered in the Theia command registry. +These commands always have the command id \`ai-chat.command-chat-response.generic\`. +The arguments are an array and may differ, depending on the user's instructions. + +\`\`\`json +{ + "type": "custom-handler", + "commandId": "ai-chat.command-chat-response.generic", + "arguments": ["foo", "bar"] +} +\`\`\` + +## Example 4 + +This reply of type no-command is for cases where you can't find a proper command. +You may use the message to explain the situation to the user. + +\`\`\`json +{ + "type": "no-command", + "message": "a message explaining what is wrong" +} +\`\`\` + +# Rules + +## Theia Commands + +If a user asks for a Theia command, or the context implies it is about a command in Theia, return a response with \`"type": "theia-command"\`. +You need to exchange the "commandId". +The available command ids in Theia are in the list below. The list of commands is formatted like this: + +command-id1: Label1 +command-id2: Label2 +command-id3: +command-id4: Label4 + +The Labels may be empty, but there is always a command-id. + +Suggest a command that probably fits the user's message based on the label and the command ids you know. +If you have multiple commands that fit, return the one that fits best. We only want a single command in the reply. +If the user says that the last command was not right, try to return the next best fit based on the conversation history with the user. + +If there are no more command ids that seem to fit, return a response of \`"type": "no-command"\` explaining the situation. + +Here are the known Theia commands: + +Begin List: +{{command-ids}} +End List + +You may only use commands from this list when responding with \`"type": "theia-command"\`. +Do not come up with command ids that are not in this list. +If you need to do this, use the \`"type": "no-command"\`. instead + +## Custom Handlers + +If the user asks for a command that is not a Theia command, return a response with \`"type": "custom-handler"\`. + +## Other Cases + +In all other cases, return a reply of \`"type": "no-command"\`. + +# Examples of Invalid Responses + +## Invalid Response Example 1 + +This example is invalid because it returns text and two commands. +Only one command should be replied, and it must be parseable JSON. + +### The Example + +Yes, there are a few more theme-related commands. Here is another one: + +\`\`\`json +{ + "type": "theia-command", + "commandId": "workbench.action.selectIconTheme" +} +\`\`\` + +And another one: + +\`\`\`json +{ + "type": "theia-command", + "commandId": "core.close.right.tabs" +} +\`\`\` + +## Invalid Response Example 2 + +The following example is invalid because it only returns the command id and is not parseable JSON: + +### The Example + +workbench.action.selectIconTheme + +## Invalid Response Example 3 + +The following example is invalid because it returns a message with the command id. We need JSON objects based on the above rules. +Do not respond like this in any case! We need a command of \`"type": "theia-command"\`. + +The expected response would be: +\`\`\`json +{ + "type": "theia-command", + "commandId": "core.close.right.tabs" +} +\`\`\` + +### The Example + +I found this command that might help you: core.close.right.tabs + +## Invalid Response Example 4 + +The following example is invalid because it has an explanation string before the JSON. +We only want the JSON! + +### The Example + +You can toggle high contrast mode with this command: + +\`\`\`json +{ + "type": "theia-command", + "commandId": "editor.action.toggleHighContrast" +} +\`\`\` + +## Invalid Response Example 5 + +The following example is invalid because it explains that no command was found. +We want a response of \`"type": "no-command"\` and have the message there. + +### The Example + +There is no specific command available to "open the windows" in the provided Theia command list. + +## Invalid Response Example 6 + +In this example we were using the following theia id command list: + +Begin List: +container--theia-open-editors-widget: Hello +foo:toggle-visibility-explorer-view-container--files: Label 1 +foo:toggle-visibility-explorer-view-container--plugin-view: Label 2 +End List + +The problem is that workbench.action.toggleHighContrast is not in this list. +theia-command types may only use commandIds from this list. +This should have been of \`"type": "no-command"\`. + +### The Example + +\`\`\`json +{ + "type": "theia-command", + "commandId": "workbench.action.toggleHighContrast" +} +\`\`\` + +`}; + +interface ParsedCommand { + type: 'theia-command' | 'custom-handler' | 'no-command' + commandId: string; + arguments?: string[]; + message?: string; +} + +@injectable() +export class CommandChatAgent extends AbstractTextToModelParsingChatAgent implements ChatAgent { + @inject(CommandRegistry) + protected commandRegistry: CommandRegistry; + @inject(MessageService) + protected messageService: MessageService; + + readonly name: string; + readonly description: string; + readonly variables: string[]; + readonly promptTemplates: PromptTemplate[]; + constructor( + ) { + super('Command', [{ + purpose: 'command', + identifier: 'openai/gpt-4o', + }], 'command'); + this.name = 'Command Chat Agent'; + this.description = 'This agent is aware of all commands that the user can execute within the Theia IDE, the tool that the user is currently working with. \ + Based on the user request, it can find the right command and then let the user execute it.'; + this.variables = []; + this.promptTemplates = [commandChatAgentSystemPromptTemplate]; + } + + protected async getSystemMessageDescription(): Promise { + const knownCommands: string[] = []; + for (const command of this.commandRegistry.getAllCommands()) { + knownCommands.push(`${command.id}: ${command.label}`); + } + const systemPrompt = await this.promptService.getPrompt('command-chat-agent-system-prompt-template', { + 'command-ids': knownCommands.join('\n') + }); + if (systemPrompt === undefined) { + throw new Error('Couldn\'t get system prompt '); + } + return SystemMessageDescription.fromResolvedPromptTemplate(systemPrompt); + } + + /** + * @param text the text received from the language model + * @returns the parsed command if the text contained a valid command. + * If there was no json in the text, return a no-command response. + */ + protected async parseTextResponse(text: string): Promise { + const jsonMatch = text.match(/(\{[\s\S]*\})/); + const jsonString = jsonMatch ? jsonMatch[1] : `{ + "type": "no-command", + "message": "Please try again." +}`; + const parsedCommand = JSON.parse(jsonString) as ParsedCommand; + return parsedCommand; + } + + protected createResponseContent(parsedCommand: ParsedCommand, request: ChatRequestModelImpl): ChatResponseContent { + if (parsedCommand.type === 'theia-command') { + const theiaCommand = this.commandRegistry.getCommand(parsedCommand.commandId); + if (theiaCommand === undefined) { + console.error(`No Theia Command with id ${parsedCommand.commandId}`); + request.response.cancel(); + } + const args = parsedCommand.arguments !== undefined && + parsedCommand.arguments.length > 0 + ? parsedCommand.arguments + : undefined; + + return new HorizontalLayoutChatResponseContentImpl([ + new MarkdownChatResponseContentImpl( + 'I found this command that might help you:' + ), + new CommandChatResponseContentImpl(theiaCommand, undefined, args), + ]); + } else if (parsedCommand.type === 'custom-handler') { + const id = `ai-command-${generateUuid()}`; + const commandArgs = parsedCommand.arguments !== undefined && parsedCommand.arguments.length > 0 ? parsedCommand.arguments : []; + const args = [id, ...commandArgs]; + const customCallback: CustomCallback = { + label: 'AI command', + callback: () => this.commandCallback(...args), + }; + return new HorizontalLayoutChatResponseContentImpl([ + new MarkdownChatResponseContentImpl( + 'Try executing this:' + ), + new CommandChatResponseContentImpl(undefined, customCallback, args), + ]); + } else { + return new MarkdownChatResponseContentImpl(parsedCommand.message ?? 'Sorry, I can\'t find such a command'); + } + } + + protected async commandCallback(...commandArgs: unknown[]): Promise { + this.messageService.info(`Executing callback with args ${commandArgs.join(', ')}. The first arg is the command id registered for the dynamically registered command. + The other args are the actual args for the handler.`, 'Got it'); + } +} diff --git a/packages/ai-chat/src/common/index.ts b/packages/ai-chat/src/common/index.ts new file mode 100644 index 0000000000000..7fdd58621cc6b --- /dev/null +++ b/packages/ai-chat/src/common/index.ts @@ -0,0 +1,24 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export * from './chat-agent-service'; +export * from './chat-agents'; +export * from './chat-model'; +export * from './parsed-chat-request'; +export * from './chat-request-parser'; +export * from './chat-service'; +export * from './command-chat-agents'; +export * from './universal-chat-agent'; +export * from './orchestrator-chat-agent'; diff --git a/packages/ai-chat/src/common/orchestrator-chat-agent.ts b/packages/ai-chat/src/common/orchestrator-chat-agent.ts new file mode 100644 index 0000000000000..2f5f257eb5356 --- /dev/null +++ b/packages/ai-chat/src/common/orchestrator-chat-agent.ts @@ -0,0 +1,146 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { getJsonOfResponse, LanguageModelResponse } from '@theia/ai-core'; +import { + PromptTemplate +} from '@theia/ai-core/lib/common'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatAgentService } from './chat-agent-service'; +import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; +import { ChatRequestModelImpl, InformationalChatResponseContentImpl } from './chat-model'; + +export const delegateTemplate: PromptTemplate = { + id: 'default-delegate-template', + template: `# Instructions + +Your task is to identify which Chat Agent(s) should best reply a given user's message. +You consider all messages of the conversation to ensure consistency and avoid agent switches without a clear context change. +You should select the best Chat Agent based on the name and description of the agents, matching them to the user message. + +## Constraints + +Your response must be a JSON array containing the id(s) of the selected Chat Agent(s). + +* Do not use ids that are not provided in the list below. +* Do not include any additional information, explanations, or questions for the user. +* If there is no suitable choice, pick \`Universal\`. +* If there are multiple good choices, return all of them. + +Unless there is a more specific agent available, select \`Universal\`, especially for general programming-related questions. +You must only use the \`id\` attribute of the agent, never the name. + +### Example Results + +\`\`\`json +["Universal"] +\`\`\` + +\`\`\`json +["AnotherChatAgent", "Universal"] +\`\`\` + +## List of Currently Available Chat Agents + +{{chatAgents}} +`}; + +export const OrchestratorChatAgentId = 'Orchestrator'; + +@injectable() +export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent { + name: string; + description: string; + variables: string[]; + promptTemplates: PromptTemplate[]; + fallBackChatAgentId: string; + constructor() { + super(OrchestratorChatAgentId, [{ + purpose: 'agent-selection', + identifier: 'openai/gpt-4o', + }], 'agent-selection', 'codicon codicon-symbol-boolean'); + this.name = OrchestratorChatAgentId; + this.description = 'This agent analyzes the user request against the description of all available chat agents and selects the best fitting agent to answer the request \ + (by using AI).The user\'s request will be directly delegated to the selected agent without further confirmation.'; + this.variables = ['chatAgents']; + this.promptTemplates = [delegateTemplate]; + this.fallBackChatAgentId = 'Universal'; + } + + @inject(ChatAgentService) + protected chatAgentService: ChatAgentService; + + override invoke(request: ChatRequestModelImpl): Promise { + request.response.addProgressMessage({ content: 'Determining the most appropriate agent', status: 'inProgress' }); + return super.invoke(request); + } + + protected async getSystemMessageDescription(): Promise { + const resolvedPrompt = await this.promptService.getPrompt(delegateTemplate.id); + return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; + } + + protected override async addContentsToResponse(response: LanguageModelResponse, request: ChatRequestModelImpl): Promise { + let agentIds: string[] = []; + try { + const jsonResponse = await getJsonOfResponse(response); + if (Array.isArray(jsonResponse)) { + agentIds = jsonResponse.filter((id: string) => id !== this.id); + } + } catch (error: unknown) { + // The llm sometimes does not return a parseable result + this.logger.error('Failed to parse JSON response', error); + } + + if (agentIds.length < 1) { + this.logger.error('No agent was selected, delegating to fallback chat agent'); + request.response.progressMessages.forEach(progressMessage => + request.response.updateProgressMessage({ ...progressMessage, status: 'failed' }) + ); + agentIds = [this.fallBackChatAgentId]; + } + + // check if selected (or fallback) agent exists + if (!this.chatAgentService.getAgent(agentIds[0])) { + this.logger.error(`Chat agent ${agentIds[0]} not found. Falling back to first registered agent.`); + const firstRegisteredAgent = this.chatAgentService.getAgents().filter(a => a.id !== this.id)[0]?.id; + if (firstRegisteredAgent) { + agentIds = [firstRegisteredAgent]; + } else { + throw new Error('No chat agent available to handle request. Please check your configuration whether any are enabled.'); + } + } + + // TODO support delegating to more than one agent + const delegatedToAgent = agentIds[0]; + request.response.response.addContent(new InformationalChatResponseContentImpl( + `*Orchestrator*: Delegating to \`@${delegatedToAgent}\` + + --- + + ` + )); + request.response.overrideAgentId(delegatedToAgent); + request.response.progressMessages.forEach(progressMessage => + request.response.updateProgressMessage({ ...progressMessage, status: 'completed' }) + ); + const agent = this.chatAgentService.getAgent(delegatedToAgent); + if (!agent) { + throw new Error(`Chat agent ${delegatedToAgent} not found.`); + } + await agent.invoke(request); + } +} diff --git a/packages/ai-chat/src/common/parsed-chat-request.ts b/packages/ai-chat/src/common/parsed-chat-request.ts new file mode 100644 index 0000000000000..816e3fd8dffd8 --- /dev/null +++ b/packages/ai-chat/src/common/parsed-chat-request.ts @@ -0,0 +1,112 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/editor/common/core/offsetRange.ts + +import { AIVariable, ResolvedAIVariable, ToolRequest, toolRequestToPromptText } from '@theia/ai-core'; +import { ChatRequest } from './chat-model'; + +export const chatVariableLeader = '#'; +export const chatAgentLeader = '@'; +export const chatFunctionLeader = '~'; +export const chatSubcommandLeader = '/'; + +/********************** + * INTERFACES AND TYPE GUARDS + **********************/ + +export interface OffsetRange { + readonly start: number; + readonly endExclusive: number; +} + +export interface ParsedChatRequest { + readonly request: ChatRequest; + readonly parts: ParsedChatRequestPart[]; + readonly toolRequests: Map; + readonly variables: Map; +} + +export interface ParsedChatRequestPart { + readonly kind: string; + /** + * The text as represented in the ChatRequest + */ + readonly text: string; + /** + * The text as will be sent to the LLM + */ + readonly promptText: string; + + readonly range: OffsetRange; +} + +export class ParsedChatRequestTextPart implements ParsedChatRequestPart { + readonly kind: 'text'; + + constructor(readonly range: OffsetRange, readonly text: string) { } + + get promptText(): string { + return this.text; + } +} + +export class ParsedChatRequestVariablePart implements ParsedChatRequestPart { + readonly kind: 'var'; + + public resolution: ResolvedAIVariable; + + constructor(readonly range: OffsetRange, readonly variableName: string, readonly variableArg: string | undefined) { } + + get text(): string { + const argPart = this.variableArg ? `:${this.variableArg}` : ''; + return `${chatVariableLeader}${this.variableName}${argPart}`; + } + + get promptText(): string { + return this.resolution?.value ?? this.text; + } +} + +export class ParsedChatRequestFunctionPart implements ParsedChatRequestPart { + readonly kind: 'function'; + constructor(readonly range: OffsetRange, readonly toolRequest: ToolRequest) { } + + get text(): string { + return `${chatFunctionLeader}${this.toolRequest.id}`; + } + + get promptText(): string { + return toolRequestToPromptText(this.toolRequest); + } +} + +export class ParsedChatRequestAgentPart implements ParsedChatRequestPart { + readonly kind: 'agent'; + constructor(readonly range: OffsetRange, readonly agentId: string, readonly agentName: string) { } + + get text(): string { + return `${chatAgentLeader}${this.agentName}`; + } + + get promptText(): string { + return ''; + } +} diff --git a/packages/ai-chat/src/common/universal-chat-agent.ts b/packages/ai-chat/src/common/universal-chat-agent.ts new file mode 100644 index 0000000000000..703a61820e25d --- /dev/null +++ b/packages/ai-chat/src/common/universal-chat-agent.ts @@ -0,0 +1,104 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + PromptTemplate +} from '@theia/ai-core/lib/common'; +import { injectable } from '@theia/core/shared/inversify'; +import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; + +export const defaultTemplate: PromptTemplate = { + id: 'default-template', + template: `# Instructions + +You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by +providing concise and accurate answers to programming-related questions. Your role is to enhance the +developer's productivity by offering quick solutions, explanations, and best practices. +Keep responses short and to the point, focusing on delivering valuable insights, best practices and +simple solutions. + +### Guidelines + +1. **Understand Context:** + - Assess the context of the code or issue when available. + - Tailor responses to be relevant to the programming language, framework, or tools like Eclipse Theia. + - Ask clarifying questions if necessary to provide accurate assistance. + +2. **Provide Clear Solutions:** + - Offer direct answers or code snippets that solve the problem or clarify the concept. + - Avoid lengthy explanations unless necessary for understanding. + +3. **Promote Best Practices:** + - Suggest best practices and common patterns relevant to the question. + - Provide links to official documentation for further reading when applicable. + +4. **Support Multiple Languages and Tools:** + - Be familiar with popular programming languages, frameworks, IDEs like Eclipse Theia, and command-line tools. + - Adapt advice based on the language, environment, or tools specified by the developer. + +5. **Facilitate Learning:** + - Encourage learning by explaining why a solution works or why a particular approach is recommended. + - Keep explanations concise and educational. + +6. **Maintain Professional Tone:** + - Communicate in a friendly, professional manner. + - Use technical jargon appropriately, ensuring clarity for the target audience. + +7. **Stay on Topic:** + - Limit responses strictly to topics related to software development, frameworks, Eclipse Theia, terminal usage, and relevant technologies. + - Politely decline to answer questions unrelated to these areas by saying, "I'm here to assist with programming-related questions. + For other topics, please refer to a specialized source." + +### Example Interactions + +- **Question:** "What's the difference between \`let\` and \`var\` in JavaScript?" + **Answer:** "\`let\` is block-scoped, while \`var\` is function-scoped. Prefer \`let\` to avoid scope-related bugs." + +- **Question:** "How do I handle exceptions in Java?" + **Answer:** "Use try-catch blocks: \`\`\`java try { /* code */ } catch (ExceptionType e) { /* handle exception */ }\`\`\`." + +- **Question:** "What is the capital of France?" + **Answer:** "I'm here to assist with programming-related queries. For other topics, please refer to a specialized source." +` +}; + +@injectable() +export class UniversalChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent { + name: string; + description: string; + variables: string[]; + promptTemplates: PromptTemplate[]; + + constructor() { + super('Universal', [{ + purpose: 'chat', + identifier: 'openai/gpt-4o', + }], 'chat'); + this.name = 'Universal'; + this.description = 'This agent is designed to help software developers by providing concise and accurate ' + + 'answers to general programming and software development questions. It is also the fall-back for any generic ' + + 'questions the user might ask. The universal agent currently does not have any context by default, i.e. it cannot ' + + 'access the current user context or the workspace.'; + this.variables = []; + this.promptTemplates = [defaultTemplate]; + } + + protected override async getSystemMessageDescription(): Promise { + const resolvedPrompt = await this.promptService.getPrompt(defaultTemplate.id); + return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; + } + +} diff --git a/packages/ai-chat/tsconfig.json b/packages/ai-chat/tsconfig.json new file mode 100644 index 0000000000000..e7d3cda9e5fdb --- /dev/null +++ b/packages/ai-chat/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../ai-history" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-code-completion/.eslintrc.js b/packages/ai-code-completion/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-code-completion/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-code-completion/README.md b/packages/ai-code-completion/README.md new file mode 100644 index 0000000000000..938ca2c78ffd9 --- /dev/null +++ b/packages/ai-code-completion/README.md @@ -0,0 +1,30 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Code Completion

      + +
      + +
      + +## Description + +The `@theia/ai-code-completion` extension contributes Ai based code completion. +The user can separately enable code completion items as well as inline code completion. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-code-completion/package.json b/packages/ai-code-completion/package.json new file mode 100644 index 0000000000000..8ab9c57a3de79 --- /dev/null +++ b/packages/ai-code-completion/package.json @@ -0,0 +1,54 @@ +{ + "name": "@theia/ai-code-completion", + "version": "1.53.0", + "description": "Theia - AI Core", + "dependencies": { + "@theia/ai-core": "1.53.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco-editor-core": "1.83.101", + "@theia/output": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2" + }, + "main": "lib/common", + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-code-completion-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts b/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts new file mode 100644 index 0000000000000..69dd3b5daf7db --- /dev/null +++ b/packages/ai-code-completion/src/browser/ai-code-completion-frontend-module.ts @@ -0,0 +1,39 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ILogger } from '@theia/core'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { CodeCompletionAgent, CodeCompletionAgentImpl } from '../common/code-completion-agent'; +import { AICodeCompletionProvider } from './ai-code-completion-provider'; +import { AIFrontendApplicationContribution } from './ai-code-frontend-application-contribution'; +import { FrontendApplicationContribution, PreferenceContribution } from '@theia/core/lib/browser'; +import { Agent } from '@theia/ai-core'; +import { AICodeCompletionPreferencesSchema } from './ai-code-completion-preference'; +import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider'; + +export default new ContainerModule(bind => { + bind(ILogger).toDynamicValue(ctx => { + const parentLogger = ctx.container.get(ILogger); + return parentLogger.child('code-completion-agent'); + }).inSingletonScope().whenTargetNamed('code-completion-agent'); + bind(CodeCompletionAgentImpl).toSelf().inSingletonScope(); + bind(CodeCompletionAgent).toService(CodeCompletionAgentImpl); + bind(Agent).toService(CodeCompletionAgentImpl); + bind(AICodeCompletionProvider).toSelf().inSingletonScope(); + bind(AICodeInlineCompletionsProvider).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).to(AIFrontendApplicationContribution).inSingletonScope(); + bind(PreferenceContribution).toConstantValue({ schema: AICodeCompletionPreferencesSchema }); +}); diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts new file mode 100644 index 0000000000000..9b457a854f0ec --- /dev/null +++ b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts @@ -0,0 +1,46 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution'; +import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; + +export const PREF_AI_CODE_COMPLETION_ENABLE = 'ai-features.code-completion.enable'; +export const PREF_AI_CODE_COMPLETION_PRECOMPUTE = 'ai-features.code-completion.precompute'; +export const PREF_AI_INLINE_COMPLETION_ENABLE = 'ai-features.code-completion-inline.enable'; + +export const AICodeCompletionPreferencesSchema: PreferenceSchema = { + type: 'object', + properties: { + [PREF_AI_CODE_COMPLETION_ENABLE]: { + title: AI_CORE_PREFERENCES_TITLE, + type: 'boolean', + description: 'Enable AI completion items within any (Monaco) editor.', + default: false + }, + [PREF_AI_CODE_COMPLETION_PRECOMPUTE]: { + title: AI_CORE_PREFERENCES_TITLE, + type: 'boolean', + description: 'Precompute AI completion items. This will improve completion previews, however it will trigger many more requests and will take longer to complete.', + default: false + }, + [PREF_AI_INLINE_COMPLETION_ENABLE]: { + title: AI_CORE_PREFERENCES_TITLE, + type: 'boolean', + description: 'Enable AI completions inline within any (Monaco) editor.', + default: false + } + } +}; diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-provider.ts b/packages/ai-code-completion/src/browser/ai-code-completion-provider.ts new file mode 100644 index 0000000000000..b320dcbb878e5 --- /dev/null +++ b/packages/ai-code-completion/src/browser/ai-code-completion-provider.ts @@ -0,0 +1,84 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as monaco from '@theia/monaco-editor-core'; + +import { CodeCompletionAgent } from '../common/code-completion-agent'; +import { injectable, inject } from '@theia/core/shared/inversify'; +import { PreferenceService } from '@theia/core/lib/browser'; +import { CancellationTokenSource } from '@theia/core'; +import { PREF_AI_CODE_COMPLETION_PRECOMPUTE } from './ai-code-completion-preference'; + +interface WithArgs { + args: T; +} +const hasArgs = (object: {}): object is WithArgs => 'args' in object && Array.isArray(object['args']); + +@injectable() +export class AICodeCompletionProvider implements monaco.languages.CompletionItemProvider { + + @inject(CodeCompletionAgent) + protected readonly agent: CodeCompletionAgent; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + async provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position, + context: monaco.languages.CompletionContext, token: monaco.CancellationToken): Promise { + if (!this.preferenceService.get(PREF_AI_CODE_COMPLETION_PRECOMPUTE, false)) { + const result = { + suggestions: [{ + label: 'AI Code Completion', + detail: 'computes after trigger', + kind: monaco.languages.CompletionItemKind.Text, + insertText: '', + range: { + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: position.lineNumber, + endColumn: position.column + }, + args: [] + }] + }; + (result.suggestions[0] as WithArgs).args = [...arguments]; + return result; + } + const cancellationTokenSource = new CancellationTokenSource(); + token.onCancellationRequested(() => { cancellationTokenSource.cancel(); }); + return this.agent.provideCompletionItems(model, position, context, cancellationTokenSource.token); + } + + async resolveCompletionItem(item: monaco.languages.CompletionItem, token: monaco.CancellationToken): Promise { + if (!hasArgs>(item)) { + return item; + } + const args = item.args; + const cancellationTokenSource = new CancellationTokenSource(); + token.onCancellationRequested(() => { cancellationTokenSource.cancel(); }); + const resolvedItems = await this.agent.provideCompletionItems(args[0], args[1], args[2], cancellationTokenSource.token); + item.insertText = resolvedItems?.suggestions[0].insertText ?? ''; + item.additionalTextEdits = [{ + range: { + startLineNumber: args[1].lineNumber, + startColumn: args[1].column, + endLineNumber: args[1].lineNumber, + endColumn: args[1].column + }, text: resolvedItems?.suggestions[0].insertText ?? '' + }]; + return item; + } +} diff --git a/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts new file mode 100644 index 0000000000000..c29fa7510113d --- /dev/null +++ b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts @@ -0,0 +1,73 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as monaco from '@theia/monaco-editor-core'; + +import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; +import { AICodeCompletionProvider } from './ai-code-completion-provider'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { AIActivationService } from '@theia/ai-core/lib/browser'; +import { Disposable } from '@theia/core'; +import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider'; +import { PREF_AI_CODE_COMPLETION_ENABLE, PREF_AI_INLINE_COMPLETION_ENABLE } from './ai-code-completion-preference'; + +@injectable() +export class AIFrontendApplicationContribution implements FrontendApplicationContribution { + @inject(AICodeCompletionProvider) + protected codeCompletionProvider: AICodeCompletionProvider; + + @inject(AICodeInlineCompletionsProvider) + private inlineCodeCompletionProvider: AICodeInlineCompletionsProvider; + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + @inject(AIActivationService) + protected readonly activationService: AIActivationService; + + private toDispose = new Map(); + + onDidInitializeLayout(): void { + this.preferenceService.ready.then(() => { + this.handlePreference(PREF_AI_CODE_COMPLETION_ENABLE, enable => this.handleCodeCompletions(enable)); + this.handlePreference(PREF_AI_INLINE_COMPLETION_ENABLE, enable => this.handleInlineCompletions(enable)); + }); + } + + protected handlePreference(name: string, handler: (enable: boolean) => Disposable): void { + const enable = this.preferenceService.get(name, false) && this.activationService.isActive; + this.toDispose.set(name, handler(enable)); + + this.preferenceService.onPreferenceChanged(event => { + if (event.preferenceName === name) { + this.toDispose.get(name)?.dispose(); + this.toDispose.set(name, handler(event.newValue && this.activationService.isActive)); + } + }); + this.activationService.onDidChangeActiveStatus(change => { + this.toDispose.get(name)?.dispose(); + this.toDispose.set(name, handler(this.preferenceService.get(name, false) && change)); + }); + } + + protected handleCodeCompletions(enable: boolean): Disposable { + return enable ? monaco.languages.registerCompletionItemProvider({ scheme: 'file' }, this.codeCompletionProvider) : Disposable.NULL; + } + + protected handleInlineCompletions(enable: boolean): Disposable { + return enable ? monaco.languages.registerInlineCompletionsProvider({ scheme: 'file' }, this.inlineCodeCompletionProvider) : Disposable.NULL; + } +} diff --git a/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts b/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts new file mode 100644 index 0000000000000..22fb3847513e8 --- /dev/null +++ b/packages/ai-code-completion/src/browser/ai-code-inline-completion-provider.ts @@ -0,0 +1,43 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as monaco from '@theia/monaco-editor-core'; + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CodeCompletionAgent } from '../common/code-completion-agent'; +import { CompletionTriggerKind } from '@theia/core/shared/vscode-languageserver-protocol'; + +@injectable() +export class AICodeInlineCompletionsProvider implements monaco.languages.InlineCompletionsProvider { + @inject(CodeCompletionAgent) + protected readonly agent: CodeCompletionAgent; + + async provideInlineCompletions(model: monaco.editor.ITextModel, position: monaco.Position, + context: monaco.languages.InlineCompletionContext, token: monaco.CancellationToken): Promise { + if (this.agent.provideInlineCompletions) { + return this.agent.provideInlineCompletions(model, position, context, token); + } + // map from regular completion items + const items = await this.agent.provideCompletionItems(model, position, { ...context, triggerKind: CompletionTriggerKind.Invoked }, token); + return { + items: items?.suggestions.map(suggestion => ({ insertText: suggestion.insertText })) ?? [] + }; + } + + freeInlineCompletions(completions: monaco.languages.InlineCompletions): void { + // nothing to do + } +} diff --git a/packages/ai-code-completion/src/browser/index.ts b/packages/ai-code-completion/src/browser/index.ts new file mode 100644 index 0000000000000..be8ba477f3df9 --- /dev/null +++ b/packages/ai-code-completion/src/browser/index.ts @@ -0,0 +1,18 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './ai-code-completion-provider'; +export * from '../common/code-completion-agent'; diff --git a/packages/ai-code-completion/src/common/code-completion-agent.ts b/packages/ai-code-completion/src/common/code-completion-agent.ts new file mode 100644 index 0000000000000..21331d6a4ebb7 --- /dev/null +++ b/packages/ai-code-completion/src/common/code-completion-agent.ts @@ -0,0 +1,154 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + Agent, CommunicationHistoryEntry, CommunicationRecordingService, getTextOfResponse, + LanguageModelRegistry, LanguageModelRequest, LanguageModelRequirement, PromptService, PromptTemplate +} from '@theia/ai-core/lib/common'; +import { CancellationToken, generateUuid, ILogger } from '@theia/core'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import * as monaco from '@theia/monaco-editor-core'; + +export const CodeCompletionAgent = Symbol('CodeCompletionAgent'); +export interface CodeCompletionAgent extends Agent { + provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position, + context: monaco.languages.CompletionContext, token: monaco.CancellationToken): Promise; + provideInlineCompletions?(model: monaco.editor.ITextModel, position: monaco.Position, + context: monaco.languages.InlineCompletionContext, token: monaco.CancellationToken): Promise +} + +@injectable() +export class CodeCompletionAgentImpl implements CodeCompletionAgent { + variables: string[] = []; + + @inject(ILogger) @named('code-completion-agent') + protected logger: ILogger; + + @inject(LanguageModelRegistry) + protected languageModelRegistry: LanguageModelRegistry; + + @inject(PromptService) + protected promptService: PromptService; + + @inject(CommunicationRecordingService) + protected recordingService: CommunicationRecordingService; + + async provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position, + context: monaco.languages.CompletionContext, token: CancellationToken): Promise { + + const languageModel = await this.languageModelRegistry.selectLanguageModel({ + agent: this.id, + ...this.languageModelRequirements[0] + }); + if (!languageModel) { + this.logger.error('No language model found for code-completion-agent'); + return undefined; + } + + // Get text until the given position + const textUntilPosition = model.getValueInRange({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column + }); + + // Get text after the given position + const textAfterPosition = model.getValueInRange({ + startLineNumber: position.lineNumber, + startColumn: position.column, + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()) + }); + + const snippet = `${textUntilPosition}{{MARKER}}${textAfterPosition}`; + const file = model.uri.toString(false); + const language = model.getLanguageId(); + + if (token.isCancellationRequested) { + return undefined; + } + const prompt = await this.promptService.getPrompt('code-completion-prompt', { snippet, file, language }).then(p => p?.text); + if (!prompt) { + this.logger.error('No prompt found for code-completion-agent'); + return undefined; + } + + // since we do not actually hold complete conversions, the request/response pair is considered a session + const sessionId = generateUuid(); + const requestId = generateUuid(); + const request: LanguageModelRequest = { messages: [{ type: 'text', actor: 'user', query: prompt }] }; + const requestEntry: CommunicationHistoryEntry = { + agentId: this.id, + sessionId, + timestamp: Date.now(), + requestId, + request: prompt + }; + if (token.isCancellationRequested) { + return undefined; + } + this.recordingService.recordRequest(requestEntry); + const response = await languageModel.request(request, token); + if (token.isCancellationRequested) { + return undefined; + } + const completionText = await getTextOfResponse(response); + if (token.isCancellationRequested) { + return undefined; + } + this.recordingService.recordResponse({ + agentId: this.id, + sessionId, + timestamp: Date.now(), + requestId, + response: completionText + }); + + const suggestions: monaco.languages.CompletionItem[] = []; + const completionItem: monaco.languages.CompletionItem = { + preselect: true, + label: `${completionText.substring(0, 20)}`, + detail: 'AI Generated', + documentation: `Generated via ${languageModel.id}`, + kind: monaco.languages.CompletionItemKind.Text, + insertText: completionText, + range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column) + }; + suggestions.push(completionItem); + return { suggestions }; + + }; + id = 'Code Completion'; + name = 'Code Completion'; + description = 'This agent provides inline code completion in the code editor in the Theia IDE.'; + promptTemplates: PromptTemplate[] = [ + { + id: 'code-completion-prompt', + template: `You are a code completion agent. The current file you have to complete is named {{file}}. +The language of the file is {{language}}. Return your result as plain text without markdown formatting. +Finish the following code snippet. + +{{snippet}} + +Only return the exact replacement for {{MARKER}} to complete the snippet.`, + } + ]; + languageModelRequirements: LanguageModelRequirement[] = [{ + purpose: 'code-completion', + identifier: 'openai/gpt-4o' + }]; +} diff --git a/packages/ai-code-completion/src/package.spec.ts b/packages/ai-code-completion/src/package.spec.ts new file mode 100644 index 0000000000000..fec76f95059b4 --- /dev/null +++ b/packages/ai-code-completion/src/package.spec.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-code-completion package', () => { + + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-code-completion/tsconfig.json b/packages/ai-code-completion/tsconfig.json new file mode 100644 index 0000000000000..548b369565b41 --- /dev/null +++ b/packages/ai-code-completion/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../output" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-core/.eslintrc.js b/packages/ai-core/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-core/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-core/README.md b/packages/ai-core/README.md new file mode 100644 index 0000000000000..1cd399aaff0e1 --- /dev/null +++ b/packages/ai-core/README.md @@ -0,0 +1,30 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Core EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-core` extension serves as the basis of all AI integration in Theia. +It manages the integration of language models and provides core concepts like agents, prompts and AI variables. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-core/data/prompttemplate.tmLanguage.json b/packages/ai-core/data/prompttemplate.tmLanguage.json new file mode 100644 index 0000000000000..da9b33b66723a --- /dev/null +++ b/packages/ai-core/data/prompttemplate.tmLanguage.json @@ -0,0 +1,52 @@ +{ + "scopeName": "source.prompttemplate", + "patterns": [ + { + "name": "variable.other.prompttemplate", + "begin": "{{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.brace.begin" + } + }, + "end": "}}", + "endCaptures": { + "0": { + "name": "punctuation.definition.brace.end" + } + }, + "patterns": [ + { + "name": "keyword.control", + "match": "[a-zA-Z_][a-zA-Z0-9_]*" + } + ] + }, + { + "name": "support.function.prompttemplate", + "begin": "~{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.brace.begin" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.brace.end" + } + }, + "patterns": [ + { + "name": "keyword.control", + "match": "[a-zA-Z_][a-zA-Z0-9_\\-]*" + } + ] + } + ], + "repository": {}, + "name": "PromptTemplate", + "fileTypes": [ + ".prompttemplate" + ] +} diff --git a/packages/ai-core/package.json b/packages/ai-core/package.json new file mode 100644 index 0000000000000..294fbcb871fc2 --- /dev/null +++ b/packages/ai-core/package.json @@ -0,0 +1,58 @@ +{ + "name": "@theia/ai-core", + "version": "1.53.0", + "description": "Theia - AI Core", + "dependencies": { + "@theia/core": "1.53.0", + "@theia/editor": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/monaco": "1.53.0", + "@theia/monaco-editor-core": "1.83.101", + "@theia/output": "1.53.0", + "@theia/variable-resolver": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2" + }, + "main": "lib/common", + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-core-frontend-module", + "backend": "lib/node/ai-core-backend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "data", + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-core/src/browser/ai-activation-service.ts b/packages/ai-core/src/browser/ai-activation-service.ts new file mode 100644 index 0000000000000..0bc83ec002bde --- /dev/null +++ b/packages/ai-core/src/browser/ai-activation-service.ts @@ -0,0 +1,52 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; +import { Emitter, MaybePromise, Event, } from '@theia/core'; +import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service'; +import { PREFERENCE_NAME_ENABLE_EXPERIMENTAL } from './ai-core-preferences'; + +export const EXPERIMENTAL_AI_CONTEXT_KEY = 'ai.experimental.enabled'; + +@injectable() +export class AIActivationService implements FrontendApplicationContribution { + @inject(ContextKeyService) + protected readonly contextKeyService: ContextKeyService; + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + protected isExperimentalEnabledKey: ContextKey; + + protected onDidChangeExperimental = new Emitter(); + get onDidChangeActiveStatus(): Event { + return this.onDidChangeExperimental.event; + } + + get isActive(): boolean { + return this.isExperimentalEnabledKey.get() ?? false; + } + + initialize(): MaybePromise { + this.isExperimentalEnabledKey = this.contextKeyService.createKey(PREFERENCE_NAME_ENABLE_EXPERIMENTAL, false); + this.preferenceService.onPreferenceChanged(e => { + if (e.preferenceName === PREFERENCE_NAME_ENABLE_EXPERIMENTAL) { + this.isExperimentalEnabledKey.set(e.newValue); + this.onDidChangeExperimental.fire(e.newValue); + } + }); + } +} diff --git a/packages/ai-core/src/browser/ai-command-handler-factory.ts b/packages/ai-core/src/browser/ai-command-handler-factory.ts new file mode 100644 index 0000000000000..f5f82856be386 --- /dev/null +++ b/packages/ai-core/src/browser/ai-command-handler-factory.ts @@ -0,0 +1,20 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CommandHandler } from '@theia/core'; + +export type AICommandHandlerFactory = (handler: CommandHandler) => CommandHandler; +export const AICommandHandlerFactory = Symbol('AICommandHandlerFactory'); diff --git a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx new file mode 100644 index 0000000000000..d793e35903dc0 --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx @@ -0,0 +1,154 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { codicon, ReactWidget } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { Agent, LanguageModel, LanguageModelRegistry, PromptCustomizationService } from '../../common'; +import { AISettingsService } from '../ai-settings-service'; +import { LanguageModelRenderer } from './language-model-renderer'; +import { TemplateRenderer } from './template-settings-renderer'; +import { AIConfigurationSelectionService } from './ai-configuration-service'; +import { AIVariableConfigurationWidget } from './variable-configuration-widget'; +import { AgentService } from '../../common/agent-service'; + +@injectable() +export class AIAgentConfigurationWidget extends ReactWidget { + + static readonly ID = 'ai-agent-configuration-container-widget'; + static readonly LABEL = 'Agents'; + + @inject(AgentService) + protected readonly agentService: AgentService; + + @inject(LanguageModelRegistry) + protected readonly languageModelRegistry: LanguageModelRegistry; + + @inject(PromptCustomizationService) + protected readonly promptCustomizationService: PromptCustomizationService; + + @inject(AISettingsService) + protected readonly aiSettingsService: AISettingsService; + + @inject(AIConfigurationSelectionService) + protected readonly aiConfigurationSelectionService: AIConfigurationSelectionService; + + protected languageModels: LanguageModel[] | undefined; + + @postConstruct() + protected init(): void { + this.id = AIAgentConfigurationWidget.ID; + this.title.label = AIAgentConfigurationWidget.LABEL; + this.title.closable = false; + + this.languageModelRegistry.getLanguageModels().then(models => { + this.languageModels = models ?? []; + this.update(); + }); + this.toDispose.push(this.languageModelRegistry.onChange(({ models }) => { + this.languageModels = models; + this.update(); + })); + + this.aiSettingsService.onDidChange(() => this.update()); + this.aiConfigurationSelectionService.onDidAgentChange(() => this.update()); + this.update(); + } + + protected render(): React.ReactNode { + return
      +
      +
        + {this.agentService.getAllAgents().map(agent => +
      • this.setActiveAgent(agent)}>{agent.name}
      • + )} +
      +
      +
      + {this.renderAgentDetails()} +
      +
      ; + } + + private renderAgentDetails(): React.ReactNode { + const agent = this.aiConfigurationSelectionService.getActiveAgent(); + if (!agent) { + return
      Please select an Agent first!
      ; + } + + const enabled = this.agentService.isEnabled(agent.id); + + return
      +
      {agent.name}
      +
      {agent.description}
      +
      + +
      +
      + Variables: +
        + {agent.variables.map(variableId =>
      • +
        { this.showVariableConfigurationTab(); }} className='variable-reference'> + {variableId} + +
      • )} +
      +
      +
      + {agent.promptTemplates?.map(template => + )} +
      +
      + +
      +
      ; + } + + protected showVariableConfigurationTab(): void { + this.aiConfigurationSelectionService.selectConfigurationTab(AIVariableConfigurationWidget.ID); + } + + protected setActiveAgent(agent: Agent): void { + this.aiConfigurationSelectionService.setActiveAgent(agent); + this.update(); + } + + private toggleAgentEnabled = () => { + const agent = this.aiConfigurationSelectionService.getActiveAgent(); + if (!agent) { + return false; + } + const enabled = this.agentService.isEnabled(agent.id); + if (enabled) { + this.agentService.disableAgent(agent.id); + } else { + this.agentService.enableAgent(agent.id); + } + this.update(); + }; + +} diff --git a/packages/ai-core/src/browser/ai-configuration/ai-configuration-service.ts b/packages/ai-core/src/browser/ai-configuration/ai-configuration-service.ts new file mode 100644 index 0000000000000..bd364a9a1d3cd --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/ai-configuration-service.ts @@ -0,0 +1,43 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Emitter } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { Agent } from '../../common'; + +@injectable() +export class AIConfigurationSelectionService { + protected activeAgent?: Agent; + + protected readonly onDidSelectConfigurationEmitter = new Emitter(); + onDidSelectConfiguration = this.onDidSelectConfigurationEmitter.event; + + protected readonly onDidAgentChangeEmitter = new Emitter(); + onDidAgentChange = this.onDidSelectConfigurationEmitter.event; + + public getActiveAgent(): Agent | undefined { + return this.activeAgent; + } + + public setActiveAgent(agent?: Agent): void { + this.activeAgent = agent; + this.onDidAgentChangeEmitter.fire(agent); + } + + public selectConfigurationTab(widgetId: string): void { + this.onDidSelectConfigurationEmitter.fire(widgetId); + } +} diff --git a/packages/ai-core/src/browser/ai-configuration/ai-configuration-view-contribution.ts b/packages/ai-core/src/browser/ai-configuration/ai-configuration-view-contribution.ts new file mode 100644 index 0000000000000..4e6e371240f46 --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/ai-configuration-view-contribution.ts @@ -0,0 +1,54 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { FrontendApplication } from '@theia/core/lib/browser'; +import { injectable } from '@theia/core/shared/inversify'; +import { AIViewContribution } from '../ai-view-contribution'; +import { AIConfigurationContainerWidget } from './ai-configuration-widget'; +import { Command, CommandRegistry } from '@theia/core'; + +export const AI_CONFIGURATION_TOGGLE_COMMAND_ID = 'aiConfiguration:toggle'; +export const OPEN_AI_CONFIG_VIEW = Command.toLocalizedCommand({ + id: 'aiConfiguration:open', + label: 'Open AI Configuration view', +}); + +@injectable() +export class AIAgentConfigurationViewContribution extends AIViewContribution { + + constructor() { + super({ + widgetId: AIConfigurationContainerWidget.ID, + widgetName: AIConfigurationContainerWidget.LABEL, + defaultWidgetOptions: { + area: 'main', + rank: 100 + }, + toggleCommandId: AI_CONFIGURATION_TOGGLE_COMMAND_ID + }); + } + + async initializeLayout(_app: FrontendApplication): Promise { + await this.openView(); + } + + override registerCommands(commands: CommandRegistry): void { + super.registerCommands(commands); + commands.registerCommand(OPEN_AI_CONFIG_VIEW, { + execute: () => this.openView({ activate: true }), + }); + } +} + diff --git a/packages/ai-core/src/browser/ai-configuration/ai-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/ai-configuration-widget.tsx new file mode 100644 index 0000000000000..909c822d8df47 --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/ai-configuration-widget.tsx @@ -0,0 +1,80 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { BaseWidget, BoxLayout, codicon, DockPanel, WidgetManager } from '@theia/core/lib/browser'; +import { TheiaDockPanel } from '@theia/core/lib/browser/shell/theia-dock-panel'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import '../../../src/browser/style/index.css'; +import { AIAgentConfigurationWidget } from './agent-configuration-widget'; +import { AIVariableConfigurationWidget } from './variable-configuration-widget'; +import { AIConfigurationSelectionService } from './ai-configuration-service'; + +@injectable() +export class AIConfigurationContainerWidget extends BaseWidget { + + static readonly ID = 'ai-configuration'; + static readonly LABEL = '✨ AI Configuration [Experimental]'; + protected dockpanel: DockPanel; + + @inject(TheiaDockPanel.Factory) + protected readonly dockPanelFactory: TheiaDockPanel.Factory; + @inject(WidgetManager) + protected readonly widgetManager: WidgetManager; + @inject(AIConfigurationSelectionService) + protected readonly aiConfigurationSelectionService: AIConfigurationSelectionService; + + protected agentsWidget: AIAgentConfigurationWidget; + protected variablesWidget: AIVariableConfigurationWidget; + + @postConstruct() + protected init(): void { + this.id = AIConfigurationContainerWidget.ID; + this.title.label = AIConfigurationContainerWidget.LABEL; + this.title.closable = true; + this.addClass('theia-settings-container'); + this.title.iconClass = codicon('hubot'); + this.initUI(); + this.initListeners(); + } + + protected async initUI(): Promise { + const layout = (this.layout = new BoxLayout({ direction: 'top-to-bottom', spacing: 0 })); + this.dockpanel = this.dockPanelFactory({ + mode: 'multiple-document', + spacing: 0 + }); + BoxLayout.setStretch(this.dockpanel, 1); + layout.addWidget(this.dockpanel); + this.dockpanel.addClass('ai-configuration-widget'); + + this.agentsWidget = await this.widgetManager.getOrCreateWidget(AIAgentConfigurationWidget.ID); + this.variablesWidget = await this.widgetManager.getOrCreateWidget(AIVariableConfigurationWidget.ID); + this.dockpanel.addWidget(this.agentsWidget); + this.dockpanel.addWidget(this.variablesWidget); + + this.update(); + } + + protected initListeners(): void { + this.aiConfigurationSelectionService.onDidSelectConfiguration(widgetId => { + if (widgetId === AIAgentConfigurationWidget.ID) { + this.dockpanel.activateWidget(this.agentsWidget); + } else if (widgetId === AIVariableConfigurationWidget.ID) { + this.dockpanel.activateWidget(this.variablesWidget); + } + }); + } +} diff --git a/packages/ai-core/src/browser/ai-configuration/language-model-renderer.tsx b/packages/ai-core/src/browser/ai-configuration/language-model-renderer.tsx new file mode 100644 index 0000000000000..c3ba5b2e06b50 --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/language-model-renderer.tsx @@ -0,0 +1,113 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import * as React from '@theia/core/shared/react'; +import { Agent, LanguageModelRequirement } from '../../common'; +import { LanguageModel, LanguageModelRegistry } from '../../common/language-model'; +import { AISettingsService } from '../ai-settings-service'; +import { Mutable } from '@theia/core'; + +export interface LanguageModelSettingsProps { + agent: Agent; + languageModels?: LanguageModel[]; + aiSettingsService: AISettingsService; + languageModelRegistry: LanguageModelRegistry; +} + +export const LanguageModelRenderer: React.FC = ( + { agent, languageModels, aiSettingsService, languageModelRegistry }) => { + + const findLanguageModelRequirement = (purpose: string): LanguageModelRequirement | undefined => { + const requirementSetting = aiSettingsService.getAgentSettings(agent.id); + return requirementSetting?.languageModelRequirements.find(e => e.purpose === purpose); + }; + + const [lmRequirementMap, setLmRequirementMap] = React.useState>({}); + + React.useEffect(() => { + const computeLmRequirementMap = async () => { + const map = await agent.languageModelRequirements.reduce(async (accPromise, curr) => { + const acc = await accPromise; + // take the agents requirements and override them with the user settings if present + const lmRequirement = findLanguageModelRequirement(curr.purpose) ?? curr; + // if no llm is selected through the identifier, see what would be the default + if (!lmRequirement.identifier) { + const llm = await languageModelRegistry.selectLanguageModel({ agent: agent.id, ...lmRequirement }); + (lmRequirement as Mutable).identifier = llm?.id; + } + acc[curr.purpose] = lmRequirement; + return acc; + }, Promise.resolve({} as Record)); + setLmRequirementMap(map); + }; + computeLmRequirementMap(); + }, []); + + const renderLanguageModelMetadata = (requirement: LanguageModelRequirement, index: number) => { + const languageModel = languageModels?.find(model => model.id === requirement.identifier); + if (!languageModel) { + return
      ; + } + + return <> +
      {requirement.purpose}
      +
      + {languageModel.id &&

      Identifier: {languageModel.id}

      } + {languageModel.name &&

      Name: {languageModel.name}

      } + {languageModel.vendor &&

      Vendor: {languageModel.vendor}

      } + {languageModel.version &&

      Version: {languageModel.version}

      } + {languageModel.family &&

      Family: {languageModel.family}

      } + {languageModel.maxInputTokens &&

      Min Input Tokens: {languageModel.maxInputTokens}

      } + {languageModel.maxOutputTokens &&

      Max Output Tokens: {languageModel.maxOutputTokens}

      } +
      + ; + + }; + + const onSelectedModelChange = (purpose: string, event: React.ChangeEvent): void => { + const newLmRequirementMap = { ...lmRequirementMap, [purpose]: { purpose, identifier: event.target.value } }; + aiSettingsService.updateAgentSettings(agent.id, { languageModelRequirements: Object.values(newLmRequirementMap) }); + setLmRequirementMap(newLmRequirementMap); + }; + + return
      + {Object.values(lmRequirementMap).map((requirements, index) => ( + +
      Purpose:
      +
      + {/* language model metadata */} + {renderLanguageModelMetadata(requirements, index)} + {/* language model selector */} + <> + + + +
      +
      +
      + ))} + +
      ; +}; diff --git a/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx b/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx new file mode 100644 index 0000000000000..01125ebf58e0a --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/template-settings-renderer.tsx @@ -0,0 +1,39 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import * as React from '@theia/core/shared/react'; +import { PromptCustomizationService } from '../../common/prompt-service'; +import { PromptTemplate } from '../../common'; + +export interface TemplateSettingProps { + agentId: string; + template: PromptTemplate; + promptCustomizationService: PromptCustomizationService; +} + +export const TemplateRenderer: React.FC = ({ agentId, template, promptCustomizationService }) => { + const openTemplate = React.useCallback(async () => { + promptCustomizationService.editTemplate(template.id); + }, [template, promptCustomizationService]); + const resetTemplate = React.useCallback(async () => { + promptCustomizationService.resetTemplate(template.id); + }, [promptCustomizationService, template]); + + return <> + {template.id} + + + ; +}; diff --git a/packages/ai-core/src/browser/ai-configuration/variable-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/variable-configuration-widget.tsx new file mode 100644 index 0000000000000..64cbdfffe6122 --- /dev/null +++ b/packages/ai-core/src/browser/ai-configuration/variable-configuration-widget.tsx @@ -0,0 +1,110 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { codicon, ReactWidget } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { Agent, AIVariable, AIVariableService } from '../../common'; +import { AIAgentConfigurationWidget } from './agent-configuration-widget'; +import { AIConfigurationSelectionService } from './ai-configuration-service'; +import { AgentService } from '../../common/agent-service'; + +@injectable() +export class AIVariableConfigurationWidget extends ReactWidget { + + static readonly ID = 'ai-variable-configuration-container-widget'; + static readonly LABEL = 'Variables'; + + @inject(AIVariableService) + protected readonly variableService: AIVariableService; + + @inject(AgentService) + protected readonly agentService: AgentService; + + @inject(AIConfigurationSelectionService) + protected readonly aiConfigurationSelectionService: AIConfigurationSelectionService; + + @postConstruct() + protected init(): void { + this.id = AIVariableConfigurationWidget.ID; + this.title.label = AIVariableConfigurationWidget.LABEL; + this.title.closable = false; + this.update(); + this.toDispose.push(this.variableService.onDidChangeVariables(() => this.update())); + } + + protected render(): React.ReactNode { + return
      +
        + {this.variableService.getVariables().map(variable => +
      • +
        {variable.name}
        + {variable.id} + {variable.description} + {this.renderReferencedVariables(variable)} + {this.renderArgs(variable)} +
      • + )} +
      +
      ; + } + + protected renderReferencedVariables(variable: AIVariable): React.ReactNode | undefined { + const agents = this.getAgentsForVariable(variable); + if (agents.length === 0) { + return; + } + + return
      +

      Agents

      +
        + {agents.map(agent =>
      • +
        { this.showAgentConfiguration(agent); }} className='variable-reference'> + {agent.name} + +
      • )} +
      +
      ; + } + + protected renderArgs(variable: AIVariable): React.ReactNode | undefined { + if (variable.args === undefined || variable.args.length === 0) { + return; + } + + return
      +

      Variable Arguments

      +
      + {variable.args.map(arg => + + {arg.name} + {arg.description} + + )} +
      +
      ; + } + + protected showAgentConfiguration(agent: Agent): void { + this.aiConfigurationSelectionService.setActiveAgent(agent); + this.aiConfigurationSelectionService.selectConfigurationTab(AIAgentConfigurationWidget.ID); + } + + protected getAgentsForVariable(variable: AIVariable): Agent[] { + return this.agentService.getAgents().filter(a => a.variables?.includes(variable.id)); + } +} + diff --git a/packages/ai-core/src/browser/ai-core-frontend-application-contribution.ts b/packages/ai-core/src/browser/ai-core-frontend-application-contribution.ts new file mode 100644 index 0000000000000..c419620b2401f --- /dev/null +++ b/packages/ai-core/src/browser/ai-core-frontend-application-contribution.ts @@ -0,0 +1,40 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { PromptService } from '../common'; +import { AgentService } from '../common/agent-service'; + +@injectable() +export class AICoreFrontendApplicationContribution implements FrontendApplicationContribution { + @inject(AgentService) + private readonly agentService: AgentService; + + @inject(PromptService) + private readonly promptService: PromptService; + + onStart(): void { + this.agentService.getAllAgents().forEach(a => { + a.promptTemplates.forEach(t => { + this.promptService.storePrompt(t.id, t.template); + }); + }); + } + + onStop(): void { + } +} diff --git a/packages/ai-core/src/browser/ai-core-frontend-module.ts b/packages/ai-core/src/browser/ai-core-frontend-module.ts new file mode 100644 index 0000000000000..9e1404f8f782a --- /dev/null +++ b/packages/ai-core/src/browser/ai-core-frontend-module.ts @@ -0,0 +1,160 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { bindContributionProvider, CommandContribution, CommandHandler } from '@theia/core'; +import { + RemoteConnectionProvider, + ServiceConnectionProvider, +} from '@theia/core/lib/browser/messaging/service-connection-provider'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { + AIVariableContribution, + AIVariableService, + ToolInvocationRegistry, + ToolInvocationRegistryImpl, + LanguageModelDelegateClient, + languageModelDelegatePath, + LanguageModelFrontendDelegate, + LanguageModelProvider, + LanguageModelRegistry, + LanguageModelRegistryClient, + languageModelRegistryDelegatePath, + LanguageModelRegistryFrontendDelegate, + PromptCustomizationService, + PromptService, + PromptServiceImpl, + ToolProvider +} from '../common'; +import { + FrontendLanguageModelRegistryImpl, + LanguageModelDelegateClientImpl, +} from './frontend-language-model-registry'; + +import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { LanguageGrammarDefinitionContribution } from '@theia/monaco/lib/browser/textmate'; +import { AIAgentConfigurationWidget } from './ai-configuration/agent-configuration-widget'; +import { AIConfigurationSelectionService } from './ai-configuration/ai-configuration-service'; +import { AIAgentConfigurationViewContribution } from './ai-configuration/ai-configuration-view-contribution'; +import { AIConfigurationContainerWidget } from './ai-configuration/ai-configuration-widget'; +import { AIVariableConfigurationWidget } from './ai-configuration/variable-configuration-widget'; +import { AICoreFrontendApplicationContribution } from './ai-core-frontend-application-contribution'; +import { bindAICorePreferences } from './ai-core-preferences'; +import { AISettingsService } from './ai-settings-service'; +import { FrontendPromptCustomizationServiceImpl } from './frontend-prompt-customization-service'; +import { FrontendVariableService } from './frontend-variable-service'; +import { PromptTemplateContribution } from './prompttemplate-contribution'; +import { TomorrowVariableContribution } from '../common/tomorrow-variable-contribution'; +import { TheiaVariableContribution } from './theia-variable-contribution'; +import { TodayVariableContribution } from '../common/today-variable-contribution'; +import { AgentsVariableContribution } from '../common/agents-variable-contribution'; +import { AIActivationService } from './ai-activation-service'; +import { AgentService, AgentServiceImpl } from '../common/agent-service'; +import { AICommandHandlerFactory } from './ai-command-handler-factory'; + +export default new ContainerModule(bind => { + bindContributionProvider(bind, LanguageModelProvider); + + bind(FrontendLanguageModelRegistryImpl).toSelf().inSingletonScope(); + bind(LanguageModelRegistry).toService(FrontendLanguageModelRegistryImpl); + + bind(LanguageModelDelegateClientImpl).toSelf().inSingletonScope(); + bind(LanguageModelDelegateClient).toService(LanguageModelDelegateClientImpl); + bind(LanguageModelRegistryClient).toService(LanguageModelDelegateClient); + + bind(LanguageModelRegistryFrontendDelegate).toDynamicValue( + ctx => { + const connection = ctx.container.get(RemoteConnectionProvider); + const client = ctx.container.get(LanguageModelRegistryClient); + return connection.createProxy(languageModelRegistryDelegatePath, client); + } + ); + + bind(LanguageModelFrontendDelegate) + .toDynamicValue(ctx => { + const connection = ctx.container.get(RemoteConnectionProvider); + const client = ctx.container.get(LanguageModelDelegateClient); + return connection.createProxy(languageModelDelegatePath, client); + }) + .inSingletonScope(); + + bindAICorePreferences(bind); + + bind(FrontendPromptCustomizationServiceImpl).toSelf().inSingletonScope(); + bind(PromptCustomizationService).toService(FrontendPromptCustomizationServiceImpl); + bind(PromptServiceImpl).toSelf().inSingletonScope(); + bind(PromptService).toService(PromptServiceImpl); + + bind(PromptTemplateContribution).toSelf().inSingletonScope(); + bind(LanguageGrammarDefinitionContribution).toService(PromptTemplateContribution); + bind(CommandContribution).toService(PromptTemplateContribution); + bind(TabBarToolbarContribution).toService(PromptTemplateContribution); + + bind(AIConfigurationSelectionService).toSelf().inSingletonScope(); + bind(AIConfigurationContainerWidget).toSelf(); + bind(WidgetFactory) + .toDynamicValue(ctx => ({ + id: AIConfigurationContainerWidget.ID, + createWidget: () => ctx.container.get(AIConfigurationContainerWidget) + })) + .inSingletonScope(); + + bindViewContribution(bind, AIAgentConfigurationViewContribution); + bind(AISettingsService).toSelf().inRequestScope(); + bindContributionProvider(bind, AIVariableContribution); + bind(FrontendVariableService).toSelf().inSingletonScope(); + bind(AIVariableService).toService(FrontendVariableService); + bind(FrontendApplicationContribution).toService(FrontendVariableService); + bind(AIVariableContribution).to(TheiaVariableContribution).inSingletonScope(); + bind(AIVariableContribution).to(TodayVariableContribution).inSingletonScope(); + bind(AIVariableContribution).to(TomorrowVariableContribution).inSingletonScope(); + bind(AIVariableContribution).to(AgentsVariableContribution).inSingletonScope(); + + bind(FrontendApplicationContribution).to(AICoreFrontendApplicationContribution).inSingletonScope(); + + bind(AIVariableConfigurationWidget).toSelf(); + bind(WidgetFactory) + .toDynamicValue(ctx => ({ + id: AIVariableConfigurationWidget.ID, + createWidget: () => ctx.container.get(AIVariableConfigurationWidget) + })) + .inSingletonScope(); + + bind(AIAgentConfigurationWidget).toSelf(); + bind(WidgetFactory) + .toDynamicValue(ctx => ({ + id: AIAgentConfigurationWidget.ID, + createWidget: () => ctx.container.get(AIAgentConfigurationWidget) + })) + .inSingletonScope(); + + bind(ToolInvocationRegistry).to(ToolInvocationRegistryImpl).inSingletonScope(); + bindContributionProvider(bind, ToolProvider); + + bind(AIActivationService).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(AIActivationService); + bind(AgentServiceImpl).toSelf().inSingletonScope(); + bind(AgentService).toService(AgentServiceImpl); + + bind(AICommandHandlerFactory).toFactory(context => (handler: CommandHandler) => { + const activationService = context.container.get(AIActivationService); + return { + execute: (...args: unknown[]) => handler.execute(...args), + isEnabled: (...args: unknown[]) => activationService.isActive && (handler.isEnabled?.(...args) ?? true), + isVisible: (...args: unknown[]) => activationService.isActive && (handler.isVisible?.(...args) ?? true), + isToggled: (...args: unknown[]) => handler.isToggled?.(...args) ?? false + }; + }); +}); diff --git a/packages/ai-core/src/browser/ai-core-preferences.ts b/packages/ai-core/src/browser/ai-core-preferences.ts new file mode 100644 index 0000000000000..5afda4aafbf9b --- /dev/null +++ b/packages/ai-core/src/browser/ai-core-preferences.ts @@ -0,0 +1,74 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PreferenceContribution, PreferenceProxy, PreferenceSchema } from '@theia/core/lib/browser'; +import { PreferenceProxyFactory } from '@theia/core/lib/browser/preferences/injectable-preference-proxy'; +import { interfaces } from '@theia/core/shared/inversify'; + +export const AI_CORE_PREFERENCES_TITLE = '✨ AI Features [Experimental]'; +export const PREFERENCE_NAME_ENABLE_EXPERIMENTAL = 'ai-features.ai-enable.enable'; +export const PREFERENCE_NAME_PROMPT_TEMPLATES = 'ai-features.templates.templates-folder'; + +export const aiCorePreferenceSchema: PreferenceSchema = { + type: 'object', + properties: { + [PREFERENCE_NAME_ENABLE_EXPERIMENTAL]: { + title: AI_CORE_PREFERENCES_TITLE, + markdownDescription: '❗ This setting allows you to access and experiment with our latest AI capabilities.\ + \n\ + Please note that these features are in an experimental phase, which means they may be unstable,\ + undergo significant changes, or incur additional costs.\ + \n\ + By enabling this option, you acknowledge these risks and agree to provide feedback to help us improve.\ +  \n\ + **Please note! The settings below in this section will only take effect\n\ + once the main feature setting is enabled.**', + type: 'boolean', + default: false, + }, + [PREFERENCE_NAME_PROMPT_TEMPLATES]: { + title: AI_CORE_PREFERENCES_TITLE, + description: 'Folder for managing custom prompt templates. If not customized the user config directory is used.', + type: 'string', + default: '', + typeDetails: { + isFilepath: true, + selectionProps: { + openLabel: 'Select Folder', + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false + } + }, + + } + } +}; +export interface AICoreConfiguration { + [PREFERENCE_NAME_ENABLE_EXPERIMENTAL]: boolean | undefined; + [PREFERENCE_NAME_PROMPT_TEMPLATES]: string | undefined; +} + +export const AICorePreferences = Symbol('AICorePreferences'); +export type AICorePreferences = PreferenceProxy; + +export function bindAICorePreferences(bind: interfaces.Bind): void { + bind(AICorePreferences).toDynamicValue(ctx => { + const factory = ctx.container.get(PreferenceProxyFactory); + return factory(aiCorePreferenceSchema); + }).inSingletonScope(); + bind(PreferenceContribution).toConstantValue({ schema: aiCorePreferenceSchema }); +} diff --git a/packages/ai-core/src/browser/ai-settings-service.ts b/packages/ai-core/src/browser/ai-settings-service.ts new file mode 100644 index 0000000000000..5ea34f158791b --- /dev/null +++ b/packages/ai-core/src/browser/ai-settings-service.ts @@ -0,0 +1,56 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { DisposableCollection, Emitter, Event } from '@theia/core'; +import { PreferenceScope, PreferenceService } from '@theia/core/lib/browser'; +import { JSONObject } from '@theia/core/shared/@phosphor/coreutils'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { LanguageModelRequirement } from '../common'; + +@injectable() +export class AISettingsService { + @inject(PreferenceService) protected preferenceService: PreferenceService; + static readonly PREFERENCE_NAME = 'ai.settings'; + + protected toDispose = new DisposableCollection(); + + protected readonly onDidChangeEmitter = new Emitter(); + onDidChange: Event = this.onDidChangeEmitter.event; + + updateAgentSettings(agent: string, agentSettings: AgentSettings): void { + const settings = this.getSettings(); + settings.agents[agent] = agentSettings; + this.preferenceService.set(AISettingsService.PREFERENCE_NAME, settings, PreferenceScope.User); + this.onDidChangeEmitter.fire(); + } + + getAgentSettings(agent: string): AgentSettings | undefined { + const settings = this.getSettings(); + return settings.agents[agent]; + } + + getSettings(): AISettings { + const pref = this.preferenceService.inspect(AISettingsService.PREFERENCE_NAME); + return pref?.value ? pref.value : { agents: {} }; + } + +} +export interface AISettings extends JSONObject { + agents: Record +} + +interface AgentSettings extends JSONObject { + languageModelRequirements: LanguageModelRequirement[]; +} diff --git a/packages/ai-core/src/browser/ai-view-contribution.ts b/packages/ai-core/src/browser/ai-view-contribution.ts new file mode 100644 index 0000000000000..9d43b455b5e13 --- /dev/null +++ b/packages/ai-core/src/browser/ai-view-contribution.ts @@ -0,0 +1,77 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommandRegistry, MenuModelRegistry } from '@theia/core'; +import { AbstractViewContribution, CommonMenus, KeybindingRegistry, PreferenceService, Widget } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { AIActivationService, EXPERIMENTAL_AI_CONTEXT_KEY } from './ai-activation-service'; +import { AICommandHandlerFactory } from './ai-command-handler-factory'; + +@injectable() +export class AIViewContribution extends AbstractViewContribution { + + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + + @inject(AIActivationService) + protected readonly activationService: AIActivationService; + + @inject(AICommandHandlerFactory) + protected readonly commandHandlerFactory: AICommandHandlerFactory; + + @postConstruct() + protected init(): void { + this.activationService.onDidChangeActiveStatus(active => { + if (!active) { + this.closeView(); + } + }); + } + + override registerCommands(commands: CommandRegistry): void { + if (this.toggleCommand) { + + commands.registerCommand(this.toggleCommand, this.commandHandlerFactory({ + execute: () => this.toggleView(), + })); + } + this.quickView?.registerItem({ + label: this.viewLabel, + when: EXPERIMENTAL_AI_CONTEXT_KEY, + open: () => this.openView({ activate: true }) + }); + + } + + override registerMenus(menus: MenuModelRegistry): void { + if (this.toggleCommand) { + menus.registerMenuAction(CommonMenus.VIEW_VIEWS, { + commandId: this.toggleCommand.id, + when: EXPERIMENTAL_AI_CONTEXT_KEY, + label: this.viewLabel + }); + } + } + override registerKeybindings(keybindings: KeybindingRegistry): void { + if (this.toggleCommand && this.options.toggleKeybinding) { + keybindings.registerKeybinding({ + command: this.toggleCommand.id, + when: EXPERIMENTAL_AI_CONTEXT_KEY, + keybinding: this.options.toggleKeybinding + }); + } + } +} + diff --git a/packages/ai-core/src/browser/frontend-language-model-registry.ts b/packages/ai-core/src/browser/frontend-language-model-registry.ts new file mode 100644 index 0000000000000..baecabaa0e716 --- /dev/null +++ b/packages/ai-core/src/browser/frontend-language-model-registry.ts @@ -0,0 +1,405 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CancellationToken, ILogger } from '@theia/core'; +import { + inject, + injectable, + postConstruct, +} from '@theia/core/shared/inversify'; +import { + OutputChannel, + OutputChannelManager, + OutputChannelSeverity, +} from '@theia/output/lib/browser/output-channel'; +import { + DefaultLanguageModelRegistryImpl, + isLanguageModelParsedResponse, + isLanguageModelStreamResponse, + isLanguageModelStreamResponseDelegate, + isLanguageModelTextResponse, + isModelMatching, + LanguageModel, + LanguageModelDelegateClient, + LanguageModelFrontendDelegate, + LanguageModelMetaData, + LanguageModelRegistryClient, + LanguageModelRegistryFrontendDelegate, + LanguageModelRequest, + LanguageModelResponse, + LanguageModelSelector, + LanguageModelStreamResponsePart, +} from '../common'; +import { AISettingsService } from './ai-settings-service'; + +@injectable() +export class LanguageModelDelegateClientImpl + implements LanguageModelDelegateClient, LanguageModelRegistryClient { + protected receiver: FrontendLanguageModelRegistryImpl; + + setReceiver(receiver: FrontendLanguageModelRegistryImpl): void { + this.receiver = receiver; + } + + send(id: string, token: LanguageModelStreamResponsePart | undefined): void { + this.receiver.send(id, token); + } + + toolCall(requestId: string, toolId: string, args_string: string): Promise { + return this.receiver.toolCall(requestId, toolId, args_string); + } + + languageModelAdded(metadata: LanguageModelMetaData): void { + this.receiver.languageModelAdded(metadata); + } + + languageModelRemoved(id: string): void { + this.receiver.languageModelRemoved(id); + } +} + +interface StreamState { + id: string; + tokens: (LanguageModelStreamResponsePart | undefined)[]; + resolve?: (_: unknown) => void; +} + +@injectable() +export class FrontendLanguageModelRegistryImpl + extends DefaultLanguageModelRegistryImpl { + + // called by backend + languageModelAdded(metadata: LanguageModelMetaData): void { + this.addLanguageModels([metadata]); + } + // called by backend + languageModelRemoved(id: string): void { + this.removeLanguageModels([id]); + } + @inject(LanguageModelRegistryFrontendDelegate) + protected registryDelegate: LanguageModelRegistryFrontendDelegate; + + @inject(LanguageModelFrontendDelegate) + protected providerDelegate: LanguageModelFrontendDelegate; + + @inject(LanguageModelDelegateClientImpl) + protected client: LanguageModelDelegateClientImpl; + + @inject(ILogger) + protected override logger: ILogger; + + @inject(OutputChannelManager) + protected outputChannelManager: OutputChannelManager; + + @inject(AISettingsService) + protected settingsService: AISettingsService; + + private static requestCounter: number = 0; + + override addLanguageModels(models: LanguageModelMetaData[] | LanguageModel[]): void { + let modelAdded = false; + for (const model of models) { + if (this.languageModels.find(m => m.id === model.id)) { + console.warn(`Tried to add an existing model ${model.id}`); + continue; + } + if (LanguageModel.is(model)) { + this.languageModels.push( + new Proxy( + model, + languageModelOutputHandler( + () => this.outputChannelManager.getChannel( + model.id + ) + ) + ) + ); + modelAdded = true; + } else { + this.languageModels.push( + new Proxy( + this.createFrontendLanguageModel( + model + ), + languageModelOutputHandler( + () => this.outputChannelManager.getChannel( + model.id + ) + ) + ) + ); + modelAdded = true; + } + } + if (modelAdded) { + this.changeEmitter.fire({ models: this.languageModels }); + } + } + + @postConstruct() + protected override init(): void { + this.client.setReceiver(this); + + const contributions = + this.languageModelContributions.getContributions(); + const promises = contributions.map(provider => provider()); + const backendDescriptions = + this.registryDelegate.getLanguageModelDescriptions(); + + Promise.allSettled([backendDescriptions, ...promises]).then( + results => { + const backendDescriptionsResult = results[0]; + if (backendDescriptionsResult.status === 'fulfilled') { + this.addLanguageModels(backendDescriptionsResult.value); + } else { + this.logger.error( + 'Failed to add language models contributed from the backend', + backendDescriptionsResult.reason + ); + } + for (let i = 1; i < results.length; i++) { + // assert that index > 0 contains only language models + const languageModelResult = results[i] as + | PromiseRejectedResult + | PromiseFulfilledResult; + if (languageModelResult.status === 'fulfilled') { + this.addLanguageModels(languageModelResult.value); + } else { + this.logger.error( + 'Failed to add some language models:', + languageModelResult.reason + ); + } + } + this.markInitialized(); + } + ); + } + + createFrontendLanguageModel( + description: LanguageModelMetaData + ): LanguageModel { + return { + ...description, + request: async (request: LanguageModelRequest, cancellationToken?: CancellationToken) => { + const requestId = `${FrontendLanguageModelRegistryImpl.requestCounter++}`; + this.requests.set(requestId, request); + cancellationToken?.onCancellationRequested(() => { + this.providerDelegate.cancel(requestId); + }); + const response = await this.providerDelegate.request( + description.id, + request, + requestId, + cancellationToken + ); + if (isLanguageModelTextResponse(response) || isLanguageModelParsedResponse(response)) { + return response; + } + if (isLanguageModelStreamResponseDelegate(response)) { + if (!this.streams.has(response.streamId)) { + const newStreamState = { + id: response.streamId, + tokens: [], + }; + this.streams.set(response.streamId, newStreamState); + } + const streamState = this.streams.get(response.streamId)!; + return { + stream: this.getIterable(streamState), + }; + } + this.logger.error( + `Received unknown response in frontend for request to language model ${description.id}. Trying to continue without touching the response.`, + response + ); + return response; + }, + }; + } + + private streams = new Map(); + private requests = new Map(); + + async *getIterable( + state: StreamState + ): AsyncIterable { + let current = -1; + while (true) { + if (current < state.tokens.length - 1) { + current++; + const token = state.tokens[current]; + if (token === undefined) { + // message is finished + break; + } + if (token !== undefined) { + yield token; + } + } else { + await new Promise(resolve => { + state.resolve = resolve; + }); + } + } + this.streams.delete(state.id); + } + + // called by backend via the "delegate client" with new tokens + send(id: string, token: LanguageModelStreamResponsePart | undefined): void { + if (!this.streams.has(id)) { + const newStreamState = { + id, + tokens: [], + }; + this.streams.set(id, newStreamState); + } + const streamState = this.streams.get(id)!; + streamState.tokens.push(token); + if (streamState.resolve) { + streamState.resolve(token); + } + } + + // called by backend once tool is invoked + toolCall(id: string, toolId: string, arg_string: string): Promise { + if (!this.requests.has(id)) { + throw new Error('Somehow we got a callback for a non existing request!'); + } + const request = this.requests.get(id)!; + const tool = request.tools?.find(t => t.id === toolId); + if (tool) { + return tool.handler(arg_string); + } + throw new Error(`Could not find a tool for ${toolId}!`); + } + + override async selectLanguageModels(request: LanguageModelSelector): Promise { + await this.initialized; + const userSettings = this.settingsService.getAgentSettings(request.agent)?.languageModelRequirements.find(req => req.purpose === request.purpose); + if (userSettings?.identifier) { + const model = await this.getLanguageModel(userSettings.identifier); + if (model) { + return [model]; + } + } + return this.languageModels.filter(model => isModelMatching(request, model)); + } + + override async selectLanguageModel(request: LanguageModelSelector): Promise { + return (await this.selectLanguageModels(request))[0]; + } +} + +const formatJsonWithIndentation = (obj: unknown): string[] => { + // eslint-disable-next-line no-null/no-null + const jsonString = JSON.stringify(obj, null, 2); + const lines = jsonString.split('\n'); + const formattedLines: string[] = []; + + lines.forEach(line => { + const subLines = line.split('\\n'); + const index = indexOfValue(subLines[0]) + 1; + formattedLines.push(subLines[0]); + const prefix = index > 0 ? ' '.repeat(index) : ''; + if (index !== -1) { + for (let i = 1; i < subLines.length; i++) { + formattedLines.push(prefix + subLines[i]); + } + } + }); + + return formattedLines; +}; + +const indexOfValue = (jsonLine: string): number => { + const pattern = /"([^"]+)"\s*:\s*/g; + const match = pattern.exec(jsonLine); + return match ? match.index + match[0].length : -1; +}; + +const languageModelOutputHandler = ( + outputChannelGetter: () => OutputChannel +): ProxyHandler => ({ + get( + target: LanguageModel, + prop: K, + ): LanguageModel[K] | LanguageModel['request'] { + const original = target[prop]; + if (prop === 'request' && typeof original === 'function') { + return async function ( + ...args: Parameters + ): Promise { + const outputChannel = outputChannelGetter(); + outputChannel.appendLine( + 'Sending request:' + ); + const formattedRequest = formatJsonWithIndentation(args[0]); + formattedRequest.forEach(line => outputChannel.appendLine(line)); + if (args[1]) { + args[1] = new Proxy(args[1], { + get( + cTarget: CancellationToken, + cProp: CK + ): CancellationToken[CK] | CancellationToken['onCancellationRequested'] { + if (cProp === 'onCancellationRequested') { + return (...cargs: Parameters) => cTarget.onCancellationRequested(() => { + outputChannel.appendLine('\nCancel requested', OutputChannelSeverity.Warning); + cargs[0](); + }, cargs[1], cargs[2]); + } + return cTarget[cProp]; + } + }); + } + try { + const result = await original.apply(target, args); + if (isLanguageModelStreamResponse(result)) { + outputChannel.appendLine('Received a response stream'); + const stream = result.stream; + const loggedStream = { + async *[Symbol.asyncIterator](): AsyncIterator { + for await (const part of stream) { + outputChannel.append(part.content || ''); + yield part; + } + outputChannel.append('\n'); + outputChannel.appendLine('End of stream'); + }, + }; + return { + ...result, + stream: loggedStream, + }; + } else { + outputChannel.appendLine('Received a response'); + outputChannel.appendLine(JSON.stringify(result)); + return result; + } + } catch (err) { + outputChannel.appendLine('An error occurred'); + if (err instanceof Error) { + outputChannel.appendLine( + err.message, + OutputChannelSeverity.Error + ); + } + throw err; + } + }; + } + return original; + }, +}); diff --git a/packages/ai-core/src/browser/frontend-prompt-customization-service.ts b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts new file mode 100644 index 0000000000000..2f3d5525f2bca --- /dev/null +++ b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts @@ -0,0 +1,191 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { DisposableCollection, URI } from '@theia/core'; +import { OpenerService } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import { PromptCustomizationService, PromptTemplate } from '../common'; +import { BinaryBuffer } from '@theia/core/lib/common/buffer'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { FileChangesEvent } from '@theia/filesystem/lib/common/files'; +import { AICorePreferences, PREFERENCE_NAME_PROMPT_TEMPLATES } from './ai-core-preferences'; +import { AgentService } from '../common/agent-service'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; + +@injectable() +export class FrontendPromptCustomizationServiceImpl implements PromptCustomizationService { + + @inject(EnvVariablesServer) + protected readonly envVariablesServer: EnvVariablesServer; + + @inject(AICorePreferences) + protected readonly preferences: AICorePreferences; + + @inject(FileService) + protected readonly fileService: FileService; + + @inject(OpenerService) + protected readonly openerService: OpenerService; + + @inject(AgentService) + protected readonly agentService: AgentService; + + protected readonly trackedTemplateURIs = new Set(); + protected templates = new Map(); + + protected toDispose = new DisposableCollection(); + + @postConstruct() + protected init(): void { + this.preferences.onPreferenceChanged(event => { + if (event.preferenceName === PREFERENCE_NAME_PROMPT_TEMPLATES) { + this.update(); + } + }); + this.update(); + } + + protected async update(): Promise { + this.toDispose.dispose(); + // we need to assign a local variable, so that updates running in parallel don't interfere with each other + const _templates = new Map(); + this.templates = _templates; + this.trackedTemplateURIs.clear(); + + const templateURI = await this.getTemplatesDirectoryURI(); + + this.toDispose.push(this.fileService.watch(templateURI, { recursive: true, excludes: [] })); + this.toDispose.push(this.fileService.onDidFilesChange(async (event: FileChangesEvent) => { + + for (const child of this.trackedTemplateURIs) { + // check deletion and updates + if (event.contains(new URI(child))) { + for (const deletedFile of event.getDeleted()) { + if (this.trackedTemplateURIs.has(deletedFile.resource.toString())) { + this.trackedTemplateURIs.delete(deletedFile.resource.toString()); + _templates.delete(deletedFile.resource.path.name); + } + } + for (const updatedFile of event.getUpdated()) { + if (this.trackedTemplateURIs.has(updatedFile.resource.toString())) { + const filecontent = await this.fileService.read(updatedFile.resource); + _templates.set(this.removePromptTemplateSuffix(updatedFile.resource.path.name), filecontent.value); + } + } + } + } + + // check new templates + for (const addedFile of event.getAdded()) { + if (addedFile.resource.parent.toString() === templateURI.toString() && addedFile.resource.path.ext === '.prompttemplate') { + this.trackedTemplateURIs.add(addedFile.resource.toString()); + const filecontent = await this.fileService.read(addedFile.resource); + _templates.set(this.removePromptTemplateSuffix(addedFile.resource.path.name), filecontent.value); + } + } + + })); + + const stat = await this.fileService.resolve(templateURI); + if (stat.children === undefined) { + return; + } + + for (const file of stat.children) { + if (!file.isFile) { + continue; + } + const fileURI = file.resource; + if (fileURI.path.ext === '.prompttemplate') { + this.trackedTemplateURIs.add(fileURI.toString()); + const filecontent = await this.fileService.read(fileURI); + _templates.set(this.removePromptTemplateSuffix(file.name), filecontent.value); + } + } + } + + protected async getTemplatesDirectoryURI(): Promise { + const templatesFolder = this.preferences[PREFERENCE_NAME_PROMPT_TEMPLATES]; + if (templatesFolder && templatesFolder.trim().length > 0) { + return URI.fromFilePath(templatesFolder); + } + const theiaConfigDir = await this.envVariablesServer.getConfigDirUri(); + return new URI(theiaConfigDir).resolve('prompt-templates'); + } + + protected async getTemplateURI(templateId: string): Promise { + return (await this.getTemplatesDirectoryURI()).resolve(`${templateId}.prompttemplate`); + } + + protected removePromptTemplateSuffix(filename: string): string { + const suffix = '.prompttemplate'; + if (filename.endsWith(suffix)) { + return filename.slice(0, -suffix.length); + } + return filename; + } + + isPromptTemplateCustomized(id: string): boolean { + return this.templates.has(id); + } + + getCustomizedPromptTemplate(id: string): string | undefined { + return this.templates.get(id); + } + + async editTemplate(id: string, content?: string): Promise { + const template = this.getOriginalTemplate(id); + if (template === undefined) { + throw new Error(`Unable to edit template ${id}: template not found.`); + } + const editorUri = await this.getTemplateURI(id); + if (! await this.fileService.exists(editorUri)) { + await this.fileService.createFile(editorUri, BinaryBuffer.fromString(content ?? template.template)); + } else if (content) { + // Write content to the file before opening it + await this.fileService.writeFile(editorUri, BinaryBuffer.fromString(content)); + } + const openHandler = await this.openerService.getOpener(editorUri); + openHandler.open(editorUri); + } + + async resetTemplate(id: string): Promise { + const editorUri = await this.getTemplateURI(id); + if (await this.fileService.exists(editorUri)) { + await this.fileService.delete(editorUri); + } + } + + getOriginalTemplate(id: string): PromptTemplate | undefined { + for (const agent of this.agentService.getAllAgents()) { + for (const template of agent.promptTemplates) { + if (template.id === id) { + return template; + } + } + } + return undefined; + } + + getTemplateIDFromURI(uri: URI): string | undefined { + const id = this.removePromptTemplateSuffix(uri.path.name); + if (this.templates.has(id)) { + return id; + } + return undefined; + } + +} diff --git a/packages/ai-core/src/browser/frontend-variable-service.ts b/packages/ai-core/src/browser/frontend-variable-service.ts new file mode 100644 index 0000000000000..56ceda7e4edd8 --- /dev/null +++ b/packages/ai-core/src/browser/frontend-variable-service.ts @@ -0,0 +1,26 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { DefaultAIVariableService } from '../common'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; + +@injectable() +export class FrontendVariableService extends DefaultAIVariableService implements FrontendApplicationContribution { + onStart(): void { + this.initContributions(); + } +} diff --git a/packages/ai-core/src/browser/index.ts b/packages/ai-core/src/browser/index.ts new file mode 100644 index 0000000000000..443f3894e72f4 --- /dev/null +++ b/packages/ai-core/src/browser/index.ts @@ -0,0 +1,26 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './ai-activation-service'; +export * from './ai-core-frontend-application-contribution'; +export * from './ai-core-frontend-module'; +export * from './ai-core-preferences'; +export * from './ai-settings-service'; +export * from './ai-view-contribution'; +export * from './frontend-language-model-registry'; +export * from './frontend-variable-service'; +export * from './prompttemplate-contribution'; +export * from './theia-variable-contribution'; diff --git a/packages/ai-core/src/browser/prompttemplate-contribution.ts b/packages/ai-core/src/browser/prompttemplate-contribution.ts new file mode 100644 index 0000000000000..d3cf4f99a6814 --- /dev/null +++ b/packages/ai-core/src/browser/prompttemplate-contribution.ts @@ -0,0 +1,250 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, named } from '@theia/core/shared/inversify'; +import { GrammarDefinition, GrammarDefinitionProvider, LanguageGrammarDefinitionContribution, TextmateRegistry } from '@theia/monaco/lib/browser/textmate'; +import * as monaco from '@theia/monaco-editor-core'; +import { Command, CommandContribution, CommandRegistry, ContributionProvider, MessageService } from '@theia/core'; +import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; + +import { codicon, Widget } from '@theia/core/lib/browser'; +import { EditorWidget, ReplaceOperation } from '@theia/editor/lib/browser'; +import { PromptCustomizationService, PromptService, ToolProvider } from '../common'; +import { ProviderResult } from '@theia/monaco-editor-core/esm/vs/editor/common/languages'; + +const PROMPT_TEMPLATE_LANGUAGE_ID = 'theia-ai-prompt-template'; +const PROMPT_TEMPLATE_TEXTMATE_SCOPE = 'source.prompttemplate'; + +export const DISCARD_PROMPT_TEMPLATE_CUSTOMIZATIONS: Command = { + id: 'theia-ai-prompt-template:discard', + iconClass: codicon('discard'), + category: 'Theia AI Prompt Templates' +}; + +// TODO this command is mainly for testing purposes +export const SHOW_ALL_PROMPTS_COMMAND: Command = { + id: 'theia-ai-prompt-template:show-prompts-command', + label: 'Show all prompts', + iconClass: codicon('beaker'), + category: 'Theia AI Prompt Templates', +}; + +@injectable() +export class PromptTemplateContribution implements LanguageGrammarDefinitionContribution, CommandContribution, TabBarToolbarContribution { + + @inject(PromptService) + private readonly promptService: PromptService; + + @inject(MessageService) + private readonly messageService: MessageService; + + @inject(PromptCustomizationService) + protected readonly customizationService: PromptCustomizationService; + + @inject(ContributionProvider) + @named(ToolProvider) + private toolProviders: ContributionProvider; + + readonly config: monaco.languages.LanguageConfiguration = + { + 'brackets': [ + ['${', '}'], + ['~{', '}'] + ], + 'autoClosingPairs': [ + { 'open': '${', 'close': '}' }, + { 'open': '~{', 'close': '}' }, + ], + 'surroundingPairs': [ + { 'open': '${', 'close': '}' }, + { 'open': '~{', 'close': '}' } + ] + }; + + registerTextmateLanguage(registry: TextmateRegistry): void { + monaco.languages.register({ + id: PROMPT_TEMPLATE_LANGUAGE_ID, + 'aliases': [ + 'Theia AI Prompt Templates' + ], + 'extensions': [ + '.prompttemplate', + ], + 'filenames': [] + }); + + monaco.languages.setLanguageConfiguration(PROMPT_TEMPLATE_LANGUAGE_ID, this.config); + + monaco.languages.registerCompletionItemProvider(PROMPT_TEMPLATE_LANGUAGE_ID, { + // Monaco only supports single character trigger characters + triggerCharacters: ['{'], + provideCompletionItems: (model, position, _context, _token): ProviderResult => this.provideFunctionCompletions(model, position), + }); + + const textmateGrammar = require('../../data/prompttemplate.tmLanguage.json'); + const grammarDefinitionProvider: GrammarDefinitionProvider = { + getGrammarDefinition: function (): Promise { + return Promise.resolve({ + format: 'json', + content: textmateGrammar + }); + } + }; + registry.registerTextmateGrammarScope(PROMPT_TEMPLATE_TEXTMATE_SCOPE, grammarDefinitionProvider); + + registry.mapLanguageIdToTextmateGrammar(PROMPT_TEMPLATE_LANGUAGE_ID, PROMPT_TEMPLATE_TEXTMATE_SCOPE); + } + + provideFunctionCompletions(model: monaco.editor.ITextModel, position: monaco.Position): ProviderResult { + return this.getSuggestions( + model, + position, + '~{', + this.toolProviders.getContributions().map(provider => provider.getTool()), + monaco.languages.CompletionItemKind.Function, + tool => tool.id, + tool => tool.name, + tool => tool.description ?? '' + ); + } + + getCompletionRange(model: monaco.editor.ITextModel, position: monaco.Position, triggerCharacters: string): monaco.Range | undefined { + // Check if the characters before the current position are the trigger characters + const lineContent = model.getLineContent(position.lineNumber); + const triggerLength = triggerCharacters.length; + const charactersBefore = lineContent.substring( + position.column - triggerLength - 1, + position.column - 1 + ); + + if (charactersBefore !== triggerCharacters) { + // Do not return agent suggestions if the user didn't just type the trigger characters + return undefined; + } + + // Calculate the range from the position of the trigger characters + const wordInfo = model.getWordUntilPosition(position); + return new monaco.Range( + position.lineNumber, + wordInfo.startColumn, + position.lineNumber, + position.column + ); + } + + private getSuggestions( + model: monaco.editor.ITextModel, + position: monaco.Position, + triggerChars: string, + items: T[], + kind: monaco.languages.CompletionItemKind, + getId: (item: T) => string, + getName: (item: T) => string, + getDescription: (item: T) => string + ): ProviderResult { + const completionRange = this.getCompletionRange(model, position, triggerChars); + if (completionRange === undefined) { + return { suggestions: [] }; + } + const suggestions = items.map(item => ({ + insertText: getId(item), + kind: kind, + label: getName(item), + range: completionRange, + detail: getDescription(item), + })); + return { suggestions }; + } + + registerCommands(commands: CommandRegistry): void { + commands.registerCommand(DISCARD_PROMPT_TEMPLATE_CUSTOMIZATIONS, { + isVisible: (widget: Widget) => this.isPromptTemplateWidget(widget), + isEnabled: (widget: EditorWidget) => this.canDiscard(widget), + execute: (widget: EditorWidget) => this.discard(widget) + }); + + commands.registerCommand(SHOW_ALL_PROMPTS_COMMAND, { + execute: () => this.showAllPrompts() + }); + } + + protected isPromptTemplateWidget(widget: Widget): boolean { + if (widget instanceof EditorWidget) { + return PROMPT_TEMPLATE_LANGUAGE_ID === widget.editor.document.languageId; + } + return false; + } + + protected canDiscard(widget: EditorWidget): boolean { + const resourceUri = widget.editor.uri; + const id = this.customizationService.getTemplateIDFromURI(resourceUri); + if (id === undefined) { + return false; + } + const rawPrompt = this.promptService.getRawPrompt(id); + const defaultPrompt = this.promptService.getDefaultRawPrompt(id); + return rawPrompt?.template !== defaultPrompt?.template; + } + + protected async discard(widget: EditorWidget): Promise { + const resourceUri = widget.editor.uri; + const id = this.customizationService.getTemplateIDFromURI(resourceUri); + if (id === undefined) { + return; + } + const defaultPrompt = this.promptService.getDefaultRawPrompt(id); + if (defaultPrompt === undefined) { + return; + } + + const source: string = widget.editor.document.getText(); + const lastLine = widget.editor.document.getLineContent(widget.editor.document.lineCount); + + const replaceOperation: ReplaceOperation = { + range: { + start: { + line: 0, + character: 0 + }, + end: { + line: widget.editor.document.lineCount, + character: lastLine.length + } + }, + text: defaultPrompt.template + }; + + await widget.editor.replaceText({ + source, + replaceOperations: [replaceOperation] + }); + } + + private showAllPrompts(): void { + const allPrompts = this.promptService.getAllPrompts(); + Object.keys(allPrompts).forEach(id => { + this.messageService.info(`Prompt Template ID: ${id}\n${allPrompts[id].template}`, 'Got it'); + }); + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem({ + id: DISCARD_PROMPT_TEMPLATE_CUSTOMIZATIONS.id, + command: DISCARD_PROMPT_TEMPLATE_CUSTOMIZATIONS.id, + tooltip: 'Discard Customizations' + }); + } +} diff --git a/packages/ai-core/src/browser/style/index.css b/packages/ai-core/src/browser/style/index.css new file mode 100644 index 0000000000000..cddbdb327c694 --- /dev/null +++ b/packages/ai-core/src/browser/style/index.css @@ -0,0 +1,80 @@ +.ai-configuration-widget { + padding: var(--theia-ui-padding); +} + +.theia-ai-settings-container { + padding: var(--theia-ui-padding); +} + +.language-model-container { + padding-top: calc(2 * var(--theia-ui-padding)); +} + +.language-model-container .theia-select { + margin-left: var(--theia-ui-padding); +} + +.ai-templates { + display: grid; + /** Display content in 3 columns */ + grid-template-columns: 1fr auto auto; + /** add a 3px gap between rows */ + row-gap: 3px; +} + +#ai-variable-configuration-container-widget, +#ai-agent-configuration-container-widget { + margin-top: 5px; +} + +/* Variable Settings */ +#ai-variable-configuration-container-widget ul { + list-style: none; + padding: 0; + margin: 0; +} + +#ai-variable-configuration-container-widget .variable-item { + display: flex; + flex-direction: column; + margin-bottom: 1rem; +} + +#ai-variable-configuration-container-widget .variable-args { + display: grid; + grid-template-columns: 1fr 2fr; +} + +/* Agent Settings */ +#ai-agent-configuration-container-widget ul { + list-style: none; + padding: 0; + margin: 0; +} + +.ai-agent-configuration-main { + display: flex; + flex-direction: row; +} + +.configuration-agents-list { + width: 128px; +} + +.configuration-agent-panel { + flex: 1; +} + +#ai-variable-configuration-container-widget .variable-references, +#ai-agent-configuration-container-widget .variable-references { + margin-left: 0.5rem; + padding: 0.5rem; + border-left: solid 1px var(--theia-tree-indentGuidesStroke); +} + +#ai-variable-configuration-container-widget .variable-reference, +#ai-agent-configuration-container-widget .variable-reference { + display: flex; + flex-direction: row; + align-items: center; +} diff --git a/packages/ai-core/src/browser/theia-variable-contribution.ts b/packages/ai-core/src/browser/theia-variable-contribution.ts new file mode 100644 index 0000000000000..f8353e5eecb00 --- /dev/null +++ b/packages/ai-core/src/browser/theia-variable-contribution.ts @@ -0,0 +1,58 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { VariableRegistry, VariableResolverService } from '@theia/variable-resolver/lib/browser'; +import { AIVariableContribution, AIVariableResolver, AIVariableService, AIVariableResolutionRequest, AIVariableContext, ResolvedAIVariable } from '../common'; + +@injectable() +export class TheiaVariableContribution implements AIVariableContribution, AIVariableResolver { + @inject(VariableResolverService) + protected readonly variableResolverService: VariableResolverService; + + @inject(VariableRegistry) + protected readonly variableRegistry: VariableRegistry; + + @inject(FrontendApplicationStateService) + protected readonly stateService: FrontendApplicationStateService; + + registerVariables(service: AIVariableService): void { + this.stateService.reachedState('initialized_layout').then(() => { + // some variable contributions in Theia are done as part of the onStart, same as our AI variable contributions + // we therefore wait for all of them to be registered before we register we map them to our own + this.variableRegistry.getVariables().forEach(variable => { + service.registerResolver({ id: `theia-${variable.name}`, name: variable.name, description: variable.description ?? 'Theia Built-in Variable' }, this); + }); + }); + } + + protected toTheiaVariable(request: AIVariableResolutionRequest): string { + return `$\{${request.variable.name}${request.arg ? ':' + request.arg : ''}}`; + } + + async canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + // some variables are not resolvable without providing a specific context + // this may be expensive but was not a problem for Theia's built-in variables + const resolved = await this.variableResolverService.resolve(this.toTheiaVariable(request), context); + return !resolved ? 0 : 1; + } + + async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + const resolved = await this.variableResolverService.resolve(this.toTheiaVariable(request), context); + return resolved ? { value: resolved, variable: request.variable } : undefined; + } +} + diff --git a/packages/ai-core/src/common/agent-service.ts b/packages/ai-core/src/common/agent-service.ts new file mode 100644 index 0000000000000..e17097bb8fda4 --- /dev/null +++ b/packages/ai-core/src/common/agent-service.ts @@ -0,0 +1,92 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; +import { ContributionProvider } from '@theia/core'; +import { Agent } from './agent'; + +export const AgentService = Symbol('AgentService'); + +/** + * Service to access the list of known Agents. + */ +export interface AgentService { + /** + * Retrieves a list of all available agents, i.e. agents which are not disabled + */ + getAgents(): Agent[]; + /** + * Retrieves a list of all agents, including disabled ones. + */ + getAllAgents(): Agent[]; + /** + * Enable the agent with the specified id. + * @param agentId the agent id. + */ + enableAgent(agentId: string): void; + /** + * disable the agent with the specified id. + * @param agentId the agent id. + */ + disableAgent(agentId: string): void; + /** + * query whether this agent is currently enabled or disabled. + * @param agentId the agent id. + * @return true if the agent is enabled, false otherwise. + */ + isEnabled(agentId: string): boolean; +} + +@injectable() +export class AgentServiceImpl implements AgentService { + + @inject(ContributionProvider) @named(Agent) + protected readonly agentsProvider: ContributionProvider; + + protected disabledAgents = new Set(); + + protected agents: Agent[] = []; + + @postConstruct() + init(): void { + for (const agent of this.agentsProvider.getContributions()) { + this.registerAgent(agent); + } + } + + registerAgent(agent: Agent): void { + this.agents.push(agent); + } + + getAgents(): Agent[] { + return this.agents.filter(agent => this.isEnabled(agent.id)); + } + + getAllAgents(): Agent[] { + return this.agents; + } + + enableAgent(agentId: string): void { + this.disabledAgents.delete(agentId); + } + + disableAgent(agentId: string): void { + this.disabledAgents.add(agentId); + } + + isEnabled(agentId: string): boolean { + return !this.disabledAgents.has(agentId); + } +} diff --git a/packages/ai-core/src/common/agent.ts b/packages/ai-core/src/common/agent.ts new file mode 100644 index 0000000000000..9d788c55e2005 --- /dev/null +++ b/packages/ai-core/src/common/agent.ts @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { LanguageModelRequirement } from './language-model'; +import { PromptTemplate } from './prompt-service'; + +export const Agent = Symbol('Agent'); +/** + * Agents represent the main functionality of the AI system. They are responsible for processing user input, collecting information from the environment, + * invoking and processing LLM responses, and providing the final response to the user while recording their actions in the AI history. + * + * Agents are meant to cover all use cases, from specialized scenarios to general purpose chat bots. + * + * Agents are encouraged to provide a detailed description of their functionality and their processed inputs. + * They can also declare their used prompt templates, which makes them configurable for the user. + */ +export interface Agent { + /** Used to identify an agent, e.g. when it is requesting language models, etc. */ + readonly id: string; + + /** Human-readable name shown to users to identify the agent. */ + readonly name: string; + + /** A markdown description of its functionality and its privacy-relevant requirements, including function call handlers that access some data autonomously. */ + readonly description: string; + + /** The list of variable identifiers this agent needs to clarify its context requirements. See #39. */ + readonly variables: string[]; + + /** The prompt templates introduced and used by this agent. */ + readonly promptTemplates: PromptTemplate[]; + + /** Required language models. This includes the purpose and optional language model selector arguments. See #47. */ + readonly languageModelRequirements: LanguageModelRequirement[]; +} diff --git a/packages/ai-core/src/common/agents-variable-contribution.ts b/packages/ai-core/src/common/agents-variable-contribution.ts new file mode 100644 index 0000000000000..67803cabb9fe0 --- /dev/null +++ b/packages/ai-core/src/common/agents-variable-contribution.ts @@ -0,0 +1,64 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { inject, injectable } from '@theia/core/shared/inversify'; +import { AIVariable, AIVariableContext, AIVariableContribution, AIVariableResolutionRequest, AIVariableResolver, AIVariableService, ResolvedAIVariable } from './variable-service'; +import { MaybePromise } from '@theia/core'; +import { AgentService } from './agent-service'; + +export const AGENTS_VARIABLE: AIVariable = { + id: 'agents', + name: 'agents', + description: 'Returns the list of agents available in the system' +}; + +export interface ResolvedAgentsVariable extends ResolvedAIVariable { + agents: AgentDescriptor[]; +} + +export interface AgentDescriptor { + id: string; + name: string; + description: string; +} + +@injectable() +export class AgentsVariableContribution implements AIVariableContribution, AIVariableResolver { + + @inject(AgentService) + protected readonly agentService: AgentService; + + registerVariables(service: AIVariableService): void { + service.registerResolver(AGENTS_VARIABLE, this); + } + + canResolve(request: AIVariableResolutionRequest, _context: AIVariableContext): MaybePromise { + if (request.variable.name === AGENTS_VARIABLE.name) { + return 1; + } + return -1; + } + + async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + if (request.variable.name === AGENTS_VARIABLE.name) { + const agents = this.agentService.getAgents().map(agent => ({ + id: agent.id, + name: agent.name, + description: agent.description + })); + return { variable: AGENTS_VARIABLE, agents, value: JSON.stringify(agents) }; + } + } +} diff --git a/packages/ai-core/src/common/communication-recording-service.ts b/packages/ai-core/src/common/communication-recording-service.ts new file mode 100644 index 0000000000000..491d8065173e5 --- /dev/null +++ b/packages/ai-core/src/common/communication-recording-service.ts @@ -0,0 +1,44 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Event } from '@theia/core'; + +export type CommunicationHistory = CommunicationHistoryEntry[]; + +export interface CommunicationHistoryEntry { + agentId: string; + sessionId: string; + timestamp: number; + requestId: string; + request?: string; + response?: string; + responseTime?: number; + messages?: unknown[]; +} + +export type CommunicationRequestEntry = Omit; +export type CommunicationResponseEntry = Omit; + +export const CommunicationRecordingService = Symbol('CommunicationRecordingService'); +export interface CommunicationRecordingService { + recordRequest(requestEntry: CommunicationRequestEntry): void; + readonly onDidRecordRequest: Event; + + recordResponse(responseEntry: CommunicationResponseEntry): void; + readonly onDidRecordResponse: Event; + + getHistory(agentId: string): CommunicationHistory; +} diff --git a/packages/ai-core/src/common/index.ts b/packages/ai-core/src/common/index.ts new file mode 100644 index 0000000000000..9ebeec5a068ed --- /dev/null +++ b/packages/ai-core/src/common/index.ts @@ -0,0 +1,29 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export * from './agent-service'; +export * from './agent'; +export * from './agents-variable-contribution'; +export * from './communication-recording-service'; +export * from './tool-invocation-registry'; +export * from './language-model-delegate'; +export * from './language-model-util'; +export * from './language-model'; +export * from './prompt-service'; +export * from './prompt-service-util'; +export * from './protocol'; +export * from './today-variable-contribution'; +export * from './tomorrow-variable-contribution'; +export * from './variable-service'; diff --git a/packages/ai-core/src/common/language-model-delegate.ts b/packages/ai-core/src/common/language-model-delegate.ts new file mode 100644 index 0000000000000..5edbfe4b18ac7 --- /dev/null +++ b/packages/ai-core/src/common/language-model-delegate.ts @@ -0,0 +1,45 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CancellationToken } from '@theia/core'; +import { LanguageModelMetaData, LanguageModelParsedResponse, LanguageModelRequest, LanguageModelStreamResponsePart, LanguageModelTextResponse } from './language-model'; + +export const LanguageModelDelegateClient = Symbol('LanguageModelDelegateClient'); +export interface LanguageModelDelegateClient { + toolCall(requestId: string, toolId: string, args_string: string): Promise; + send(id: string, token: LanguageModelStreamResponsePart | undefined): void; +} +export const LanguageModelRegistryFrontendDelegate = Symbol('LanguageModelRegistryFrontendDelegate'); +export interface LanguageModelRegistryFrontendDelegate { + getLanguageModelDescriptions(): Promise; +} + +export interface LanguageModelStreamResponseDelegate { + streamId: string; +} +export const isLanguageModelStreamResponseDelegate = (obj: unknown): obj is LanguageModelStreamResponseDelegate => + !!(obj && typeof obj === 'object' && 'streamId' in obj && typeof (obj as { streamId: unknown }).streamId === 'string'); + +export type LanguageModelResponseDelegate = LanguageModelTextResponse | LanguageModelParsedResponse | LanguageModelStreamResponseDelegate; + +export const LanguageModelFrontendDelegate = Symbol('LanguageModelFrontendDelegate'); +export interface LanguageModelFrontendDelegate { + cancel(requestId: string): void; + request(modelId: string, request: LanguageModelRequest, requestId: string, cancellationToken?: CancellationToken): Promise; +} + +export const languageModelRegistryDelegatePath = '/services/languageModelRegistryDelegatePath'; +export const languageModelDelegatePath = '/services/languageModelDelegatePath'; diff --git a/packages/ai-core/src/common/language-model-util.ts b/packages/ai-core/src/common/language-model-util.ts new file mode 100644 index 0000000000000..d89730b583e4d --- /dev/null +++ b/packages/ai-core/src/common/language-model-util.ts @@ -0,0 +1,67 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { isLanguageModelStreamResponse, isLanguageModelTextResponse, LanguageModelResponse, ToolRequest } from './language-model'; + +export const getTextOfResponse = async (response: LanguageModelResponse): Promise => { + if (isLanguageModelTextResponse(response)) { + return response.text; + } else if (isLanguageModelStreamResponse(response)) { + let result = ''; + for await (const chunk of response.stream) { + result += chunk.content ?? ''; + } + return result; + } + throw new Error(`Invalid response type ${response}`); +}; + +export const getJsonOfResponse = async (response: LanguageModelResponse): Promise => { + const text = await getTextOfResponse(response); + if (text.startsWith('```json')) { + const regex = /```json\s*([\s\S]*?)\s*```/g; + let match; + // eslint-disable-next-line no-null/no-null + while ((match = regex.exec(text)) !== null) { + try { + return JSON.parse(match[1]); + } catch (error) { + console.error('Failed to parse JSON:', error); + } + } + } else if (text.startsWith('{') || text.startsWith('[')) { + return JSON.parse(text); + } + throw new Error('Invalid response format'); +}; +export const toolRequestToPromptText = (toolRequest: ToolRequest): string => { + const parameters = toolRequest.parameters; + let paramsText = ''; + // parameters are supposed to be as a JSON schema. Thus, derive the parameters from its properties definition + if (parameters) { + const properties = parameters.properties; + paramsText = Object.keys(properties) + .map(key => { + const param = properties[key]; + return `${key}: ${param.type}`; + }) + .join(', '); + } + const descriptionText = toolRequest.description + ? `: ${toolRequest.description}` + : ''; + return `You can call function: ${toolRequest.id}(${paramsText})${descriptionText}`; +}; diff --git a/packages/ai-core/src/common/language-model.spec.ts b/packages/ai-core/src/common/language-model.spec.ts new file mode 100644 index 0000000000000..044b839531543 --- /dev/null +++ b/packages/ai-core/src/common/language-model.spec.ts @@ -0,0 +1,86 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { isModelMatching, LanguageModel, LanguageModelSelector } from './language-model'; +import { expect } from 'chai'; + +describe('isModelMatching', () => { + it('returns false with one of two parameter mismatches', () => { + expect( + isModelMatching( + { + name: 'XXX', + family: 'YYY', + }, + { + name: 'gpt-4o', + family: 'YYY', + } + ) + ).eql(false); + }); + it('returns false with two parameter mismatches', () => { + expect( + isModelMatching( + { + name: 'XXX', + family: 'YYY', + }, + { + name: 'gpt-4o', + family: 'ZZZ', + } + ) + ).eql(false); + }); + it('returns true with one parameter match', () => { + expect( + isModelMatching( + { + name: 'gpt-4o', + }, + { + name: 'gpt-4o', + } + ) + ).eql(true); + }); + it('returns true with two parameter matches', () => { + expect( + isModelMatching( + { + name: 'gpt-4o', + family: 'YYY', + }, + { + name: 'gpt-4o', + family: 'YYY', + } + ) + ).eql(true); + }); + it('returns true if there are no parameters in selector', () => { + expect( + isModelMatching( + {}, + { + name: 'gpt-4o', + family: 'YYY', + } + ) + ).eql(true); + }); +}); diff --git a/packages/ai-core/src/common/language-model.ts b/packages/ai-core/src/common/language-model.ts new file mode 100644 index 0000000000000..4e2755f8a46e0 --- /dev/null +++ b/packages/ai-core/src/common/language-model.ts @@ -0,0 +1,238 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContributionProvider, ILogger, isFunction, isObject, Event, Emitter, CancellationToken } from '@theia/core'; +import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; + +export type MessageActor = 'user' | 'ai' | 'system'; + +export interface LanguageModelRequestMessage { + actor: MessageActor; + type: 'text'; + query: string; +} +export const isLanguageModelRequestMessage = (obj: unknown): obj is LanguageModelRequestMessage => + !!(obj && typeof obj === 'object' && + 'type' in obj && + typeof (obj as { type: unknown }).type === 'string' && + (obj as { type: unknown }).type === 'text' && + 'query' in obj && + typeof (obj as { query: unknown }).query === 'string' + ); +export interface ToolRequest { + id: string; + name: string; + parameters?: { type?: 'object', properties: Record }; + description?: string; + handler: (arg_string: string) => Promise; +} +export interface LanguageModelRequest { + messages: LanguageModelRequestMessage[], + tools?: ToolRequest[]; + response_format?: { type: 'text' } | { type: 'json_object' } | ResponseFormatJsonSchema; + settings?: { [key: string]: unknown }; +} +export interface ResponseFormatJsonSchema { + type: 'json_schema'; + json_schema: { + name: string, + description?: string, + schema?: Record, + strict?: boolean | null + }; +} + +export interface LanguageModelTextResponse { + text: string; +} +export const isLanguageModelTextResponse = (obj: unknown): obj is LanguageModelTextResponse => + !!(obj && typeof obj === 'object' && 'text' in obj && typeof (obj as { text: unknown }).text === 'string'); + +export interface LanguageModelStreamResponsePart { + content?: string | null; + tool_calls?: ToolCall[]; +} + +export interface ToolCall { + id?: string; + function?: { + arguments?: string; + name?: string; + }, + finished?: boolean; + result?: string; +} + +export interface LanguageModelStreamResponse { + stream: AsyncIterable; +} +export const isLanguageModelStreamResponse = (obj: unknown): obj is LanguageModelStreamResponse => + !!(obj && typeof obj === 'object' && 'stream' in obj); + +export interface LanguageModelParsedResponse { + parsed: unknown; + content: string; +} +export const isLanguageModelParsedResponse = (obj: unknown): obj is LanguageModelParsedResponse => + !!(obj && typeof obj === 'object' && 'parsed' in obj && 'content' in obj); + +export type LanguageModelResponse = LanguageModelTextResponse | LanguageModelStreamResponse | LanguageModelParsedResponse; + +/////////////////////////////////////////// +// Language Model Provider +/////////////////////////////////////////// + +export const LanguageModelProvider = Symbol('LanguageModelProvider'); +export type LanguageModelProvider = () => Promise; + +// See also VS Code `ILanguageModelChatMetadata` +export interface LanguageModelMetaData { + readonly id: string; + readonly providerId: string; + readonly name?: string; + readonly vendor?: string; + readonly version?: string; + readonly family?: string; + readonly maxInputTokens?: number; + readonly maxOutputTokens?: number; +} + +export namespace LanguageModelMetaData { + export function is(arg: unknown): arg is LanguageModelMetaData { + return isObject(arg) && 'id' in arg && 'providerId' in arg; + } +} + +export interface LanguageModel extends LanguageModelMetaData { + request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise; +} + +export namespace LanguageModel { + export function is(arg: unknown): arg is LanguageModel { + return isObject(arg) && 'id' in arg && 'providerId' in arg && isFunction(arg.request); + } +} + +// See also VS Code `ILanguageModelChatSelector` +interface VsCodeLanguageModelSelector { + readonly identifier?: string; + readonly name?: string; + readonly vendor?: string; + readonly version?: string; + readonly family?: string; + readonly tokens?: number; +} + +export interface LanguageModelSelector extends VsCodeLanguageModelSelector { + readonly agent: string; + readonly purpose: string; +} + +export type LanguageModelRequirement = Omit; + +export const LanguageModelRegistry = Symbol('LanguageModelRegistry'); +export interface LanguageModelRegistry { + onChange: Event<{ models: LanguageModel[] }>; + addLanguageModels(models: LanguageModel[]): void; + getLanguageModels(): Promise; + getLanguageModel(id: string): Promise; + removeLanguageModels(id: string[]): void; + selectLanguageModel(request: LanguageModelSelector): Promise; + selectLanguageModels(request: LanguageModelSelector): Promise; +} + +@injectable() +export class DefaultLanguageModelRegistryImpl implements LanguageModelRegistry { + @inject(ILogger) + protected logger: ILogger; + @inject(ContributionProvider) @named(LanguageModelProvider) + protected readonly languageModelContributions: ContributionProvider; + + protected languageModels: LanguageModel[] = []; + + protected markInitialized: () => void; + protected initialized: Promise = new Promise(resolve => { this.markInitialized = resolve; }); + + protected changeEmitter = new Emitter<{ models: LanguageModel[] }>(); + onChange = this.changeEmitter.event; + + @postConstruct() + protected init(): void { + const contributions = this.languageModelContributions.getContributions(); + const promises = contributions.map(provider => provider()); + Promise.allSettled(promises).then(results => { + for (const result of results) { + if (result.status === 'fulfilled') { + this.languageModels.push(...result.value); + } else { + this.logger.error('Failed to add some language models:', result.reason); + } + } + this.markInitialized(); + }); + } + + addLanguageModels(models: LanguageModel[]): void { + models.forEach(model => { + if (this.languageModels.find(lm => lm.id === model.id)) { + console.warn(`Tried to add already existing language model with id ${model.id}. The new model will be ignored.`); + return; + } + this.languageModels.push(model); + this.changeEmitter.fire({ models: this.languageModels }); + }); + } + + async getLanguageModels(): Promise { + await this.initialized; + return this.languageModels; + } + + async getLanguageModel(id: string): Promise { + await this.initialized; + return this.languageModels.find(model => model.id === id); + } + + removeLanguageModels(ids: string[]): void { + ids.forEach(id => { + const index = this.languageModels.findIndex(model => model.id === id); + if (index !== -1) { + this.languageModels.splice(index, 1); + this.changeEmitter.fire({ models: this.languageModels }); + } else { + console.warn(`Language model with id ${id} was requested to be removed, however it does not exist`); + } + }); + } + + async selectLanguageModels(request: LanguageModelSelector): Promise { + await this.initialized; + // TODO check for actor and purpose against settings + return this.languageModels.filter(model => isModelMatching(request, model)); + } + + async selectLanguageModel(request: LanguageModelSelector): Promise { + return (await this.selectLanguageModels(request))[0]; + } +} + +export function isModelMatching(request: LanguageModelSelector, model: LanguageModel): boolean { + return (!request.identifier || model.id === request.identifier) && + (!request.name || model.name === request.name) && + (!request.vendor || model.vendor === request.vendor) && + (!request.version || model.version === request.version) && + (!request.family || model.family === request.family); +} diff --git a/packages/ai-core/src/common/prompt-service-util.ts b/packages/ai-core/src/common/prompt-service-util.ts new file mode 100644 index 0000000000000..0a7cf3e6b34be --- /dev/null +++ b/packages/ai-core/src/common/prompt-service-util.ts @@ -0,0 +1,21 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/** Should match the one from VariableResolverService. The format is `{{variableName:arg}}`. */ +export const PROMPT_VARIABLE_REGEX = /\{\{\s*(.*?)\s*\}\}/g; + +/** Match function/tool references in the prompt. The format is `~{functionId}`. */ +export const PROMPT_FUNCTION_REGEX = /\~\{\s*(.*?)\s*\}/g; diff --git a/packages/ai-core/src/common/prompt-service.spec.ts b/packages/ai-core/src/common/prompt-service.spec.ts new file mode 100644 index 0000000000000..c3cd138d66f66 --- /dev/null +++ b/packages/ai-core/src/common/prompt-service.spec.ts @@ -0,0 +1,98 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import 'reflect-metadata'; + +import { expect } from 'chai'; +import { Container } from 'inversify'; +import { PromptService, PromptServiceImpl } from './prompt-service'; +import { DefaultAIVariableService, AIVariableService } from './variable-service'; + +describe('PromptService', () => { + let promptService: PromptService; + + beforeEach(() => { + const container = new Container(); + container.bind(PromptService).to(PromptServiceImpl).inSingletonScope(); + + const variableService = new DefaultAIVariableService({ getContributions: () => [] }); + const nameVariable = { id: 'test', name: 'name', description: 'Test name ' }; + variableService.registerResolver(nameVariable, { + canResolve: () => 100, + resolve: async () => ({ variable: nameVariable, value: 'Jane' }) + }); + container.bind(AIVariableService).toConstantValue(variableService); + + promptService = container.get(PromptService); + promptService.storePrompt('1', 'Hello, {{name}}!'); + promptService.storePrompt('2', 'Goodbye, {{name}}!'); + promptService.storePrompt('3', 'Ciao, {{invalid}}!'); + }); + + it('should initialize prompts from PromptCollectionService', () => { + const allPrompts = promptService.getAllPrompts(); + expect(allPrompts['1'].template).to.equal('Hello, {{name}}!'); + expect(allPrompts['2'].template).to.equal('Goodbye, {{name}}!'); + expect(allPrompts['3'].template).to.equal('Ciao, {{invalid}}!'); + }); + + it('should retrieve raw prompt by id', () => { + const rawPrompt = promptService.getRawPrompt('1'); + expect(rawPrompt?.template).to.equal('Hello, {{name}}!'); + }); + + it('should format prompt with provided arguments', async () => { + const formattedPrompt = await promptService.getPrompt('1', { name: 'John' }); + expect(formattedPrompt?.text).to.equal('Hello, John!'); + }); + + it('should store a new prompt', () => { + promptService.storePrompt('3', 'Welcome, {{name}}!'); + const newPrompt = promptService.getRawPrompt('3'); + expect(newPrompt?.template).to.equal('Welcome, {{name}}!'); + }); + + it('should replace placeholders with provided arguments', async () => { + const prompt = await promptService.getPrompt('1', { name: 'John' }); + expect(prompt?.text).to.equal('Hello, John!'); + }); + + it('should use variable service to resolve placeholders if argument value is not provided', async () => { + const prompt = await promptService.getPrompt('1'); + expect(prompt?.text).to.equal('Hello, Jane!'); + }); + + it('should return the prompt even if there are no replacements', async () => { + const prompt = await promptService.getPrompt('3'); + expect(prompt?.text).to.equal('Ciao, {{invalid}}!'); + }); + + it('should return undefined if the prompt id is not found', async () => { + const prompt = await promptService.getPrompt('4'); + expect(prompt).to.be.undefined; + }); + + it('should ignore whitespace in variables', async () => { + promptService.storePrompt('4', 'Hello, {{name }}!'); + promptService.storePrompt('5', 'Hello, {{ name}}!'); + promptService.storePrompt('6', 'Hello, {{ name }}!'); + promptService.storePrompt('7', 'Hello, {{ name }}!'); + for (let i = 4; i <= 7; i++) { + const prompt = await promptService.getPrompt(`${i}`, { name: 'John' }); + expect(prompt?.text).to.equal('Hello, John!'); + } + }); +}); diff --git a/packages/ai-core/src/common/prompt-service.ts b/packages/ai-core/src/common/prompt-service.ts new file mode 100644 index 0000000000000..119373f0402a7 --- /dev/null +++ b/packages/ai-core/src/common/prompt-service.ts @@ -0,0 +1,208 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { URI } from '@theia/core'; +import { inject, injectable, optional } from '@theia/core/shared/inversify'; +import { AIVariableService } from './variable-service'; +import { ToolInvocationRegistry } from './tool-invocation-registry'; +import { toolRequestToPromptText } from './language-model-util'; +import { ToolRequest } from './language-model'; +import { PROMPT_VARIABLE_REGEX, PROMPT_FUNCTION_REGEX } from './prompt-service-util'; + +export interface PromptTemplate { + id: string; + template: string; +} + +export interface PromptMap { [id: string]: PromptTemplate } + +export interface ResolvedPromptTemplate { + id: string; + /** The resolved prompt text with variables and function requests being replaced. */ + text: string; + /** All functions referenced in the prompt template. */ + functionDescriptions?: Map; +} + +export const PromptService = Symbol('PromptService'); +export interface PromptService { + /** + * Retrieve the raw {@link PromptTemplate} object. + * @param id the id of the {@link PromptTemplate} + */ + getRawPrompt(id: string): PromptTemplate | undefined; + /** + * Retrieve the default raw {@link PromptTemplate} object. + * @param id the id of the {@link PromptTemplate} + */ + getDefaultRawPrompt(id: string): PromptTemplate | undefined; + /** + * Allows to directly replace placeholders in the prompt. The supported format is 'Hi {{name}}!'. + * The placeholder is then searched inside the args object and replaced. + * Function references are also supported via format '~{functionId}'. + * @param id the id of the prompt + * @param args the object with placeholders, mapping the placeholder key to the value + */ + getPrompt(id: string, args?: { [key: string]: unknown }): Promise; + /** + * Manually add a prompt to the list of prompts. + * @param id the id of the prompt + * @param prompt the prompt template to store + */ + storePrompt(id: string, prompt: string): void; + /** + * Return all known prompts as a {@link PromptMap map}. + */ + getAllPrompts(): PromptMap; +} + +export const PromptCustomizationService = Symbol('PromptCustomizationService'); +export interface PromptCustomizationService { + /** + * Whether there is a customization for a {@link PromptTemplate} object + * @param id the id of the {@link PromptTemplate} to check + */ + isPromptTemplateCustomized(id: string): boolean; + + /** + * Returns the customization of {@link PromptTemplate} object or undefined if there is none + * @param id the id of the {@link PromptTemplate} to check + */ + getCustomizedPromptTemplate(id: string): string | undefined + + /** + * Edit the template. If the content is specified, is will be + * used to customize the template. Otherwise, the behavior depends + * on the implementation. Implementation may for example decide to + * open an editor, or request more information from the user, ... + * @param id the template id. + * @param content optional content to customize the template. + */ + editTemplate(id: string, content?: string): void; + + /** + * Reset the template to its default value. + * @param id the template id. + */ + resetTemplate(id: string): void; + + /** + * Return the template id for a given template file. + * @param uri the uri of the template file + */ + getTemplateIDFromURI(uri: URI): string | undefined; +} + +@injectable() +export class PromptServiceImpl implements PromptService { + @inject(PromptCustomizationService) @optional() + protected readonly customizationService: PromptCustomizationService | undefined; + + @inject(AIVariableService) @optional() + protected readonly variableService: AIVariableService | undefined; + + @inject(ToolInvocationRegistry) @optional() + protected readonly toolInvocationRegistry: ToolInvocationRegistry | undefined; + + protected _prompts: PromptMap = {}; + + getRawPrompt(id: string): PromptTemplate | undefined { + if (this.customizationService !== undefined && this.customizationService.isPromptTemplateCustomized(id)) { + const template = this.customizationService.getCustomizedPromptTemplate(id); + if (template !== undefined) { + return { id, template }; + } + } + return this.getDefaultRawPrompt(id); + } + getDefaultRawPrompt(id: string): PromptTemplate | undefined { + return this._prompts[id]; + } + async getPrompt(id: string, args?: { [key: string]: unknown }): Promise { + const prompt = this.getRawPrompt(id); + if (prompt === undefined) { + return undefined; + } + + const matches = [...prompt.template.matchAll(PROMPT_VARIABLE_REGEX)]; + const variableAndArgReplacements = await Promise.all(matches.map(async match => { + const completeText = match[0]; + const variableAndArg = match[1]; + let variableName = variableAndArg; + let argument: string | undefined; + const parts = variableAndArg.split(':', 2); + if (parts.length > 1) { + variableName = parts[0]; + argument = parts[1]; + } + return { + placeholder: completeText, + value: String(args?.[variableAndArg] ?? (await this.variableService?.resolveVariable({ + variable: variableName, + arg: argument + }, {}))?.value ?? completeText) + }; + })); + + const functionMatches = [...prompt.template.matchAll(PROMPT_FUNCTION_REGEX)]; + const functions = new Map(); + const functionReplacements = functionMatches.map(match => { + const completeText = match[0]; + const functionId = match[1]; + const toolRequest = this.toolInvocationRegistry?.getFunction(functionId); + if (toolRequest) { + functions.set(toolRequest.id, toolRequest); + } + return { + placeholder: completeText, + value: toolRequest ? toolRequestToPromptText(toolRequest) : completeText + }; + }); + + let resolvedTemplate = prompt.template; + const replacements = [...variableAndArgReplacements, ...functionReplacements]; + replacements.forEach(replacement => resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value)); + return { + id, + text: resolvedTemplate, + functionDescriptions: functions.size > 0 ? functions : undefined + }; + } + getAllPrompts(): PromptMap { + if (this.customizationService !== undefined) { + const myCustomization = this.customizationService; + const result: PromptMap = {}; + Object.keys(this._prompts).forEach(id => { + if (myCustomization.isPromptTemplateCustomized(id)) { + const template = myCustomization.getCustomizedPromptTemplate(id); + if (template !== undefined) { + result[id] = { id, template }; + } else { + result[id] = { ...this._prompts[id] }; + } + } else { + result[id] = { ...this._prompts[id] }; + } + }); + return result; + } else { + return { ...this._prompts }; + } + } + storePrompt(id: string, prompt: string): void { + this._prompts[id] = { id, template: prompt }; + } +} diff --git a/packages/ai-core/src/common/protocol.ts b/packages/ai-core/src/common/protocol.ts new file mode 100644 index 0000000000000..ec1c3dbfde4b6 --- /dev/null +++ b/packages/ai-core/src/common/protocol.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { LanguageModelMetaData } from './language-model'; + +export const LanguageModelRegistryClient = Symbol('LanguageModelRegistryClient'); +export interface LanguageModelRegistryClient { + languageModelAdded(metadata: LanguageModelMetaData): void; + languageModelRemoved(id: string): void; +} diff --git a/packages/ai-core/src/common/today-variable-contribution.ts b/packages/ai-core/src/common/today-variable-contribution.ts new file mode 100644 index 0000000000000..a155618ffe85c --- /dev/null +++ b/packages/ai-core/src/common/today-variable-contribution.ts @@ -0,0 +1,67 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { MaybePromise } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { AIVariable, ResolvedAIVariable, AIVariableContribution, AIVariableResolver, AIVariableService, AIVariableResolutionRequest, AIVariableContext } from './variable-service'; + +export namespace TodayVariableArgs { + export const IN_UNIX_SECONDS = 'inUnixSeconds'; + export const IN_ISO_8601 = 'inIso8601'; +} + +export const TODAY_VARIABLE: AIVariable = { + id: 'today-provider', + description: 'Does something for today', + name: 'today', + args: [ + { name: TodayVariableArgs.IN_ISO_8601, description: 'Returns the current date in ISO 8601 format' }, + { name: TodayVariableArgs.IN_UNIX_SECONDS, description: 'Returns the current date in unix seconds format' } + ] +}; + +export interface ResolvedTodayVariable extends ResolvedAIVariable { + date: Date; +} + +@injectable() +export class TodayVariableContribution implements AIVariableContribution, AIVariableResolver { + registerVariables(service: AIVariableService): void { + service.registerResolver(TODAY_VARIABLE, this); + } + + canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise { + return 1; + } + + async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + if (request.variable.name === TODAY_VARIABLE.name) { + return this.resolveTodayVariable(request); + } + return undefined; + } + + private resolveTodayVariable(request: AIVariableResolutionRequest): ResolvedTodayVariable { + const date = new Date(); + if (request.arg === TodayVariableArgs.IN_ISO_8601) { + return { variable: request.variable, value: date.toISOString(), date }; + } + if (request.arg === TodayVariableArgs.IN_UNIX_SECONDS) { + return { variable: request.variable, value: Math.round(date.getTime() / 1000).toString(), date }; + } + return { variable: request.variable, value: date.toDateString(), date }; + } +} + diff --git a/packages/ai-core/src/common/tomorrow-variable-contribution.ts b/packages/ai-core/src/common/tomorrow-variable-contribution.ts new file mode 100644 index 0000000000000..8575505cfef82 --- /dev/null +++ b/packages/ai-core/src/common/tomorrow-variable-contribution.ts @@ -0,0 +1,66 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { MaybePromise } from '@theia/core'; +import { injectable } from '@theia/core/shared/inversify'; +import { AIVariable, AIVariableContext, AIVariableContribution, AIVariableResolutionRequest, AIVariableResolver, AIVariableService, ResolvedAIVariable } from './variable-service'; + +export namespace TomorrowVariableArgs { + export const IN_UNIX_SECONDS = 'inUnixSeconds'; + export const IN_ISO_8601 = 'inIso8601'; +} + +export const TOMORROW_VARIABLE: AIVariable = { + id: 'tomorrow-provider', + description: 'Does something for tomorrow', + name: 'tomorrow', + args: [ + { name: TomorrowVariableArgs.IN_ISO_8601, description: 'Returns the current date in ISO 8601 format' }, + { name: TomorrowVariableArgs.IN_UNIX_SECONDS, description: 'Returns the current date in unix seconds format' } + ] +}; + +export interface ResolvedTomorrowVariable extends ResolvedAIVariable { + date: Date; +} + +@injectable() +export class TomorrowVariableContribution implements AIVariableContribution, AIVariableResolver { + registerVariables(service: AIVariableService): void { + service.registerResolver(TOMORROW_VARIABLE, this); + } + + canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise { + return 1; + } + + async resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise { + if (request.variable.name === TOMORROW_VARIABLE.name) { + return this.resolveTomorrowVariable(request); + } + return undefined; + } + + private resolveTomorrowVariable(request: AIVariableResolutionRequest): ResolvedTomorrowVariable { + const date = new Date(+new Date() + 86400000); + if (request.arg === TomorrowVariableArgs.IN_ISO_8601) { + return { variable: request.variable, value: date.toISOString(), date }; + } + if (request.arg === TomorrowVariableArgs.IN_UNIX_SECONDS) { + return { variable: request.variable, value: Math.round(date.getTime() / 1000).toString(), date }; + } + return { variable: request.variable, value: date.toDateString(), date }; + } +} diff --git a/packages/ai-core/src/common/tool-invocation-registry.ts b/packages/ai-core/src/common/tool-invocation-registry.ts new file mode 100644 index 0000000000000..2ebde1921103a --- /dev/null +++ b/packages/ai-core/src/common/tool-invocation-registry.ts @@ -0,0 +1,79 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; +import { ToolRequest } from './language-model'; +import { ContributionProvider } from '@theia/core'; + +export const ToolInvocationRegistry = Symbol('ToolInvocationRegistry'); + +/** + * Registry for all the function calls available to Agents. + */ +export interface ToolInvocationRegistry { + registerTool(tool: ToolRequest): void; + + getFunction(toolId: string): ToolRequest | undefined; + + getFunctions(...toolIds: string[]): ToolRequest[]; +} + +export const ToolProvider = Symbol('ToolProvider'); +export interface ToolProvider { + getTool(): ToolRequest; +} + +@injectable() +export class ToolInvocationRegistryImpl implements ToolInvocationRegistry { + + private tools: Map = new Map(); + + @inject(ContributionProvider) + @named(ToolProvider) + private providers: ContributionProvider; + + @postConstruct() + init(): void { + this.providers.getContributions().forEach(provider => { + this.registerTool(provider.getTool()); + }); + } + + registerTool(tool: ToolRequest): void { + if (this.tools.has(tool.id)) { + console.warn(`Function with id ${tool.id} is already registered.`); + } else { + this.tools.set(tool.id, tool); + } + } + + getFunction(toolId: string): ToolRequest | undefined { + return this.tools.get(toolId); + } + + getFunctions(...toolIds: string[]): ToolRequest[] { + const tools: ToolRequest[] = toolIds.map(toolId => { + const tool = this.tools.get(toolId); + if (tool) { + return tool; + } else { + throw new Error(`Function with id ${toolId} does not exist.`); + } + }); + return tools; + } +} + diff --git a/packages/ai-core/src/common/variable-service.ts b/packages/ai-core/src/common/variable-service.ts new file mode 100644 index 0000000000000..833d322eed48f --- /dev/null +++ b/packages/ai-core/src/common/variable-service.ts @@ -0,0 +1,177 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// Partially copied from https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatVariables.ts + +import { ContributionProvider, Disposable, Emitter, ILogger, MaybePromise, Prioritizeable, Event } from '@theia/core'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; + +export interface AIVariable { + /** provider id */ + id: string; + /** variable name */ + name: string; + /** variable description */ + description: string; + args?: AIVariableDescription[]; +} + +export interface AIVariableDescription { + name: string; + description: string; +} + +export interface ResolvedAIVariable { + variable: AIVariable; + value: string; +} + +export interface AIVariableResolutionRequest { + variable: AIVariable; + arg?: string; +} + +export interface AIVariableContext { +} + +export type AIVariableArg = string | { variable: string, arg?: string } | AIVariableResolutionRequest; + +export interface AIVariableResolver { + canResolve(request: AIVariableResolutionRequest, context: AIVariableContext): MaybePromise, + resolve(request: AIVariableResolutionRequest, context: AIVariableContext): Promise; +} + +export const AIVariableService = Symbol('AIVariableService'); +export interface AIVariableService { + hasVariable(name: string): boolean; + getVariable(name: string): Readonly | undefined; + getVariables(): Readonly[]; + unregisterVariable(name: string): void; + readonly onDidChangeVariables: Event; + + registerResolver(variable: AIVariable, resolver: AIVariableResolver): Disposable; + unregisterResolver(variable: AIVariable, resolver: AIVariableResolver): void; + getResolver(name: string, arg: string | undefined, context: AIVariableContext): Promise; + + resolveVariable(variable: AIVariableArg, context: AIVariableContext): Promise; +} + +export const AIVariableContribution = Symbol('AIVariableContribution'); +export interface AIVariableContribution { + registerVariables(service: AIVariableService): void; +} + +@injectable() +export class DefaultAIVariableService implements AIVariableService { + protected variables = new Map(); + protected resolvers = new Map(); + + protected readonly onDidChangeVariablesEmitter = new Emitter(); + readonly onDidChangeVariables: Event = this.onDidChangeVariablesEmitter.event; + + @inject(ILogger) protected logger: ILogger; + + constructor( + @inject(ContributionProvider) @named(AIVariableContribution) + protected readonly contributionProvider: ContributionProvider + ) { + } + + protected initContributions(): void { + this.contributionProvider.getContributions().forEach(contribution => contribution.registerVariables(this)); + } + + protected getKey(name: string): string { + return `${name.toLowerCase()}`; + } + + async getResolver(name: string, arg: string | undefined, context: AIVariableContext): Promise { + const resolvers = await this.prioritize(name, arg, context); + return resolvers[0]; + } + + protected getResolvers(name: string): AIVariableResolver[] { + return this.resolvers.get(this.getKey(name)) ?? []; + } + + protected async prioritize(name: string, arg: string | undefined, context: AIVariableContext): Promise { + const variable = this.getVariable(name); + if (!variable) { + return []; + } + const prioritized = await Prioritizeable.prioritizeAll(this.getResolvers(name), async resolver => { + try { + return await resolver.canResolve({ variable, arg }, context); + } catch { + return 0; + } + }); + return prioritized.map(p => p.value); + } + + hasVariable(name: string): boolean { + return !!this.getVariable(name); + } + + getVariable(name: string): Readonly | undefined { + return this.variables.get(this.getKey(name)); + } + + getVariables(): Readonly[] { + return [...this.variables.values()]; + } + + registerResolver(variable: AIVariable, resolver: AIVariableResolver): Disposable { + const key = this.getKey(variable.name); + if (!this.variables.get(key)) { + this.variables.set(key, variable); + this.onDidChangeVariablesEmitter.fire(); + } + const resolvers = this.resolvers.get(key) ?? []; + resolvers.push(resolver); + this.resolvers.set(key, resolvers); + return Disposable.create(() => this.unregisterResolver(variable, resolver)); + } + + unregisterResolver(variable: AIVariable, resolver: AIVariableResolver): void { + const key = this.getKey(variable.name); + const registeredResolvers = this.resolvers.get(key); + registeredResolvers?.splice(registeredResolvers.indexOf(resolver), 1); + if (registeredResolvers?.length === 0) { + this.unregisterVariable(variable.name); + } + } + + unregisterVariable(name: string): void { + this.variables.delete(this.getKey(name)); + this.resolvers.delete(this.getKey(name)); + this.onDidChangeVariablesEmitter.fire(); + } + + async resolveVariable(request: AIVariableArg, context: AIVariableContext): Promise { + const variableName = typeof request === 'string' ? request : typeof request.variable === 'string' ? request.variable : request.variable.name; + const variable = this.getVariable(variableName); + if (!variable) { + return undefined; + } + const arg = typeof request === 'string' ? undefined : request.arg; + const resolver = await this.getResolver(variableName, arg, context); + return resolver?.resolve({ variable, arg }, context); + } +} diff --git a/packages/ai-core/src/node/ai-core-backend-module.ts b/packages/ai-core/src/node/ai-core-backend-module.ts new file mode 100644 index 0000000000000..5c23c7d37f1ac --- /dev/null +++ b/packages/ai-core/src/node/ai-core-backend-module.ts @@ -0,0 +1,83 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ContainerModule } from '@theia/core/shared/inversify'; +import { + LanguageModelFrontendDelegateImpl, + LanguageModelRegistryFrontendDelegateImpl, +} from './language-model-frontend-delegate'; +import { + ConnectionHandler, + RpcConnectionHandler, + bindContributionProvider, +} from '@theia/core'; +import { + LanguageModelRegistry, + LanguageModelProvider, + PromptService, + PromptServiceImpl, + LanguageModelDelegateClient, + LanguageModelFrontendDelegate, + LanguageModelRegistryFrontendDelegate, + languageModelDelegatePath, + languageModelRegistryDelegatePath, + LanguageModelRegistryClient +} from '../common'; +import { BackendLanguageModelRegistry } from './backend-language-model-registry'; + +export default new ContainerModule(bind => { + bindContributionProvider(bind, LanguageModelProvider); + bind(BackendLanguageModelRegistry).toSelf().inSingletonScope(); + bind(LanguageModelRegistry).toService(BackendLanguageModelRegistry); + + bind(LanguageModelRegistryFrontendDelegate).to(LanguageModelRegistryFrontendDelegateImpl).inSingletonScope(); + bind(ConnectionHandler) + .toDynamicValue( + ctx => + new RpcConnectionHandler( + languageModelRegistryDelegatePath, + client => { + const registryDelegate = ctx.container.get( + LanguageModelRegistryFrontendDelegate + ); + registryDelegate.setClient(client); + return registryDelegate; + } + ) + ) + .inSingletonScope(); + + bind(LanguageModelFrontendDelegateImpl).toSelf().inSingletonScope(); + bind(LanguageModelFrontendDelegate).toService(LanguageModelFrontendDelegateImpl); + bind(ConnectionHandler) + .toDynamicValue( + ({ container }) => + new RpcConnectionHandler( + languageModelDelegatePath, + client => { + const service = + container.get( + LanguageModelFrontendDelegateImpl + ); + service.setClient(client); + return service; + } + ) + ) + .inSingletonScope(); + + bind(PromptServiceImpl).toSelf().inSingletonScope(); + bind(PromptService).toService(PromptServiceImpl); +}); diff --git a/packages/ai-core/src/node/backend-language-model-registry.ts b/packages/ai-core/src/node/backend-language-model-registry.ts new file mode 100644 index 0000000000000..7bb23f9356cb2 --- /dev/null +++ b/packages/ai-core/src/node/backend-language-model-registry.ts @@ -0,0 +1,60 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { DefaultLanguageModelRegistryImpl, LanguageModel, LanguageModelMetaData, LanguageModelRegistryClient } from '../common'; + +/** + * Notifies a client whenever a model is added or removed + */ +@injectable() +export class BackendLanguageModelRegistry extends DefaultLanguageModelRegistryImpl { + + private client: LanguageModelRegistryClient | undefined; + + setClient(client: LanguageModelRegistryClient): void { + this.client = client; + } + + override addLanguageModels(models: LanguageModel[]): void { + const modelsLength = this.languageModels.length; + super.addLanguageModels(models); + // only notify for models which were really added + for (let i = modelsLength; i < this.languageModels.length; i++) { + this.client?.languageModelAdded(this.mapToMetaData(this.languageModels[i])); + } + } + + override removeLanguageModels(ids: string[]): void { + super.removeLanguageModels(ids); + for (const id of ids) { + this.client?.languageModelRemoved(id); + } + } + + mapToMetaData(model: LanguageModel): LanguageModelMetaData { + return { + id: model.id, + providerId: model.providerId, + name: model.name, + vendor: model.vendor, + version: model.version, + family: model.family, + maxInputTokens: model.maxInputTokens, + maxOutputTokens: model.maxOutputTokens, + }; + } +} diff --git a/packages/ai-core/src/node/language-model-frontend-delegate.ts b/packages/ai-core/src/node/language-model-frontend-delegate.ts new file mode 100644 index 0000000000000..0255c0111aa6a --- /dev/null +++ b/packages/ai-core/src/node/language-model-frontend-delegate.ts @@ -0,0 +1,116 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable } from '@theia/core/shared/inversify'; +import { CancellationToken, CancellationTokenSource, ILogger, generateUuid } from '@theia/core'; +import { + LanguageModelMetaData, + LanguageModelRegistry, + LanguageModelRequest, + isLanguageModelStreamResponse, + isLanguageModelTextResponse, + LanguageModelStreamResponsePart, + LanguageModelDelegateClient, + LanguageModelFrontendDelegate, + LanguageModelRegistryFrontendDelegate, + LanguageModelResponseDelegate, + LanguageModelRegistryClient, + isLanguageModelParsedResponse, +} from '../common'; +import { BackendLanguageModelRegistry } from './backend-language-model-registry'; + +@injectable() +export class LanguageModelRegistryFrontendDelegateImpl implements LanguageModelRegistryFrontendDelegate { + + @inject(LanguageModelRegistry) + private registry: BackendLanguageModelRegistry; + + setClient(client: LanguageModelRegistryClient): void { + this.registry.setClient(client); + } + + async getLanguageModelDescriptions(): Promise { + return (await this.registry.getLanguageModels()).map(model => this.registry.mapToMetaData(model)); + } +} + +@injectable() +export class LanguageModelFrontendDelegateImpl implements LanguageModelFrontendDelegate { + + @inject(LanguageModelRegistry) + private registry: LanguageModelRegistry; + + @inject(ILogger) + private logger: ILogger; + + private frontendDelegateClient: LanguageModelDelegateClient; + private requestCancellationTokenMap: Map = new Map(); + + setClient(client: LanguageModelDelegateClient): void { + this.frontendDelegateClient = client; + } + + cancel(requestId: string): void { + this.requestCancellationTokenMap.get(requestId)?.cancel(); + } + + async request( + modelId: string, + request: LanguageModelRequest, + requestId: string, + cancellationToken?: CancellationToken + ): Promise { + const model = await this.registry.getLanguageModel(modelId); + if (!model) { + throw new Error( + `Request was sent to non-existent language model ${modelId}` + ); + } + request.tools?.forEach(tool => { + tool.handler = async args_string => this.frontendDelegateClient.toolCall(requestId, tool.id, args_string); + }); + if (cancellationToken) { + const tokenSource = new CancellationTokenSource(); + cancellationToken = tokenSource.token; + this.requestCancellationTokenMap.set(requestId, tokenSource); + } + const response = await model.request(request, cancellationToken); + if (isLanguageModelTextResponse(response) || isLanguageModelParsedResponse(response)) { + return response; + } + if (isLanguageModelStreamResponse(response)) { + const delegate = { + streamId: generateUuid(), + }; + this.sendTokens(delegate.streamId, response.stream); + return delegate; + } + this.logger.error( + `Received unexpected response from language model ${modelId}. Trying to continue without touching the response.`, + response + ); + return response; + } + + protected sendTokens(id: string, stream: AsyncIterable): void { + (async () => { + for await (const token of stream) { + this.frontendDelegateClient.send(id, token); + } + this.frontendDelegateClient.send(id, undefined); + })(); + } +} diff --git a/packages/ai-core/tsconfig.json b/packages/ai-core/tsconfig.json new file mode 100644 index 0000000000000..4ee37e165b355 --- /dev/null +++ b/packages/ai-core/tsconfig.json @@ -0,0 +1,34 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../core" + }, + { + "path": "../editor" + }, + { + "path": "../filesystem" + }, + { + "path": "../monaco" + }, + { + "path": "../output" + }, + { + "path": "../variable-resolver" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-history/.eslintrc.js b/packages/ai-history/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-history/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-history/README.md b/packages/ai-history/README.md new file mode 100644 index 0000000000000..6992a4ae49041 --- /dev/null +++ b/packages/ai-history/README.md @@ -0,0 +1,31 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI History EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-history` extension offers a framework for agents to record their requests and responses. +It also offers a view to inspect the history. + + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-history/package.json b/packages/ai-history/package.json new file mode 100644 index 0000000000000..0eb2b6f471cc2 --- /dev/null +++ b/packages/ai-history/package.json @@ -0,0 +1,53 @@ +{ + "name": "@theia/ai-history", + "version": "1.53.0", + "description": "Theia - AI communication history", + "dependencies": { + "@theia/ai-core": "1.53.0", + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/output": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2" + }, + "main": "lib/common", + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-history-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-history/src/browser/ai-history-communication-card.tsx b/packages/ai-history/src/browser/ai-history-communication-card.tsx new file mode 100644 index 0000000000000..3320d13692729 --- /dev/null +++ b/packages/ai-history/src/browser/ai-history-communication-card.tsx @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommunicationHistoryEntry } from '@theia/ai-core'; +import * as React from '@theia/core/shared/react'; + +export interface CommunicationCardProps { + entry: CommunicationHistoryEntry; +} + +export const CommunicationCard: React.FC = ({ entry }) => ( +
      +
      + Request ID: {entry.requestId} + Session ID: {entry.sessionId} +
      +
      + {entry.request && ( +
      +

      Request

      +
      {entry.request}
      +
      + )} + {entry.response && ( +
      +

      Response

      +
      {entry.response}
      +
      + )} +
      +
      + Timestamp: {new Date(entry.timestamp).toLocaleString()} + {entry.responseTime && Response Time: {entry.responseTime}ms} +
      +
      +); diff --git a/packages/ai-history/src/browser/ai-history-contribution.ts b/packages/ai-history/src/browser/ai-history-contribution.ts new file mode 100644 index 0000000000000..f33d71cb6793d --- /dev/null +++ b/packages/ai-history/src/browser/ai-history-contribution.ts @@ -0,0 +1,52 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { FrontendApplication } from '@theia/core/lib/browser'; +import { AIViewContribution } from '@theia/ai-core/lib/browser'; +import { injectable } from '@theia/core/shared/inversify'; +import { AIHistoryView } from './ai-history-widget'; +import { Command, CommandRegistry } from '@theia/core'; + +export const AI_HISTORY_TOGGLE_COMMAND_ID = 'aiHistory:toggle'; +export const OPEN_AI_HISTORY_VIEW = Command.toLocalizedCommand({ + id: 'aiHistory:open', + label: 'Open AI History view', +}); + +@injectable() +export class AIHistoryViewContribution extends AIViewContribution { + constructor() { + super({ + widgetId: AIHistoryView.ID, + widgetName: AIHistoryView.LABEL, + defaultWidgetOptions: { + area: 'bottom', + rank: 100 + }, + toggleCommandId: AI_HISTORY_TOGGLE_COMMAND_ID, + }); + } + + async initializeLayout(_app: FrontendApplication): Promise { + await this.openView(); + } + + override registerCommands(commands: CommandRegistry): void { + super.registerCommands(commands); + commands.registerCommand(OPEN_AI_HISTORY_VIEW, { + execute: () => this.openView({ activate: true }), + }); + } +} diff --git a/packages/ai-history/src/browser/ai-history-frontend-module.ts b/packages/ai-history/src/browser/ai-history-frontend-module.ts new file mode 100644 index 0000000000000..021fc013cabdd --- /dev/null +++ b/packages/ai-history/src/browser/ai-history-frontend-module.ts @@ -0,0 +1,41 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommunicationRecordingService } from '@theia/ai-core'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { DefaultCommunicationRecordingService } from '../common/communication-recording-service'; +import { bindViewContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { ILogger } from '@theia/core'; +import { AIHistoryViewContribution } from './ai-history-contribution'; +import { AIHistoryView } from './ai-history-widget'; +import '../../src/browser/style/ai-history.css'; + +export default new ContainerModule(bind => { + bind(DefaultCommunicationRecordingService).toSelf().inSingletonScope(); + bind(CommunicationRecordingService).toService(DefaultCommunicationRecordingService); + + bind(ILogger).toDynamicValue(ctx => { + const parentLogger = ctx.container.get(ILogger); + return parentLogger.child('llm-communication-recorder'); + }).inSingletonScope().whenTargetNamed('llm-communication-recorder'); + + bindViewContribution(bind, AIHistoryViewContribution); + + bind(AIHistoryView).toSelf().inSingletonScope(); + bind(WidgetFactory).toDynamicValue(context => ({ + id: AIHistoryView.ID, + createWidget: () => context.container.get(AIHistoryView) + })).inSingletonScope(); +}); diff --git a/packages/ai-history/src/browser/ai-history-widget.tsx b/packages/ai-history/src/browser/ai-history-widget.tsx new file mode 100644 index 0000000000000..28277426f31a1 --- /dev/null +++ b/packages/ai-history/src/browser/ai-history-widget.tsx @@ -0,0 +1,96 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { Agent, AgentService, CommunicationRecordingService, CommunicationRequestEntry, CommunicationResponseEntry } from '@theia/ai-core'; +import { codicon, ReactWidget } from '@theia/core/lib/browser'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { CommunicationCard } from './ai-history-communication-card'; +import { SelectComponent, SelectOption } from '@theia/core/lib/browser/widgets/select-component'; + +@injectable() +export class AIHistoryView extends ReactWidget { + @inject(CommunicationRecordingService) + protected recordingService: CommunicationRecordingService; + @inject(AgentService) + protected readonly agentService: AgentService; + + public static ID = 'ai-history-widget'; + static LABEL = '✨ AI Agent History [Experimental]'; + + protected selectedAgent?: Agent; + + constructor() { + super(); + this.id = AIHistoryView.ID; + this.title.label = AIHistoryView.LABEL; + this.title.caption = AIHistoryView.LABEL; + this.title.closable = true; + this.title.iconClass = codicon('history'); + } + + @postConstruct() + protected init(): void { + this.update(); + this.toDispose.push(this.recordingService.onDidRecordRequest(entry => this.historyContentUpdated(entry))); + this.toDispose.push(this.recordingService.onDidRecordResponse(entry => this.historyContentUpdated(entry))); + this.selectAgent(this.agentService.getAllAgents()[0]); + } + + protected selectAgent(agent: Agent | undefined): void { + this.selectedAgent = agent; + this.update(); + } + + protected historyContentUpdated(entry: CommunicationRequestEntry | CommunicationResponseEntry): void { + if (entry.agentId === this.selectedAgent?.id) { + this.update(); + } + } + + render(): React.ReactNode { + const selectionChange = (value: SelectOption) => { + this.selectedAgent = this.agentService.getAllAgents().find(agent => agent.id === value.value); + this.update(); + }; + return ( +
      + ({ value: agent.id, label: agent.name, description: agent.description }))} + onChange={selectionChange} + defaultValue={this.selectedAgent?.id} /> +
      + {this.renderHistory()} +
      +
      + ); + } + + protected renderHistory(): React.ReactNode { + if (!this.selectedAgent) { + return
      No agent selected.
      ; + } + const history = this.recordingService.getHistory(this.selectedAgent.id); + if (history.length === 0) { + return
      No history available for the selected agent '{this.selectedAgent.name}'.
      ; + } + return history.map(entry => ); + } + + protected onClick(e: React.MouseEvent, agent: Agent): void { + e.stopPropagation(); + this.selectAgent(agent); + } +} diff --git a/packages/ai-history/src/browser/style/ai-history.css b/packages/ai-history/src/browser/style/ai-history.css new file mode 100644 index 0000000000000..0b494ea1f4498 --- /dev/null +++ b/packages/ai-history/src/browser/style/ai-history.css @@ -0,0 +1,75 @@ +.agent-history-widget { + display: flex; + flex-direction: column; + align-items: center; +} + + +.agent-history-widget .theia-select-component { + margin: 10px 0; + width: 80%; +} + +.agent-history { + width: calc(80% + 16px); + display: flex; + align-items: center; + flex-direction: column; +} + +.theia-card { + background-color: var(--theia-sideBar-background); + border: 1px solid var(--theia-sideBarSectionHeader-border); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 15px; + margin: 10px 0; + width: 100%; + box-sizing: border-box; +} + +.theia-card-meta { + display: flex; + justify-content: space-between; + font-size: 0.9em; + margin-bottom: var(--theia-ui-padding); + padding: var(--theia-ui-padding) 0; +} + +.theia-card-content { + color: var(--theia-font-color); + margin-bottom: 10px; +} + +.theia-card-content p { + margin: var(--theia-ui-padding) 0; +} + +.theia-card-request, .theia-card-response { + margin-bottom: 10px; +} + +.theia-card-request pre, +.theia-card-response pre { + font-family: monospace; + white-space: pre-wrap; + word-wrap: break-word; + background-color: var(--theia-sideBar-background); + margin: var(--theia-ui-padding) 0; +} + +.theia-card-request-id, +.theia-card-session-id, +.theia-card-timestamp, +.theia-card-response-time { + flex: 1; +} + +.theia-card-request-id, +.theia-card-timestamp { + text-align: left; +} + +.theia-card-session-id, +.theia-card-response-time { + text-align: right; +} diff --git a/packages/ai-history/src/common/communication-recording-service.spec.ts b/packages/ai-history/src/common/communication-recording-service.spec.ts new file mode 100644 index 0000000000000..662d2d58c2706 --- /dev/null +++ b/packages/ai-history/src/common/communication-recording-service.spec.ts @@ -0,0 +1,37 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ILogger } from '@theia/core'; +import { MockLogger } from '@theia/core/lib/common/test/mock-logger'; +import { DefaultCommunicationRecordingService } from './communication-recording-service'; +import { expect } from 'chai'; + +describe('DefaultCommunicationRecordingService', () => { + + it('records history', () => { + const service = new DefaultCommunicationRecordingService(); + (service as unknown as { logger: ILogger }).logger = new MockLogger(); + service.recordRequest({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 100, request: 'dummy request' }); + + const history1 = service.getHistory('agent'); + expect(history1[0].request).to.eq('dummy request'); + + service.recordResponse({ agentId: 'agent', requestId: '1', sessionId: '1', timestamp: 200, response: 'dummy response' }); + const history2 = service.getHistory('agent'); + expect(history2[0].request).to.eq('dummy request'); + expect(history2[0].response).to.eq('dummy response'); + }); + +}); diff --git a/packages/ai-history/src/common/communication-recording-service.ts b/packages/ai-history/src/common/communication-recording-service.ts new file mode 100644 index 0000000000000..9d23a6766064e --- /dev/null +++ b/packages/ai-history/src/common/communication-recording-service.ts @@ -0,0 +1,63 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommunicationHistory, CommunicationHistoryEntry, CommunicationRecordingService, CommunicationRequestEntry, CommunicationResponseEntry } from '@theia/ai-core'; +import { Emitter, Event, ILogger } from '@theia/core'; +import { inject, injectable, named } from '@theia/core/shared/inversify'; + +@injectable() +export class DefaultCommunicationRecordingService implements CommunicationRecordingService { + + @inject(ILogger) @named('llm-communication-recorder') + protected logger: ILogger; + + protected onDidRecordRequestEmitter = new Emitter(); + readonly onDidRecordRequest: Event = this.onDidRecordRequestEmitter.event; + + protected onDidRecordResponseEmitter = new Emitter(); + readonly onDidRecordResponse: Event = this.onDidRecordResponseEmitter.event; + + protected history: Map = new Map(); + + getHistory(agentId: string): CommunicationHistory { + return this.history.get(agentId) || []; + } + + recordRequest(requestEntry: CommunicationHistoryEntry): void { + this.logger.debug('Recording request:', requestEntry.request); + if (this.history.has(requestEntry.agentId)) { + this.history.get(requestEntry.agentId)?.push(requestEntry); + } else { + this.history.set(requestEntry.agentId, [requestEntry]); + } + this.onDidRecordRequestEmitter.fire(requestEntry); + } + + recordResponse(responseEntry: CommunicationHistoryEntry): void { + this.logger.debug('Recording response:', responseEntry.response); + if (this.history.has(responseEntry.agentId)) { + const entry = this.history.get(responseEntry.agentId); + if (entry) { + const matchingRequest = entry.find(e => e.requestId === responseEntry.requestId); + if (!matchingRequest) { + throw Error('No matching request found for response'); + } + matchingRequest.response = responseEntry.response; + matchingRequest.responseTime = responseEntry.timestamp - matchingRequest.timestamp; + this.onDidRecordResponseEmitter.fire(responseEntry); + } + } + } +} diff --git a/packages/ai-history/src/common/index.ts b/packages/ai-history/src/common/index.ts new file mode 100644 index 0000000000000..52a9128e1cb3f --- /dev/null +++ b/packages/ai-history/src/common/index.ts @@ -0,0 +1,17 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +export * from './communication-recording-service'; diff --git a/packages/ai-history/tsconfig.json b/packages/ai-history/tsconfig.json new file mode 100644 index 0000000000000..548b369565b41 --- /dev/null +++ b/packages/ai-history/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../output" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-openai/.eslintrc.js b/packages/ai-openai/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-openai/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-openai/README.md b/packages/ai-openai/README.md new file mode 100644 index 0000000000000..679035fe6b435 --- /dev/null +++ b/packages/ai-openai/README.md @@ -0,0 +1,31 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - Open AI EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-openai` integrates OpenAI's models with Theia AI. +The OpenAI API key and the models to use can be configured via preferences. +Alternatively the OpenAI API key can also be handed in via an environment variable. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-openai/package.json b/packages/ai-openai/package.json new file mode 100644 index 0000000000000..1a0d2f69e8859 --- /dev/null +++ b/packages/ai-openai/package.json @@ -0,0 +1,53 @@ +{ + "name": "@theia/ai-openai", + "version": "1.53.0", + "description": "Theia - OpenAI Integration", + "dependencies": { + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2", + "openai": "^4.55.7", + "@theia/ai-core": "1.53.0" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/openai-frontend-module", + "backend": "lib/node/openai-backend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts new file mode 100644 index 0000000000000..917e4c3b5a59a --- /dev/null +++ b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts @@ -0,0 +1,60 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { OpenAiLanguageModelsManager } from '../common'; +import { API_KEY_PREF, MODELS_PREF } from './openai-preferences'; + +@injectable() +export class OpenAiFrontendApplicationContribution implements FrontendApplicationContribution { + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + @inject(OpenAiLanguageModelsManager) + protected manager: OpenAiLanguageModelsManager; + + // The preferenceChange.oldValue is always undefined for some reason + protected prevModels: string[] = []; + + onStart(): void { + this.preferenceService.ready.then(() => { + const apiKey = this.preferenceService.get(API_KEY_PREF, undefined); + this.manager.setApiKey(apiKey); + + const models = this.preferenceService.get(MODELS_PREF, []); + this.manager.createLanguageModels(...models); + this.prevModels = [...models]; + + this.preferenceService.onPreferenceChanged(event => { + if (event.preferenceName === API_KEY_PREF) { + this.manager.setApiKey(event.newValue); + } else if (event.preferenceName === MODELS_PREF) { + const oldModels = new Set(this.prevModels); + const newModels = new Set(event.newValue as string[]); + + const modelsToRemove = [...oldModels].filter(model => !newModels.has(model)); + const modelsToAdd = [...newModels].filter(model => !oldModels.has(model)); + + this.manager.removeLanguageModels(...modelsToRemove); + this.manager.createLanguageModels(...modelsToAdd); + this.prevModels = [...event.newValue]; + } + }); + }); + } +} diff --git a/packages/ai-openai/src/browser/openai-frontend-module.ts b/packages/ai-openai/src/browser/openai-frontend-module.ts new file mode 100644 index 0000000000000..21ba05b95d7cd --- /dev/null +++ b/packages/ai-openai/src/browser/openai-frontend-module.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { OpenAiPreferencesSchema } from './openai-preferences'; +import { FrontendApplicationContribution, PreferenceContribution, RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser'; +import { OpenAiFrontendApplicationContribution } from './openai-frontend-application-contribution'; +import { OPENAI_LANGUAGE_MODELS_MANAGER_PATH, OpenAiLanguageModelsManager } from '../common'; + +export default new ContainerModule(bind => { + bind(PreferenceContribution).toConstantValue({ schema: OpenAiPreferencesSchema }); + bind(OpenAiFrontendApplicationContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(OpenAiFrontendApplicationContribution); + bind(OpenAiLanguageModelsManager).toDynamicValue(ctx => { + const provider = ctx.container.get(RemoteConnectionProvider); + return provider.createProxy(OPENAI_LANGUAGE_MODELS_MANAGER_PATH); + }).inSingletonScope(); +}); diff --git a/packages/ai-openai/src/browser/openai-preferences.ts b/packages/ai-openai/src/browser/openai-preferences.ts new file mode 100644 index 0000000000000..e57915e36068f --- /dev/null +++ b/packages/ai-openai/src/browser/openai-preferences.ts @@ -0,0 +1,40 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution'; +import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; + +export const API_KEY_PREF = 'ai-features.openai.api-key'; +export const MODELS_PREF = 'ai-features.openai.models'; + +export const OpenAiPreferencesSchema: PreferenceSchema = { + type: 'object', + properties: { + [API_KEY_PREF]: { + type: 'string', + description: 'OpenAI API Key', + title: AI_CORE_PREFERENCES_TITLE, + }, + [MODELS_PREF]: { + type: 'array', + title: AI_CORE_PREFERENCES_TITLE, + default: ['gpt-4o-2024-08-06', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], + items: { + type: 'string' + } + } + } +}; diff --git a/packages/ai-openai/src/common/index.ts b/packages/ai-openai/src/common/index.ts new file mode 100644 index 0000000000000..d79fbf6c3872b --- /dev/null +++ b/packages/ai-openai/src/common/index.ts @@ -0,0 +1,16 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export * from './openai-language-models-manager'; diff --git a/packages/ai-openai/src/common/openai-language-models-manager.ts b/packages/ai-openai/src/common/openai-language-models-manager.ts new file mode 100644 index 0000000000000..07fb3f3b54714 --- /dev/null +++ b/packages/ai-openai/src/common/openai-language-models-manager.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export const OPENAI_LANGUAGE_MODELS_MANAGER_PATH = '/services/open-ai/language-model-manager'; +export const OpenAiLanguageModelsManager = Symbol('OpenAiLanguageModelsManager'); +export interface OpenAiLanguageModelsManager { + apiKey: string | undefined; + setApiKey(key: string | undefined): void; + createLanguageModels(...modelIds: string[]): Promise; + removeLanguageModels(...modelIds: string[]): void +} diff --git a/packages/ai-openai/src/node/openai-backend-module.ts b/packages/ai-openai/src/node/openai-backend-module.ts new file mode 100644 index 0000000000000..311065f402cc3 --- /dev/null +++ b/packages/ai-openai/src/node/openai-backend-module.ts @@ -0,0 +1,30 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { OPENAI_LANGUAGE_MODELS_MANAGER_PATH, OpenAiLanguageModelsManager } from '../common/openai-language-models-manager'; +import { ConnectionHandler, RpcConnectionHandler } from '@theia/core'; +import { OpenAiLanguageModelsManagerImpl } from './openai-language-models-manager-impl'; + +export const OpenAiModelFactory = Symbol('OpenAiModelFactory'); + +export default new ContainerModule(bind => { + bind(OpenAiLanguageModelsManagerImpl).toSelf().inSingletonScope(); + bind(OpenAiLanguageModelsManager).toService(OpenAiLanguageModelsManagerImpl); + bind(ConnectionHandler).toDynamicValue(ctx => + new RpcConnectionHandler(OPENAI_LANGUAGE_MODELS_MANAGER_PATH, () => ctx.container.get(OpenAiLanguageModelsManager)) + ).inSingletonScope(); +}); diff --git a/packages/ai-openai/src/node/openai-language-model.ts b/packages/ai-openai/src/node/openai-language-model.ts new file mode 100644 index 0000000000000..45e3ad03630ce --- /dev/null +++ b/packages/ai-openai/src/node/openai-language-model.ts @@ -0,0 +1,185 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + LanguageModel, + LanguageModelParsedResponse, + LanguageModelRequest, + LanguageModelRequestMessage, + LanguageModelResponse, + LanguageModelStreamResponsePart +} from '@theia/ai-core'; +import { CancellationToken } from '@theia/core'; +import OpenAI from 'openai'; +import { ChatCompletionStream } from 'openai/lib/ChatCompletionStream'; +import { RunnableToolFunctionWithoutParse } from 'openai/lib/RunnableFunction'; +import { ChatCompletionMessageParam } from 'openai/resources'; + +export const OpenAiModelIdentifier = Symbol('OpenAiModelIdentifier'); + +function toOpenAIMessage(message: LanguageModelRequestMessage): ChatCompletionMessageParam { + return { + role: toOpenAiRole(message), + content: message.query || '' + }; +} + +function toOpenAiRole(message: LanguageModelRequestMessage): 'system' | 'user' | 'assistant' { + switch (message.actor) { + case 'system': + return 'system'; + case 'ai': + return 'assistant'; + default: + return 'user'; + } +} + +export class OpenAiModel implements LanguageModel { + + readonly providerId = 'openai'; + readonly vendor: string = 'OpenAI'; + + constructor(protected readonly model: string, protected apiKey: () => string | undefined) { + + } + + get id(): string { + return this.providerId + '/' + this.model; + } + + get name(): string { + return this.model; + } + + async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + const openai = this.initializeOpenAi(); + + if (request.response_format?.type === 'json_schema' && this.supportsStructuredOutput()) { + return this.handleStructuredOutputRequest(openai, request); + } + + let runner: ChatCompletionStream; + const tools = this.createTools(request); + if (tools) { + runner = openai.beta.chat.completions.runTools({ + model: this.model, + messages: request.messages.map(toOpenAIMessage), + stream: true, + tools: tools, + tool_choice: 'auto', + ...request.settings + }); + } else { + runner = openai.beta.chat.completions.stream({ + model: this.model, + messages: request.messages.map(toOpenAIMessage), + stream: true, + ...request.settings + }); + } + cancellationToken?.onCancellationRequested(() => { + runner.abort(); + }); + + let runnerEnd = false; + + let resolve: (part: LanguageModelStreamResponsePart) => void; + runner.on('error', error => { + console.error('Error in OpenAI chat completion stream:', error); + runnerEnd = true; + resolve({ content: error.message }); + }); + runner.on('message', message => { + if (message.role === 'tool') { + resolve({ tool_calls: [{ id: message.tool_call_id, finished: true, result: this.getCompletionContent(message) }] }); + } + console.debug('Received Open AI message', JSON.stringify(message)); + }); + runner.once('end', () => { + runnerEnd = true; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + resolve(runner.finalChatCompletion as any); + }); + const asyncIterator = { + async *[Symbol.asyncIterator](): AsyncIterator { + runner.on('chunk', chunk => { + if (chunk.choices[0]?.delta) { + resolve({ ...chunk.choices[0]?.delta }); + } + }); + while (!runnerEnd) { + const promise = new Promise((res, rej) => { + resolve = res; + }); + yield promise; + } + } + }; + return { stream: asyncIterator }; + } + + protected supportsStructuredOutput(): boolean { + // currently only the lastest 4o and 4o-mini models support structured output + // see https://platform.openai.com/docs/guides/structured-outputs + return this.model === 'gpt-4o-2024-08-06' || this.model === 'gpt-4o-mini'; + } + + protected async handleStructuredOutputRequest(openai: OpenAI, request: LanguageModelRequest): Promise { + // TODO implement tool support for structured output (parse() seems to require different tool format) + const result = await openai.beta.chat.completions.parse({ + model: this.model, + messages: request.messages.map(toOpenAIMessage), + response_format: request.response_format, + ...request.settings + }); + const message = result.choices[0].message; + if (message.refusal || message.parsed === undefined) { + console.error('Error in OpenAI chat completion stream:', JSON.stringify(message)); + } + return { + content: message.content ?? '', + parsed: message.parsed + }; + } + + private getCompletionContent(message: OpenAI.Chat.Completions.ChatCompletionToolMessageParam): string { + if (Array.isArray(message.content)) { + return message.content.join(''); + } + return message.content; + } + + protected createTools(request: LanguageModelRequest): RunnableToolFunctionWithoutParse[] | undefined { + return request.tools?.map(tool => ({ + type: 'function', + function: { + name: tool.name, + description: tool.description, + parameters: tool.parameters, + function: (args_string: string) => tool.handler(args_string) + } + } as RunnableToolFunctionWithoutParse)); + } + + protected initializeOpenAi(): OpenAI { + const key = this.apiKey(); + if (!key) { + throw new Error('Please provide OPENAI_API_KEY in preferences or via environment variable'); + } + return new OpenAI({ apiKey: key }); + } +} diff --git a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts new file mode 100644 index 0000000000000..c81362ecc82f0 --- /dev/null +++ b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts @@ -0,0 +1,58 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { LanguageModelRegistry } from '@theia/ai-core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { OpenAiModel } from './openai-language-model'; +import { OpenAiLanguageModelsManager } from '../common'; + +@injectable() +export class OpenAiLanguageModelsManagerImpl implements OpenAiLanguageModelsManager { + + protected _apiKey: string | undefined; + + @inject(LanguageModelRegistry) + protected readonly languageModelRegistry: LanguageModelRegistry; + + get apiKey(): string | undefined { + return this._apiKey ?? process.env.OPENAI_API_KEY; + } + + // Triggered from frontend. In case you want to use the models on the backend + // without a frontend then call this yourself + async createLanguageModels(...modelIds: string[]): Promise { + for (const id of modelIds) { + // we might be called by multiple frontends, therefore check whether a model actually needs to be created + if (!(await this.languageModelRegistry.getLanguageModel(`openai/${id}`))) { + this.languageModelRegistry.addLanguageModels([new OpenAiModel(id, () => this.apiKey)]); + } else { + console.info(`Open AI: skip creating model ${id} because it already exists`); + } + } + } + + removeLanguageModels(...modelIds: string[]): void { + this.languageModelRegistry.removeLanguageModels(modelIds.map(id => `openai/${id}`)); + } + + setApiKey(apiKey: string | undefined): void { + if (apiKey) { + this._apiKey = apiKey; + } else { + this._apiKey = undefined; + } + } +} diff --git a/packages/ai-openai/src/package.spec.ts b/packages/ai-openai/src/package.spec.ts new file mode 100644 index 0000000000000..7aa1df47bcb00 --- /dev/null +++ b/packages/ai-openai/src/package.spec.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-openai package', () => { + + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-openai/tsconfig.json b/packages/ai-openai/tsconfig.json new file mode 100644 index 0000000000000..61a997fc14fd1 --- /dev/null +++ b/packages/ai-openai/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/ai-terminal/.eslintrc.js b/packages/ai-terminal/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-terminal/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-terminal/README.md b/packages/ai-terminal/README.md new file mode 100644 index 0000000000000..9d172389e7b14 --- /dev/null +++ b/packages/ai-terminal/README.md @@ -0,0 +1,31 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Terminal EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-terminal` extension contributes an overlay to the terminal view.\ +The overlay can be used to ask a dedicated `TerminalAgent` for suggestions of terminal commands. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark + +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-terminal/package.json b/packages/ai-terminal/package.json new file mode 100644 index 0000000000000..f32d1794bf62b --- /dev/null +++ b/packages/ai-terminal/package.json @@ -0,0 +1,51 @@ +{ + "name": "@theia/ai-terminal", + "version": "1.53.0", + "description": "Theia - AI Terminal Extension", + "dependencies": { + "@theia/core": "1.53.0", + "@theia/ai-core": "1.53.0", + "@theia/ai-chat": "1.53.0", + "@theia/terminal": "1.53.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.2" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ai-terminal-frontend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-terminal/src/browser/ai-terminal-agent.ts b/packages/ai-terminal/src/browser/ai-terminal-agent.ts new file mode 100644 index 0000000000000..54d51d22ef3ae --- /dev/null +++ b/packages/ai-terminal/src/browser/ai-terminal-agent.ts @@ -0,0 +1,196 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + Agent, + getJsonOfResponse, + isLanguageModelParsedResponse, + LanguageModelRegistry, LanguageModelRequirement, + PromptService +} from '@theia/ai-core/lib/common'; +import { ILogger } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { z } from 'zod'; +import zodToJsonSchema from 'zod-to-json-schema'; + +const Commands = z.object({ + commands: z.array(z.string()), +}); +type Commands = z.infer; + +@injectable() +export class AiTerminalAgent implements Agent { + + id = 'Terminal Assistant'; + name = 'Terminal Assistant'; + description = 'This agent provides assistance to write and execute arbitrary terminal commands. \ + Based on the user\'s request, it suggests commands and allows the user to directly paste and execute them in the terminal. \ + It accesses the current directory, environment and the recent terminal output of the terminal session to provide context-aware assistance'; + variables = []; + promptTemplates = [ + { + id: 'ai-terminal:system-prompt', + name: 'AI Terminal System Prompt', + description: 'Prompt for the AI Terminal Assistant', + template: ` +# Instructions +Generate one or more command suggestions based on the user's request, considering the shell being used, +the current working directory, and the recent terminal contents. Provide the best suggestion first, +followed by other relevant suggestions if the user asks for further options. + +Parameters: +- user-request: The user's question or request. +- shell: The shell being used, e.g., /usr/bin/zsh. +- cwd: The current working directory. +- recent-terminal-contents: The last 0 to 50 recent lines visible in the terminal. + +Return the result in the following JSON format: +{ + "commands": [ + "best_command_suggestion", + "next_best_command_suggestion", + "another_command_suggestion" + ] +} + +## Example +user-request: "How do I commit changes?" +shell: "/usr/bin/zsh" +cwd: "/home/user/project" +recent-terminal-contents: +git status +On branch main +Your branch is up to date with 'origin/main'. +nothing to commit, working tree clean + +## Expected JSON output +\`\`\`json +\{ + "commands": [ + "git commit", + "git commit --amend", + "git commit -a" + ] +} +\`\`\` +` + }, + { + id: 'ai-terminal:user-prompt', + name: 'AI Terminal User Prompt', + description: 'Prompt that contains the user request', + template: ` +user-request: {{userRequest}} +shell: {{shell}} +cwd: {{cwd}} +recent-terminal-contents: +{{recentTerminalContents}} +` + } + ]; + languageModelRequirements: LanguageModelRequirement[] = [ + { + purpose: 'suggest-terminal-commands', + identifier: 'openai/gpt-4o', + } + ]; + + @inject(LanguageModelRegistry) + protected languageModelRegistry: LanguageModelRegistry; + + @inject(PromptService) + protected promptService: PromptService; + + @inject(ILogger) + protected logger: ILogger; + + async getCommands( + userRequest: string, + cwd: string, + shell: string, + recentTerminalContents: string[], + ): Promise { + const lm = await this.languageModelRegistry.selectLanguageModel({ + agent: this.id, + ...this.languageModelRequirements[0] + }); + if (!lm) { + this.logger.error('No language model available for the AI Terminal Agent.'); + return []; + } + + const parameters = { + userRequest, + shell, + cwd, + recentTerminalContents + }; + + const systemPrompt = await this.promptService.getPrompt('ai-terminal:system-prompt', parameters).then(p => p?.text); + const userPrompt = await this.promptService.getPrompt('ai-terminal:user-prompt', parameters).then(p => p?.text); + if (!systemPrompt || !userPrompt) { + this.logger.error('The prompt service didn\'t return prompts for the AI Terminal Agent.'); + return []; + } + + try { + const result = await lm.request({ + messages: [ + { + actor: 'ai', + type: 'text', + query: systemPrompt + }, + { + actor: 'user', + type: 'text', + query: userPrompt + } + ], + response_format: { + type: 'json_schema', + json_schema: { + name: 'terminal-commands', + description: 'Suggested terminal commands based on the user request', + schema: zodToJsonSchema(Commands) + } + } + }); + + if (isLanguageModelParsedResponse(result)) { + // model returned structured output + const parsedResult = Commands.safeParse(result.parsed); + if (parsedResult.success) { + return parsedResult.data.commands; + } + } + + // fall back to agent-based parsing of result + const jsonResult = await getJsonOfResponse(result); + const parsedJsonResult = Commands.safeParse(jsonResult); + if (parsedJsonResult.success) { + return parsedJsonResult.data.commands; + } + + return []; + + } catch (error) { + this.logger.error('Error obtaining the command suggestions.', error); + return []; + } + } + +} diff --git a/packages/ai-terminal/src/browser/ai-terminal-contribution.ts b/packages/ai-terminal/src/browser/ai-terminal-contribution.ts new file mode 100644 index 0000000000000..58a8156ebff4b --- /dev/null +++ b/packages/ai-terminal/src/browser/ai-terminal-contribution.ts @@ -0,0 +1,193 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { EXPERIMENTAL_AI_CONTEXT_KEY } from '@theia/ai-core/lib/browser'; +import { CommandContribution, CommandRegistry, MenuContribution, MenuModelRegistry } from '@theia/core'; +import { KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; +import { TerminalMenus } from '@theia/terminal/lib/browser/terminal-frontend-contribution'; +import { TerminalWidgetImpl } from '@theia/terminal/lib/browser/terminal-widget-impl'; +import { AiTerminalAgent } from './ai-terminal-agent'; +import { AICommandHandlerFactory } from '@theia/ai-core/lib/browser/ai-command-handler-factory'; + +const AI_TERMINAL_COMMAND = { + id: 'ai-terminal:open', + label: 'Ask the AI' +}; + +@injectable() +export class AiTerminalCommandContribution implements CommandContribution, MenuContribution, KeybindingContribution { + + @inject(TerminalService) + protected terminalService: TerminalService; + + @inject(AiTerminalAgent) + protected terminalAgent: AiTerminalAgent; + + @inject(AICommandHandlerFactory) + protected commandHandlerFactory: AICommandHandlerFactory; + + registerKeybindings(keybindings: KeybindingRegistry): void { + keybindings.registerKeybinding({ + command: AI_TERMINAL_COMMAND.id, + keybinding: 'ctrlcmd+i', + when: `terminalFocus && ${EXPERIMENTAL_AI_CONTEXT_KEY}` + }); + } + registerMenus(menus: MenuModelRegistry): void { + menus.registerMenuAction([...TerminalMenus.TERMINAL_CONTEXT_MENU, '_5'], { + when: EXPERIMENTAL_AI_CONTEXT_KEY, + commandId: AI_TERMINAL_COMMAND.id + }); + } + registerCommands(commands: CommandRegistry): void { + commands.registerCommand(AI_TERMINAL_COMMAND, this.commandHandlerFactory({ + execute: () => { + if (this.terminalService.currentTerminal instanceof TerminalWidgetImpl) { + new AiTerminalChatWidget( + this.terminalService.currentTerminal, + this.terminalAgent + ); + } + } + })); + } +} + +class AiTerminalChatWidget { + + protected chatContainer: HTMLDivElement; + protected chatInput: HTMLTextAreaElement; + protected chatResultParagraph: HTMLParagraphElement; + protected chatInputContainer: HTMLDivElement; + + protected haveResult = false; + commands: string[]; + + constructor( + protected terminalWidget: TerminalWidgetImpl, + protected terminalAgent: AiTerminalAgent + ) { + this.chatContainer = document.createElement('div'); + this.chatContainer.className = 'ai-terminal-chat-container'; + + const chatCloseButton = document.createElement('span'); + chatCloseButton.className = 'closeButton codicon codicon-close'; + chatCloseButton.onclick = () => this.dispose(); + this.chatContainer.appendChild(chatCloseButton); + + const chatResultContainer = document.createElement('div'); + chatResultContainer.className = 'ai-terminal-chat-result'; + this.chatResultParagraph = document.createElement('p'); + this.chatResultParagraph.textContent = 'How can I help you?'; + chatResultContainer.appendChild(this.chatResultParagraph); + this.chatContainer.appendChild(chatResultContainer); + + this.chatInputContainer = document.createElement('div'); + this.chatInputContainer.className = 'ai-terminal-chat-input-container'; + + this.chatInput = document.createElement('textarea'); + this.chatInput.className = 'theia-input theia-ChatInput'; + this.chatInput.placeholder = 'Ask about a terminal command...'; + this.chatInput.onkeydown = event => { + if (event.key === 'Enter' && !event.shiftKey) { + event.preventDefault(); + if (!this.haveResult) { + this.send(); + } else { + this.terminalWidget.sendText(this.chatResultParagraph.innerText); + this.dispose(); + } + } else if (event.key === 'Escape') { + this.dispose(); + } else if (event.key === 'ArrowUp' && this.haveResult) { + this.updateChatResult(this.getNextCommandIndex(1)); + } else if (event.key === 'ArrowDown' && this.haveResult) { + this.updateChatResult(this.getNextCommandIndex(-1)); + } + }; + this.chatInputContainer.appendChild(this.chatInput); + + const chatInputOptionsContainer = document.createElement('div'); + const chatInputOptionsSpan = document.createElement('span'); + chatInputOptionsSpan.className = 'codicon codicon-send option'; + chatInputOptionsSpan.title = 'Send'; + chatInputOptionsSpan.onclick = () => this.send(); + chatInputOptionsContainer.appendChild(chatInputOptionsSpan); + this.chatInputContainer.appendChild(chatInputOptionsContainer); + + this.chatContainer.appendChild(this.chatInputContainer); + + terminalWidget.node.appendChild(this.chatContainer); + + this.chatInput.focus(); + } + + protected async send(): Promise { + const userRequest = this.chatInput.value; + if (userRequest) { + this.chatInput.value = ''; + + this.chatResultParagraph.innerText = 'Loading'; + this.chatResultParagraph.className = 'loading'; + + const cwd = (await this.terminalWidget.cwd).toString(); + const processInfo = await this.terminalWidget.processInfo; + const shell = processInfo.executable; + const recentTerminalContents = this.getRecentTerminalCommands(); + + this.commands = await this.terminalAgent.getCommands(userRequest, cwd, shell, recentTerminalContents); + + if (this.commands.length > 0) { + this.chatResultParagraph.className = 'command'; + this.chatResultParagraph.innerText = this.commands[0]; + this.chatInput.placeholder = 'Hit enter to confirm'; + if (this.commands.length > 1) { + this.chatInput.placeholder += ' or use ⇅ to show alternatives...'; + } + this.haveResult = true; + } else { + this.chatResultParagraph.className = ''; + this.chatResultParagraph.innerText = 'No results'; + this.chatInput.placeholder = 'Try again...'; + } + } + } + + protected getRecentTerminalCommands(): string[] { + const maxLines = 100; + return this.terminalWidget.buffer.getLines(0, + this.terminalWidget.buffer.length > maxLines ? maxLines : this.terminalWidget.buffer.length + ); + } + + protected getNextCommandIndex(step: number): number { + const currentIndex = this.commands.indexOf(this.chatResultParagraph.innerText); + const nextIndex = (currentIndex + step + this.commands.length) % this.commands.length; + return nextIndex; + } + + protected updateChatResult(index: number): void { + this.chatResultParagraph.innerText = this.commands[index]; + } + + protected dispose(): void { + this.chatInput.value = ''; + this.terminalWidget.node.removeChild(this.chatContainer); + this.terminalWidget.getTerminal().focus(); + } +} diff --git a/packages/ai-terminal/src/browser/ai-terminal-frontend-module.ts b/packages/ai-terminal/src/browser/ai-terminal-frontend-module.ts new file mode 100644 index 0000000000000..9f8ff9c059540 --- /dev/null +++ b/packages/ai-terminal/src/browser/ai-terminal-frontend-module.ts @@ -0,0 +1,34 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Agent } from '@theia/ai-core/lib/common'; +import { CommandContribution, MenuContribution } from '@theia/core'; +import { KeybindingContribution } from '@theia/core/lib/browser'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { AiTerminalAgent } from './ai-terminal-agent'; +import { AiTerminalCommandContribution } from './ai-terminal-contribution'; + +import '../../src/browser/style/ai-terminal.css'; + +export default new ContainerModule(bind => { + bind(AiTerminalCommandContribution).toSelf().inSingletonScope(); + for (const identifier of [CommandContribution, MenuContribution, KeybindingContribution]) { + bind(identifier).toService(AiTerminalCommandContribution); + } + + bind(AiTerminalAgent).toSelf().inSingletonScope(); + bind(Agent).toService(AiTerminalAgent); +}); diff --git a/packages/ai-terminal/src/browser/style/ai-terminal.css b/packages/ai-terminal/src/browser/style/ai-terminal.css new file mode 100644 index 0000000000000..acce0a411a725 --- /dev/null +++ b/packages/ai-terminal/src/browser/style/ai-terminal.css @@ -0,0 +1,94 @@ +.ai-terminal-chat-container { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 100%; + max-width: 500px; + padding: 10px; + box-sizing: border-box; + background: var(--theia-menu-background); + color: var(--theia-menu-foreground); + margin-bottom: 12px; + display: flex; + flex-direction: column; + align-items: center; + border: 1px solid var(--theia-menu-border); +} + +.ai-terminal-chat-container .closeButton { + position: absolute; + top: 1em; + right: 1em; + cursor: pointer; +} + +.ai-terminal-chat-container .closeButton:hover { + color: var(--theia-menu-foreground); +} + +.ai-terminal-chat-result { + width: 100%; + margin-bottom: 10px; +} + +.ai-terminal-chat-input-container { + width: 100%; + display: flex; + align-items: center; +} + +.ai-terminal-chat-input-container textarea { + flex-grow: 1; + height: 36px; + background-color: var(--theia-input-background); + border-radius: 4px; + box-sizing: border-box; + padding: 8px; + resize: none; + overflow: hidden; + line-height: 1.3rem; + margin-right: 10px; /* Add some space between textarea and button */ +} + +.ai-terminal-chat-input-container .option { + width: 21px; + height: 21px; + display: inline-block; + box-sizing: border-box; + user-select: none; + background-repeat: no-repeat; + background-position: center; + border: var(--theia-border-width) solid transparent; + opacity: 0.7; + cursor: pointer; +} + +.ai-terminal-chat-input-container .option:hover { + opacity: 1; +} + +@keyframes dots { + 0%, + 20% { + content: ""; + } + 40% { + content: "."; + } + 60% { + content: ".."; + } + 80%, + 100% { + content: "..."; + } +} +.ai-terminal-chat-result p.loading::after { + content: ""; + animation: dots 1s steps(1, end) infinite; +} + +.ai-terminal-chat-result p.command { + font-family: "Droid Sans Mono", "monospace", monospace; +} diff --git a/packages/ai-terminal/src/package.spec.ts b/packages/ai-terminal/src/package.spec.ts new file mode 100644 index 0000000000000..7c55c63eb414f --- /dev/null +++ b/packages/ai-terminal/src/package.spec.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-terminal package', () => { + + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-terminal/tsconfig.json b/packages/ai-terminal/tsconfig.json new file mode 100644 index 0000000000000..9269a0f774e34 --- /dev/null +++ b/packages/ai-terminal/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-chat" + }, + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../terminal" + } + ] +} diff --git a/packages/ai-workspace-agent/.eslintrc.js b/packages/ai-workspace-agent/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-workspace-agent/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-workspace-agent/README.md b/packages/ai-workspace-agent/README.md new file mode 100644 index 0000000000000..947501725540d --- /dev/null +++ b/packages/ai-workspace-agent/README.md @@ -0,0 +1,30 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - AI Workspace Agent EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-workspace-agent` extension contributes the `Workspace` agent to Theia AI. +The agent is able to inspect the current files of the workspace, including their content, to answer questions. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [一 (Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark +"Theia" is a trademark of the Eclipse Foundation +https://www.eclipse.org/theia diff --git a/packages/ai-workspace-agent/package.json b/packages/ai-workspace-agent/package.json new file mode 100644 index 0000000000000..65a50a7f12990 --- /dev/null +++ b/packages/ai-workspace-agent/package.json @@ -0,0 +1,53 @@ +{ + "name": "@theia/ai-workspace-agent", + "version": "1.53.0", + "description": "AI Workspace Agent Extension", + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "keywords": [ + "theia-extension" + ], + "dependencies": { + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", + "@theia/navigator": "1.53.0", + "@theia/terminal": "1.53.0", + "@theia/ai-core": "1.53.0", + "@theia/ai-chat": "1.53.0" + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@theia/cli": "1.53.0", + "@theia/test": "1.53.0" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/frontend-module" + } + ], + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-workspace-agent/src/browser/frontend-module.ts b/packages/ai-workspace-agent/src/browser/frontend-module.ts new file mode 100644 index 0000000000000..101f3702b0cce --- /dev/null +++ b/packages/ai-workspace-agent/src/browser/frontend-module.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ContainerModule } from '@theia/core/shared/inversify'; +import { ChatAgent } from '@theia/ai-chat/lib/common'; +import { Agent, ToolProvider } from '@theia/ai-core/lib/common'; +import { WorkspaceAgent } from './workspace-agent'; +import { FileContentFunction, GetWorkspaceFileList } from './functions'; + +export default new ContainerModule(bind => { + bind(WorkspaceAgent).toSelf().inSingletonScope(); + bind(Agent).toService(WorkspaceAgent); + bind(ChatAgent).toService(WorkspaceAgent); + bind(ToolProvider).to(GetWorkspaceFileList); + bind(ToolProvider).to(FileContentFunction); +}); diff --git a/packages/ai-workspace-agent/src/browser/functions.ts b/packages/ai-workspace-agent/src/browser/functions.ts new file mode 100644 index 0000000000000..d6b3a4fa6d68d --- /dev/null +++ b/packages/ai-workspace-agent/src/browser/functions.ts @@ -0,0 +1,134 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ToolProvider, ToolRequest } from '@theia/ai-core'; +import { URI } from '@theia/core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { FileService } from '@theia/filesystem/lib/browser/file-service'; +import { FileStat } from '@theia/filesystem/lib/common/files'; +import { WorkspaceService } from '@theia/workspace/lib/browser'; +import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID } from '../common/functions'; + +/** + * A Function that can read the contents of a File from the Workspace. + */ +@injectable() +export class FileContentFunction implements ToolProvider { + static ID = FILE_CONTENT_FUNCTION_ID; + + getTool(): ToolRequest { + return { + id: FileContentFunction.ID, + name: FileContentFunction.ID, + description: 'Get the content of the file', + parameters: { + type: 'object', + properties: { + file: { + type: 'string', + description: 'The path of the file to retrieve content for', + } + } + }, + handler: (arg_string: string) => { + const file = this.parseArg(arg_string); + return this.getFileContent(file); + } + }; + } + + @inject(WorkspaceService) + protected workspaceService: WorkspaceService; + + @inject(FileService) + protected readonly fileService: FileService; + + private parseArg(arg_string: string): string { + const result = JSON.parse(arg_string); + return result.file; + } + + private async getFileContent(file: string): Promise { + const uri = new URI(file); + const fileContent = await this.fileService.read(uri); + return fileContent.value; + } +} + +/** + * A Function that lists all files in the workspace. + */ +@injectable() +export class GetWorkspaceFileList implements ToolProvider { + static ID = GET_WORKSPACE_FILE_LIST_FUNCTION_ID; + + getTool(): ToolRequest { + return { + id: GetWorkspaceFileList.ID, + name: GetWorkspaceFileList.ID, + description: 'List all files in the workspace', + + handler: () => this.getProjectFileList() + }; + } + + @inject(WorkspaceService) + protected workspaceService: WorkspaceService; + + @inject(FileService) + protected readonly fileService: FileService; + + async getProjectFileList(): Promise { + // Get all files from the workspace service as a flat list of qualified file names + const wsRoots = await this.workspaceService.roots; + const result: string[] = []; + for (const root of wsRoots) { + result.push(...await this.listFilesRecursively(root.resource)); + } + return result; + } + + private async listFilesRecursively(uri: URI): Promise { + const stat = await this.fileService.resolve(uri); + const result: string[] = []; + if (stat && stat.isDirectory) { + if (this.exclude(stat)) { + return result; + } + const children = await this.fileService.resolve(uri); + if (children.children) { + for (const child of children.children) { + result.push(child.resource.toString()); + result.push(...await this.listFilesRecursively(child.resource)); + } + } + } + return result; + } + + // Exclude folders which are not relevant to the AI Agent + private exclude(stat: FileStat): boolean { + if (stat.resource.path.base.startsWith('.')) { + return true; + } + if (stat.resource.path.base === 'node_modules') { + return true; + } + if (stat.resource.path.base === 'lib') { + return true; + } + return false; + } +} diff --git a/packages/ai-workspace-agent/src/browser/workspace-agent.ts b/packages/ai-workspace-agent/src/browser/workspace-agent.ts new file mode 100644 index 0000000000000..2e59f45059c9a --- /dev/null +++ b/packages/ai-workspace-agent/src/browser/workspace-agent.ts @@ -0,0 +1,48 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from '@theia/ai-chat/lib/common'; +import { PromptTemplate, ToolInvocationRegistry } from '@theia/ai-core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { template } from '../common/template'; + +@injectable() +export class WorkspaceAgent extends AbstractStreamParsingChatAgent implements ChatAgent { + name: string; + description: string; + promptTemplates: PromptTemplate[]; + variables: never[]; + + @inject(ToolInvocationRegistry) + protected toolInvocationRegistry: ToolInvocationRegistry; + + constructor() { + super('Workspace', [{ + purpose: 'chat', + identifier: 'openai/gpt-4o', + }], 'chat'); + this.name = 'Workspace'; + this.description = 'This agent can access the users workspace, it can get a list of all available files and retrieve their content. \ + It can therefore answer questions about the current project, project files and source code in the workspace, such as how to build the project, \ + where to put source code, where to find specific code or configurations, etc.'; + this.promptTemplates = [template]; + this.variables = []; + } + + protected override async getSystemMessageDescription(): Promise { + const resolvedPrompt = await this.promptService.getPrompt(template.id); + return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; + } +} diff --git a/packages/ai-workspace-agent/src/common/functions.ts b/packages/ai-workspace-agent/src/common/functions.ts new file mode 100644 index 0000000000000..852a6c8f60f95 --- /dev/null +++ b/packages/ai-workspace-agent/src/common/functions.ts @@ -0,0 +1,17 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export const FILE_CONTENT_FUNCTION_ID = 'getFileContent'; +export const GET_WORKSPACE_FILE_LIST_FUNCTION_ID = 'getWorkspaceFileList'; diff --git a/packages/ai-workspace-agent/src/common/template.ts b/packages/ai-workspace-agent/src/common/template.ts new file mode 100644 index 0000000000000..b1c2bb4aaf27b --- /dev/null +++ b/packages/ai-workspace-agent/src/common/template.ts @@ -0,0 +1,29 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { PromptTemplate } from '@theia/ai-core/lib/common'; +import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID } from './functions'; + +export const template = { + id: 'workspace-prompt', + template: `You are an AI Agent to help developers with coding inside of the IDE. + The user has the workspace open. + If needed, you can ask for more information. + The following functions are available to you: + - ~{${GET_WORKSPACE_FILE_LIST_FUNCTION_ID}} + - ~{${FILE_CONTENT_FUNCTION_ID}} + +Never shorten the file paths when using getFileContent.` +}; diff --git a/packages/ai-workspace-agent/src/package.spec.ts b/packages/ai-workspace-agent/src/package.spec.ts new file mode 100644 index 0000000000000..106f1490b2d7a --- /dev/null +++ b/packages/ai-workspace-agent/src/package.spec.ts @@ -0,0 +1,28 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-workspace-agent package', () => { + + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-workspace-agent/tsconfig.json b/packages/ai-workspace-agent/tsconfig.json new file mode 100644 index 0000000000000..60c1ac9586d07 --- /dev/null +++ b/packages/ai-workspace-agent/tsconfig.json @@ -0,0 +1,40 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../../dev-packages/cli" + }, + { + "path": "../ai-chat" + }, + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../navigator" + }, + { + "path": "../terminal" + }, + { + "path": "../test" + }, + { + "path": "../workspace" + } + ] +} diff --git a/packages/core/src/browser/widgets/extractable-widget.ts b/packages/core/src/browser/widgets/extractable-widget.ts index d5ae144348edb..640bfab6656a4 100644 --- a/packages/core/src/browser/widgets/extractable-widget.ts +++ b/packages/core/src/browser/widgets/extractable-widget.ts @@ -28,6 +28,6 @@ export interface ExtractableWidget extends Widget { export namespace ExtractableWidget { export function is(widget: unknown): widget is ExtractableWidget { - return widget instanceof Widget && widget.hasOwnProperty('isExtractable') && (widget as ExtractableWidget).isExtractable === true; + return widget instanceof Widget && 'isExtractable' in widget && (widget as ExtractableWidget).isExtractable === true; } } diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 50cf0d7d2bc03..3545725b893ab 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -112,7 +112,7 @@ export class EditorManager extends NavigatableWidgetOpenHandler { if (!(editorPromise instanceof Widget)) { editorPromise.then(editor => this.revealSelection(editor, options, uri)); } else { - this.revealSelection(editorPromise, options); + this.revealSelection(editorPromise, options, uri); } } return editorPromise; diff --git a/packages/editor/src/browser/editor-variable-contribution.ts b/packages/editor/src/browser/editor-variable-contribution.ts index d732f4eeaf337..4d8a3ffcbcb7c 100644 --- a/packages/editor/src/browser/editor-variable-contribution.ts +++ b/packages/editor/src/browser/editor-variable-contribution.ts @@ -39,7 +39,15 @@ export class EditorVariableContribution implements VariableContribution { description: 'The current selected text in the active file', resolve: () => { const editor = this.getCurrentEditor(); - return editor ? editor.document.getText(editor.selection) : undefined; + return editor?.document.getText(editor.selection); + } + }); + variables.registerVariable({ + name: 'currentText', + description: 'The current text in the active file', + resolve: () => { + const editor = this.getCurrentEditor(); + return editor?.document.getText(); } }); } diff --git a/packages/getting-started/src/browser/getting-started-widget.tsx b/packages/getting-started/src/browser/getting-started-widget.tsx index c40b7c95ce93c..9e64d35897705 100644 --- a/packages/getting-started/src/browser/getting-started-widget.tsx +++ b/packages/getting-started/src/browser/getting-started-widget.tsx @@ -14,18 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import * as React from '@theia/core/shared/react'; -import URI from '@theia/core/lib/common/uri'; -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; -import { CommandRegistry, isOSX, environment, Path } from '@theia/core/lib/common'; -import { WorkspaceCommands, WorkspaceService } from '@theia/workspace/lib/browser'; -import { KeymapsCommands } from '@theia/keymaps/lib/browser'; -import { Message, ReactWidget, CommonCommands, LabelProvider, Key, KeyCode, codicon, PreferenceService } from '@theia/core/lib/browser'; -import { ApplicationInfo, ApplicationServer } from '@theia/core/lib/common/application-protocol'; +import { codicon, CommonCommands, Key, KeyCode, LabelProvider, Message, PreferenceService, ReactWidget } from '@theia/core/lib/browser'; import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { WindowService } from '@theia/core/lib/browser/window/window-service'; +import { CommandRegistry, environment, isOSX, Path } from '@theia/core/lib/common'; +import { ApplicationInfo, ApplicationServer } from '@theia/core/lib/common/application-protocol'; +import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { nls } from '@theia/core/lib/common/nls'; +import URI from '@theia/core/lib/common/uri'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; +import * as React from '@theia/core/shared/react'; +import { KeymapsCommands } from '@theia/keymaps/lib/browser'; +import { WorkspaceCommands, WorkspaceService } from '@theia/workspace/lib/browser'; /** * Default implementation of the `GettingStartedWidget`. @@ -71,6 +71,11 @@ export class GettingStartedWidget extends ReactWidget { */ protected recentWorkspaces: string[] = []; + /** + * Indicates whether the "ai-core" extension is available. + */ + protected aiIsIncluded: boolean; + /** * Collection of useful links to display for end users. */ @@ -78,6 +83,8 @@ export class GettingStartedWidget extends ReactWidget { protected readonly compatibilityUrl = 'https://eclipse-theia.github.io/vscode-theia-comparator/status.html'; protected readonly extensionUrl = 'https://www.theia-ide.org/docs/authoring_extensions'; protected readonly pluginUrl = 'https://www.theia-ide.org/docs/authoring_plugins'; + protected readonly theiaAIDocUrl = 'https://theia-ide.org/docs/user_ai/'; + protected readonly ghProjectUrl = 'https://github.com/eclipse-theia/theia/issues/new/choose'; @inject(ApplicationServer) protected readonly appServer: ApplicationServer; @@ -114,6 +121,9 @@ export class GettingStartedWidget extends ReactWidget { this.applicationInfo = await this.appServer.getApplicationInfo(); this.recentWorkspaces = await this.workspaceService.recentWorkspaces(); this.home = new URI(await this.environments.getHomeDirUri()).path.toString(); + + const extensions = await this.appServer.getExtensionsInfos(); + this.aiIsIncluded = extensions.find(ext => ext.name === '@theia/ai-core') !== undefined; this.update(); } @@ -131,6 +141,11 @@ export class GettingStartedWidget extends ReactWidget { protected render(): React.ReactNode { return
      + {this.aiIsIncluded && +
      + {this.renderAIBanner()} +
      + } {this.renderHeader()}
      @@ -387,6 +402,69 @@ export class GettingStartedWidget extends ReactWidget { return ; } + protected renderAIBanner(): React.ReactNode { + return
      +
      +
      +

      🚀 Theia AI [Experimental] is available! ✨

      +
      +
      + Theia IDE now contains the experimental "Theia AI" feature, which offers early access to cutting-edge AI capabilities within your IDE. +
      +
      + Please note that these features are disabled by default, ensuring that users can opt-in at their discretion without any concerns. + For those who choose to enable Theia AI, it is important to be aware that these experimental features may generate continuous + requests to the language models (LLMs) you provide access to, potentially incurring additional costs. +
      + For more details, please visit   + this.doOpenExternalLink(this.theiaAIDocUrl)} + onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.theiaAIDocUrl)}> + {'Theia AI Documentation'} + . +
      +
      + We encourage feedback, contributions, and sponsorship to support the ongoing development of the Theia AI initiative use our  + this.doOpenExternalLink(this.ghProjectUrl)} + onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.ghProjectUrl)}> + {'Github Project'} + . +  Thank you for being part of our community! +
      +
      + Please note that this feature is currently in development and may undergo frequent changes. 🚧 +
      +
      + +
      +
      +
      +
      +
      ; + } + + protected doOpenAIChatView = () => this.commandRegistry.executeCommand('ai-chat:open'); + protected doOpenAIChatViewEnter = (e: React.KeyboardEvent) => { + if (this.isEnterKey(e)) { + this.doOpenAIChatView(); + } + }; + /** * Build the list of workspace paths. * @param workspaces {string[]} the list of workspaces. diff --git a/packages/getting-started/src/browser/style/index.css b/packages/getting-started/src/browser/style/index.css index 17216da4df9da..274fefe468e47 100644 --- a/packages/getting-started/src/browser/style/index.css +++ b/packages/getting-started/src/browser/style/index.css @@ -107,3 +107,23 @@ body { display: flex; align-items: center; } + +.gs-float { + float: right; + width: 50%; + margin-top: 100px; +} + +.gs-container.gs-experimental-container { + border: 1px solid var(--theia-focusBorder); + padding: 15px; +} + +.shadow-pulse { + animation: shadowPulse 2s infinite ease-in-out; +} + +@keyframes shadowPulse { + 0%, 100% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); } + 50% { box-shadow: 0 0 15px rgba(0, 0, 0, 0.4); } +} diff --git a/packages/preferences/src/browser/util/preference-layout.ts b/packages/preferences/src/browser/util/preference-layout.ts index 43ba032e0ca7f..d810e3e0340b1 100644 --- a/packages/preferences/src/browser/util/preference-layout.ts +++ b/packages/preferences/src/browser/util/preference-layout.ts @@ -303,6 +303,10 @@ export const DEFAULT_LAYOUT: PreferenceLayout[] = [ } ] }, + { + id: 'ai-features', + label: 'AI Features', // TODO localize + }, { id: 'extensions', label: nls.localizeByDefault('Extensions'), diff --git a/packages/terminal/src/browser/terminal-link-provider.ts b/packages/terminal/src/browser/terminal-link-provider.ts index d6db9be69dfea..3d12f7522cc6f 100644 --- a/packages/terminal/src/browser/terminal-link-provider.ts +++ b/packages/terminal/src/browser/terminal-link-provider.ts @@ -25,7 +25,7 @@ import { TerminalWidgetImpl } from './terminal-widget-impl'; export const TerminalLinkProvider = Symbol('TerminalLinkProvider'); export interface TerminalLinkProvider { - provideLinks(line: string, terminal: TerminalWidget, cancelationToken?: CancellationToken): Promise; + provideLinks(line: string, terminal: TerminalWidget, cancellationToken?: CancellationToken): Promise; } export const TerminalLink = Symbol('TerminalLink'); diff --git a/tsconfig.json b/tsconfig.json index 53c3b18c75a75..3ae111513cd48 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -54,6 +54,30 @@ { "path": "examples/playwright" }, + { + "path": "packages/ai-chat" + }, + { + "path": "packages/ai-chat-ui" + }, + { + "path": "packages/ai-code-completion" + }, + { + "path": "packages/ai-core" + }, + { + "path": "packages/ai-history" + }, + { + "path": "packages/ai-openai" + }, + { + "path": "packages/ai-terminal" + }, + { + "path": "packages/ai-workspace-agent" + }, { "path": "packages/bulk-edit" }, diff --git a/yarn.lock b/yarn.lock index 881c9757b4b88..df0f13c9de69c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2133,7 +2133,7 @@ resolved "https://registry.yarnpkg.com/@types/node-abi/-/node-abi-3.0.3.tgz#a8334d75fe45ccd4cdb2a6c1ae82540a7a76828c" integrity sha512-5oos6sivyXcDEuVC5oX3+wLwfgrGZu4NIOn826PGAjPCHsqp2zSPTGU7H1Tv+GZBOiDUY3nBXY1MdaofSEt4fw== -"@types/node-fetch@^2.5.7": +"@types/node-fetch@^2.5.7", "@types/node-fetch@^2.6.4": version "2.6.11" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== @@ -2141,7 +2141,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18", "@types/node@^20.9.0": +"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18": version "18.19.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.18.tgz#7526471b28828d1fef1f7e4960fb9477e6e4369c" integrity sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg== @@ -2774,6 +2774,13 @@ abbrev@^1.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3362,7 +3369,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4963,13 +4970,13 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@^30.1.2: - version "30.3.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.1.tgz#fe27ca2a4739bec832b2edd6f46140ab46bf53a0" - integrity sha512-Ai/OZ7VlbFAVYMn9J5lyvtr+ZWyEbXDVd5wBLb5EVrp4352SRmMAmN5chcIe3n9mjzcgehV9n4Hwy15CJW+YbA== +electron@^28.2.8: + version "28.2.10" + resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.10.tgz#4e168568406a8b1e9b9a5859e988c905b9a57570" + integrity sha512-0rGBJNogcl2FIRxGRUv9zuMaBP78nSBJW+Bd1U7OGeg8IEkSIbHOhfn71XoGxgbOUSCEXjjyftq4mtAAVbUsZQ== dependencies: "@electron/get" "^2.0.0" - "@types/node" "^20.9.0" + "@types/node" "^18.11.18" extract-zip "^2.0.1" emoji-regex@^8.0.0: @@ -5472,6 +5479,11 @@ event-stream@=3.3.4: stream-combiner "~0.0.4" through "~2.3.1" +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -5666,11 +5678,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fflate@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" - integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== - figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -5844,11 +5851,6 @@ follow-redirects@^1.0.0, follow-redirects@^1.15.4: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== -follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - font-awesome@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -5877,6 +5879,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -5886,6 +5893,14 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -7127,11 +7142,6 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isomorphic.js@^0.2.4: - version "0.2.5" - resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.5.tgz#13eecf36f2dba53e85d355e11bf9d4208c6f7f88" - integrity sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -7600,20 +7610,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lib0@^0.2.52, lib0@^0.2.94: - version "0.2.94" - resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.94.tgz#fc28b4b65f816599f1e2f59d3401e231709535b3" - integrity sha512-hZ3p54jL4Wpu7IOg26uC7dnEWiMyNlUrb9KoG7+xYs45WkQwpVvKFndVq2+pqLYKe1u8Fp3+zAfZHVvTK34PvQ== - dependencies: - isomorphic.js "^0.2.4" - -lib0@^0.2.85, lib0@^0.2.86: - version "0.2.93" - resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.93.tgz#95487c2a97657313cb1d91fbcf9f6d64b7fcd062" - integrity sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q== - dependencies: - isomorphic.js "^0.2.4" - libnpmaccess@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" @@ -8457,10 +8453,10 @@ mute-stream@~1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -nan@2.20.0, nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" - integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== +nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== nano@^10.1.3: version "10.1.3" @@ -8558,6 +8554,11 @@ node-api-version@^0.1.4: dependencies: semver "^7.3.5" +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -8642,16 +8643,6 @@ node-ssh@^12.0.1: shell-escape "^0.2.0" ssh2 "^1.5.0" -nodejs-file-downloader@4.13.0: - version "4.13.0" - resolved "https://registry.yarnpkg.com/nodejs-file-downloader/-/nodejs-file-downloader-4.13.0.tgz#da87c30081de5ff4e8b864062c98cdec03e66ad0" - integrity sha512-nI2fKnmJWWFZF6SgMPe1iBodKhfpztLKJTtCtNYGhm/9QXmWa/Pk9Sv00qHgzEvNLe1x7hjGDRor7gcm/ChaIQ== - dependencies: - follow-redirects "^1.15.6" - https-proxy-agent "^5.0.0" - mime-types "^2.1.27" - sanitize-filename "^1.6.3" - noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -9055,26 +9046,6 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open-collaboration-protocol@0.2.0, open-collaboration-protocol@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/open-collaboration-protocol/-/open-collaboration-protocol-0.2.0.tgz#f3f93f22bb5fbb46e3fd31e6bb87f52a9ce6526b" - integrity sha512-ZaLMTMyVoJJ0vPjoMXGhNZqiycbfyJPbNCkbI9uHTOYRsvZqreRAFhSd7p9RbxLJNS5xeQGNSfldrhhec94Bmg== - dependencies: - base64-js "^1.5.1" - fflate "^0.8.2" - msgpackr "^1.10.2" - semver "^7.6.2" - socket.io-client "^4.7.5" - -open-collaboration-yjs@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/open-collaboration-yjs/-/open-collaboration-yjs-0.2.0.tgz#7c7e30dba444b9f6947fe76ae02a7c3fdaec6172" - integrity sha512-HT2JU/HJObIaQMF/MHt5/5VdOnGn+bVTaTJnyYfyaa/vjqg4Z4Glas3Hc9Ua970ssP3cOIRUQoHQumM0giaxrw== - dependencies: - lib0 "^0.2.94" - open-collaboration-protocol "^0.2.0" - y-protocols "^1.0.6" - open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -9092,6 +9063,19 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +openai@^4.55.7: + version "4.55.7" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.55.7.tgz#2bba4ae9224ad205c0d087d1412fe95421397dff" + integrity sha512-I2dpHTINt0Zk+Wlns6KzkKu77MmNW3VfIIQf5qYziEUI6t7WciG1zTobfKqdPzBmZi3TTM+3DtjPumxQdcvzwA== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + opener@^1.5.1: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -10433,13 +10417,6 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sanitize-filename@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" - integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== - dependencies: - truncate-utf8-bytes "^1.0.0" - sax@>=0.6.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" @@ -10540,11 +10517,6 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semve dependencies: lru-cache "^6.0.0" -semver@^7.6.2: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -10806,16 +10778,6 @@ socket.io-client@^4.5.3: engine.io-client "~6.5.2" socket.io-parser "~4.2.4" -socket.io-client@^4.7.5: - version "4.7.5" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" - integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== - dependencies: - "@socket.io/component-emitter" "~3.1.0" - debug "~4.3.2" - engine.io-client "~6.5.2" - socket.io-parser "~4.2.4" - socket.io-parser@~4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" @@ -11047,7 +11009,7 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11065,6 +11027,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11130,7 +11101,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11151,6 +11122,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -11532,13 +11510,6 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -truncate-utf8-bytes@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" - integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== - dependencies: - utf8-byte-length "^1.0.1" - ts-api-utils@^1.0.1: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -11978,11 +11949,6 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" -utf8-byte-length@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e" - integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -12132,6 +12098,11 @@ wcwidth@^1.0.0, wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -12351,7 +12322,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12369,6 +12340,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -12526,13 +12506,6 @@ xterm@^5.3.0: resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== -y-protocols@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.6.tgz#66dad8a95752623443e8e28c0e923682d2c0d495" - integrity sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q== - dependencies: - lib0 "^0.2.85" - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -12654,13 +12627,6 @@ yazl@^2.2.2: dependencies: buffer-crc32 "~0.2.3" -yjs@^13.6.7: - version "13.6.15" - resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.15.tgz#5a2402632aabf83e5baf56342b4c82fe40859306" - integrity sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ== - dependencies: - lib0 "^0.2.86" - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" @@ -12674,3 +12640,13 @@ zip-stream@^4.1.0: archiver-utils "^3.0.4" compress-commons "^4.1.2" readable-stream "^3.6.0" + +zod-to-json-schema@^3.23.2: + version "3.23.2" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz#bc7e379c8050462538383e382964c03d8fe008f9" + integrity sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw== + +zod@^3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== From cc46ceb830758b6dca66f6f03e06792e839e7bed Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Fri, 16 Aug 2024 13:34:04 +0200 Subject: [PATCH 366/441] feat: Ollama integration for Theia AI Integrates Ollama language models into Theia via the new 'ai-ollama' package. The endpoint and models can be configured via the preferences. --- examples/browser-only/package.json | 1 + examples/browser-only/tsconfig.json | 3 + examples/browser/package.json | 1 + examples/browser/tsconfig.json | 3 + examples/electron/package.json | 1 + examples/electron/tsconfig.json | 3 + packages/ai-ollama/.eslintrc.js | 10 ++ packages/ai-ollama/README.md | 30 ++++ packages/ai-ollama/package.json | 53 ++++++ ...llama-frontend-application-contribution.ts | 59 +++++++ .../src/browser/ollama-frontend-module.ts | 31 ++++ .../src/browser/ollama-preferences.ts | 41 +++++ packages/ai-ollama/src/common/index.ts | 16 ++ .../common/ollama-language-models-manager.ts | 23 +++ .../src/node/ollama-backend-module.ts | 30 ++++ .../src/node/ollama-language-model.ts | 155 ++++++++++++++++++ .../ollama-language-models-manager-impl.ts | 58 +++++++ packages/ai-ollama/src/package.spec.ts | 27 +++ packages/ai-ollama/tsconfig.json | 25 +++ tsconfig.json | 3 + yarn.lock | 141 ++++++++++++++-- 21 files changed, 703 insertions(+), 11 deletions(-) create mode 100644 packages/ai-ollama/.eslintrc.js create mode 100644 packages/ai-ollama/README.md create mode 100644 packages/ai-ollama/package.json create mode 100644 packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts create mode 100644 packages/ai-ollama/src/browser/ollama-frontend-module.ts create mode 100644 packages/ai-ollama/src/browser/ollama-preferences.ts create mode 100644 packages/ai-ollama/src/common/index.ts create mode 100644 packages/ai-ollama/src/common/ollama-language-models-manager.ts create mode 100644 packages/ai-ollama/src/node/ollama-backend-module.ts create mode 100644 packages/ai-ollama/src/node/ollama-language-model.ts create mode 100644 packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts create mode 100644 packages/ai-ollama/src/package.spec.ts create mode 100644 packages/ai-ollama/tsconfig.json diff --git a/examples/browser-only/package.json b/examples/browser-only/package.json index e1ed7399bd982..bec4142e20b8c 100644 --- a/examples/browser-only/package.json +++ b/examples/browser-only/package.json @@ -21,6 +21,7 @@ "@theia/ai-core": "1.53.0", "@theia/ai-history": "1.53.0", "@theia/ai-openai": "1.53.0", + "@theia/ai-ollama": "1.53.0", "@theia/api-samples": "1.53.0", "@theia/bulk-edit": "1.53.0", "@theia/callhierarchy": "1.53.0", diff --git a/examples/browser-only/tsconfig.json b/examples/browser-only/tsconfig.json index 2d8d29a0d1fa1..2c35a886f30d0 100644 --- a/examples/browser-only/tsconfig.json +++ b/examples/browser-only/tsconfig.json @@ -23,6 +23,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-ollama" + }, { "path": "../../packages/ai-openai" }, diff --git a/examples/browser/package.json b/examples/browser/package.json index e6b387674d0b7..21ce9dccb8410 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -26,6 +26,7 @@ "@theia/ai-core": "1.53.0", "@theia/ai-history": "1.53.0", "@theia/ai-openai": "1.53.0", + "@theia/ai-ollama": "1.53.0", "@theia/ai-terminal": "1.53.0", "@theia/ai-workspace-agent": "1.53.0", "@theia/api-provider-sample": "1.53.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index ed5f00cd84c77..f6b8cad219535 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -23,6 +23,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-ollama" + }, { "path": "../../packages/ai-openai" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index 16c72e673eef9..6080cd438088e 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -32,6 +32,7 @@ "@theia/ai-core": "1.53.0", "@theia/ai-history": "1.53.0", "@theia/ai-openai": "1.53.0", + "@theia/ai-ollama": "1.53.0", "@theia/ai-terminal": "1.53.0", "@theia/ai-workspace-agent": "1.53.0", "@theia/api-provider-sample": "1.53.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index b67a624574045..e36e0e73f4457 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-ollama" + }, { "path": "../../packages/ai-openai" }, diff --git a/packages/ai-ollama/.eslintrc.js b/packages/ai-ollama/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-ollama/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-ollama/README.md b/packages/ai-ollama/README.md new file mode 100644 index 0000000000000..f9eba7a29e173 --- /dev/null +++ b/packages/ai-ollama/README.md @@ -0,0 +1,30 @@ +
      + +
      + +theia-ext-logo + +

      ECLIPSE THEIA - Ollama EXTENSION

      + +
      + +
      + +## Description + +The `@theia/ai-ollama` integrates Ollama's models with Theia AI. + +## Additional Information + +- [Theia - GitHub](https://github.com/eclipse-theia/theia) +- [Theia - Website](https://theia-ide.org/) + +## License + +- [Eclipse Public License 2.0](http://www.eclipse.org/legal/epl-2.0/) +- [(Secondary) GNU General Public License, version 2 with the GNU Classpath Exception](https://projects.eclipse.org/license/secondary-gpl-2.0-cp) + +## Trademark + +"Theia" is a trademark of the Eclipse Foundation + diff --git a/packages/ai-ollama/package.json b/packages/ai-ollama/package.json new file mode 100644 index 0000000000000..779515df07d0b --- /dev/null +++ b/packages/ai-ollama/package.json @@ -0,0 +1,53 @@ +{ + "name": "@theia/ai-ollama", + "version": "1.53.0", + "description": "Theia - Ollama Integration", + "dependencies": { + "@theia/core": "1.53.0", + "@theia/filesystem": "1.53.0", + "@theia/workspace": "1.53.0", + "minimatch": "^5.1.0", + "tslib": "^2.6.2", + "ollama": "^0.5.8", + "@theia/ai-core": "1.53.0" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/ollama-frontend-module", + "backend": "lib/node/ollama-backend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.53.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts b/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts new file mode 100644 index 0000000000000..e08680563cd72 --- /dev/null +++ b/packages/ai-ollama/src/browser/ollama-frontend-application-contribution.ts @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { OllamaLanguageModelsManager } from '../common'; +import { HOST_PREF, MODELS_PREF } from './ollama-preferences'; + +@injectable() +export class OllamaFrontendApplicationContribution implements FrontendApplicationContribution { + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + @inject(OllamaLanguageModelsManager) + protected manager: OllamaLanguageModelsManager; + + protected prevModels: string[] = []; + + onStart(): void { + this.preferenceService.ready.then(() => { + const host = this.preferenceService.get(HOST_PREF, 'http://localhost:11434'); + this.manager.setHost(host); + + const models = this.preferenceService.get(MODELS_PREF, []); + this.manager.createLanguageModels(...models); + this.prevModels = [...models]; + + this.preferenceService.onPreferenceChanged(event => { + if (event.preferenceName === HOST_PREF) { + this.manager.setHost(event.newValue); + } else if (event.preferenceName === MODELS_PREF) { + const oldModels = new Set(this.prevModels); + const newModels = new Set(event.newValue as string[]); + + const modelsToRemove = [...oldModels].filter(model => !newModels.has(model)); + const modelsToAdd = [...newModels].filter(model => !oldModels.has(model)); + + this.manager.removeLanguageModels(...modelsToRemove); + this.manager.createLanguageModels(...modelsToAdd); + this.prevModels = [...event.newValue]; + } + }); + }); + } +} diff --git a/packages/ai-ollama/src/browser/ollama-frontend-module.ts b/packages/ai-ollama/src/browser/ollama-frontend-module.ts new file mode 100644 index 0000000000000..7228aba4b4332 --- /dev/null +++ b/packages/ai-ollama/src/browser/ollama-frontend-module.ts @@ -0,0 +1,31 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { OllamaPreferencesSchema } from './ollama-preferences'; +import { FrontendApplicationContribution, PreferenceContribution, RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser'; +import { OllamaFrontendApplicationContribution } from './ollama-frontend-application-contribution'; +import { OLLAMA_LANGUAGE_MODELS_MANAGER_PATH, OllamaLanguageModelsManager } from '../common'; + +export default new ContainerModule(bind => { + bind(PreferenceContribution).toConstantValue({ schema: OllamaPreferencesSchema }); + bind(OllamaFrontendApplicationContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(OllamaFrontendApplicationContribution); + bind(OllamaLanguageModelsManager).toDynamicValue(ctx => { + const provider = ctx.container.get(RemoteConnectionProvider); + return provider.createProxy(OLLAMA_LANGUAGE_MODELS_MANAGER_PATH); + }).inSingletonScope(); +}); diff --git a/packages/ai-ollama/src/browser/ollama-preferences.ts b/packages/ai-ollama/src/browser/ollama-preferences.ts new file mode 100644 index 0000000000000..b7088001bccc0 --- /dev/null +++ b/packages/ai-ollama/src/browser/ollama-preferences.ts @@ -0,0 +1,41 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference-contribution'; +import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; + +export const HOST_PREF = 'ai-features.ollama.host'; +export const MODELS_PREF = 'ai-features.ollama.models'; + +export const OllamaPreferencesSchema: PreferenceSchema = { + type: 'object', + properties: { + [HOST_PREF]: { + type: 'string', + title: AI_CORE_PREFERENCES_TITLE, + description: 'Ollama Host', + default: 'http://localhost:11434' + }, + [MODELS_PREF]: { + type: 'array', + title: AI_CORE_PREFERENCES_TITLE, + default: ['llama3', 'gemma2'], + items: { + type: 'string' + } + } + } +}; diff --git a/packages/ai-ollama/src/common/index.ts b/packages/ai-ollama/src/common/index.ts new file mode 100644 index 0000000000000..8d6821f9cc79e --- /dev/null +++ b/packages/ai-ollama/src/common/index.ts @@ -0,0 +1,16 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export * from './ollama-language-models-manager'; diff --git a/packages/ai-ollama/src/common/ollama-language-models-manager.ts b/packages/ai-ollama/src/common/ollama-language-models-manager.ts new file mode 100644 index 0000000000000..2714ef3a7757a --- /dev/null +++ b/packages/ai-ollama/src/common/ollama-language-models-manager.ts @@ -0,0 +1,23 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export const OLLAMA_LANGUAGE_MODELS_MANAGER_PATH = '/services/ollama/language-model-manager'; +export const OllamaLanguageModelsManager = Symbol('OllamaLanguageModelsManager'); +export interface OllamaLanguageModelsManager { + host: string | undefined; + setHost(host: string | undefined): void; + createLanguageModels(...modelIds: string[]): Promise; + removeLanguageModels(...modelIds: string[]): void +} diff --git a/packages/ai-ollama/src/node/ollama-backend-module.ts b/packages/ai-ollama/src/node/ollama-backend-module.ts new file mode 100644 index 0000000000000..667729c7fc4e2 --- /dev/null +++ b/packages/ai-ollama/src/node/ollama-backend-module.ts @@ -0,0 +1,30 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { OLLAMA_LANGUAGE_MODELS_MANAGER_PATH, OllamaLanguageModelsManager } from '../common/ollama-language-models-manager'; +import { ConnectionHandler, RpcConnectionHandler } from '@theia/core'; +import { OllamaLanguageModelsManagerImpl } from './ollama-language-models-manager-impl'; + +export const OllamaModelFactory = Symbol('OllamaModelFactory'); + +export default new ContainerModule(bind => { + bind(OllamaLanguageModelsManagerImpl).toSelf().inSingletonScope(); + bind(OllamaLanguageModelsManager).toService(OllamaLanguageModelsManagerImpl); + bind(ConnectionHandler).toDynamicValue(ctx => + new RpcConnectionHandler(OLLAMA_LANGUAGE_MODELS_MANAGER_PATH, () => ctx.container.get(OllamaLanguageModelsManager)) + ).inSingletonScope(); +}); diff --git a/packages/ai-ollama/src/node/ollama-language-model.ts b/packages/ai-ollama/src/node/ollama-language-model.ts new file mode 100644 index 0000000000000..899305831f7b5 --- /dev/null +++ b/packages/ai-ollama/src/node/ollama-language-model.ts @@ -0,0 +1,155 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { + LanguageModel, + LanguageModelParsedResponse, + LanguageModelRequest, + LanguageModelRequestMessage, + LanguageModelResponse, + LanguageModelStreamResponsePart, + ToolRequest +} from '@theia/ai-core'; +import { CancellationToken } from '@theia/core'; +import { ChatRequest, ChatResponse, Message, Ollama, Tool } from 'ollama'; + +export const OllamaModelIdentifier = Symbol('OllamaModelIdentifier'); + +export class OllamaModel implements LanguageModel { + + protected readonly DEFAULT_REQUEST_SETTINGS: Partial> = { + keep_alive: '15m' + }; + + readonly providerId = 'ollama'; + readonly vendor: string = 'Ollama'; + + constructor(protected readonly model: string, protected host: () => string | undefined) { + } + + get id(): string { + return this.providerId + '/' + this.model; + } + + get name(): string { + return this.model; + } + + async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { + const ollama = this.initializeOllama(); + + if (request.response_format?.type === 'json_schema') { + return this.handleStructuredOutputRequest(ollama, request); + } + const response = await ollama.chat({ + ...this.DEFAULT_REQUEST_SETTINGS, + model: this.model, + messages: request.messages.map(this.toOllamaMessage), + stream: true, + tools: request.tools?.map(this.toOllamaTool), + ...request.settings + }); + + cancellationToken?.onCancellationRequested(() => { + response.abort(); + }); + + async function* wrapAsyncIterator(inputIterable: AsyncIterable): AsyncIterable { + for await (const item of inputIterable) { + // TODO handle tool calls + yield { content: item.message.content }; + } + } + return { stream: wrapAsyncIterator(response) }; + } + + protected async handleStructuredOutputRequest(ollama: Ollama, request: LanguageModelRequest): Promise { + const result = await ollama.chat({ + ...this.DEFAULT_REQUEST_SETTINGS, + model: this.model, + messages: request.messages.map(this.toOllamaMessage), + format: 'json', + ...request.settings + }); + try { + return { + content: result.message.content, + parsed: JSON.parse(result.message.content) + }; + } catch (error) { + // TODO use ILogger + console.log('Failed to parse structured response from the language model.', error); + return { + content: result.message.content, + parsed: {} + }; + } + } + + protected initializeOllama(): Ollama { + const host = this.host(); + if (!host) { + throw new Error('Please provide OLLAMA_HOST in preferences or via environment variable'); + } + return new Ollama({ host: host }); + } + + protected toOllamaTool(tool: ToolRequest): Tool { + const transform = (props: Record | undefined) => { + if (!props) { + return undefined; + } + const result: Record = {}; + for (const key in props) { + if (Object.prototype.hasOwnProperty.call(props, key)) { + result[key] = { + type: props[key].type, + description: key + }; + } + } + return result; + }; + return { + type: 'function', + function: { + name: tool.name, + description: tool.description ?? 'Tool named ' + tool.name, + parameters: { + type: tool.parameters?.type ?? 'object', + required: Object.keys(tool.parameters?.properties ?? {}), + properties: transform(tool.parameters?.properties) ?? {} + }, + } + }; + } + + protected toOllamaMessage(message: LanguageModelRequestMessage): Message { + if (message.actor === 'ai') { + return { role: 'assistant', content: message.query || '' }; + } + if (message.actor === 'user') { + return { role: 'user', content: message.query || '' }; + } + if (message.actor === 'system') { + return { role: 'system', content: message.query || '' }; + } + return { role: 'system', content: '' }; + } +} diff --git a/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts b/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts new file mode 100644 index 0000000000000..1fbd1f520c3c8 --- /dev/null +++ b/packages/ai-ollama/src/node/ollama-language-models-manager-impl.ts @@ -0,0 +1,58 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { LanguageModelRegistry } from '@theia/ai-core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { OllamaModel } from './ollama-language-model'; +import { OllamaLanguageModelsManager } from '../common'; + +@injectable() +export class OllamaLanguageModelsManagerImpl implements OllamaLanguageModelsManager { + + protected _host: string | undefined; + + @inject(LanguageModelRegistry) + protected readonly languageModelRegistry: LanguageModelRegistry; + + get host(): string | undefined { + return this._host ?? process.env.OLLAMA_HOST; + } + + // Triggered from frontend. In case you want to use the models on the backend + // without a frontend then call this yourself + async createLanguageModels(...modelIds: string[]): Promise { + for (const id of modelIds) { + // TODO check that the model exists in Ollama using `list`. Ask and trigger download if not. + if (!(await this.languageModelRegistry.getLanguageModel(`ollama/${id}`))) { + this.languageModelRegistry.addLanguageModels([new OllamaModel(id, () => this.host)]); + } else { + console.info(`Ollama: skip creating model ${id} because it already exists`); + } + } + } + + removeLanguageModels(...modelIds: string[]): void { + this.languageModelRegistry.removeLanguageModels(modelIds.map(id => `ollama/${id}`)); + } + + setHost(host: string | undefined): void { + if (host) { + this._host = host; + } else { + this._host = undefined; + } + } +} diff --git a/packages/ai-ollama/src/package.spec.ts b/packages/ai-ollama/src/package.spec.ts new file mode 100644 index 0000000000000..2b813c28eef54 --- /dev/null +++ b/packages/ai-ollama/src/package.spec.ts @@ -0,0 +1,27 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-ollama package', () => { + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-ollama/tsconfig.json b/packages/ai-ollama/tsconfig.json new file mode 100644 index 0000000000000..61a997fc14fd1 --- /dev/null +++ b/packages/ai-ollama/tsconfig.json @@ -0,0 +1,25 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../filesystem" + }, + { + "path": "../workspace" + } + ] +} diff --git a/tsconfig.json b/tsconfig.json index 3ae111513cd48..ee1bdb0025812 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -69,6 +69,9 @@ { "path": "packages/ai-history" }, + { + "path": "packages/ai-ollama" + }, { "path": "packages/ai-openai" }, diff --git a/yarn.lock b/yarn.lock index df0f13c9de69c..29d43d8b58818 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2141,7 +2141,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18": +"@types/node@*", "@types/node@18", "@types/node@>=10.0.0", "@types/node@^10.14.22", "@types/node@^18.11.18", "@types/node@^20.9.0": version "18.19.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.18.tgz#7526471b28828d1fef1f7e4960fb9477e6e4369c" integrity sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg== @@ -3369,7 +3369,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -4970,13 +4970,13 @@ electron-window@^0.8.0: dependencies: is-electron-renderer "^2.0.0" -electron@^28.2.8: - version "28.2.10" - resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.10.tgz#4e168568406a8b1e9b9a5859e988c905b9a57570" - integrity sha512-0rGBJNogcl2FIRxGRUv9zuMaBP78nSBJW+Bd1U7OGeg8IEkSIbHOhfn71XoGxgbOUSCEXjjyftq4mtAAVbUsZQ== +electron@^30.1.2: + version "30.3.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.3.1.tgz#fe27ca2a4739bec832b2edd6f46140ab46bf53a0" + integrity sha512-Ai/OZ7VlbFAVYMn9J5lyvtr+ZWyEbXDVd5wBLb5EVrp4352SRmMAmN5chcIe3n9mjzcgehV9n4Hwy15CJW+YbA== dependencies: "@electron/get" "^2.0.0" - "@types/node" "^18.11.18" + "@types/node" "^20.9.0" extract-zip "^2.0.1" emoji-regex@^8.0.0: @@ -5678,6 +5678,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fflate@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + figures@3.2.0, figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -5851,6 +5856,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.15.4: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + font-awesome@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -7142,6 +7152,11 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +isomorphic.js@^0.2.4: + version "0.2.5" + resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.5.tgz#13eecf36f2dba53e85d355e11bf9d4208c6f7f88" + integrity sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -7610,6 +7625,20 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lib0@^0.2.52, lib0@^0.2.94: + version "0.2.94" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.94.tgz#fc28b4b65f816599f1e2f59d3401e231709535b3" + integrity sha512-hZ3p54jL4Wpu7IOg26uC7dnEWiMyNlUrb9KoG7+xYs45WkQwpVvKFndVq2+pqLYKe1u8Fp3+zAfZHVvTK34PvQ== + dependencies: + isomorphic.js "^0.2.4" + +lib0@^0.2.85, lib0@^0.2.86: + version "0.2.93" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.93.tgz#95487c2a97657313cb1d91fbcf9f6d64b7fcd062" + integrity sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q== + dependencies: + isomorphic.js "^0.2.4" + libnpmaccess@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" @@ -8453,10 +8482,10 @@ mute-stream@~1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== -nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" - integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== +nan@2.20.0, nan@^2.14.0, nan@^2.17.0, nan@^2.18.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.20.0.tgz#08c5ea813dd54ed16e5bd6505bf42af4f7838ca3" + integrity sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw== nano@^10.1.3: version "10.1.3" @@ -8643,6 +8672,16 @@ node-ssh@^12.0.1: shell-escape "^0.2.0" ssh2 "^1.5.0" +nodejs-file-downloader@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/nodejs-file-downloader/-/nodejs-file-downloader-4.13.0.tgz#da87c30081de5ff4e8b864062c98cdec03e66ad0" + integrity sha512-nI2fKnmJWWFZF6SgMPe1iBodKhfpztLKJTtCtNYGhm/9QXmWa/Pk9Sv00qHgzEvNLe1x7hjGDRor7gcm/ChaIQ== + dependencies: + follow-redirects "^1.15.6" + https-proxy-agent "^5.0.0" + mime-types "^2.1.27" + sanitize-filename "^1.6.3" + noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -9025,6 +9064,13 @@ octicons@^7.1.0: dependencies: object-assign "^4.1.1" +ollama@^0.5.8: + version "0.5.8" + resolved "https://registry.yarnpkg.com/ollama/-/ollama-0.5.8.tgz#d52f20345b4b49e26734cf2e8749dd95899c2c99" + integrity sha512-frBGdfSV34i7JybLZUeyCYDx0CMyDiG4On8xOK+cNRWM04HImhoWgIMpF4p7vTkQumadbSxOteR7SZyKqNmOXg== + dependencies: + whatwg-fetch "^3.6.20" + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -9046,6 +9092,26 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open-collaboration-protocol@0.2.0, open-collaboration-protocol@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/open-collaboration-protocol/-/open-collaboration-protocol-0.2.0.tgz#f3f93f22bb5fbb46e3fd31e6bb87f52a9ce6526b" + integrity sha512-ZaLMTMyVoJJ0vPjoMXGhNZqiycbfyJPbNCkbI9uHTOYRsvZqreRAFhSd7p9RbxLJNS5xeQGNSfldrhhec94Bmg== + dependencies: + base64-js "^1.5.1" + fflate "^0.8.2" + msgpackr "^1.10.2" + semver "^7.6.2" + socket.io-client "^4.7.5" + +open-collaboration-yjs@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/open-collaboration-yjs/-/open-collaboration-yjs-0.2.0.tgz#7c7e30dba444b9f6947fe76ae02a7c3fdaec6172" + integrity sha512-HT2JU/HJObIaQMF/MHt5/5VdOnGn+bVTaTJnyYfyaa/vjqg4Z4Glas3Hc9Ua970ssP3cOIRUQoHQumM0giaxrw== + dependencies: + lib0 "^0.2.94" + open-collaboration-protocol "^0.2.0" + y-protocols "^1.0.6" + open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -10417,6 +10483,13 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sanitize-filename@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + sax@>=0.6.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" @@ -10517,6 +10590,11 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semve dependencies: lru-cache "^6.0.0" +semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -10778,6 +10856,16 @@ socket.io-client@^4.5.3: engine.io-client "~6.5.2" socket.io-parser "~4.2.4" +socket.io-client@^4.7.5: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" + integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.5.2" + socket.io-parser "~4.2.4" + socket.io-parser@~4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" @@ -11510,6 +11598,13 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ== + dependencies: + utf8-byte-length "^1.0.1" + ts-api-utils@^1.0.1: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -11949,6 +12044,11 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" +utf8-byte-length@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz#f9f63910d15536ee2b2d5dd4665389715eac5c1e" + integrity sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -12183,6 +12283,11 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-fetch@^3.6.20: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" @@ -12506,6 +12611,13 @@ xterm@^5.3.0: resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0.tgz#867daf9cc826f3d45b5377320aabd996cb0fce46" integrity sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg== +y-protocols@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.6.tgz#66dad8a95752623443e8e28c0e923682d2c0d495" + integrity sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q== + dependencies: + lib0 "^0.2.85" + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -12627,6 +12739,13 @@ yazl@^2.2.2: dependencies: buffer-crc32 "~0.2.3" +yjs@^13.6.7: + version "13.6.15" + resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.15.tgz#5a2402632aabf83e5baf56342b4c82fe40859306" + integrity sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ== + dependencies: + lib0 "^0.2.86" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From 8ed50018438bee8486c9b7683aab3a29be7be56e Mon Sep 17 00:00:00 2001 From: Lucas Koehler Date: Thu, 12 Sep 2024 10:26:06 +0200 Subject: [PATCH 367/441] ai: Improve prompt of workspace agent (#14159) Fixes #14128 Extend the prompt of the Theia AI workspace agent to get better answers and get the agent to base the answers on the actual workspace contents instead of assuming general workspace and file contents. --- .../ai-workspace-agent/src/common/template.ts | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/packages/ai-workspace-agent/src/common/template.ts b/packages/ai-workspace-agent/src/common/template.ts index b1c2bb4aaf27b..4d59d7d6d9aef 100644 --- a/packages/ai-workspace-agent/src/common/template.ts +++ b/packages/ai-workspace-agent/src/common/template.ts @@ -17,13 +17,47 @@ import { PromptTemplate } from '@theia/ai-core/lib/common'; import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID } from './functions'; export const template = { - id: 'workspace-prompt', - template: `You are an AI Agent to help developers with coding inside of the IDE. - The user has the workspace open. - If needed, you can ask for more information. - The following functions are available to you: - - ~{${GET_WORKSPACE_FILE_LIST_FUNCTION_ID}} - - ~{${FILE_CONTENT_FUNCTION_ID}} - -Never shorten the file paths when using getFileContent.` + id: 'workspace-prompt', + template: `# Instructions + + You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by +providing concise and accurate answers to programming-related questions. Your role is to enhance the +developer's productivity by offering quick solutions, explanations, and best practices. +Keep responses short and to the point, focusing on delivering valuable insights, best practices and +simple solutions. +You are specialized in providing insights based on the Theia IDE's workspace and its files. +Use the following functions to access the workspace: +- ~{${GET_WORKSPACE_FILE_LIST_FUNCTION_ID}} +- ~{${FILE_CONTENT_FUNCTION_ID}}. Never shorten the file paths when using this function. + +## Guidelines + +1. **Understand Context:** + - **Always answer in context of the workspace and its files. Avoid general answers**. + - Use the provided functions to access the workspace files. **Never assume the workspace structure or file contents.** + - Tailor responses to be relevant to the programming language, framework, or tools like Eclipse Theia used in the workspace. + - Ask clarifying questions if necessary to provide accurate assistance. Always assume it is okay to read additional files from the workspace. + +2. **Provide Clear Solutions:** + - Offer direct answers or code snippets that solve the problem or clarify the concept. + - Avoid lengthy explanations unless necessary for understanding. + - Provide links to official documentation for further reading when applicable. + +3. **Support Multiple Languages and Tools:** + - Be familiar with popular programming languages, frameworks, IDEs like Eclipse Theia, and command-line tools. + - Adapt advice based on the language, environment, or tools specified by the developer. + +4. **Facilitate Learning:** + - Encourage learning by explaining why a solution works or why a particular approach is recommended. + - Keep explanations concise and educational. + +5. **Maintain Professional Tone:** + - Communicate in a friendly, professional manner. + - Use technical jargon appropriately, ensuring clarity for the target audience. + +6. **Stay on Topic:** + - Limit responses strictly to topics related to software development, frameworks, Eclipse Theia, terminal usage, and relevant technologies. + - Politely decline to answer questions unrelated to these areas by saying, "I'm here to assist with programming-related questions. + For other topics, please refer to a specialized source." +` }; From e7e196326e974f4e1669cef02e23222e6dd8d10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 12 Sep 2024 14:28:43 +0200 Subject: [PATCH 368/441] Add handling of multiple accounts to authentication api (#14149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds handling of multiple accounts to authentication api Fixes #14110 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- CHANGELOG.md | 1 + .../src/browser/authentication-service.ts | 37 +++++++++---------- .../plugin-ext/src/common/plugin-api-rpc.ts | 5 ++- .../src/main/browser/authentication-main.ts | 35 ++++++++++++------ .../src/plugin/authentication-ext.ts | 14 ++++--- .../plugin-ext/src/plugin/plugin-context.ts | 3 ++ packages/plugin/src/theia.d.ts | 37 ++++++++++++++++++- 7 files changed, 93 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49cd5cf7bab25..320f08d4d921f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ +- [core] Updated AuthenticationService to handle multiple accounts per provider [#14149](https://github.com/eclipse-theia/theia/pull/14149) - Contributed on behalf of STMicroelectronics ## 1.53.0 - 08/29/2024 diff --git a/packages/core/src/browser/authentication-service.ts b/packages/core/src/browser/authentication-service.ts index a1f1c7120ff01..fa06485e39600 100644 --- a/packages/core/src/browser/authentication-service.ts +++ b/packages/core/src/browser/authentication-service.ts @@ -32,6 +32,13 @@ export interface AuthenticationSessionAccountInformation { readonly id: string; readonly label: string; } +export interface AuthenticationProviderSessionOptions { + /** + * The account that is being asked about. If this is passed in, the provider should + * attempt to return the sessions that are only related to this account. + */ + account?: AuthenticationSessionAccountInformation; +} export interface AuthenticationSession { id: string; @@ -82,16 +89,6 @@ export interface AuthenticationProvider { updateSessionItems(event: AuthenticationProviderAuthenticationSessionsChangeEvent): Promise; - /** - * @deprecated use `createSession` instead. - */ - login(scopes: string[]): Promise; - - /** - * @deprecated use `removeSession` instead. - */ - logout(sessionId: string): Promise; - /** * An [event](#Event) which fires when the array of sessions has changed, or data * within a session has changed. @@ -102,16 +99,18 @@ export interface AuthenticationProvider { * Get a list of sessions. * @param scopes An optional list of scopes. If provided, the sessions returned should match * these permissions, otherwise all sessions should be returned. + * @param account The optional account that you would like to get the session for * @returns A promise that resolves to an array of authentication sessions. */ - getSessions(scopes?: string[]): Thenable>; + getSessions(scopes: string[] | undefined, account?: AuthenticationSessionAccountInformation): Thenable>; /** * Prompts a user to login. * @param scopes A list of scopes, permissions, that the new session should be created with. + * @param options The options for createing the session * @returns A promise that resolves to an authentication session. */ - createSession(scopes: string[]): Thenable; + createSession(scopes: string[], options: AuthenticationProviderSessionOptions): Thenable; /** * Removes the session corresponding to session id. @@ -134,10 +133,10 @@ export interface AuthenticationService { readonly onDidChangeSessions: Event<{ providerId: string, label: string, event: AuthenticationProviderAuthenticationSessionsChangeEvent }>; readonly onDidUpdateSignInCount: Event; - getSessions(providerId: string, scopes?: string[]): Promise>; + getSessions(providerId: string, scopes?: string[], user?: AuthenticationSessionAccountInformation): Promise>; getLabel(providerId: string): string; supportsMultipleAccounts(providerId: string): boolean; - login(providerId: string, scopes: string[]): Promise; + login(providerId: string, scopes: string[], options?: AuthenticationProviderSessionOptions): Promise; logout(providerId: string, sessionId: string): Promise; signOutOfAccount(providerId: string, accountName: string): Promise; @@ -300,7 +299,7 @@ export class AuthenticationServiceImpl implements AuthenticationService { } const previousSize = this.signInRequestItems.size; - const sessions = await provider.getSessions(); + const sessions = await provider.getSessions(undefined); Object.keys(existingRequestsForProvider).forEach(requestedScopes => { if (sessions.some(session => session.scopes.slice().sort().join('') === requestedScopes)) { const sessionRequest = existingRequestsForProvider[requestedScopes]; @@ -411,19 +410,19 @@ export class AuthenticationServiceImpl implements AuthenticationService { } } - async getSessions(id: string, scopes?: string[]): Promise> { + async getSessions(id: string, scopes?: string[], user?: AuthenticationSessionAccountInformation): Promise> { const authProvider = this.authenticationProviders.get(id); if (authProvider) { - return authProvider.getSessions(scopes); + return authProvider.getSessions(scopes, user); } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } } - async login(id: string, scopes: string[]): Promise { + async login(id: string, scopes: string[], options?: AuthenticationProviderSessionOptions): Promise { const authProvider = this.authenticationProviders.get(id); if (authProvider) { - return authProvider.createSession(scopes); + return authProvider.createSession(scopes, options || {}); } else { throw new Error(`No authentication provider '${id}' is currently registered.`); } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index ed117bb1eac19..dab7e870d0251 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -2398,13 +2398,14 @@ export interface TasksMain { } export interface AuthenticationExt { - $getSessions(id: string, scopes?: string[]): Promise>; - $createSession(id: string, scopes: string[]): Promise; + $getSessions(providerId: string, scopes: string[] | undefined, options: theia.AuthenticationProviderSessionOptions): Promise>; + $createSession(id: string, scopes: string[], options: theia.AuthenticationProviderSessionOptions): Promise; $removeSession(id: string, sessionId: string): Promise; $onDidChangeAuthenticationSessions(provider: theia.AuthenticationProviderInformation): Promise; } export interface AuthenticationMain { + $getAccounts(providerId: string): Thenable; $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void; $unregisterAuthenticationProvider(id: string): void; $onDidChangeSessions(providerId: string, event: AuthenticationProviderAuthenticationSessionsChangeEvent): void; diff --git a/packages/plugin-ext/src/main/browser/authentication-main.ts b/packages/plugin-ext/src/main/browser/authentication-main.ts index 65765e457ab98..58f3c4d8c935d 100644 --- a/packages/plugin-ext/src/main/browser/authentication-main.ts +++ b/packages/plugin-ext/src/main/browser/authentication-main.ts @@ -27,7 +27,10 @@ import { MessageService } from '@theia/core/lib/common/message-service'; import { ConfirmDialog, Dialog, StorageService } from '@theia/core/lib/browser'; import { AuthenticationProvider, + AuthenticationProviderSessionOptions, AuthenticationService, + AuthenticationSession, + AuthenticationSessionAccountInformation, readAllowedExtensions } from '@theia/core/lib/browser/authentication-service'; import { QuickPickService } from '@theia/core/lib/common/quick-pick-service'; @@ -77,9 +80,13 @@ export class AuthenticationMainImpl implements AuthenticationMain { return this.authenticationService.requestNewSession(providerId, scopes, extensionId, extensionName); } + $getAccounts(providerId: string): Thenable { + return this.authenticationService.getSessions(providerId).then(sessions => sessions.map(session => session.account)); + } + async $getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: theia.AuthenticationGetSessionOptions): Promise { - const sessions = await this.authenticationService.getSessions(providerId, scopes); + const sessions = await this.authenticationService.getSessions(providerId, scopes, options?.account); // Error cases if (options.forceNewSession && !sessions.length) { @@ -140,26 +147,32 @@ export class AuthenticationMainImpl implements AuthenticationMain { } protected async selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, - potentialSessions: Readonly, scopes: string[], clearSessionPreference: boolean): Promise { + potentialSessions: Readonly, scopes: string[], clearSessionPreference: boolean): Promise { + if (!potentialSessions.length) { throw new Error('No potential sessions found'); } return new Promise(async (resolve, reject) => { - const items: QuickPickValue<{ session?: theia.AuthenticationSession }>[] = potentialSessions.map(session => ({ + const items: QuickPickValue<{ session?: AuthenticationSession, account?: AuthenticationSessionAccountInformation }>[] = potentialSessions.map(session => ({ label: session.account.label, value: { session } })); items.push({ label: nls.localizeByDefault('Sign in to another account'), - value: { session: undefined } + value: {} }); + + // VS Code has code here that pushes accounts that have no active sessions. However, since we do not store + // any accounts that don't have sessions, we dont' do this. const selected = await this.quickPickService.show(items, { title: nls.localizeByDefault("The extension '{0}' wants to access a {1} account", extensionName, providerName), ignoreFocusOut: true }); if (selected) { + + // if we ever have accounts without sessions, pass the account to the login call const session = selected.value?.session ?? await this.authenticationService.login(providerId, scopes); const accountName = session.account.label; @@ -318,13 +331,13 @@ export class AuthenticationProviderImpl implements AuthenticationProvider { } } - async getSessions(scopes?: string[]): Promise> { - return this.proxy.$getSessions(this.id, scopes); + async getSessions(scopes?: string[], account?: AuthenticationSessionAccountInformation): Promise> { + return this.proxy.$getSessions(this.id, scopes, { account: account }); } async updateSessionItems(event: theia.AuthenticationProviderAuthenticationSessionsChangeEvent): Promise { const { added, removed } = event; - const session = await this.proxy.$getSessions(this.id); + const session = await this.proxy.$getSessions(this.id, undefined, {}); const addedSessions = added ? session.filter(s => added.some(addedSession => addedSession.id === s.id)) : []; removed?.forEach(removedSession => { @@ -347,16 +360,16 @@ export class AuthenticationProviderImpl implements AuthenticationProvider { addedSessions.forEach(s => this.registerSession(s)); } - async login(scopes: string[]): Promise { - return this.createSession(scopes); + async login(scopes: string[], options: AuthenticationProviderSessionOptions): Promise { + return this.createSession(scopes, options); } async logout(sessionId: string): Promise { return this.removeSession(sessionId); } - createSession(scopes: string[]): Thenable { - return this.proxy.$createSession(this.id, scopes); + createSession(scopes: string[], options: AuthenticationProviderSessionOptions): Thenable { + return this.proxy.$createSession(this.id, scopes, options); } removeSession(sessionId: string): Thenable { diff --git a/packages/plugin-ext/src/plugin/authentication-ext.ts b/packages/plugin-ext/src/plugin/authentication-ext.ts index 7cf0e3e1c349a..8069406f13daa 100644 --- a/packages/plugin-ext/src/plugin/authentication-ext.ts +++ b/packages/plugin-ext/src/plugin/authentication-ext.ts @@ -57,6 +57,10 @@ export class AuthenticationExtImpl implements AuthenticationExt { return this.proxy.$getSession(providerId, scopes, extensionId, extensionName, options); } + getAccounts(providerId: string): Thenable { + return this.proxy.$getAccounts(providerId); + } + registerAuthenticationProvider(id: string, label: string, provider: theia.AuthenticationProvider, options?: theia.AuthenticationProviderOptions): theia.Disposable { if (this.authenticationProviders.get(id)) { throw new Error(`An authentication provider with id '${id}' is already registered.`); @@ -64,7 +68,7 @@ export class AuthenticationExtImpl implements AuthenticationExt { this.authenticationProviders.set(id, provider); - provider.getSessions().then(sessions => { // sessions might have been restored from secret storage + provider.getSessions(undefined, {}).then(sessions => { // sessions might have been restored from secret storage if (sessions.length > 0) { this.proxy.$onDidChangeSessions(id, { added: sessions, @@ -87,10 +91,10 @@ export class AuthenticationExtImpl implements AuthenticationExt { }); } - $createSession(providerId: string, scopes: string[]): Promise { + $createSession(providerId: string, scopes: string[], options: theia.AuthenticationProviderSessionOptions): Promise { const authProvider = this.authenticationProviders.get(providerId); if (authProvider) { - return Promise.resolve(authProvider.createSession(scopes)); + return Promise.resolve(authProvider.createSession(scopes, options)); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); @@ -105,10 +109,10 @@ export class AuthenticationExtImpl implements AuthenticationExt { throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - async $getSessions(providerId: string, scopes?: string[]): Promise> { + async $getSessions(providerId: string, scopes: string[] | undefined, options: theia.AuthenticationProviderSessionOptions): Promise> { const authProvider = this.authenticationProviders.get(providerId); if (authProvider) { - const sessions = await authProvider.getSessions(scopes); + const sessions = await authProvider.getSessions(scopes, options); /* Wrap the session object received from the plugin to prevent serialization mismatches e.g. if the plugin object is constructed with the help of getters they won't be serialized: diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 8a36492244edc..e4bfae6643065 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -338,6 +338,9 @@ export function createAPIFactory( }, get onDidChangeSessions(): theia.Event { return authenticationExt.onDidChangeSessions; + }, + getAccounts(providerId: string): Thenable { + return authenticationExt.getAccounts(providerId); } }; function commandIsDeclaredInPackage(id: string, model: PluginPackage): boolean { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 4ce0f20f46c2a..14cf710ea9f12 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -14084,6 +14084,11 @@ export module '@theia/plugin' { * Note: you cannot use this option with any other options that prompt the user like {@link createIfNone}. */ silent?: boolean; + + /** + * The account that you would like to get a session for. This is passed down to the Authentication Provider to be used for creating the correct session. + */ + account?: AuthenticationSessionAccountInformation; } /** @@ -14144,6 +14149,18 @@ export module '@theia/plugin' { readonly changed: readonly AuthenticationSession[] | undefined; } + /** + * The options passed in to the {@link AuthenticationProvider.getSessions} and + * {@link AuthenticationProvider.createSession} call. + */ + export interface AuthenticationProviderSessionOptions { + /** + * The account that is being asked about. If this is passed in, the provider should + * attempt to return the sessions that are only related to this account. + */ + account?: AuthenticationSessionAccountInformation; + } + /** * A provider for performing authentication to a service. */ @@ -14158,9 +14175,10 @@ export module '@theia/plugin' { * Get a list of sessions. * @param scopes An optional list of scopes. If provided, the sessions returned should match * these permissions, otherwise all sessions should be returned. + * @param options Additional options for getting sessions. * @returns A promise that resolves to an array of authentication sessions. */ - getSessions(scopes?: readonly string[]): Thenable; + getSessions(scopes: readonly string[] | undefined, options: AuthenticationProviderSessionOptions): Thenable; /** * Prompts a user to login. @@ -14173,9 +14191,10 @@ export module '@theia/plugin' { * then this should never be called if there is already an existing session matching these * scopes. * @param scopes A list of scopes, permissions, that the new session should be created with. + * @param options Additional options for creating a session. * @returns A promise that resolves to an authentication session. */ - createSession(scopes: readonly string[]): Thenable; + createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Thenable; /** * Removes the session corresponding to session id. @@ -14235,6 +14254,20 @@ export module '@theia/plugin' { */ export function getSession(providerId: string, scopes: readonly string[], options?: AuthenticationGetSessionOptions): Thenable; + /** + * Get all accounts that the user is logged in to for the specified provider. + * Use this paired with {@link getSession} in order to get an authentication session for a specific account. + * + * Currently, there are only two authentication providers that are contributed from built in extensions + * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. + * + * Note: Getting accounts does not imply that your extension has access to that account or its authentication sessions. You can verify access to the account by calling {@link getSession}. + * + * @param providerId The id of the provider to use + * @returns A thenable that resolves to a readonly array of authentication accounts. + */ + export function getAccounts(providerId: string): Thenable; + /** * An {@link Event event} which fires when the authentication sessions of an authentication provider have * been added, removed, or changed. From bdd63e456e232ae19270353eb688fa8872cd8d26 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Fri, 13 Sep 2024 19:14:46 +0200 Subject: [PATCH 369/441] Consistent prompt ids (#14162) fixed #14161 Signed-off-by: Jonas Helming --- packages/ai-chat/src/common/command-chat-agents.ts | 8 ++++---- packages/ai-chat/src/common/orchestrator-chat-agent.ts | 8 ++++---- packages/ai-chat/src/common/universal-chat-agent.ts | 8 ++++---- packages/ai-terminal/src/browser/ai-terminal-agent.ts | 8 ++++---- .../ai-workspace-agent/src/browser/workspace-agent.ts | 6 +++--- packages/ai-workspace-agent/src/common/template.ts | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/ai-chat/src/common/command-chat-agents.ts b/packages/ai-chat/src/common/command-chat-agents.ts index 0aac25e178f12..6f3a38d89ece0 100644 --- a/packages/ai-chat/src/common/command-chat-agents.ts +++ b/packages/ai-chat/src/common/command-chat-agents.ts @@ -33,8 +33,8 @@ import { generateUuid, } from '@theia/core'; -export const commandChatAgentSystemPromptTemplate: PromptTemplate = { - id: 'command-chat-agent-system-prompt-template', +export const commandTemplate: PromptTemplate = { + id: 'command-system', template: `# System Prompt You are a service that helps users find commands to execute in an IDE. @@ -267,7 +267,7 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent { @@ -275,7 +275,7 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent { - const resolvedPrompt = await this.promptService.getPrompt(delegateTemplate.id); + const resolvedPrompt = await this.promptService.getPrompt(orchestratorTemplate.id); return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; } diff --git a/packages/ai-chat/src/common/universal-chat-agent.ts b/packages/ai-chat/src/common/universal-chat-agent.ts index 703a61820e25d..32706aedf802b 100644 --- a/packages/ai-chat/src/common/universal-chat-agent.ts +++ b/packages/ai-chat/src/common/universal-chat-agent.ts @@ -20,8 +20,8 @@ import { import { injectable } from '@theia/core/shared/inversify'; import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; -export const defaultTemplate: PromptTemplate = { - id: 'default-template', +export const universalTemplate: PromptTemplate = { + id: 'universal-system', template: `# Instructions You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by @@ -93,11 +93,11 @@ export class UniversalChatAgent extends AbstractStreamParsingChatAgent implement + 'questions the user might ask. The universal agent currently does not have any context by default, i.e. it cannot ' + 'access the current user context or the workspace.'; this.variables = []; - this.promptTemplates = [defaultTemplate]; + this.promptTemplates = [universalTemplate]; } protected override async getSystemMessageDescription(): Promise { - const resolvedPrompt = await this.promptService.getPrompt(defaultTemplate.id); + const resolvedPrompt = await this.promptService.getPrompt(universalTemplate.id); return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; } diff --git a/packages/ai-terminal/src/browser/ai-terminal-agent.ts b/packages/ai-terminal/src/browser/ai-terminal-agent.ts index 54d51d22ef3ae..fdc911c50a3ed 100644 --- a/packages/ai-terminal/src/browser/ai-terminal-agent.ts +++ b/packages/ai-terminal/src/browser/ai-terminal-agent.ts @@ -42,7 +42,7 @@ export class AiTerminalAgent implements Agent { variables = []; promptTemplates = [ { - id: 'ai-terminal:system-prompt', + id: 'terminal-system', name: 'AI Terminal System Prompt', description: 'Prompt for the AI Terminal Assistant', template: ` @@ -89,7 +89,7 @@ nothing to commit, working tree clean ` }, { - id: 'ai-terminal:user-prompt', + id: 'terminal-user', name: 'AI Terminal User Prompt', description: 'Prompt that contains the user request', template: ` @@ -139,8 +139,8 @@ recent-terminal-contents: recentTerminalContents }; - const systemPrompt = await this.promptService.getPrompt('ai-terminal:system-prompt', parameters).then(p => p?.text); - const userPrompt = await this.promptService.getPrompt('ai-terminal:user-prompt', parameters).then(p => p?.text); + const systemPrompt = await this.promptService.getPrompt('terminal-system', parameters).then(p => p?.text); + const userPrompt = await this.promptService.getPrompt('terminal-user', parameters).then(p => p?.text); if (!systemPrompt || !userPrompt) { this.logger.error('The prompt service didn\'t return prompts for the AI Terminal Agent.'); return []; diff --git a/packages/ai-workspace-agent/src/browser/workspace-agent.ts b/packages/ai-workspace-agent/src/browser/workspace-agent.ts index 2e59f45059c9a..2fd47fbd260e1 100644 --- a/packages/ai-workspace-agent/src/browser/workspace-agent.ts +++ b/packages/ai-workspace-agent/src/browser/workspace-agent.ts @@ -16,7 +16,7 @@ import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from '@theia/ai-chat/lib/common'; import { PromptTemplate, ToolInvocationRegistry } from '@theia/ai-core'; import { inject, injectable } from '@theia/core/shared/inversify'; -import { template } from '../common/template'; +import { workspaceTemplate } from '../common/template'; @injectable() export class WorkspaceAgent extends AbstractStreamParsingChatAgent implements ChatAgent { @@ -37,12 +37,12 @@ export class WorkspaceAgent extends AbstractStreamParsingChatAgent implements Ch this.description = 'This agent can access the users workspace, it can get a list of all available files and retrieve their content. \ It can therefore answer questions about the current project, project files and source code in the workspace, such as how to build the project, \ where to put source code, where to find specific code or configurations, etc.'; - this.promptTemplates = [template]; + this.promptTemplates = [workspaceTemplate]; this.variables = []; } protected override async getSystemMessageDescription(): Promise { - const resolvedPrompt = await this.promptService.getPrompt(template.id); + const resolvedPrompt = await this.promptService.getPrompt(workspaceTemplate.id); return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; } } diff --git a/packages/ai-workspace-agent/src/common/template.ts b/packages/ai-workspace-agent/src/common/template.ts index 4d59d7d6d9aef..ec825d90a98df 100644 --- a/packages/ai-workspace-agent/src/common/template.ts +++ b/packages/ai-workspace-agent/src/common/template.ts @@ -16,8 +16,8 @@ import { PromptTemplate } from '@theia/ai-core/lib/common'; import { GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID } from './functions'; -export const template = { - id: 'workspace-prompt', +export const workspaceTemplate = { + id: 'workspace-system', template: `# Instructions You are an AI assistant integrated into the Theia IDE, specifically designed to help software developers by From e1aeb7c2c59822e5393fcd657e50124d60ea1901 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Sun, 15 Sep 2024 20:44:00 +0200 Subject: [PATCH 370/441] Adapt default LLM for Theia AI to gpt-4o (#14165) Add gpt-4o-2024-05-13 as an option fixed #14164 Signed-off-by: Jonas Helming --- packages/ai-openai/src/browser/openai-preferences.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai-openai/src/browser/openai-preferences.ts b/packages/ai-openai/src/browser/openai-preferences.ts index e57915e36068f..49f2d29840ea4 100644 --- a/packages/ai-openai/src/browser/openai-preferences.ts +++ b/packages/ai-openai/src/browser/openai-preferences.ts @@ -31,7 +31,7 @@ export const OpenAiPreferencesSchema: PreferenceSchema = { [MODELS_PREF]: { type: 'array', title: AI_CORE_PREFERENCES_TITLE, - default: ['gpt-4o-2024-08-06', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], + default: ['gpt-4o', 'gpt-4o-2024-08-06', 'gpt-4o-2024-05-13', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'], items: { type: 'string' } From beabfbafd6c02976a7c4dd84367b3c2fd4f3d137 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Mon, 16 Sep 2024 09:23:02 +0200 Subject: [PATCH 371/441] fix: integrate AI preferencex context key The ai-core package offers a preference context key, which can be used in scenarios like 'when' clauses for menus. This key was not integrated, leading to the deactivation of all dependent features. This is now fixed. --- packages/ai-core/src/browser/ai-activation-service.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ai-core/src/browser/ai-activation-service.ts b/packages/ai-core/src/browser/ai-activation-service.ts index 0bc83ec002bde..c26c8c1d15e53 100644 --- a/packages/ai-core/src/browser/ai-activation-service.ts +++ b/packages/ai-core/src/browser/ai-activation-service.ts @@ -19,7 +19,11 @@ import { Emitter, MaybePromise, Event, } from '@theia/core'; import { ContextKeyService, ContextKey } from '@theia/core/lib/browser/context-key-service'; import { PREFERENCE_NAME_ENABLE_EXPERIMENTAL } from './ai-core-preferences'; -export const EXPERIMENTAL_AI_CONTEXT_KEY = 'ai.experimental.enabled'; +/** + * Context key for the experimental AI feature. It is set to `true` if the feature is enabled. + */ +// We reuse the enablement preference for the context key +export const EXPERIMENTAL_AI_CONTEXT_KEY = PREFERENCE_NAME_ENABLE_EXPERIMENTAL; @injectable() export class AIActivationService implements FrontendApplicationContribution { @@ -41,7 +45,7 @@ export class AIActivationService implements FrontendApplicationContribution { } initialize(): MaybePromise { - this.isExperimentalEnabledKey = this.contextKeyService.createKey(PREFERENCE_NAME_ENABLE_EXPERIMENTAL, false); + this.isExperimentalEnabledKey = this.contextKeyService.createKey(EXPERIMENTAL_AI_CONTEXT_KEY, false); this.preferenceService.onPreferenceChanged(e => { if (e.preferenceName === PREFERENCE_NAME_ENABLE_EXPERIMENTAL) { this.isExperimentalEnabledKey.set(e.newValue); From 136419172d13d52c05ec511687cc1606784951af Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 16 Sep 2024 12:29:30 +0200 Subject: [PATCH 372/441] Support `workbench.editorAssociations` preference (#14139) --- packages/core/src/browser/core-preferences.ts | 9 +++ .../core/src/browser/open-with-service.ts | 61 ++++++++++++++++--- packages/core/src/browser/opener-service.ts | 13 ++++ packages/core/src/common/glob.ts | 8 +-- packages/editor/src/browser/editor-manager.ts | 13 +++- .../src/browser/notebook-open-handler.ts | 20 +++--- .../custom-editors/custom-editor-opener.tsx | 23 +++++-- .../plugin-custom-editor-registry.ts | 10 ++- 8 files changed, 129 insertions(+), 28 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 04678d4888cb5..c9827c5aaf490 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -281,6 +281,15 @@ export const corePreferenceSchema: PreferenceSchema = { default: 200, minimum: 10, description: nls.localize('theia/core/tabDefaultSize', 'Specifies the default size for tabs.') + }, + 'workbench.editorAssociations': { + type: 'object', + markdownDescription: nls.localizeByDefault('Configure [glob patterns](https://aka.ms/vscode-glob-patterns) to editors (for example `"*.hex": "hexEditor.hexedit"`). These have precedence over the default behavior.'), + patternProperties: { + '.*': { + type: 'string' + } + } } } }; diff --git a/packages/core/src/browser/open-with-service.ts b/packages/core/src/browser/open-with-service.ts index c186feb34d559..1e347ca88301b 100644 --- a/packages/core/src/browser/open-with-service.ts +++ b/packages/core/src/browser/open-with-service.ts @@ -19,7 +19,9 @@ import { Disposable } from '../common/disposable'; import { nls } from '../common/nls'; import { MaybePromise } from '../common/types'; import { URI } from '../common/uri'; -import { QuickInputService } from './quick-input'; +import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator } from './quick-input'; +import { PreferenceScope, PreferenceService } from './preferences'; +import { getDefaultHandler } from './opener-service'; export interface OpenWithHandler { /** @@ -46,6 +48,11 @@ export interface OpenWithHandler { * A returned value indicating a priority of this handler. */ canHandle(uri: URI): number; + /** + * Test whether this handler and open the given URI + * and return the order of this handler in the list. + */ + getOrder?(uri: URI): number; /** * Open a widget for the given URI and options. * Resolve to an opened widget or undefined, e.g. if a page is opened. @@ -54,12 +61,19 @@ export interface OpenWithHandler { open(uri: URI): MaybePromise; } +export interface OpenWithQuickPickItem extends QuickPickItem { + handler: OpenWithHandler; +} + @injectable() export class OpenWithService { @inject(QuickInputService) protected readonly quickInputService: QuickInputService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + protected readonly handlers: OpenWithHandler[] = []; registerHandler(handler: OpenWithHandler): Disposable { @@ -73,17 +87,50 @@ export class OpenWithService { } async openWith(uri: URI): Promise { + // Clone the object, because all objects returned by the preferences service are frozen. + const associations: Record = { ...this.preferenceService.get('workbench.editorAssociations') }; + const ext = `*${uri.path.ext}`; const handlers = this.getHandlers(uri); - const result = await this.quickInputService.pick(handlers.map(handler => ({ - handler: handler, - label: handler.label ?? handler.id, - detail: handler.providerName - })), { + const ordered = handlers.slice().sort((a, b) => this.getOrder(b, uri) - this.getOrder(a, uri)); + const defaultHandler = getDefaultHandler(uri, this.preferenceService) ?? handlers[0]?.id; + const items = this.getQuickPickItems(ordered, defaultHandler); + // Only offer to select a default editor when the file has a file extension + const extraItems: QuickPickItemOrSeparator[] = uri.path.ext ? [{ + type: 'separator' + }, { + label: nls.localizeByDefault("Configure default editor for '{0}'...", ext) + }] : []; + const result = await this.quickInputService.pick([...items, ...extraItems], { placeHolder: nls.localizeByDefault("Select editor for '{0}'", uri.path.base) }); if (result) { - return result.handler.open(uri); + if ('handler' in result) { + return result.handler.open(uri); + } else if (result.label) { + const configureResult = await this.quickInputService.pick(items, { + placeHolder: nls.localizeByDefault("Select new default editor for '{0}'", ext) + }); + if (configureResult) { + associations[ext] = configureResult.handler.id; + this.preferenceService.set('workbench.editorAssociations', associations, PreferenceScope.User); + return configureResult.handler.open(uri); + } + } } + return undefined; + } + + protected getQuickPickItems(handlers: OpenWithHandler[], defaultHandler?: string): OpenWithQuickPickItem[] { + return handlers.map(handler => ({ + handler, + label: handler.label ?? handler.id, + detail: handler.providerName ?? '', + description: handler.id === defaultHandler ? nls.localizeByDefault('Default') : undefined + })); + } + + protected getOrder(handler: OpenWithHandler, uri: URI): number { + return handler.getOrder ? handler.getOrder(uri) : handler.canHandle(uri); } getHandlers(uri: URI): OpenWithHandler[] { diff --git a/packages/core/src/browser/opener-service.ts b/packages/core/src/browser/opener-service.ts index d4f58f44a6fac..f8362fa4cc747 100644 --- a/packages/core/src/browser/opener-service.ts +++ b/packages/core/src/browser/opener-service.ts @@ -17,6 +17,8 @@ import { named, injectable, inject } from 'inversify'; import URI from '../common/uri'; import { ContributionProvider, Prioritizeable, MaybePromise, Emitter, Event, Disposable } from '../common'; +import { PreferenceService } from './preferences'; +import { match } from '../common/glob'; export interface OpenerOptions { } @@ -96,6 +98,17 @@ export async function open(openerService: OpenerService, uri: URI, options?: Ope return opener.open(uri, options); } +export function getDefaultHandler(uri: URI, preferenceService: PreferenceService): string | undefined { + const associations = preferenceService.get('workbench.editorAssociations', {}); + const defaultHandler = Object.entries(associations).find(([key]) => match(key, uri.path.base))?.[1]; + if (typeof defaultHandler === 'string') { + return defaultHandler; + } + return undefined; +} + +export const defaultHandlerPriority = 100_000; + @injectable() export class DefaultOpenerService implements OpenerService { // Collection of open-handlers for custom-editor contributions. diff --git a/packages/core/src/common/glob.ts b/packages/core/src/common/glob.ts index d32394ec90191..1676fc4234e45 100644 --- a/packages/core/src/common/glob.ts +++ b/packages/core/src/common/glob.ts @@ -454,10 +454,10 @@ function toRegExp(pattern: string): ParsedStringPattern { /** * Simplified glob matching. Supports a subset of glob patterns: - * - * matches anything inside a path segment - * - ? matches 1 character inside a path segment - * - ** matches anything including an empty path segment - * - simple brace expansion ({js,ts} => js or ts) + * - `*` matches anything inside a path segment + * - `?` matches 1 character inside a path segment + * - `**` matches anything including an empty path segment + * - simple brace expansion (`{js,ts}` => js or ts) * - character ranges (using [...]) */ export function match(pattern: string | IRelativePattern, path: string): boolean; diff --git a/packages/editor/src/browser/editor-manager.ts b/packages/editor/src/browser/editor-manager.ts index 3545725b893ab..8e2288e7f358b 100644 --- a/packages/editor/src/browser/editor-manager.ts +++ b/packages/editor/src/browser/editor-manager.ts @@ -17,7 +17,10 @@ import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { RecursivePartial, Emitter, Event, MaybePromise, CommandService, nls } from '@theia/core/lib/common'; -import { WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands, OpenWithService } from '@theia/core/lib/browser'; +import { + WidgetOpenerOptions, NavigatableWidgetOpenHandler, NavigatableWidgetOptions, Widget, PreferenceService, CommonCommands, OpenWithService, getDefaultHandler, + defaultHandlerPriority +} from '@theia/core/lib/browser'; import { EditorWidget } from './editor-widget'; import { Range, Position, Location, TextEditor } from './editor'; import { EditorWidgetFactory } from './editor-widget-factory'; @@ -86,12 +89,13 @@ export class EditorManager extends NavigatableWidgetOpenHandler { } } this.openWithService.registerHandler({ - id: this.id, + id: 'default', label: this.label, providerName: nls.localizeByDefault('Built-in'), + canHandle: () => 100, // Higher priority than any other handler // so that the text editor always appears first in the quick pick - canHandle: uri => this.canHandle(uri) * 100, + getOrder: () => 10000, open: uri => this.open(uri) }); this.updateCurrentEditor(); @@ -198,6 +202,9 @@ export class EditorManager extends NavigatableWidgetOpenHandler { } canHandle(uri: URI, options?: WidgetOpenerOptions): number { + if (getDefaultHandler(uri, this.preferenceService) === 'default') { + return defaultHandlerPriority; + } return 100; } diff --git a/packages/notebook/src/browser/notebook-open-handler.ts b/packages/notebook/src/browser/notebook-open-handler.ts index d1f1ab307ef1a..80181f7a1a6fd 100644 --- a/packages/notebook/src/browser/notebook-open-handler.ts +++ b/packages/notebook/src/browser/notebook-open-handler.ts @@ -15,8 +15,8 @@ // ***************************************************************************** import { URI, MaybePromise, Disposable } from '@theia/core'; -import { NavigatableWidgetOpenHandler, WidgetOpenerOptions } from '@theia/core/lib/browser'; -import { injectable } from '@theia/core/shared/inversify'; +import { NavigatableWidgetOpenHandler, PreferenceService, WidgetOpenerOptions, getDefaultHandler, defaultHandlerPriority } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; import { NotebookFileSelector, NotebookTypeDescriptor } from '../common/notebook-protocol'; import { NotebookEditorWidget } from './notebook-editor-widget'; import { match } from '@theia/core/lib/common/glob'; @@ -33,6 +33,9 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler { @@ -41,15 +44,16 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler { + const defaultHandler = getDefaultHandler(uri, this.preferenceService); if (options?.notebookType) { - return this.canHandleType(uri, this.notebookTypes.find(type => type.type === options.notebookType)); + return this.canHandleType(uri, this.notebookTypes.find(type => type.type === options.notebookType), defaultHandler); } - return Math.max(...this.notebookTypes.map(type => this.canHandleType(uri, type))); + return Math.max(...this.notebookTypes.map(type => this.canHandleType(uri, type), defaultHandler)); } - canHandleType(uri: URI, notebookType?: NotebookTypeDescriptor): number { + canHandleType(uri: URI, notebookType?: NotebookTypeDescriptor, defaultHandler?: string): number { if (notebookType?.selector && this.matches(notebookType.selector, uri)) { - return this.calculatePriority(notebookType); + return notebookType.type === defaultHandler ? defaultHandlerPriority : this.calculatePriority(notebookType); } else { return 0; } @@ -93,7 +97,9 @@ export class NotebookOpenHandler extends NavigatableWidgetOpenHandler type.type === defaultHandler) + || this.findHighestPriorityType(uri); if (!notebookType) { throw new Error('No notebook types registered for uri: ' + uri.toString()); } diff --git a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx index 3671fd12b2687..4c9746a9f5914 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx +++ b/packages/plugin-ext/src/main/browser/custom-editors/custom-editor-opener.tsx @@ -15,7 +15,9 @@ // ***************************************************************************** import URI from '@theia/core/lib/common/uri'; -import { ApplicationShell, DiffUris, OpenHandler, SplitWidget, Widget, WidgetManager, WidgetOpenerOptions } from '@theia/core/lib/browser'; +import { + ApplicationShell, DiffUris, OpenHandler, OpenerOptions, PreferenceService, SplitWidget, Widget, WidgetManager, WidgetOpenerOptions, getDefaultHandler, defaultHandlerPriority +} from '@theia/core/lib/browser'; import { CustomEditor, CustomEditorPriority, CustomEditorSelector } from '../../../common'; import { CustomEditorWidget } from './custom-editor-widget'; import { PluginCustomEditorRegistry } from './plugin-custom-editor-registry'; @@ -35,7 +37,8 @@ export class CustomEditorOpener implements OpenHandler { private readonly editor: CustomEditor, protected readonly shell: ApplicationShell, protected readonly widgetManager: WidgetManager, - protected readonly editorRegistry: PluginCustomEditorRegistry + protected readonly editorRegistry: PluginCustomEditorRegistry, + protected readonly preferenceService: PreferenceService ) { this.id = CustomEditorOpener.toCustomEditorId(this.editor.viewType); this.label = this.editor.displayName; @@ -45,14 +48,26 @@ export class CustomEditorOpener implements OpenHandler { return `custom-editor-${editorViewType}`; } - canHandle(uri: URI): number { + canHandle(uri: URI, options?: OpenerOptions): number { + let priority = 0; const { selector } = this.editor; if (DiffUris.isDiffUri(uri)) { const [left, right] = DiffUris.decode(uri); if (this.matches(selector, right) && this.matches(selector, left)) { - return this.getPriority(); + priority = this.getPriority(); } } else if (this.matches(selector, uri)) { + if (getDefaultHandler(uri, this.preferenceService) === this.editor.viewType) { + priority = defaultHandlerPriority; + } else { + priority = this.getPriority(); + } + } + return priority; + } + + canOpenWith(uri: URI): number { + if (this.matches(this.editor.selector, uri)) { return this.getPriority(); } return 0; diff --git a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts index 7250e3f4c0908..27738141688cf 100644 --- a/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts +++ b/packages/plugin-ext/src/main/browser/custom-editors/plugin-custom-editor-registry.ts @@ -20,7 +20,7 @@ import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposa import { Deferred } from '@theia/core/lib/common/promise-util'; import { CustomEditorOpener } from './custom-editor-opener'; import { Emitter } from '@theia/core'; -import { ApplicationShell, DefaultOpenerService, OpenWithService, WidgetManager } from '@theia/core/lib/browser'; +import { ApplicationShell, DefaultOpenerService, OpenWithService, PreferenceService, WidgetManager } from '@theia/core/lib/browser'; import { CustomEditorWidget } from './custom-editor-widget'; @injectable() @@ -44,6 +44,9 @@ export class PluginCustomEditorRegistry { @inject(OpenWithService) protected readonly openWithService: OpenWithService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + @postConstruct() protected init(): void { this.widgetManager.onDidCreateWidget(({ factoryId, widget }) => { @@ -76,7 +79,8 @@ export class PluginCustomEditorRegistry { editor, this.shell, this.widgetManager, - this + this, + this.preferenceService ); toDispose.push(this.defaultOpenerService.addHandler(editorOpenHandler)); toDispose.push( @@ -84,7 +88,7 @@ export class PluginCustomEditorRegistry { id: editor.viewType, label: editorOpenHandler.label, providerName: plugin.metadata.model.displayName, - canHandle: uri => editorOpenHandler.canHandle(uri), + canHandle: uri => editorOpenHandler.canOpenWith(uri), open: uri => editorOpenHandler.open(uri) }) ); From 5abefebb740b0797e73bd1de192b8151c5db42b4 Mon Sep 17 00:00:00 2001 From: Remi Schnekenburger Date: Mon, 16 Sep 2024 14:23:11 +0200 Subject: [PATCH 373/441] Move TerminalShellIntegration stubs to main theia API (#14168) fixes #14107 contributed on behalf of STMicroelectronics Signed-off-by: Remi Schnekenburger --- CHANGELOG.md | 2 + .../plugin-ext/src/plugin/terminal-ext.ts | 1 + packages/plugin/src/theia.d.ts | 363 +++++++++++++++++- ...eia.proposed.terminalShellIntegration.d.ts | 329 ---------------- 4 files changed, 365 insertions(+), 330 deletions(-) delete mode 100644 packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 320f08d4d921f..913dce7b1602f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - [core] Updated AuthenticationService to handle multiple accounts per provider [#14149](https://github.com/eclipse-theia/theia/pull/14149) - Contributed on behalf of STMicroelectronics diff --git a/packages/plugin-ext/src/plugin/terminal-ext.ts b/packages/plugin-ext/src/plugin/terminal-ext.ts index 3e450bc88a329..ba849bc9fb305 100644 --- a/packages/plugin-ext/src/plugin/terminal-ext.ts +++ b/packages/plugin-ext/src/plugin/terminal-ext.ts @@ -478,6 +478,7 @@ export class TerminalExtImpl implements theia.Terminal { this.creationOptions = this.options; } + /** @stubbed Terminal Shell Ingration */ shellIntegration: theia.TerminalShellIntegration | undefined = undefined; sendText(text: string, shouldExecute: boolean = true): void { diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 14cf710ea9f12..722ec2eea11fe 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -43,7 +43,6 @@ import './theia.proposed.resolvers'; import './theia.proposed.scmValidation'; import './theia.proposed.shareProvider'; import './theia.proposed.terminalQuickFixProvider'; -import './theia.proposed.terminalShellIntegration'; import './theia.proposed.textSearchProvider'; import './theia.proposed.timeline'; @@ -3058,6 +3057,19 @@ export module '@theia/plugin' { */ readonly state: TerminalState; + /** + * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered + * features for the terminal. This will always be `undefined` immediately after the terminal + * is created. Listen to {@link window.onDidChangeTerminalShellIntegration} to be notified + * when shell integration is activated for a terminal. + * + * Note that this object may remain undefined if shell integation never activates. For + * example Command Prompt does not support shell integration and a user's shell setup could + * conflict with the automatic shell integration activation. + * @stubbed + */ + readonly shellIntegration: TerminalShellIntegration | undefined; + /** * Send text to the terminal. * @param text - The text to send. @@ -3101,6 +3113,333 @@ export module '@theia/plugin' { readonly isInteractedWith: boolean; } + /** + * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. + * @stubbed + */ + export interface TerminalShellIntegration { + /** + * The current working directory of the terminal. This {@link Uri} may represent a file on + * another machine (eg. ssh into another machine). This requires the shell integration to + * support working directory reporting. + * @stubbed + */ + readonly cwd: Uri | undefined; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * @param commandLine The command line to execute, this is the exact text that will be sent + * to the terminal. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidChangeTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const execution = shellIntegration.executeCommand('echo "Hello world"'); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const execution = shellIntegration.executeCommand({ commandLine }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * console.log(`Command exited with code ${event.exitCode}`); + * } + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * @stubbed + */ + executeCommand(commandLine: string): TerminalShellExecution; + + /** + * Execute a command, sending ^C as necessary to interrupt any running command if needed. + * + * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) + * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to + * verify whether it was successful. + * + * @param command A command to run. + * @param args Arguments to launch the executable with which will be automatically escaped + * based on the executable type. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { + * if (terminal === myTerm) { + * const command = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } + * })); + * // Fallback to sendText if there is no shell integration within 3 seconds of launching + * setTimeout(() => { + * if (!myTerm.shellIntegration) { + * myTerm.sendText('echo "Hello world"'); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * }, 3000); + * + * @example + * // Send command to terminal that has been alive for a while + * const commandLine = 'echo "Hello world"'; + * if (term.shellIntegration) { + * const command = term.shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * const code = await command.exitCode; + * console.log(`Command exited with code ${code}`); + * } else { + * term.sendText(commandLine); + * // Without shell integration, we can't know when the command has finished or what the + * // exit code was. + * } + * @stubbed + */ + executeCommand(executable: string, args: string[]): TerminalShellExecution; + } + + /** + * A command that was executed in a terminal. + * @stubbed + */ + export interface TerminalShellExecution { + /** + * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} + * of this value depends on the specific shell's shell integration implementation. This + * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is + * fired. + * + * @example + * // Log the details of the command line on start and end + * window.onDidStartTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command started\n${summarizeCommandLine(commandLine)}`); + * }); + * window.onDidEndTerminalShellExecution(event => { + * const commandLine = event.execution.commandLine; + * console.log(`Command ended\n${summarizeCommandLine(commandLine)}`); + * }); + * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { + * return [ + * ` Command line: ${command.commandLine.value}`, + * ` Confidence: ${command.commandLine.confidence}`, + * ` Trusted: ${command.commandLine.isTrusted} + * ].join('\n'); + * } + * @stubbed + */ + readonly commandLine: TerminalShellExecutionCommandLine; + + /** + * The working directory that was reported by the shell when this command executed. This + * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This + * requires the shell integration to support working directory reporting. + * @stubbed + */ + readonly cwd: Uri | undefined; + + /** + * Creates a stream of raw data (including escape sequences) that is written to the + * terminal. This will only include data that was written after `read` was called for + * the first time, ie. you must call `read` immediately after the command is executed via + * {@link TerminalShellIntegration.executeCommand} or + * {@link window.onDidStartTerminalShellExecution} to not miss any data. + * + * @example + * // Log all data written to the terminal for a command + * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); + * const stream = command.read(); + * for await (const data of stream) { + * console.log(data); + * } + * @stubbed + */ + read(): AsyncIterable; + } + + /** + * A command line that was executed in a terminal. + * @stubbed + */ + export interface TerminalShellExecutionCommandLine { + /** + * The full command line that was executed, including both the command and its arguments. + * @stubbed + */ + readonly value: string; + + /** + * Whether the command line value came from a trusted source and is therefore safe to + * execute without user additional confirmation, such as a notification that asks "Do you + * want to execute (command)?". This verification is likely only needed if you are going to + * execute the command again. + * + * This is `true` only when the command line was reported explicitly by the shell + * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) + * and it used a nonce for verification. + * @stubbed + */ + readonly isTrusted: boolean; + + /** + * The confidence of the command line value which is determined by how the value was + * obtained. This depends upon the implementation of the shell integration script. + * @stubbed + */ + readonly confidence: TerminalShellExecutionCommandLineConfidence; + } + + /** + * The confidence of a {@link TerminalShellExecutionCommandLine} value. + */ + enum TerminalShellExecutionCommandLineConfidence { + /** + * The command line value confidence is low. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. Additionally one + * of the following conditions will be met: + * + * - The command started on the very left-most column which is unusual, or + * - The command is multi-line which is more difficult to accurately detect due to line + * continuation characters and right prompts. + * - Command line markers were not reported by the shell integration script. + */ + Low = 0, + + /** + * The command line value confidence is medium. This means that the value was read from the + * terminal buffer using markers reported by the shell integration script. The command is + * single-line and does not start on the very left-most column (which is unusual). + */ + Medium = 1, + + /** + * The command line value confidence is high. This means that the value was explicitly sent + * from the shell integration script or the command was executed via the + * {@link TerminalShellIntegration.executeCommand} API. + */ + High = 2 + } + + /** + * An event signalling that a terminal's shell integration has changed. + * @stubbed + */ + export interface TerminalShellIntegrationChangeEvent { + /** + * The terminal that shell integration has been activated in. + * @stubbed + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + * @stubbed + */ + readonly shellIntegration: TerminalShellIntegration; + } + + /** + * An event signalling that an execution has started in a terminal. + * @stubbed + */ + export interface TerminalShellExecutionStartEvent { + /** + * The terminal that shell integration has been activated in. + * @stubbed + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + * @stubbed + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + * @stubbed + */ + readonly execution: TerminalShellExecution; + } + + /** + * An event signalling that an execution has ended in a terminal. + * @stubbed + */ + export interface TerminalShellExecutionEndEvent { + /** + * The terminal that shell integration has been activated in. + * @stubbed + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + * @stubbed + */ + readonly shellIntegration: TerminalShellIntegration; + + /** + * The terminal shell execution that has ended. + * @stubbed + */ + readonly execution: TerminalShellExecution; + + /** + * The exit code reported by the shell. + * + * Note that `undefined` means the shell either did not report an exit code (ie. the shell + * integration script is misbehaving) or the shell reported a command started before the command + * finished (eg. a sub-shell was opened). Generally this should not happen, depending on the use + * case, it may be best to treat this as a failure. + * + * @example + * const execution = shellIntegration.executeCommand({ + * command: 'echo', + * args: ['Hello world'] + * }); + * window.onDidEndTerminalShellExecution(event => { + * if (event.execution === execution) { + * if (event.exitCode === undefined) { + * console.log('Command finished but exit code is unknown'); + * } else if (event.exitCode === 0) { + * console.log('Command succeeded'); + * } else { + * console.log('Command failed'); + * } + * } + * }); + * @stubbed + */ + readonly exitCode: number | undefined; + } + /** * Options to create terminal widget. */ @@ -5500,6 +5839,28 @@ export module '@theia/plugin' { */ export const onDidChangeTerminalState: Event; + /** + * Fires when shell integration activates or one of its properties changes in a terminal. + * @stubbed + */ + export const onDidChangeTerminalShellIntegration: Event; + + /** + * This will be fired when a terminal command is started. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + * @stubbed + */ + export const onDidStartTerminalShellExecution: Event; + + /** + * This will be fired when a terminal command is ended. This event will fire only when + * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is + * activated for the terminal. + * @stubbed + */ + export const onDidEndTerminalShellExecution: Event; + /** * Create new terminal with predefined options. * @param - terminal options. diff --git a/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts b/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts deleted file mode 100644 index e9f945fb87e20..0000000000000 --- a/packages/plugin/src/theia.proposed.terminalShellIntegration.d.ts +++ /dev/null @@ -1,329 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2024 Typefox and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module '@theia/plugin' { - - // https://github.com/microsoft/vscode/issues/145234 - - /** - * A command that was executed in a terminal. - */ - export interface TerminalShellExecution { - /** - * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence} - * of this value depends on the specific shell's shell integration implementation. This - * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is - * fired. - * - * @example - * // Log the details of the command line on start and end - * window.onDidStartTerminalShellExecution(event => { - * const commandLine = event.execution.commandLine; - * console.log(`Command started\n${summarizeCommandLine(commandLine)}`); - * }); - * window.onDidEndTerminalShellExecution(event => { - * const commandLine = event.execution.commandLine; - * console.log(`Command ended\n${summarizeCommandLine(commandLine)}`); - * }); - * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) { - * return [ - * ` Command line: ${command.ommandLine.value}`, - * ` Confidence: ${command.ommandLine.confidence}`, - * ` Trusted: ${command.ommandLine.isTrusted} - * ].join('\n'); - * } - */ - readonly commandLine: TerminalShellExecutionCommandLine; - - /** - * The working directory that was reported by the shell when this command executed. This - * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This - * requires the shell integration to support working directory reporting. - */ - readonly cwd: Uri | undefined; - - /** - * Creates a stream of raw data (including escape sequences) that is written to the - * terminal. This will only include data that was written after `read` was called for - * the first time, ie. you must call `read` immediately after the command is executed via - * {@link TerminalShellIntegration.executeCommand} or - * {@link window.onDidStartTerminalShellExecution} to not miss any data. - * - * @example - * // Log all data written to the terminal for a command - * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' }); - * const stream = command.read(); - * for await (const data of stream) { - * console.log(data); - * } - */ - read(): AsyncIterable; - } - - /** - * A command line that was executed in a terminal. - */ - export interface TerminalShellExecutionCommandLine { - /** - * The full command line that was executed, including both the command and its arguments. - */ - readonly value: string; - - /** - * Whether the command line value came from a trusted source and is therefore safe to - * execute without user additional confirmation, such as a notification that asks "Do you - * want to execute (command)?". This verification is likely only needed if you are going to - * execute the command again. - * - * This is `true` only when the command line was reported explicitly by the shell - * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}) - * and it used a nonce for verification. - */ - readonly isTrusted: boolean; - - /** - * The confidence of the command line value which is determined by how the value was - * obtained. This depends upon the implementation of the shell integration script. - */ - readonly confidence: TerminalShellExecutionCommandLineConfidence; - } - - /** - * The confidence of a {@link TerminalShellExecutionCommandLine} value. - */ - enum TerminalShellExecutionCommandLineConfidence { - /** - * The command line value confidence is low. This means that the value was read from the - * terminal buffer using markers reported by the shell integration script. Additionally one - * of the following conditions will be met: - * - * - The command started on the very left-most column which is unusual, or - * - The command is multi-line which is more difficult to accurately detect due to line - * continuation characters and right prompts. - * - Command line markers were not reported by the shell integration script. - */ - Low = 0, - - /** - * The command line value confidence is medium. This means that the value was read from the - * terminal buffer using markers reported by the shell integration script. The command is - * single-line and does not start on the very left-most column (which is unusual). - */ - Medium = 1, - - /** - * The command line value confidence is high. This means that the value was explicitly sent - * from the shell integration script or the command was executed via the - * {@link TerminalShellIntegration.executeCommand} API. - */ - High = 2 - } - - export interface Terminal { - /** - * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered - * features for the terminal. This will always be `undefined` immediately after the terminal - * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified - * when shell integration is activated for a terminal. - * - * Note that this object may remain undefined if shell integation never activates. For - * example Command Prompt does not support shell integration and a user's shell setup could - * conflict with the automatic shell integration activation. - */ - readonly shellIntegration: TerminalShellIntegration | undefined; - } - - /** - * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. - */ - export interface TerminalShellIntegration { - /** - * The current working directory of the terminal. This {@link Uri} may represent a file on - * another machine (eg. ssh into another machine). This requires the shell integration to - * support working directory reporting. - */ - readonly cwd: Uri | undefined; - - /** - * Execute a command, sending ^C as necessary to interrupt any running command if needed. - * - * @param commandLine The command line to execute, this is the exact text that will be sent - * to the terminal. - * - * @example - * // Execute a command in a terminal immediately after being created - * const myTerm = window.createTerminal(); - * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { - * if (terminal === myTerm) { - * const command = shellIntegration.executeCommand('echo "Hello world"'); - * const code = await command.exitCode; - * console.log(`Command exited with code ${code}`); - * } - * })); - * // Fallback to sendText if there is no shell integration within 3 seconds of launching - * setTimeout(() => { - * if (!myTerm.shellIntegration) { - * myTerm.sendText('echo "Hello world"'); - * // Without shell integration, we can't know when the command has finished or what the - * // exit code was. - * } - * }, 3000); - * - * @example - * // Send command to terminal that has been alive for a while - * const commandLine = 'echo "Hello world"'; - * if (term.shellIntegration) { - * const command = term.shellIntegration.executeCommand({ commandLine }); - * const code = await command.exitCode; - * console.log(`Command exited with code ${code}`); - * } else { - * term.sendText(commandLine); - * // Without shell integration, we can't know when the command has finished or what the - * // exit code was. - * } - */ - executeCommand(commandLine: string): TerminalShellExecution; - - /** - * Execute a command, sending ^C as necessary to interrupt any running command if needed. - * - * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) - * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to - * verify whether it was successful. - * - * @param command A command to run. - * @param args Arguments to launch the executable with which will be automatically escaped - * based on the executable type. - * - * @example - * // Execute a command in a terminal immediately after being created - * const myTerm = window.createTerminal(); - * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => { - * if (terminal === myTerm) { - * const command = shellIntegration.executeCommand({ - * command: 'echo', - * args: ['Hello world'] - * }); - * const code = await command.exitCode; - * console.log(`Command exited with code ${code}`); - * } - * })); - * // Fallback to sendText if there is no shell integration within 3 seconds of launching - * setTimeout(() => { - * if (!myTerm.shellIntegration) { - * myTerm.sendText('echo "Hello world"'); - * // Without shell integration, we can't know when the command has finished or what the - * // exit code was. - * } - * }, 3000); - * - * @example - * // Send command to terminal that has been alive for a while - * const commandLine = 'echo "Hello world"'; - * if (term.shellIntegration) { - * const command = term.shellIntegration.executeCommand({ - * command: 'echo', - * args: ['Hello world'] - * }); - * const code = await command.exitCode; - * console.log(`Command exited with code ${code}`); - * } else { - * term.sendText(commandLine); - * // Without shell integration, we can't know when the command has finished or what the - * // exit code was. - * } - */ - executeCommand(executable: string, args: string[]): TerminalShellExecution; - } - - export interface TerminalShellIntegrationChangeEvent { - /** - * The terminal that shell integration has been activated in. - */ - readonly terminal: Terminal; - - /** - * The shell integration object. - */ - readonly shellIntegration: TerminalShellIntegration; - } - - export interface TerminalShellExecutionStartEvent { - /** - * The terminal that shell integration has been activated in. - */ - readonly terminal: Terminal; - - /** - * The shell integration object. - */ - readonly shellIntegration: TerminalShellIntegration; - - /** - * The terminal shell execution that has ended. - */ - readonly execution: TerminalShellExecution; - } - - export interface TerminalShellExecutionEndEvent { - /** - * The terminal that shell integration has been activated in. - */ - readonly terminal: Terminal; - - /** - * The shell integration object. - */ - readonly shellIntegration: TerminalShellIntegration; - - /** - * The terminal shell execution that has ended. - */ - readonly execution: TerminalShellExecution; - - /** - * The exit code reported by the shell. `undefined` means the shell did not report an exit - * code or the shell reported a command started before the command finished. - */ - readonly exitCode: number | undefined; - } - - export namespace window { - /** - * Fires when shell integration activates or one of its properties changes in a terminal. - */ - export const onDidChangeTerminalShellIntegration: Event; - - /** - * This will be fired when a terminal command is started. This event will fire only when - * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is - * activated for the terminal. - */ - export const onDidStartTerminalShellExecution: Event; - - /** - * This will be fired when a terminal command is ended. This event will fire only when - * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is - * activated for the terminal. - */ - export const onDidEndTerminalShellExecution: Event; - } -} From 1fea534a214bfce55ace32efc1b6e899e3364daa Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 16 Sep 2024 14:57:56 +0200 Subject: [PATCH 374/441] Remove stub tag from `TerminalOptions#color` (#14171) --- packages/plugin/src/theia.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index 722ec2eea11fe..6caa690fdacf1 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -3520,7 +3520,6 @@ export module '@theia/plugin' { * The icon {@link ThemeColor} for the terminal. * The `terminal.ansi*` theme keys are * recommended for the best contrast and consistency across themes. - * @stubbed */ color?: ThemeColor; } From d167d1eaa2a0fb978f8aa49cd58087102b6237a0 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 16 Sep 2024 18:40:33 +0200 Subject: [PATCH 375/441] Focus notebook cell container correctly (#14175) --- .../contributions/notebook-actions-contribution.ts | 5 ++++- .../notebook-cell-actions-contribution.ts | 12 ++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts index 3d4e4ae30e838..3a95c0c644d6b 100644 --- a/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-actions-contribution.ts @@ -126,7 +126,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon registerCommands(commands: CommandRegistry): void { commands.registerCommand(NotebookCommands.ADD_NEW_CELL_COMMAND, { - execute: (notebookModel: NotebookModel, cellKind: CellKind = CellKind.Markup, index?: number | 'above' | 'below') => { + execute: (notebookModel: NotebookModel, cellKind: CellKind = CellKind.Markup, index?: number | 'above' | 'below', focusContainer?: boolean) => { notebookModel = notebookModel ?? this.notebookEditorWidgetService.focusedEditor?.model; let insertIndex: number = 0; @@ -154,6 +154,9 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon metadata: {}, }] }], true); + if (focusContainer) { + notebookModel.selectedCell?.requestBlurEditor(); + } } }); diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 8242947a4d116..8cfcd68531f32 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -374,13 +374,13 @@ export class NotebookCellActionContribution implements MenuContribution, Command (_, __, output) => output?.requestOutputPresentationUpdate() )); - const insertCommand = (type: CellKind, index: number | 'above' | 'below'): CommandHandler => this.editableCellCommandHandler(() => - commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index) + const insertCommand = (type: CellKind, index: number | 'above' | 'below', focusContainer: boolean): CommandHandler => this.editableCellCommandHandler(() => + commands.executeCommand(NotebookCommands.ADD_NEW_CELL_COMMAND.id, undefined, type, index, focusContainer) ); - commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_ABOVE_COMMAND, insertCommand(CellKind.Code, 'above')); - commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND, insertCommand(CellKind.Code, 'below')); - commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_ABOVE_COMMAND, insertCommand(CellKind.Markup, 'above')); - commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below')); + commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_ABOVE_COMMAND, insertCommand(CellKind.Code, 'above', true)); + commands.registerCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND, insertCommand(CellKind.Code, 'below', true)); + commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_ABOVE_COMMAND, insertCommand(CellKind.Markup, 'above', false)); + commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below', false)); commands.registerCommand(NotebookCellCommands.TO_CODE_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => { changeCellType(notebookModel, cell, CellKind.Code, this.notebookService.getCodeCellLanguage(notebookModel)); From d4ca4a008f6aa9ba0ba36c7df596cb6dcdd3f46c Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Tue, 17 Sep 2024 10:30:32 +0200 Subject: [PATCH 376/441] Consitently name agents and add tags (#14182) * Consitently name agents and add tags fixed #14180 Signed-off-by: Jonas Helming --- packages/ai-chat/src/common/chat-agents.ts | 3 ++- packages/ai-chat/src/common/command-chat-agents.ts | 2 +- .../agent-configuration-widget.tsx | 11 +++++++++-- packages/ai-core/src/browser/style/index.css | 8 ++++++++ packages/ai-core/src/common/agent.ts | 14 ++++++++++++-- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts index fd101832c9bb3..ee279125855be 100644 --- a/packages/ai-chat/src/common/chat-agents.ts +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -126,7 +126,8 @@ export abstract class AbstractChatAgent { public languageModelRequirements: LanguageModelRequirement[], protected defaultLanguageModelPurpose: string, public iconClass: string = 'codicon codicon-copilot', - public locations: ChatAgentLocation[] = ChatAgentLocation.ALL) { + public locations: ChatAgentLocation[] = ChatAgentLocation.ALL, + public tags: String[] = ['Chat']) { } async invoke(request: ChatRequestModelImpl): Promise { diff --git a/packages/ai-chat/src/common/command-chat-agents.ts b/packages/ai-chat/src/common/command-chat-agents.ts index 6f3a38d89ece0..d291ef17eca1c 100644 --- a/packages/ai-chat/src/common/command-chat-agents.ts +++ b/packages/ai-chat/src/common/command-chat-agents.ts @@ -263,7 +263,7 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent
        {this.agentService.getAllAgents().map(agent => -
      • this.setActiveAgent(agent)}>{agent.name}
      • +
      • this.setActiveAgent(agent)}> + {this.renderAgentName(agent)} +
      • )}
      @@ -83,6 +85,11 @@ export class AIAgentConfigurationWidget extends ReactWidget { ; } + private renderAgentName(agent: Agent): React.ReactNode { + const tagsSuffix = agent.tags?.length ? {agent.tags.map(tag => {tag})} : ''; + return {agent.name} {tagsSuffix}; + } + private renderAgentDetails(): React.ReactNode { const agent = this.aiConfigurationSelectionService.getActiveAgent(); if (!agent) { @@ -92,7 +99,7 @@ export class AIAgentConfigurationWidget extends ReactWidget { const enabled = this.agentService.isEnabled(agent.id); return
      -
      {agent.name}
      +
      {this.renderAgentName(agent)}
      {agent.description}
      ; } else { - return
      + return
      ; } From 363865fd7ecea164eb134f491e95a4005ba6df0d Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Mon, 30 Sep 2024 13:07:49 +0200 Subject: [PATCH 404/441] fix: render html text in Theia AI markdown renderers Adapt rendering of all Theia AI markdown renderers to use markdown-it directly. The MarkdownRenderer exposed by Theia automatically strips html tags while we still want to show them as text in requests and responses. The html is not interpreted and therefore can't be used for injections. fixes #14208 --- .../markdown-part-renderer.tsx | 49 +++++++++++++------ .../chat-tree-view/chat-view-tree-widget.tsx | 25 ++++------ 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx index a2fcc7329d53b..8fba51ad2fcbd 100644 --- a/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/markdown-part-renderer.tsx @@ -15,7 +15,7 @@ // ***************************************************************************** import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { ChatResponseContent, InformationalChatResponseContent, @@ -23,12 +23,13 @@ import { } from '@theia/ai-chat/lib/common'; import { ReactNode, useEffect, useRef } from '@theia/core/shared/react'; import * as React from '@theia/core/shared/react'; +import * as markdownit from '@theia/core/shared/markdown-it'; +import * as DOMPurify from '@theia/core/shared/dompurify'; import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; -import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; @injectable() export class MarkdownPartRenderer implements ChatResponsePartRenderer { - @inject(MarkdownRenderer) private renderer: MarkdownRenderer; + protected readonly markdownIt = markdownit(); canHandle(response: ChatResponseContent): number { if (MarkdownChatResponseContent.is(response)) { return 10; @@ -38,9 +39,6 @@ export class MarkdownPartRenderer implements ChatResponsePartRenderer; + + return ; } } -export const MarkdownWrapper = (props: { data: MarkdownString, renderCallback: (md: MarkdownString) => HTMLElement }) => { - // eslint-disable-next-line no-null/no-null - const ref: React.MutableRefObject = useRef(null); +const MarkdownRender = ({ response }: { response: MarkdownChatResponseContent | InformationalChatResponseContent }) => { + const ref = useMarkdownRendering(response.content); - useEffect(() => { - const myDomElement = props.renderCallback(props.data); + return
      ; +}; +/** + * This hook uses markdown-it directly to render markdown. + * The reason to use markdown-it directly is that the MarkdownRenderer is + * overriden by theia with a monaco version. This monaco version strips all html + * tags from the markdown with empty content. + * This leads to unexpected behavior when rendering markdown with html tags. + * + * @param markdown the string to render as markdown + * @returns the ref to use in an element to render the markdown + */ +export const useMarkdownRendering = (markdown: string | MarkdownString) => { + // eslint-disable-next-line no-null/no-null + const ref = useRef(null); + const markdownString = typeof markdown === 'string' ? markdown : markdown.value; + useEffect(() => { + const markdownIt = markdownit(); + const host = document.createElement('div'); + const html = markdownIt.render(markdownString); + host.innerHTML = DOMPurify.sanitize(html, { + ALLOW_UNKNOWN_PROTOCOLS: true // DOMPurify usually strips non http(s) links from hrefs + }); while (ref?.current?.firstChild) { ref.current.removeChild(ref.current.firstChild); } - ref?.current?.appendChild(myDomElement); - }, [props.data.value]); + ref?.current?.appendChild(host); + }, [markdownString]); - return
      ; + return ref; }; diff --git a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx index 04fa4d4253c5c..19d7daacdd9b8 100644 --- a/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx +++ b/packages/ai-chat-ui/src/browser/chat-tree-view/chat-view-tree-widget.tsx @@ -35,7 +35,6 @@ import { TreeProps, TreeWidget, } from '@theia/core/lib/browser'; -import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; import { inject, injectable, @@ -44,10 +43,9 @@ import { } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; -import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { ChatNodeToolbarActionContribution } from '../chat-node-toolbar-action-contribution'; import { ChatResponsePartRenderer } from '../chat-response-part-renderer'; -import { MarkdownWrapper } from '../chat-response-renderer/markdown-part-renderer'; +import { useMarkdownRendering } from '../chat-response-renderer/markdown-part-renderer'; // TODO Instead of directly operating on the ChatRequestModel we could use an intermediate view model export interface RequestNode extends TreeNode { @@ -76,9 +74,6 @@ export class ChatViewTreeWidget extends TreeWidget { @inject(ContributionProvider) @named(ChatNodeToolbarActionContribution) protected readonly chatNodeToolbarActionContributions: ContributionProvider; - @inject(MarkdownRenderer) - private renderer: MarkdownRenderer; - @inject(ChatAgentService) protected chatAgentService: ChatAgentService; @@ -336,16 +331,7 @@ export class ChatViewTreeWidget extends TreeWidget { } private renderChatRequest(node: RequestNode): React.ReactNode { - const text = node.request.request.displayText ?? node.request.request.text; - const markdownString = new MarkdownStringImpl(text, { supportHtml: true, isTrusted: true }); - return ( -
      - { this.renderer.render(markdownString).element} - >} -
      - ); + return ; } private renderChatResponse(node: ResponseNode): React.ReactNode { @@ -389,6 +375,13 @@ export class ChatViewTreeWidget extends TreeWidget { } } +const ChatRequestRender = ({ node }: { node: RequestNode }) => { + const text = node.request.request.displayText ?? node.request.request.text; + const ref = useMarkdownRendering(text); + + return
      ; +}; + const ProgressMessage = (c: ChatProgressMessage) => (
      {c.content} From 915c8ea1b75b9253ca13813390d417f75a2ac02b Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 2 Oct 2024 09:12:37 +0200 Subject: [PATCH 405/441] Fix duplicate text editor entry (#14238) --- packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts | 2 ++ packages/core/src/browser/open-with-service.ts | 4 ++++ .../src/browser/editor-preview-tree-decorator.ts | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts index 293d58c26c5c7..285f2cadd42fc 100644 --- a/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts +++ b/packages/ai-chat-ui/src/browser/ai-chat-ui-frontend-module.ts @@ -35,6 +35,7 @@ import { ChatViewMenuContribution } from './chat-view-contribution'; import { ChatViewLanguageContribution } from './chat-view-language-contribution'; import { ChatViewWidget } from './chat-view-widget'; import { ChatViewWidgetToolbarContribution } from './chat-view-widget-toolbar-contribution'; +import { EditorPreviewManager } from '@theia/editor-preview/lib/browser/editor-preview-manager'; export default new ContainerModule((bind, _unbind, _isBound, rebind) => { bindViewContribution(bind, AIChatContribution); @@ -71,6 +72,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => { bind(AIEditorManager).toSelf().inSingletonScope(); rebind(EditorManager).toService(AIEditorManager); + rebind(EditorPreviewManager).toService(AIEditorManager); bindContributionProvider(bind, AIEditorSelectionResolver); bind(AIEditorSelectionResolver).to(GitHubSelectionResolver).inSingletonScope(); diff --git a/packages/core/src/browser/open-with-service.ts b/packages/core/src/browser/open-with-service.ts index 1e347ca88301b..56eb7c4d54a93 100644 --- a/packages/core/src/browser/open-with-service.ts +++ b/packages/core/src/browser/open-with-service.ts @@ -77,6 +77,10 @@ export class OpenWithService { protected readonly handlers: OpenWithHandler[] = []; registerHandler(handler: OpenWithHandler): Disposable { + if (this.handlers.some(h => h.id === handler.id)) { + console.warn('Duplicate OpenWithHandler registration: ' + handler.id); + return Disposable.NULL; + } this.handlers.push(handler); return Disposable.create(() => { const index = this.handlers.indexOf(handler); diff --git a/packages/editor-preview/src/browser/editor-preview-tree-decorator.ts b/packages/editor-preview/src/browser/editor-preview-tree-decorator.ts index 21c6671cee3ca..c5908ef105624 100644 --- a/packages/editor-preview/src/browser/editor-preview-tree-decorator.ts +++ b/packages/editor-preview/src/browser/editor-preview-tree-decorator.ts @@ -30,11 +30,10 @@ import { import { Disposable } from '@theia/core/lib/common'; import { OpenEditorNode } from '@theia/navigator/lib/browser/open-editors-widget/navigator-open-editors-tree-model'; import { EditorPreviewWidget } from './editor-preview-widget'; -import { EditorPreviewManager } from './editor-preview-manager'; @injectable() export class EditorPreviewTreeDecorator implements TreeDecorator, FrontendApplicationContribution { - @inject(EditorPreviewManager) protected readonly editorPreviewManager: EditorPreviewManager; + @inject(ApplicationShell) protected readonly shell: ApplicationShell; readonly id = 'theia-open-editors-file-decorator'; From f6ace675e712c3d2227d1cd19e9c905a6e4d9ad6 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Wed, 2 Oct 2024 09:19:15 +0200 Subject: [PATCH 406/441] feat(ai): Make response parsing extensible (#14196) Turns the response parsing method into a more flexible algorithm that can work with multiple response content matchers. Each response content matcher has a start and end regexp to define a match, as well as a `contentFactory` function that turns the matched content into a `ChatResponseContent` object. Additionally, the parsing method has a fallback content factory that will be applied to all unmatched parts, e.g. markdown by default. Both, the response content matchers and the fallback content factory and the list of matchers are extensible via DI. Contributed on behalf of STMicroelectronics. --- .../src/browser/ai-chat-frontend-module.ts | 6 + packages/ai-chat/src/common/chat-agents.ts | 119 ++++++++------- packages/ai-chat/src/common/chat-model.ts | 18 ++- .../ai-chat/src/common/parse-contents.spec.ts | 142 ++++++++++++++++++ packages/ai-chat/src/common/parse-contents.ts | 92 ++++++++++++ .../src/common/response-content-matcher.ts | 102 +++++++++++++ 6 files changed, 414 insertions(+), 65 deletions(-) create mode 100644 packages/ai-chat/src/common/parse-contents.spec.ts create mode 100644 packages/ai-chat/src/common/parse-contents.ts create mode 100644 packages/ai-chat/src/common/response-content-matcher.ts diff --git a/packages/ai-chat/src/browser/ai-chat-frontend-module.ts b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts index d4ed71579b514..c2231c64734aa 100644 --- a/packages/ai-chat/src/browser/ai-chat-frontend-module.ts +++ b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts @@ -33,6 +33,7 @@ import { UniversalChatAgent } from '../common/universal-chat-agent'; import { aiChatPreferences } from './ai-chat-preferences'; import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution'; import { FrontendChatServiceImpl } from './frontend-chat-service'; +import { DefaultResponseContentMatcherProvider, DefaultResponseContentFactory, ResponseContentMatcherProvider } from '../common/response-content-matcher'; export default new ContainerModule(bind => { bindContributionProvider(bind, Agent); @@ -42,6 +43,11 @@ export default new ContainerModule(bind => { bind(ChatAgentService).toService(ChatAgentServiceImpl); bind(DefaultChatAgentId).toConstantValue({ id: OrchestratorChatAgentId }); + bindContributionProvider(bind, ResponseContentMatcherProvider); + bind(DefaultResponseContentMatcherProvider).toSelf().inSingletonScope(); + bind(ResponseContentMatcherProvider).toService(DefaultResponseContentMatcherProvider); + bind(DefaultResponseContentFactory).toSelf().inSingletonScope(); + bind(AIVariableContribution).to(ChatAgentsVariableContribution).inSingletonScope(); bind(ChatRequestParserImpl).toSelf().inSingletonScope(); diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts index ee279125855be..ce6bbd741a17c 100644 --- a/packages/ai-chat/src/common/chat-agents.ts +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -25,6 +25,7 @@ import { LanguageModel, LanguageModelRequirement, LanguageModelResponse, + LanguageModelStreamResponse, PromptService, ResolvedPromptTemplate, ToolRequest, @@ -37,19 +38,20 @@ import { LanguageModelStreamResponsePart, MessageActor, } from '@theia/ai-core/lib/common'; -import { CancellationToken, CancellationTokenSource, ILogger, isArray } from '@theia/core'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { CancellationToken, CancellationTokenSource, ContributionProvider, ILogger, isArray } from '@theia/core'; +import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify'; import { ChatAgentService } from './chat-agent-service'; import { ChatModel, ChatRequestModel, ChatRequestModelImpl, ChatResponseContent, - CodeChatResponseContentImpl, ErrorChatResponseContentImpl, MarkdownChatResponseContentImpl, ToolCallChatResponseContentImpl } from './chat-model'; +import { findFirstMatch, parseContents } from './parse-contents'; +import { DefaultResponseContentFactory, ResponseContentMatcher, ResponseContentMatcherProvider } from './response-content-matcher'; /** * A conversation consists of a sequence of ChatMessages. @@ -121,6 +123,14 @@ export abstract class AbstractChatAgent { @inject(ILogger) protected logger: ILogger; @inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService; @inject(PromptService) protected promptService: PromptService; + + @inject(ContributionProvider) @named(ResponseContentMatcherProvider) + protected contentMatcherProviders: ContributionProvider; + protected contentMatchers: ResponseContentMatcher[] = []; + + @inject(DefaultResponseContentFactory) + protected defaultContentFactory: DefaultResponseContentFactory; + constructor( public id: string, public languageModelRequirements: LanguageModelRequirement[], @@ -130,6 +140,11 @@ export abstract class AbstractChatAgent { public tags: String[] = ['Chat']) { } + @postConstruct() + init(): void { + this.contentMatchers = this.contentMatcherProviders.getContributions().flatMap(provider => provider.matchers); + } + async invoke(request: ChatRequestModelImpl): Promise { try { const languageModel = await this.getLanguageModel(this.defaultLanguageModelPurpose); @@ -189,6 +204,14 @@ export abstract class AbstractChatAgent { } } + protected parseContents(text: string): ChatResponseContent[] { + return parseContents( + text, + this.contentMatchers, + this.defaultContentFactory?.create.bind(this.defaultContentFactory) + ); + }; + protected handleError(request: ChatRequestModelImpl, error: Error): void { request.response.response.addContent(new ErrorChatResponseContentImpl(error)); request.response.error(error); @@ -281,9 +304,8 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { protected override async addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise { if (isLanguageModelTextResponse(languageModelResponse)) { - request.response.response.addContent( - new MarkdownChatResponseContentImpl(languageModelResponse.text) - ); + const contents = this.parseContents(languageModelResponse.text); + request.response.response.addContents(contents); request.response.complete(); this.recordingService.recordResponse({ agentId: this.id, @@ -295,57 +317,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { return; } if (isLanguageModelStreamResponse(languageModelResponse)) { - for await (const token of languageModelResponse.stream) { - const newContents = this.parse(token, request.response.response.content); - if (isArray(newContents)) { - newContents.forEach(newContent => request.response.response.addContent(newContent)); - } else { - request.response.response.addContent(newContents); - } - - const lastContent = request.response.response.content.pop(); - if (lastContent === undefined) { - return; - } - const text = lastContent.asString?.(); - if (text === undefined) { - return; - } - let curSearchIndex = 0; - const result: ChatResponseContent[] = []; - while (curSearchIndex < text.length) { - // find start of code block: ```[language]\n[\n]``` - const codeStartIndex = text.indexOf('```', curSearchIndex); - if (codeStartIndex === -1) { - break; - } - - // find language specifier if present - const newLineIndex = text.indexOf('\n', codeStartIndex + 3); - const language = codeStartIndex + 3 < newLineIndex ? text.substring(codeStartIndex + 3, newLineIndex) : undefined; - - // find end of code block - const codeEndIndex = text.indexOf('```', codeStartIndex + 3); - if (codeEndIndex === -1) { - break; - } - - // add text before code block as markdown content - result.push(new MarkdownChatResponseContentImpl(text.substring(curSearchIndex, codeStartIndex))); - // add code block as code content - const codeText = text.substring(newLineIndex + 1, codeEndIndex).trimEnd(); - result.push(new CodeChatResponseContentImpl(codeText, language)); - curSearchIndex = codeEndIndex + 3; - } - - if (result.length > 0) { - result.forEach(r => { - request.response.response.addContent(r); - }); - } else { - request.response.response.addContent(lastContent); - } - } + await this.addStreamResponse(languageModelResponse, request); request.response.complete(); this.recordingService.recordResponse({ agentId: this.id, @@ -366,11 +338,38 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { ); } - private parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] { + protected async addStreamResponse(languageModelResponse: LanguageModelStreamResponse, request: ChatRequestModelImpl): Promise { + for await (const token of languageModelResponse.stream) { + const newContents = this.parse(token, request.response.response.content); + if (isArray(newContents)) { + request.response.response.addContents(newContents); + } else { + request.response.response.addContent(newContents); + } + + const lastContent = request.response.response.content.pop(); + if (lastContent === undefined) { + return; + } + const text = lastContent.asString?.(); + if (text === undefined) { + return; + } + + const result: ChatResponseContent[] = findFirstMatch(this.contentMatchers, text) ? this.parseContents(text) : []; + if (result.length > 0) { + request.response.response.addContents(result); + } else { + request.response.response.addContent(lastContent); + } + } + } + + protected parse(token: LanguageModelStreamResponsePart, previousContent: ChatResponseContent[]): ChatResponseContent | ChatResponseContent[] { const content = token.content; // eslint-disable-next-line no-null/no-null if (content !== undefined && content !== null) { - return new MarkdownChatResponseContentImpl(content); + return this.defaultContentFactory.create(content); } const toolCalls = token.tool_calls; if (toolCalls !== undefined) { @@ -378,7 +377,7 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { new ToolCallChatResponseContentImpl(toolCall.id, toolCall.function?.name, toolCall.function?.arguments, toolCall.finished, toolCall.result)); return toolCallContents; } - return new MarkdownChatResponseContentImpl(''); + return this.defaultContentFactory.create(''); } } diff --git a/packages/ai-chat/src/common/chat-model.ts b/packages/ai-chat/src/common/chat-model.ts index 777cb0bac21e7..c485c69c7c4c8 100644 --- a/packages/ai-chat/src/common/chat-model.ts +++ b/packages/ai-chat/src/common/chat-model.ts @@ -601,10 +601,20 @@ class ChatResponseImpl implements ChatResponse { return this._content; } + addContents(contents: ChatResponseContent[]): void { + contents.forEach(c => this.doAddContent(c)); + this._onDidChangeEmitter.fire(); + } + addContent(nextContent: ChatResponseContent): void { // TODO: Support more complex merges affecting different content than the last, e.g. via some kind of ProcessorRegistry // TODO: Support more of the built-in VS Code behavior, see // https://github.com/microsoft/vscode/blob/a2cab7255c0df424027be05d58e1b7b941f4ea60/src/vs/workbench/contrib/chat/common/chatModel.ts#L188-L244 + this.doAddContent(nextContent); + this._onDidChangeEmitter.fire(); + } + + protected doAddContent(nextContent: ChatResponseContent): void { if (ToolCallChatResponseContent.is(nextContent) && nextContent.id !== undefined) { const fittingTool = this._content.find(c => ToolCallChatResponseContent.is(c) && c.id === nextContent.id); if (fittingTool !== undefined) { @@ -613,10 +623,9 @@ class ChatResponseImpl implements ChatResponse { this._content.push(nextContent); } } else { - const lastElement = - this._content.length > 0 - ? this._content[this._content.length - 1] - : undefined; + const lastElement = this._content.length > 0 + ? this._content[this._content.length - 1] + : undefined; if (lastElement?.kind === nextContent.kind && ChatResponseContent.hasMerge(lastElement)) { const mergeSuccess = lastElement.merge(nextContent); if (!mergeSuccess) { @@ -627,7 +636,6 @@ class ChatResponseImpl implements ChatResponse { } } this._updateResponseRepresentation(); - this._onDidChangeEmitter.fire(); } protected _updateResponseRepresentation(): void { diff --git a/packages/ai-chat/src/common/parse-contents.spec.ts b/packages/ai-chat/src/common/parse-contents.spec.ts new file mode 100644 index 0000000000000..c0a009f8cb814 --- /dev/null +++ b/packages/ai-chat/src/common/parse-contents.spec.ts @@ -0,0 +1,142 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { expect } from 'chai'; +import { ChatResponseContent, CodeChatResponseContentImpl, MarkdownChatResponseContentImpl } from './chat-model'; +import { parseContents } from './parse-contents'; +import { CodeContentMatcher, ResponseContentMatcher } from './response-content-matcher'; + +export class CommandChatResponseContentImpl implements ChatResponseContent { + constructor(public readonly command: string) { } + kind = 'command'; +} + +export const CommandContentMatcher: ResponseContentMatcher = { + start: /^$/m, + end: /^<\/command>$/m, + contentFactory: (content: string) => { + const code = content.replace(/^\n|<\/command>$/g, ''); + return new CommandChatResponseContentImpl(code.trim()); + } +}; + +describe('parseContents', () => { + it('should parse code content', () => { + const text = '```typescript\nconsole.log("Hello World");\n```'; + const result = parseContents(text); + expect(result).to.deep.equal([new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript')]); + }); + + it('should parse markdown content', () => { + const text = 'Hello **World**'; + const result = parseContents(text); + expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Hello **World**')]); + }); + + it('should parse multiple content blocks', () => { + const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), + new MarkdownChatResponseContentImpl('\nHello **World**') + ]); + }); + + it('should parse multiple content blocks with different languages', () => { + const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")\n```'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), + new CodeChatResponseContentImpl('print("Hello World")', 'python') + ]); + }); + + it('should parse multiple content blocks with different languages and markdown', () => { + const text = '```typescript\nconsole.log("Hello World");\n```\nHello **World**\n```python\nprint("Hello World")\n```'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), + new MarkdownChatResponseContentImpl('\nHello **World**\n'), + new CodeChatResponseContentImpl('print("Hello World")', 'python') + ]); + }); + + it('should parse content blocks with empty content', () => { + const text = '```typescript\n```\nHello **World**\n```python\nprint("Hello World")\n```'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new CodeChatResponseContentImpl('', 'typescript'), + new MarkdownChatResponseContentImpl('\nHello **World**\n'), + new CodeChatResponseContentImpl('print("Hello World")', 'python') + ]); + }); + + it('should parse content with markdown, code, and markdown', () => { + const text = 'Hello **World**\n```typescript\nconsole.log("Hello World");\n```\nGoodbye **World**'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new MarkdownChatResponseContentImpl('Hello **World**\n'), + new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), + new MarkdownChatResponseContentImpl('\nGoodbye **World**') + ]); + }); + + it('should handle text with no special content', () => { + const text = 'Just some plain text.'; + const result = parseContents(text); + expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('Just some plain text.')]); + }); + + it('should handle text with only start code block', () => { + const text = '```typescript\nconsole.log("Hello World");'; + const result = parseContents(text); + expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('```typescript\nconsole.log("Hello World");')]); + }); + + it('should handle text with only end code block', () => { + const text = 'console.log("Hello World");\n```'; + const result = parseContents(text); + expect(result).to.deep.equal([new MarkdownChatResponseContentImpl('console.log("Hello World");\n```')]); + }); + + it('should handle text with unmatched code block', () => { + const text = '```typescript\nconsole.log("Hello World");\n```\n```python\nprint("Hello World")'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new CodeChatResponseContentImpl('console.log("Hello World");', 'typescript'), + new MarkdownChatResponseContentImpl('\n```python\nprint("Hello World")') + ]); + }); + + it('should parse code block without newline after language', () => { + const text = '```typescript console.log("Hello World");```'; + const result = parseContents(text); + expect(result).to.deep.equal([ + new MarkdownChatResponseContentImpl('```typescript console.log("Hello World");```') + ]); + }); + + it('should parse with matches of multiple different matchers and default', () => { + const text = '\nMY_SPECIAL_COMMAND\n\nHello **World**\n```python\nprint("Hello World")\n```\n\nMY_SPECIAL_COMMAND2\n'; + const result = parseContents(text, [CodeContentMatcher, CommandContentMatcher]); + expect(result).to.deep.equal([ + new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND'), + new MarkdownChatResponseContentImpl('\nHello **World**\n'), + new CodeChatResponseContentImpl('print("Hello World")', 'python'), + new CommandChatResponseContentImpl('MY_SPECIAL_COMMAND2'), + ]); + }); +}); diff --git a/packages/ai-chat/src/common/parse-contents.ts b/packages/ai-chat/src/common/parse-contents.ts new file mode 100644 index 0000000000000..16f405495ce20 --- /dev/null +++ b/packages/ai-chat/src/common/parse-contents.ts @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 EclipseSource GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + */ +import { ChatResponseContent } from './chat-model'; +import { CodeContentMatcher, MarkdownContentFactory, ResponseContentFactory, ResponseContentMatcher } from './response-content-matcher'; + +interface Match { + matcher: ResponseContentMatcher; + index: number; + content: string; +} + +export function parseContents( + text: string, + contentMatchers: ResponseContentMatcher[] = [CodeContentMatcher], + defaultContentFactory: ResponseContentFactory = MarkdownContentFactory +): ChatResponseContent[] { + const result: ChatResponseContent[] = []; + + let currentIndex = 0; + while (currentIndex < text.length) { + const remainingText = text.substring(currentIndex); + const match = findFirstMatch(contentMatchers, remainingText); + if (!match) { + // Add the remaining text as default content + if (remainingText.length > 0) { + result.push(defaultContentFactory(remainingText)); + } + break; + } + // We have a match + // 1. Add preceding text as default content + if (match.index > 0) { + const precedingContent = remainingText.substring(0, match.index); + if (precedingContent.trim().length > 0) { + result.push(defaultContentFactory(precedingContent)); + } + } + // 2. Add the matched content object + result.push(match.matcher.contentFactory(match.content)); + // Update currentIndex to the end of the end of the match + // And continue with the search after the end of the match + currentIndex += match.index + match.content.length; + } + + return result; +} + +export function findFirstMatch(contentMatchers: ResponseContentMatcher[], text: string): Match | undefined { + let firstMatch: { matcher: ResponseContentMatcher, index: number, content: string } | undefined; + for (const matcher of contentMatchers) { + const startMatch = matcher.start.exec(text); + if (!startMatch) { + // No start match found, try next matcher. + continue; + } + const endOfStartMatch = startMatch.index + startMatch[0].length; + if (endOfStartMatch >= text.length) { + // There is no text after the start match. + // No need to search for the end match yet, try next matcher. + continue; + } + const remainingTextAfterStartMatch = text.substring(endOfStartMatch); + const endMatch = matcher.end.exec(remainingTextAfterStartMatch); + if (!endMatch) { + // No end match found, try next matcher. + continue; + } + // Found start and end match. + // Record the full match, if it is the earliest found so far. + const index = startMatch.index; + const contentEnd = index + startMatch[0].length + endMatch.index + endMatch[0].length; + const content = text.substring(index, contentEnd); + if (!firstMatch || index < firstMatch.index) { + firstMatch = { matcher, index, content }; + } + } + return firstMatch; +} + diff --git a/packages/ai-chat/src/common/response-content-matcher.ts b/packages/ai-chat/src/common/response-content-matcher.ts new file mode 100644 index 0000000000000..3fb785e603c5f --- /dev/null +++ b/packages/ai-chat/src/common/response-content-matcher.ts @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2024 EclipseSource GmbH. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 + */ +import { + ChatResponseContent, + CodeChatResponseContentImpl, + MarkdownChatResponseContentImpl +} from './chat-model'; +import { injectable } from '@theia/core/shared/inversify'; + +export type ResponseContentFactory = (content: string) => ChatResponseContent; + +export const MarkdownContentFactory: ResponseContentFactory = (content: string) => + new MarkdownChatResponseContentImpl(content); + +/** + * Default response content factory used if no other `ResponseContentMatcher` applies. + * By default, this factory creates a markdown content object. + * + * @see MarkdownChatResponseContentImpl + */ +@injectable() +export class DefaultResponseContentFactory { + create(content: string): ChatResponseContent { + return MarkdownContentFactory(content); + } +} + +/** + * Clients can contribute response content matchers to parse a chat response into specific + * `ChatResponseContent` instances. + */ +export interface ResponseContentMatcher { + /** Regular expression for finding the start delimiter. */ + start: RegExp; + /** Regular expression for finding the start delimiter. */ + end: RegExp; + /** + * The factory creating a response content from the matching content, + * from start index to end index of the match (including delimiters). + */ + contentFactory: ResponseContentFactory; +} + +export const CodeContentMatcher: ResponseContentMatcher = { + start: /^```.*?$/m, + end: /^```$/m, + contentFactory: (content: string) => { + const language = content.match(/^```(\w+)/)?.[1] || ''; + const code = content.replace(/^```(\w+)\n|```$/g, ''); + return new CodeChatResponseContentImpl(code.trim(), language); + } +}; + +/** + * Clients can contribute response content matchers to parse the response content. + * + * The default chat user interface will collect all contributed matchers and use them + * to parse the response into structured content parts (e.g. code blocks, markdown blocks), + * which are then rendered with a `ChatResponsePartRenderer` registered for the respective + * content part type. + * + * ### Example + * ```ts + * bind(ResponseContentMatcherProvider).to(MyResponseContentMatcherProvider); + * ... + * @injectable() + * export class MyResponseContentMatcherProvider implements ResponseContentMatcherProvider { + * readonly matchers: ResponseContentMatcher[] = [{ + * start: /^$/m, + * end: /^$/m, + * contentFactory: (content: string) => { + * const command = content.replace(/^\n|<\/command>$/g, ''); + * return new MyChatResponseContentImpl(command.trim()); + * } + * }]; + * } + * ``` + * + * @see ResponseContentMatcher + */ +export const ResponseContentMatcherProvider = Symbol('ResponseContentMatcherProvider'); +export interface ResponseContentMatcherProvider { + readonly matchers: ResponseContentMatcher[]; +} + +@injectable() +export class DefaultResponseContentMatcherProvider implements ResponseContentMatcherProvider { + readonly matchers: ResponseContentMatcher[] = [CodeContentMatcher]; +} From a9b01fe49fc8cef56de7521433f1fed6efc85f3c Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 2 Oct 2024 16:49:05 +0200 Subject: [PATCH 407/441] Improve widget specific status bar handling (#14239) --- .../browser/frontend-application-module.ts | 5 + packages/core/src/browser/index.ts | 1 + .../src/browser/widget-status-bar-service.ts | 84 +++++++++++++ .../editor/src/browser/editor-contribution.ts | 59 +++++---- .../src/browser/editor-frontend-module.ts | 11 +- .../getting-started-frontend-module.ts | 3 +- .../src/browser/keymaps-frontend-module.ts | 3 +- .../src/browser/monaco-frontend-module.ts | 5 +- .../browser/monaco-status-bar-contribution.ts | 117 +++++++++--------- .../notebook-status-bar-contribution.ts | 57 ++++----- .../src/browser/notebook-frontend-module.ts | 6 +- .../browser/plugin-ext-frontend-module.ts | 5 +- .../src/browser/preference-frontend-module.ts | 5 +- .../browser/vsx-registry-frontend-module.ts | 5 +- 14 files changed, 234 insertions(+), 132 deletions(-) create mode 100644 packages/core/src/browser/widget-status-bar-service.ts diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index 51131f9b1ce93..ea29eff0ebf87 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -144,6 +144,7 @@ import { bindTreePreferences } from './tree'; import { OpenWithService } from './open-with-service'; import { ViewColumnService } from './shell/view-column-service'; import { DomInputUndoRedoHandler, UndoRedoHandler, UndoRedoHandlerService } from './undo-redo-handler'; +import { WidgetStatusBarContribution, WidgetStatusBarService } from './widget-status-bar-service'; export { bindResourceProvider, bindMessageService, bindPreferenceService }; @@ -471,4 +472,8 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is bindContributionProvider(bind, UndoRedoHandler); bind(DomInputUndoRedoHandler).toSelf().inSingletonScope(); bind(UndoRedoHandler).toService(DomInputUndoRedoHandler); + + bind(WidgetStatusBarService).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(WidgetStatusBarService); + bindContributionProvider(bind, WidgetStatusBarContribution); }); diff --git a/packages/core/src/browser/index.ts b/packages/core/src/browser/index.ts index ecc1ccc4da2f8..02cae0fbdf1f4 100644 --- a/packages/core/src/browser/index.ts +++ b/packages/core/src/browser/index.ts @@ -48,3 +48,4 @@ export * from './styling-service'; export * from './hover-service'; export * from './saveable-service'; export * from './undo-redo-handler'; +export * from './widget-status-bar-service'; diff --git a/packages/core/src/browser/widget-status-bar-service.ts b/packages/core/src/browser/widget-status-bar-service.ts new file mode 100644 index 0000000000000..d705880d90546 --- /dev/null +++ b/packages/core/src/browser/widget-status-bar-service.ts @@ -0,0 +1,84 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { inject, injectable, named } from 'inversify'; +import { Widget } from './widgets'; +import { StatusBar } from './status-bar'; +import { FrontendApplicationContribution } from './frontend-application-contribution'; +import { ContributionProvider } from '../common'; +import { FrontendApplication } from './frontend-application'; + +export const WidgetStatusBarContribution = Symbol('WidgetStatusBarContribution'); + +export interface WidgetStatusBarContribution { + canHandle(widget: Widget): widget is T; + activate(statusBar: StatusBar, widget: T): void; + deactivate(statusBar: StatusBar): void; +} + +/** + * Creates an empty {@link WidgetStatusBarContribution} that does nothing. + * Useful for widgets that are not handled by any other contribution, for example: + * * Settings widget + * * Welcome widget + * * Webview widget + * + * @param prototype Prototype to identify the kind of the widget. + * @returns An empty {@link WidgetStatusBarContribution}. + */ +export function noopWidgetStatusBarContribution(prototype: Function): WidgetStatusBarContribution { + return { + canHandle(widget: Widget): widget is Widget { + return widget instanceof prototype; + }, + activate: () => { }, + deactivate: () => { } + }; +} + +@injectable() +export class WidgetStatusBarService implements FrontendApplicationContribution { + + @inject(ContributionProvider) @named(WidgetStatusBarContribution) + protected readonly contributionProvider: ContributionProvider>; + + @inject(StatusBar) + protected readonly statusBar: StatusBar; + + onStart(app: FrontendApplication): void { + app.shell.onDidChangeCurrentWidget(event => { + if (event.newValue) { + this.show(event.newValue); + } + }); + } + + protected show(widget: Widget): void { + const contributions = this.contributionProvider.getContributions(); + // If any contribution can handle the widget, activate it + // If none can, keep everything as is + if (contributions.some(contribution => contribution.canHandle(widget))) { + for (const contribution of contributions) { + // Deactivate all contributions + contribution.deactivate(this.statusBar); + if (contribution.canHandle(widget)) { + // Selectively re-activate them + contribution.activate(this.statusBar, widget); + } + } + } + } +} diff --git a/packages/editor/src/browser/editor-contribution.ts b/packages/editor/src/browser/editor-contribution.ts index a51191531fdf4..00dca84019da8 100644 --- a/packages/editor/src/browser/editor-contribution.ts +++ b/packages/editor/src/browser/editor-contribution.ts @@ -20,7 +20,9 @@ import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { StatusBarAlignment, StatusBar } from '@theia/core/lib/browser/status-bar/status-bar'; import { FrontendApplicationContribution, DiffUris, DockLayout, - QuickInputService, KeybindingRegistry, KeybindingContribution, SHELL_TABBAR_CONTEXT_SPLIT, ApplicationShell + QuickInputService, KeybindingRegistry, KeybindingContribution, SHELL_TABBAR_CONTEXT_SPLIT, ApplicationShell, + WidgetStatusBarContribution, + Widget } from '@theia/core/lib/browser'; import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { CommandHandler, DisposableCollection, MenuContribution, MenuModelRegistry } from '@theia/core'; @@ -33,9 +35,9 @@ import { EditorWidget } from './editor-widget'; import { EditorLanguageStatusService } from './language-status/editor-language-status-service'; @injectable() -export class EditorContribution implements FrontendApplicationContribution, CommandContribution, KeybindingContribution, MenuContribution { +export class EditorContribution implements FrontendApplicationContribution, + CommandContribution, KeybindingContribution, MenuContribution, WidgetStatusBarContribution { - @inject(StatusBar) protected readonly statusBar: StatusBar; @inject(EditorManager) protected readonly editorManager: EditorManager; @inject(EditorLanguageStatusService) protected readonly languageStatusService: EditorLanguageStatusService; @inject(ApplicationShell) protected readonly shell: ApplicationShell; @@ -48,9 +50,6 @@ export class EditorContribution implements FrontendApplicationContribution, Comm onStart(): void { this.initEditorContextKeys(); - - this.updateStatusBar(); - this.editorManager.onCurrentEditorChanged(() => this.updateStatusBar()); } protected initEditorContextKeys(): void { @@ -72,33 +71,41 @@ export class EditorContribution implements FrontendApplicationContribution, Comm } protected readonly toDisposeOnCurrentEditorChanged = new DisposableCollection(); - protected updateStatusBar(): void { + + canHandle(widget: Widget): widget is EditorWidget { + return widget instanceof EditorWidget; + } + + activate(statusBar: StatusBar, widget: EditorWidget): void { this.toDisposeOnCurrentEditorChanged.dispose(); + const editor = widget.editor; + this.updateLanguageStatus(statusBar, editor); + this.updateEncodingStatus(statusBar, editor); + this.setCursorPositionStatus(statusBar, editor); + this.toDisposeOnCurrentEditorChanged.pushAll([ + editor.onLanguageChanged(() => this.updateLanguageStatus(statusBar, editor)), + editor.onEncodingChanged(() => this.updateEncodingStatus(statusBar, editor)), + editor.onCursorPositionChanged(() => this.setCursorPositionStatus(statusBar, editor)) + ]); + } - const widget = this.editorManager.currentEditor; - const editor = widget && widget.editor; - this.updateLanguageStatus(editor); - this.updateEncodingStatus(editor); - this.setCursorPositionStatus(editor); - if (editor) { - this.toDisposeOnCurrentEditorChanged.pushAll([ - editor.onLanguageChanged(() => this.updateLanguageStatus(editor)), - editor.onEncodingChanged(() => this.updateEncodingStatus(editor)), - editor.onCursorPositionChanged(() => this.setCursorPositionStatus(editor)) - ]); - } + deactivate(statusBar: StatusBar): void { + this.toDisposeOnCurrentEditorChanged.dispose(); + this.updateLanguageStatus(statusBar, undefined); + this.updateEncodingStatus(statusBar, undefined); + this.setCursorPositionStatus(statusBar, undefined); } - protected updateLanguageStatus(editor: TextEditor | undefined): void { + protected updateLanguageStatus(statusBar: StatusBar, editor: TextEditor | undefined): void { this.languageStatusService.updateLanguageStatus(editor); } - protected updateEncodingStatus(editor: TextEditor | undefined): void { + protected updateEncodingStatus(statusBar: StatusBar, editor: TextEditor | undefined): void { if (!editor) { - this.statusBar.removeElement('editor-status-encoding'); + statusBar.removeElement('editor-status-encoding'); return; } - this.statusBar.setElement('editor-status-encoding', { + statusBar.setElement('editor-status-encoding', { text: SUPPORTED_ENCODINGS[editor.getEncoding()].labelShort, alignment: StatusBarAlignment.RIGHT, priority: 10, @@ -107,13 +114,13 @@ export class EditorContribution implements FrontendApplicationContribution, Comm }); } - protected setCursorPositionStatus(editor: TextEditor | undefined): void { + protected setCursorPositionStatus(statusBar: StatusBar, editor: TextEditor | undefined): void { if (!editor) { - this.statusBar.removeElement('editor-status-cursor-position'); + statusBar.removeElement('editor-status-cursor-position'); return; } const { cursor } = editor; - this.statusBar.setElement('editor-status-cursor-position', { + statusBar.setElement('editor-status-cursor-position', { text: nls.localizeByDefault('Ln {0}, Col {1}', cursor.line + 1, editor.getVisibleColumn(cursor)), alignment: StatusBarAlignment.RIGHT, priority: 100, diff --git a/packages/editor/src/browser/editor-frontend-module.ts b/packages/editor/src/browser/editor-frontend-module.ts index e4a089ae90b2a..d5142fa29fa9e 100644 --- a/packages/editor/src/browser/editor-frontend-module.ts +++ b/packages/editor/src/browser/editor-frontend-module.ts @@ -19,7 +19,7 @@ import '../../src/browser/language-status/editor-language-status.css'; import { ContainerModule } from '@theia/core/shared/inversify'; import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; -import { OpenHandler, WidgetFactory, FrontendApplicationContribution, KeybindingContribution } from '@theia/core/lib/browser'; +import { OpenHandler, WidgetFactory, FrontendApplicationContribution, KeybindingContribution, WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { VariableContribution } from '@theia/variable-resolver/lib/browser'; import { EditorManager, EditorAccess, ActiveEditorAccess, CurrentEditorAccess } from './editor-manager'; import { EditorContribution } from './editor-contribution'; @@ -59,7 +59,6 @@ export default new ContainerModule(bind => { bind(KeybindingContribution).toService(EditorKeybindingContribution); bind(EditorContribution).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(EditorContribution); bind(EditorLanguageStatusService).toSelf().inSingletonScope(); bind(EditorLineNumberContribution).toSelf().inSingletonScope(); @@ -73,7 +72,13 @@ export default new ContainerModule(bind => { bind(VariableContribution).to(EditorVariableContribution).inSingletonScope(); - [CommandContribution, KeybindingContribution, MenuContribution].forEach(serviceIdentifier => { + [ + FrontendApplicationContribution, + WidgetStatusBarContribution, + CommandContribution, + KeybindingContribution, + MenuContribution + ].forEach(serviceIdentifier => { bind(serviceIdentifier).toService(EditorContribution); }); bind(QuickEditorService).toSelf().inSingletonScope(); diff --git a/packages/getting-started/src/browser/getting-started-frontend-module.ts b/packages/getting-started/src/browser/getting-started-frontend-module.ts index fbcc828646f02..d029dd7a2236c 100644 --- a/packages/getting-started/src/browser/getting-started-frontend-module.ts +++ b/packages/getting-started/src/browser/getting-started-frontend-module.ts @@ -17,13 +17,14 @@ import { GettingStartedContribution } from './getting-started-contribution'; import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { GettingStartedWidget } from './getting-started-widget'; -import { WidgetFactory, FrontendApplicationContribution, bindViewContribution } from '@theia/core/lib/browser'; +import { WidgetFactory, FrontendApplicationContribution, bindViewContribution, noopWidgetStatusBarContribution, WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { bindGettingStartedPreferences } from './getting-started-preferences'; import '../../src/browser/style/index.css'; export default new ContainerModule((bind: interfaces.Bind) => { bindViewContribution(bind, GettingStartedContribution); bind(FrontendApplicationContribution).toService(GettingStartedContribution); + bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(GettingStartedWidget)); bind(GettingStartedWidget).toSelf(); bind(WidgetFactory).toDynamicValue(context => ({ id: GettingStartedWidget.ID, diff --git a/packages/keymaps/src/browser/keymaps-frontend-module.ts b/packages/keymaps/src/browser/keymaps-frontend-module.ts index 2056444246231..5a2c1476bc357 100644 --- a/packages/keymaps/src/browser/keymaps-frontend-module.ts +++ b/packages/keymaps/src/browser/keymaps-frontend-module.ts @@ -22,7 +22,7 @@ import { KeymapsFrontendContribution } from './keymaps-frontend-contribution'; import { CommandContribution, MenuContribution } from '@theia/core/lib/common'; import { KeybindingContribution } from '@theia/core/lib/browser/keybinding'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { WidgetFactory } from '@theia/core/lib/browser'; +import { noopWidgetStatusBarContribution, WidgetFactory, WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { KeybindingWidget } from './keybindings-widget'; import { KeybindingSchemaUpdater } from './keybinding-schema-updater'; import { JsonSchemaContribution } from '@theia/core/lib/browser/json-schema-store'; @@ -41,4 +41,5 @@ export default new ContainerModule(bind => { })).inSingletonScope(); bind(KeybindingSchemaUpdater).toSelf().inSingletonScope(); bind(JsonSchemaContribution).toService(KeybindingSchemaUpdater); + bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(KeybindingWidget)); }); diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 0bd3a74f2500c..e7d727453bb07 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -21,7 +21,8 @@ import { FrontendApplicationContribution, KeybindingContribution, PreferenceService, PreferenceSchemaProvider, createPreferenceProxy, PreferenceScope, PreferenceChange, OVERRIDE_PROPERTY_PATTERN, QuickInputService, StylingParticipant, WebSocketConnectionProvider, - UndoRedoHandler + UndoRedoHandler, + WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { TextEditorProvider, DiffNavigatorProvider, TextEditor } from '@theia/editor/lib/browser'; import { MonacoEditorProvider, MonacoEditorFactory } from './monaco-editor-provider'; @@ -135,7 +136,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(FrontendApplicationContribution).toService(MonacoFormattingConflictsContribution); bind(MonacoStatusBarContribution).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(MonacoStatusBarContribution); + bind(WidgetStatusBarContribution).toService(MonacoStatusBarContribution); bind(MonacoCommandRegistry).toSelf().inSingletonScope(); bind(MonacoEditorCommandHandlers).toSelf().inSingletonScope(); diff --git a/packages/monaco/src/browser/monaco-status-bar-contribution.ts b/packages/monaco/src/browser/monaco-status-bar-contribution.ts index fae85805e9f1a..c7ac733d29987 100644 --- a/packages/monaco/src/browser/monaco-status-bar-contribution.ts +++ b/packages/monaco/src/browser/monaco-status-bar-contribution.ts @@ -14,93 +14,90 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject } from '@theia/core/shared/inversify'; +import { injectable } from '@theia/core/shared/inversify'; import { DisposableCollection, nls } from '@theia/core'; -import { FrontendApplicationContribution, FrontendApplication, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; -import { EditorCommands, EditorManager, EditorWidget } from '@theia/editor/lib/browser'; +import { StatusBar, StatusBarAlignment, Widget, WidgetStatusBarContribution } from '@theia/core/lib/browser'; +import { EditorCommands, EditorWidget } from '@theia/editor/lib/browser'; import { MonacoEditor } from './monaco-editor'; import * as monaco from '@theia/monaco-editor-core'; +export const EDITOR_STATUS_TABBING_CONFIG = 'editor-status-tabbing-config'; +export const EDITOR_STATUS_EOL = 'editor-status-eol'; + @injectable() -export class MonacoStatusBarContribution implements FrontendApplicationContribution { +export class MonacoStatusBarContribution implements WidgetStatusBarContribution { protected readonly toDispose = new DisposableCollection(); - constructor( - @inject(EditorManager) protected readonly editorManager: EditorManager, - @inject(StatusBar) protected readonly statusBar: StatusBar - ) { } - - onStart(app: FrontendApplication): void { - this.updateStatusBar(); - this.editorManager.onCurrentEditorChanged(() => this.updateStatusBar()); + canHandle(widget: Widget): widget is EditorWidget { + if (widget instanceof EditorWidget) { + return Boolean(this.getModel(widget)); + } + return false; } - protected updateStatusBar(): void { - const editor = this.editorManager.currentEditor; + activate(statusBar: StatusBar, editor: EditorWidget): void { + this.toDispose.dispose(); const editorModel = this.getModel(editor); - if (editor && editorModel) { - this.setConfigTabSizeWidget(); - this.setLineEndingWidget(); - - this.toDispose.dispose(); + if (editorModel) { + this.setConfigTabSizeWidget(statusBar, editorModel); + this.setLineEndingWidget(statusBar, editorModel); this.toDispose.push(editorModel.onDidChangeOptions(() => { - this.setConfigTabSizeWidget(); - this.setLineEndingWidget(); + this.setConfigTabSizeWidget(statusBar, editorModel); + this.setLineEndingWidget(statusBar, editorModel); })); let previous = editorModel.getEOL(); this.toDispose.push(editorModel.onDidChangeContent(e => { if (previous !== e.eol) { previous = e.eol; - this.setLineEndingWidget(); + this.setLineEndingWidget(statusBar, editorModel); } })); } else { - this.removeConfigTabSizeWidget(); - this.removeLineEndingWidget(); + this.deactivate(statusBar); } } - protected setConfigTabSizeWidget(): void { - const editor = this.editorManager.currentEditor; - const editorModel = this.getModel(editor); - if (editor && editorModel) { - const modelOptions = editorModel.getOptions(); - const tabSize = modelOptions.tabSize; - const indentSize = modelOptions.indentSize; - const spaceOrTabSizeMessage = modelOptions.insertSpaces - ? nls.localizeByDefault('Spaces: {0}', indentSize) - : nls.localizeByDefault('Tab Size: {0}', tabSize); - this.statusBar.setElement('editor-status-tabbing-config', { - text: spaceOrTabSizeMessage, - alignment: StatusBarAlignment.RIGHT, - priority: 10, - command: EditorCommands.CONFIG_INDENTATION.id, - tooltip: nls.localizeByDefault('Select Indentation') - }); - } + deactivate(statusBar: StatusBar): void { + this.toDispose.dispose(); + this.removeConfigTabSizeWidget(statusBar); + this.removeLineEndingWidget(statusBar); } - protected removeConfigTabSizeWidget(): void { - this.statusBar.removeElement('editor-status-tabbing-config'); + + protected setConfigTabSizeWidget(statusBar: StatusBar, model: monaco.editor.ITextModel): void { + const modelOptions = model.getOptions(); + const tabSize = modelOptions.tabSize; + const indentSize = modelOptions.indentSize; + const spaceOrTabSizeMessage = modelOptions.insertSpaces + ? nls.localizeByDefault('Spaces: {0}', indentSize) + : nls.localizeByDefault('Tab Size: {0}', tabSize); + statusBar.setElement(EDITOR_STATUS_TABBING_CONFIG, { + text: spaceOrTabSizeMessage, + alignment: StatusBarAlignment.RIGHT, + priority: 10, + command: EditorCommands.CONFIG_INDENTATION.id, + tooltip: nls.localizeByDefault('Select Indentation') + }); } - protected setLineEndingWidget(): void { - const editor = this.editorManager.currentEditor; - const editorModel = this.getModel(editor); - if (editor && editorModel) { - const eol = editorModel.getEOL(); - const text = eol === '\n' ? 'LF' : 'CRLF'; - this.statusBar.setElement('editor-status-eol', { - text: `${text}`, - alignment: StatusBarAlignment.RIGHT, - priority: 11, - command: EditorCommands.CONFIG_EOL.id, - tooltip: nls.localizeByDefault('Select End of Line Sequence') - }); - } + protected removeConfigTabSizeWidget(statusBar: StatusBar): void { + statusBar.removeElement(EDITOR_STATUS_TABBING_CONFIG); } - protected removeLineEndingWidget(): void { - this.statusBar.removeElement('editor-status-eol'); + + protected setLineEndingWidget(statusBar: StatusBar, model: monaco.editor.ITextModel): void { + const eol = model.getEOL(); + const text = eol === '\n' ? 'LF' : 'CRLF'; + statusBar.setElement(EDITOR_STATUS_EOL, { + text: `${text}`, + alignment: StatusBarAlignment.RIGHT, + priority: 11, + command: EditorCommands.CONFIG_EOL.id, + tooltip: nls.localizeByDefault('Select End of Line Sequence') + }); + } + + protected removeLineEndingWidget(statusBar: StatusBar): void { + statusBar.removeElement(EDITOR_STATUS_EOL); } protected getModel(editor: EditorWidget | undefined): monaco.editor.ITextModel | undefined { diff --git a/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts b/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts index 05ded847b5bb3..740970baf76ca 100644 --- a/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-status-bar-contribution.ts @@ -14,10 +14,9 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { inject, injectable } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser'; +import { injectable } from '@theia/core/shared/inversify'; +import { StatusBar, StatusBarAlignment, Widget, WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { Disposable } from '@theia/core/lib/common'; -import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NotebookEditorWidget } from '../notebook-editor-widget'; import { nls } from '@theia/core'; import { NotebookCommands } from './notebook-actions-contribution'; @@ -25,53 +24,43 @@ import { NotebookCommands } from './notebook-actions-contribution'; export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position'; @injectable() -export class NotebookStatusBarContribution implements FrontendApplicationContribution { +export class NotebookStatusBarContribution implements WidgetStatusBarContribution { - @inject(StatusBar) protected readonly statusBar: StatusBar; - @inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService; + protected onDeactivate: Disposable | undefined; - protected currentCellSelectionListener: Disposable | undefined; - protected lastFocusedEditor: NotebookEditorWidget | undefined; + canHandle(widget: Widget): widget is NotebookEditorWidget { + return widget instanceof NotebookEditorWidget; + } - onStart(): void { - this.editorWidgetService.onDidChangeFocusedEditor(editor => { - this.currentCellSelectionListener?.dispose(); - this.currentCellSelectionListener = editor?.model?.onDidChangeSelectedCell(() => - this.updateStatusbar(editor) - ); - editor?.onDidDispose(() => { - this.lastFocusedEditor = undefined; - this.updateStatusbar(); + activate(statusBar: StatusBar, widget: NotebookEditorWidget): void { + widget.ready.then(model => { + this.onDeactivate = model.onDidChangeSelectedCell(() => { + this.updateStatusbar(statusBar, widget); }); - this.updateStatusbar(editor); - this.lastFocusedEditor = editor; }); - if (this.editorWidgetService.focusedEditor) { - this.updateStatusbar(); - } + this.updateStatusbar(statusBar, widget); } - protected async updateStatusbar(editor?: NotebookEditorWidget): Promise { - if ((!editor && !this.lastFocusedEditor?.isVisible) || editor?.model?.cells.length === 0) { - this.statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID); - return; - } + deactivate(statusBar: StatusBar): void { + this.onDeactivate?.dispose(); + this.updateStatusbar(statusBar); + } - await editor?.ready; - if (!editor?.model) { + protected async updateStatusbar(statusBar: StatusBar, editor?: NotebookEditorWidget): Promise { + const model = await editor?.ready; + if (!model || model.cells.length === 0 || !model.selectedCell) { + statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID); return; } - const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : ''; + const selectedCellIndex = model.cells.indexOf(model.selectedCell) + 1; - this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, { - text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length), + statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, { + text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, model.cells.length), alignment: StatusBarAlignment.RIGHT, priority: 100, command: NotebookCommands.CENTER_ACTIVE_CELL.id, arguments: [editor] }); - } - } diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index 839d16908a17d..b5158315668b2 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -16,7 +16,9 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; -import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory } from '@theia/core/lib/browser'; +import { + FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory, WidgetStatusBarContribution +} from '@theia/core/lib/browser'; import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution'; import { NotebookOpenHandler } from './notebook-open-handler'; import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core'; @@ -117,5 +119,5 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(UndoRedoHandler).toService(NotebookUndoRedoHandler); bind(NotebookStatusBarContribution).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution); + bind(WidgetStatusBarContribution).toService(NotebookStatusBarContribution); }); diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index a8510268870f3..5e592c30c397b 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -22,7 +22,9 @@ import { ContainerModule } from '@theia/core/shared/inversify'; import { FrontendApplicationContribution, WidgetFactory, bindViewContribution, ViewContainerIdentifier, ViewContainer, createTreeContainer, TreeWidget, LabelProviderContribution, LabelProvider, - UndoRedoHandler, DiffUris, Navigatable, SplitWidget + UndoRedoHandler, DiffUris, Navigatable, SplitWidget, + noopWidgetStatusBarContribution, + WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { MaybePromise, CommandContribution, ResourceResolver, bindContributionProvider, URI, generateUuid } from '@theia/core/lib/common'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging'; @@ -191,6 +193,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(WebviewSecondaryWindowSupport).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(WebviewSecondaryWindowSupport); bind(FrontendApplicationContribution).toService(WebviewContextKeys); + bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(WebviewWidget)); bind(PluginCustomEditorRegistry).toSelf().inSingletonScope(); bind(CustomEditorService).toSelf().inSingletonScope(); diff --git a/packages/preferences/src/browser/preference-frontend-module.ts b/packages/preferences/src/browser/preference-frontend-module.ts index ef7d553bcee84..d55f47e63d60b 100644 --- a/packages/preferences/src/browser/preference-frontend-module.ts +++ b/packages/preferences/src/browser/preference-frontend-module.ts @@ -17,7 +17,7 @@ import '../../src/browser/style/index.css'; import './preferences-monaco-contribution'; import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; -import { bindViewContribution, FrontendApplicationContribution, OpenHandler } from '@theia/core/lib/browser'; +import { bindViewContribution, FrontendApplicationContribution, noopWidgetStatusBarContribution, OpenHandler, WidgetStatusBarContribution } from '@theia/core/lib/browser'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { PreferenceTreeGenerator } from './util/preference-tree-generator'; import { bindPreferenceProviders } from './preference-bindings'; @@ -33,6 +33,7 @@ import { CliPreferences, CliPreferencesPath } from '../common/cli-preferences'; import { ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider'; import { PreferenceFrontendContribution } from './preference-frontend-contribution'; import { PreferenceLayoutProvider } from './util/preference-layout'; +import { PreferencesWidget } from './views/preference-widget'; export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind): void { bindPreferenceProviders(bind, unbind); @@ -59,6 +60,8 @@ export function bindPreferences(bind: interfaces.Bind, unbind: interfaces.Unbind bind(CliPreferences).toDynamicValue(ctx => ServiceConnectionProvider.createProxy(ctx.container, CliPreferencesPath)).inSingletonScope(); bind(PreferenceFrontendContribution).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(PreferenceFrontendContribution); + + bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(PreferencesWidget)); } export default new ContainerModule((bind, unbind, isBound, rebind) => { diff --git a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts index 2bb0b34eec0c9..19736daf75b62 100644 --- a/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts +++ b/packages/vsx-registry/src/browser/vsx-registry-frontend-module.ts @@ -18,7 +18,9 @@ import '../../src/browser/style/index.css'; import { ContainerModule } from '@theia/core/shared/inversify'; import { - WidgetFactory, bindViewContribution, FrontendApplicationContribution, ViewContainerIdentifier, OpenHandler, WidgetManager, WebSocketConnectionProvider + WidgetFactory, bindViewContribution, FrontendApplicationContribution, ViewContainerIdentifier, OpenHandler, WidgetManager, WebSocketConnectionProvider, + WidgetStatusBarContribution, + noopWidgetStatusBarContribution } from '@theia/core/lib/browser'; import { VSXExtensionsViewContainer } from './vsx-extensions-view-container'; import { VSXExtensionsContribution } from './vsx-extensions-contribution'; @@ -64,6 +66,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { })).inSingletonScope(); bind(VSXExtensionEditorManager).toSelf().inSingletonScope(); bind(OpenHandler).toService(VSXExtensionEditorManager); + bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(VSXExtensionEditor)); bind(WidgetFactory).toDynamicValue(({ container }) => ({ id: VSXExtensionsWidget.ID, From ce5e13fa3a8b4fc602409f9732541a2785cfbbcf Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 3 Oct 2024 10:39:17 +0200 Subject: [PATCH 408/441] [Theia AI] Terminal agent records its requests (#14246) * Terminal agent records its requests fixed #14245 Signed-off-by: Jonas Helming --- .../src/browser/ai-terminal-agent.ts | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/ai-terminal/src/browser/ai-terminal-agent.ts b/packages/ai-terminal/src/browser/ai-terminal-agent.ts index f68f73eeb3424..98d083d7bee76 100644 --- a/packages/ai-terminal/src/browser/ai-terminal-agent.ts +++ b/packages/ai-terminal/src/browser/ai-terminal-agent.ts @@ -16,12 +16,13 @@ import { Agent, + CommunicationRecordingService, getJsonOfResponse, isLanguageModelParsedResponse, LanguageModelRegistry, LanguageModelRequirement, PromptService } from '@theia/ai-core/lib/common'; -import { ILogger } from '@theia/core'; +import { generateUuid, ILogger } from '@theia/core'; import { inject, injectable } from '@theia/core/shared/inversify'; import { z } from 'zod'; import zodToJsonSchema from 'zod-to-json-schema'; @@ -33,6 +34,8 @@ type Commands = z.infer; @injectable() export class AiTerminalAgent implements Agent { + @inject(CommunicationRecordingService) + protected recordingService: CommunicationRecordingService; id = 'Terminal Assistant'; name = 'Terminal Assistant'; @@ -153,6 +156,18 @@ recent-terminal-contents: return []; } + // since we do not actually hold complete conversions, the request/response pair is considered a session + const sessionId = generateUuid(); + const requestId = generateUuid(); + this.recordingService.recordRequest({ + agentId: this.id, + sessionId, + timestamp: Date.now(), + requestId, + request: systemPrompt, + messages: [userPrompt], + }); + try { const result = await lm.request({ messages: [ @@ -181,12 +196,28 @@ recent-terminal-contents: // model returned structured output const parsedResult = Commands.safeParse(result.parsed); if (parsedResult.success) { + const responseTextfromParsed = JSON.stringify(parsedResult.data.commands); + this.recordingService.recordResponse({ + agentId: this.id, + sessionId, + timestamp: Date.now(), + requestId, + response: responseTextfromParsed, + }); return parsedResult.data.commands; } } // fall back to agent-based parsing of result const jsonResult = await getJsonOfResponse(result); + const responseTextFromJSON = JSON.stringify(jsonResult); + this.recordingService.recordResponse({ + agentId: this.id, + sessionId, + timestamp: Date.now(), + requestId, + response: responseTextFromJSON + }); const parsedJsonResult = Commands.safeParse(jsonResult); if (parsedJsonResult.success) { return parsedJsonResult.data.commands; From ac0c246fbcd439c259b4778e41199708d4354899 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Thu, 3 Oct 2024 09:14:30 +0200 Subject: [PATCH 409/441] feat(ai): Update OpenAI models supporting structured output --- packages/ai-openai/src/node/openai-language-model.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/ai-openai/src/node/openai-language-model.ts b/packages/ai-openai/src/node/openai-language-model.ts index e7edc59b0fb23..7692a21f8a9e4 100644 --- a/packages/ai-openai/src/node/openai-language-model.ts +++ b/packages/ai-openai/src/node/openai-language-model.ts @@ -134,9 +134,12 @@ export class OpenAiModel implements LanguageModel { } protected supportsStructuredOutput(): boolean { - // currently only the lastest 4o and 4o-mini models support structured output - // see https://platform.openai.com/docs/guides/structured-outputs - return this.model === 'gpt-4o-2024-08-06' || this.model === 'gpt-4o-mini'; + // see https://platform.openai.com/docs/models/gpt-4o + return [ + 'gpt-4o', + 'gpt-4o-2024-08-06', + 'gpt-4o-mini' + ].includes(this.model); } protected async handleStructuredOutputRequest(openai: OpenAI, request: LanguageModelRequest): Promise { From 7ea9c133f6c88ca341a9d3166d9f7b8cdb961f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 7 Oct 2024 09:36:51 +0200 Subject: [PATCH 410/441] Wrap api objects returned to clients in a proxy (#14213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13522 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../src/plugin/file-system-ext-impl.ts | 10 ++-- .../plugin-ext/src/plugin/plugin-context.ts | 48 +++++++++++++------ packages/plugin-ext/src/plugin/scm.ts | 9 ++-- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/packages/plugin-ext/src/plugin/file-system-ext-impl.ts b/packages/plugin-ext/src/plugin/file-system-ext-impl.ts index 9eb59c06cc12c..b54be1a8dc093 100644 --- a/packages/plugin-ext/src/plugin/file-system-ext-impl.ts +++ b/packages/plugin-ext/src/plugin/file-system-ext-impl.ts @@ -40,8 +40,9 @@ import { State, StateMachine, LinkComputer, Edge } from '../common/link-computer import { commonPrefixLength } from '@theia/core/lib/common/strings'; import { CharCode } from '@theia/core/lib/common/char-code'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; -import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol'; import { MarkdownString } from '../common/plugin-api-rpc-model'; +import { Emitter } from '@theia/core/lib/common'; +import { createAPIObject } from './plugin-context'; type IDisposable = vscode.Disposable; @@ -137,8 +138,11 @@ export class FsLinkProvider { } class ConsumerFileSystem implements vscode.FileSystem { + apiObject: vscode.FileSystem; - constructor(private _proxy: FileSystemMain, private _capabilities: Map) { } + constructor(private _proxy: FileSystemMain, private _capabilities: Map) { + this.apiObject = createAPIObject(this); + } stat(uri: vscode.Uri): Promise { return this._proxy.$stat(uri).catch(ConsumerFileSystem._handleError); @@ -210,7 +214,7 @@ export class FileSystemExtImpl implements FileSystemExt { private _handlePool: number = 0; - readonly fileSystem: vscode.FileSystem; + readonly fileSystem: ConsumerFileSystem; constructor(rpc: RPCProtocol) { this._proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.FILE_SYSTEM_MAIN); diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index f32102a2aa1bf..61688deddeecf 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -279,6 +279,21 @@ import { NotebookEditorsExtImpl } from './notebook/notebook-editors'; import { TestingExtImpl } from './tests'; import { UriExtImpl } from './uri-ext'; +export function createAPIObject(rawObject: T): T { + return new Proxy(rawObject, { + get(target, p, receiver) { + const isOwnProperty = !!Object.getOwnPropertyDescriptor(target, p); + const val = Reflect.get(target, p); + if (!isOwnProperty && typeof val === 'function') { + // bind functions that are inherited from the prototype to the object itself. + // This should handle the case of events. + return val.bind(target); + } + return val; + }, + }) as T; +} + export function createAPIFactory( rpc: RPCProtocol, pluginManager: PluginManager, @@ -496,7 +511,8 @@ export function createAPIFactory( return quickOpenExt.showQuickPick(plugin, items, options, token); }, createQuickPick(): theia.QuickPick { - return quickOpenExt.createQuickPick(plugin); + + return createAPIObject(quickOpenExt.createQuickPick(plugin)); }, showWorkspaceFolderPick(options?: theia.WorkspaceFolderPickOptions): PromiseLike { return workspaceExt.pickWorkspaceFolder(options); @@ -535,9 +551,12 @@ export function createAPIFactory( priority = priorityOrAlignment; } + // TODO: here return statusBarMessageRegistryExt.createStatusBarItem(alignment, priority, id); }, createOutputChannel(name: string, options?: { log: true }): any { + + // TODO: here return !options ? outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin)) : outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin), options); @@ -546,7 +565,7 @@ export function createAPIFactory( title: string, showOptions: theia.ViewColumn | theia.WebviewPanelShowOptions, options: theia.WebviewPanelOptions & theia.WebviewOptions = {}): theia.WebviewPanel { - return webviewExt.createWebview(viewType, title, showOptions, options, plugin); + return createAPIObject(webviewExt.createWebview(viewType, title, showOptions, options, plugin)); }, registerWebviewPanelSerializer(viewType: string, serializer: theia.WebviewPanelSerializer): theia.Disposable { return webviewExt.registerWebviewPanelSerializer(viewType, serializer, plugin); @@ -574,19 +593,19 @@ export function createAPIFactory( createTerminal(nameOrOptions: theia.TerminalOptions | theia.ExtensionTerminalOptions | theia.ExtensionTerminalOptions | (string | undefined), shellPath?: string, shellArgs?: string[] | string): theia.Terminal { - return terminalExt.createTerminal(plugin, nameOrOptions, shellPath, shellArgs); + return createAPIObject(terminalExt.createTerminal(plugin, nameOrOptions, shellPath, shellArgs)); }, onDidChangeTerminalState, onDidCloseTerminal, onDidOpenTerminal, createTextEditorDecorationType(options: theia.DecorationRenderOptions): theia.TextEditorDecorationType { - return editors.createTextEditorDecorationType(options); + return createAPIObject(editors.createTextEditorDecorationType(options)); }, registerTreeDataProvider(viewId: string, treeDataProvider: theia.TreeDataProvider): Disposable { return treeViewsExt.registerTreeDataProvider(plugin, viewId, treeDataProvider); }, createTreeView(viewId: string, options: theia.TreeViewOptions): theia.TreeView { - return treeViewsExt.createTreeView(plugin, viewId, options); + return createAPIObject(treeViewsExt.createTreeView(plugin, viewId, options)); }, withScmProgress(task: (progress: theia.Progress) => Thenable) { const options: ProgressOptions = { location: ProgressLocation.SourceControl }; @@ -605,7 +624,7 @@ export function createAPIFactory( return uriExt.registerUriHandler(handler, pluginToPluginInfo(plugin)); }, createInputBox(): theia.InputBox { - return quickOpenExt.createInputBox(plugin); + return createAPIObject(quickOpenExt.createInputBox(plugin)); }, registerTerminalLinkProvider(provider: theia.TerminalLinkProvider): theia.Disposable { return terminalExt.registerTerminalLinkProvider(provider); @@ -653,7 +672,7 @@ export function createAPIFactory( const workspace: typeof theia.workspace = { get fs(): theia.FileSystem { - return fileSystemExt.fileSystem; + return fileSystemExt.fileSystem.apiObject; }, get rootPath(): string | undefined { @@ -756,7 +775,7 @@ export function createAPIFactory( return notebooksExt.getNotebookDocument(uri).apiNotebook; }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): theia.FileSystemWatcher => - extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete), + createAPIObject(extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete)), findFiles(include: theia.GlobPattern, exclude?: theia.GlobPattern | null, maxResults?: number, token?: CancellationToken): PromiseLike { return workspaceExt.findFiles(include, exclude, maxResults, token); }, @@ -849,7 +868,7 @@ export function createAPIFactory( return telemetryExt.onDidChangeTelemetryEnabled; }, createTelemetryLogger(sender: theia.TelemetrySender, options?: theia.TelemetryLoggerOptions): theia.TelemetryLogger { - return telemetryExt.createTelemetryLogger(sender, options); + return createAPIObject(telemetryExt.createTelemetryLogger(sender, options)); }, get remoteName(): string | undefined { return envExt.remoteName; }, get machineId(): string { return envExt.machineId; }, @@ -924,7 +943,7 @@ export function createAPIFactory( return languagesExt.getDiagnostics(resource); }, createDiagnosticCollection(name?: string): theia.DiagnosticCollection { - return languagesExt.createDiagnosticCollection(name); + return createAPIObject(languagesExt.createDiagnosticCollection(name)); }, setLanguageConfiguration(language: string, configuration: theia.LanguageConfiguration): theia.Disposable { return languagesExt.setLanguageConfiguration(language, configuration); @@ -1061,7 +1080,7 @@ export function createAPIFactory( const tests: typeof theia.tests = { createTestController(id, label: string) { - return testingExt.createTestController(id, label); + return createAPIObject(testingExt.createTestController(id, label)); } }; /* End of Tests API */ @@ -1173,6 +1192,7 @@ export function createAPIFactory( }, get taskExecutions(): ReadonlyArray { + // TODO: here return tasksExt.taskExecutions; }, onDidStartTask(listener, thisArg?, disposables?) { @@ -1193,19 +1213,19 @@ export function createAPIFactory( get inputBox(): theia.SourceControlInputBox { const inputBox = scmExt.getLastInputBox(plugin); if (inputBox) { - return inputBox; + return inputBox.apiObject; } else { throw new Error('Input box not found!'); } }, createSourceControl(id: string, label: string, rootUri?: URI): theia.SourceControl { - return scmExt.createSourceControl(plugin, id, label, rootUri); + return createAPIObject(scmExt.createSourceControl(plugin, id, label, rootUri)); } }; const comments: typeof theia.comments = { createCommentController(id: string, label: string): theia.CommentController { - return commentsExt.createCommentController(plugin, id, label); + return createAPIObject(commentsExt.createCommentController(plugin, id, label)); } }; diff --git a/packages/plugin-ext/src/plugin/scm.ts b/packages/plugin-ext/src/plugin/scm.ts index 4703ab3626f14..f34556cee2ae8 100644 --- a/packages/plugin-ext/src/plugin/scm.ts +++ b/packages/plugin-ext/src/plugin/scm.ts @@ -39,6 +39,7 @@ import { URI, ThemeIcon } from './types-impl'; import { ScmCommandArg } from '../common/plugin-api-rpc'; import { sep } from '@theia/core/lib/common/paths'; import { PluginIconPath } from './plugin-icon-path'; +import { createAPIObject } from './plugin-context'; type ProviderHandle = number; type GroupHandle = number; type ResourceStateHandle = number; @@ -290,6 +291,7 @@ interface ValidateInput { export class ScmInputBoxImpl implements theia.SourceControlInputBox { private _value: string = ''; + apiObject: theia.SourceControlInputBox; get value(): string { return this._value; @@ -354,7 +356,7 @@ export class ScmInputBoxImpl implements theia.SourceControlInputBox { } constructor(private plugin: Plugin, private proxy: ScmMain, private sourceControlHandle: number) { - // noop + this.apiObject = createAPIObject(this); } onInputBoxValueChange(value: string): void { @@ -543,8 +545,7 @@ class SourceControlImpl implements theia.SourceControl { return this._rootUri; } - private _inputBox: ScmInputBoxImpl; - get inputBox(): ScmInputBoxImpl { return this._inputBox; } + readonly inputBox: ScmInputBoxImpl; private _count: number | undefined = undefined; @@ -642,7 +643,7 @@ class SourceControlImpl implements theia.SourceControl { private _label: string, private _rootUri?: theia.Uri ) { - this._inputBox = new ScmInputBoxImpl(plugin, this.proxy, this.handle); + this.inputBox = new ScmInputBoxImpl(plugin, this.proxy, this.handle); this.proxy.$registerSourceControl(this.handle, _id, _label, _rootUri); } From 54ab65dfc856afd8263ac79ef0e71e9f352739df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Mon, 7 Oct 2024 11:17:38 +0200 Subject: [PATCH 411/441] Fix css calc expression to have space around operator (#14241) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14204 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/browser/style/tabs.css | 193 +++++++---------------- 1 file changed, 55 insertions(+), 138 deletions(-) diff --git a/packages/core/src/browser/style/tabs.css b/packages/core/src/browser/style/tabs.css index d81927dca9e90..661030977fa90 100644 --- a/packages/core/src/browser/style/tabs.css +++ b/packages/core/src/browser/style/tabs.css @@ -9,10 +9,7 @@ --theia-private-horizontal-tab-scrollbar-height: 5px; --theia-tabbar-toolbar-z-index: 1001; --theia-toolbar-active-transform-scale: 1.272019649; - --theia-horizontal-toolbar-height: calc( - var(--theia-private-horizontal-tab-height) + - var(--theia-private-horizontal-tab-scrollbar-rail-height) / 2 - ); + --theia-horizontal-toolbar-height: calc(var(--theia-private-horizontal-tab-height) + var(--theia-private-horizontal-tab-scrollbar-rail-height) / 2); --theia-dragover-tab-border-width: 2px; } @@ -75,9 +72,7 @@ border-left: var(--theia-border-width) solid var(--theia-editorGroup-border); } -#theia-main-content-panel - .p-DockPanel-handle[data-orientation="vertical"] - + .p-TabBar { +#theia-main-content-panel .p-DockPanel-handle[data-orientation="vertical"]+.p-TabBar { border-top: var(--theia-border-width) solid var(--theia-editorGroup-border); } @@ -142,11 +137,9 @@ -webkit-appearance: none; -moz-appearance: none; - background-image: linear-gradient( - 45deg, + background-image: linear-gradient(45deg, transparent 50%, - var(--theia-icon-foreground) 50% - ), + var(--theia-icon-foreground) 50%), linear-gradient(135deg, var(--theia-icon-foreground) 50%, transparent 50%); background-position: calc(100% - 6px) 8px, calc(100% - 2px) 8px, 100% 0; background-size: 4px 5px; @@ -225,12 +218,8 @@ visibility: hidden; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable - > .p-TabBar-tabCloseIcon, -.p-TabBar.theia-app-centers - .p-TabBar-tab.theia-mod-pinned - > .p-TabBar-tabCloseIcon { +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable>.p-TabBar-tabCloseIcon, +.p-TabBar.theia-app-centers .p-TabBar-tab.theia-mod-pinned>.p-TabBar-tabCloseIcon { padding: 2px; margin-top: 2px; margin-left: 4px; @@ -248,31 +237,19 @@ -ms-user-select: none; } -.p-TabBar.theia-app-centers.dynamic-tabs - .p-TabBar-tab.p-mod-closable - > .p-TabBar-tabCloseIcon, -.p-TabBar.theia-app-centers.dynamic-tabs - .p-TabBar-tab.theia-mod-pinned - > .p-TabBar-tabCloseIcon { +.p-TabBar.theia-app-centers.dynamic-tabs .p-TabBar-tab.p-mod-closable>.p-TabBar-tabCloseIcon, +.p-TabBar.theia-app-centers.dynamic-tabs .p-TabBar-tab.theia-mod-pinned>.p-TabBar-tabCloseIcon { /* hide close icon for dynamic tabs strategy*/ display: none; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-current - > .p-TabBar-tabCloseIcon, -.p-TabBar.theia-app-centers - .p-TabBar-tab:hover.p-mod-closable - > .p-TabBar-tabCloseIcon, -.p-TabBar.theia-app-centers - .p-TabBar-tab:hover.theia-mod-pinned - > .p-TabBar-tabCloseIcon { +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-current>.p-TabBar-tabCloseIcon, +.p-TabBar.theia-app-centers .p-TabBar-tab:hover.p-mod-closable>.p-TabBar-tabCloseIcon, +.p-TabBar.theia-app-centers .p-TabBar-tab:hover.theia-mod-pinned>.p-TabBar-tabCloseIcon { display: inline-block; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable - > .p-TabBar-tabCloseIcon:hover { +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable>.p-TabBar-tabCloseIcon:hover { border-radius: 5px; background-color: rgba(50%, 50%, 50%, 0.2); } @@ -282,33 +259,21 @@ padding-right: 4px; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable:not(.theia-mod-dirty):hover - > .p-TabBar-tabCloseIcon:before, -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable:not(.theia-mod-dirty).p-TabBar-tab.p-mod-current - > .p-TabBar-tabCloseIcon:before, -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable.theia-mod-dirty - > .p-TabBar-tabCloseIcon:hover:before { +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable:not(.theia-mod-dirty):hover>.p-TabBar-tabCloseIcon:before, +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable:not(.theia-mod-dirty).p-TabBar-tab.p-mod-current>.p-TabBar-tabCloseIcon:before, +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty>.p-TabBar-tabCloseIcon:hover:before { content: "\ea76"; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.p-mod-closable.theia-mod-dirty - > .p-TabBar-tabCloseIcon:before { +.p-TabBar.theia-app-centers .p-TabBar-tab.p-mod-closable.theia-mod-dirty>.p-TabBar-tabCloseIcon:before { content: "\ea71"; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.theia-mod-pinned - > .p-TabBar-tabCloseIcon:before { +.p-TabBar.theia-app-centers .p-TabBar-tab.theia-mod-pinned>.p-TabBar-tabCloseIcon:before { content: "\eba0"; } -.p-TabBar.theia-app-centers - .p-TabBar-tab.theia-mod-pinned.theia-mod-dirty - > .p-TabBar-tabCloseIcon:before { +.p-TabBar.theia-app-centers .p-TabBar-tab.theia-mod-pinned.theia-mod-dirty>.p-TabBar-tabCloseIcon:before { content: "\ebb2"; } @@ -351,72 +316,35 @@ | Perfect scrollbar |----------------------------------------------------------------------------*/ -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x { +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x { height: var(--theia-private-horizontal-tab-scrollbar-rail-height); z-index: 1000; } -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x - > .ps__thumb-x { +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x>.ps__thumb-x { height: var(--theia-private-horizontal-tab-scrollbar-height) !important; - bottom: calc( - ( - var(--theia-private-horizontal-tab-scrollbar-rail-height) - - var(--theia-private-horizontal-tab-scrollbar-height) - ) / 2 - ); -} - -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x:hover, -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x:focus { + bottom: calc((var(--theia-private-horizontal-tab-scrollbar-rail-height) - var(--theia-private-horizontal-tab-scrollbar-height)) / 2); +} + +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x:hover, +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x:focus { height: var(--theia-private-horizontal-tab-scrollbar-rail-height) !important; } -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x:hover - > .ps__thumb-x, -.p-TabBar[data-orientation="horizontal"] - .p-TabBar-content-container - > .ps__rail-x:focus - > .ps__thumb-x { - height: calc( - var(--theia-private-horizontal-tab-scrollbar-height) / 2 - ) !important; - bottom: calc( - ( - var(--theia-private-horizontal-tab-scrollbar-rail-height) - - var(--theia-private-horizontal-tab-scrollbar-height) - ) / 2 - ); -} - -.p-TabBar[data-orientation="vertical"] - .p-TabBar-content-container - > .ps__rail-y { +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x:hover>.ps__thumb-x, +.p-TabBar[data-orientation="horizontal"] .p-TabBar-content-container>.ps__rail-x:focus>.ps__thumb-x { + height: calc(var(--theia-private-horizontal-tab-scrollbar-height) / 2) !important; + bottom: calc((var(--theia-private-horizontal-tab-scrollbar-rail-height) - var(--theia-private-horizontal-tab-scrollbar-height)) / 2); +} + +.p-TabBar[data-orientation="vertical"] .p-TabBar-content-container>.ps__rail-y { width: var(--theia-private-horizontal-tab-scrollbar-rail-height); z-index: 1000; } -.p-TabBar[data-orientation="vertical"] - .p-TabBar-content-container - > .ps__rail-y - > .ps__thumb-y { +.p-TabBar[data-orientation="vertical"] .p-TabBar-content-container>.ps__rail-y>.ps__thumb-y { width: var(--theia-private-horizontal-tab-scrollbar-height) !important; - right: calc( - ( - var(--theia-private-horizontal-tab-scrollbar-rail-height) - - var(--theia-private-horizontal-tab-scrollbar-height) - ) / 2 - ); + right: calc((var(--theia-private-horizontal-tab-scrollbar-rail-height) - var(--theia-private-horizontal-tab-scrollbar-height)) / 2); } .p-TabBar[data-orientation="vertical"] .p-TabBar-content-container { @@ -447,9 +375,8 @@ |----------------------------------------------------------------------------*/ .p-TabBar-toolbar { - z-index: var( - --theia-tabbar-toolbar-z-index - ); /* Due to the scrollbar (`z-index: 1000;`) it has a greater `z-index`. */ + z-index: var(--theia-tabbar-toolbar-z-index); + /* Due to the scrollbar (`z-index: 1000;`) it has a greater `z-index`. */ display: flex; flex-direction: row-reverse; padding: 4px; @@ -460,7 +387,8 @@ .p-TabBar-content-container { display: flex; flex: 1; - position: relative; /* This is necessary for perfect-scrollbar */ + position: relative; + /* This is necessary for perfect-scrollbar */ } .p-TabBar-toolbar .item { @@ -473,7 +401,7 @@ } .p-TabBar-toolbar .item>div { - height: 100%; + height: 100%; } .p-TabBar-toolbar .item.enabled { @@ -491,14 +419,14 @@ background-color: var(--theia-inputOption-activeBackground); } -.p-TabBar-toolbar .item > div { - line-height: calc(var(--theia-icon-size)+2px); - height: calc(var(--theia-icon-size)+2px); +.p-TabBar-toolbar .item>div { + line-height: calc(var(--theia-icon-size) + 2px); + height: calc(var(--theia-icon-size) + 2px); background-repeat: no-repeat; line-height: 18px; } -.p-TabBar-toolbar .item > div.no-icon { +.p-TabBar-toolbar .item>div.no-icon { /* Make room for a text label instead of an icon. */ width: 100%; } @@ -532,9 +460,7 @@ vertical-align: bottom; } -#theia-main-content-panel - .p-TabBar:not(.theia-tabBar-active) - .p-TabBar-toolbar { +#theia-main-content-panel .p-TabBar:not(.theia-tabBar-active) .p-TabBar-toolbar { display: none; } @@ -543,9 +469,7 @@ } .p-TabBar.theia-tabBar-multirow[data-orientation="horizontal"] { - min-height: calc( - var(--theia-breadcrumbs-height) + var(--theia-horizontal-toolbar-height) - ); + min-height: calc(var(--theia-breadcrumbs-height) + var(--theia-horizontal-toolbar-height)); flex-direction: column; } @@ -569,19 +493,14 @@ flex-direction: column; } -.p-TabBar.theia-app-centers[data-orientation="horizontal"].dynamic-tabs - .p-TabBar-tabLabel { +.p-TabBar.theia-app-centers[data-orientation="horizontal"].dynamic-tabs .p-TabBar-tabLabel { /* fade out text with dynamic tabs strategy */ - mask-image: linear-gradient( - to left, - rgba(0, 0, 0, 0.3), - rgba(0, 0, 0, 1) 15px - ); - -webkit-mask-image: linear-gradient( - to left, - rgba(0, 0, 0, 0.3), - rgba(0, 0, 0, 1) 15px - ); + mask-image: linear-gradient(to left, + rgba(0, 0, 0, 0.3), + rgba(0, 0, 0, 1) 15px); + -webkit-mask-image: linear-gradient(to left, + rgba(0, 0, 0, 0.3), + rgba(0, 0, 0, 1) 15px); flex: 1; } @@ -625,13 +544,11 @@ /*----------------------------------------------------------------------------- | Open tabs dropdown |----------------------------------------------------------------------------*/ -.theia-tabBar-open-tabs - > .theia-select-component - .theia-select-component-label { +.theia-tabBar-open-tabs>.theia-select-component .theia-select-component-label { display: none; } -.theia-tabBar-open-tabs > .theia-select-component { +.theia-tabBar-open-tabs>.theia-select-component { min-width: auto; height: 100%; } @@ -644,4 +561,4 @@ .theia-tabBar-open-tabs.p-mod-hidden { display: none; -} +} \ No newline at end of file From b388d4cf088c5efb7e2f41d1973db5be3d004868 Mon Sep 17 00:00:00 2001 From: eyyyyyyy3 <64200003+eyyyyyyy3@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:21:10 +0200 Subject: [PATCH 412/441] Update Plugin-API.md (#14254) Fixed the "RCP" typo to "RPC" --- doc/Plugin-API.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/Plugin-API.md b/doc/Plugin-API.md index ae614f70a101b..54c0f91e27a64 100644 --- a/doc/Plugin-API.md +++ b/doc/Plugin-API.md @@ -77,9 +77,9 @@ For headless plugins, "Main-Ext" pattern is very similar, except that there is o ![Communication between Theia and Plugin API for Headless Plugins](./images/headless-plugin-diagram.svg) -As the lifecycle of a plugin starts inside its process on the `Ext` side, anything that the plugin needs from Theia (e.g. state, command execution, access to services) has to be invoked over RCP via an implementation on the `Main` side. +As the lifecycle of a plugin starts inside its process on the `Ext` side, anything that the plugin needs from Theia (e.g. state, command execution, access to services) has to be invoked over RPC via an implementation on the `Main` side. In the inverse direction, the same is true for code that runs on the `Main` side and that needs something from the plugin side (e.g. changing plugin state after a user input). -It needs to be invoked over RCP via an implementation on the `Ext` side. +It needs to be invoked over RPC via an implementation on the `Ext` side. Therefore, `Main` and `Ext` interfaces usually come in pairs (e.g. [LanguagesExt](https://github.com/eclipse-theia/theia/blob/541b300adc029ab1dd729da1ca49179ace1447b2/packages/plugin-ext/src/common/plugin-api-rpc.ts#L1401) and [LanguagesMain](https://github.com/eclipse-theia/theia/blob/541b300adc029ab1dd729da1ca49179ace1447b2/packages/plugin-ext/src/common/plugin-api-rpc.ts#L1474)). To communicate with each other, the implementation of each side of the API - `Main` and `Ext` - has an RPC proxy of its corresponding counterpart. @@ -160,9 +160,9 @@ export function createAPIFactory( ### Adding new Ext and Main interfaces with implementations -`Ext` and `Main` interfaces only contain the functions called over RCP. +`Ext` and `Main` interfaces only contain the functions called over RPC. Further functions are just part of the implementations. -Functions to be called over RCP must start with `$`, e.g. `$executeStuff`. +Functions to be called over RPC must start with `$`, e.g. `$executeStuff`. - Define `Ext` and `Main` interfaces in [plugin-ext/src/common/plugin-api-rpc.ts](https://github.com/eclipse-theia/theia/blob/master/packages/plugin-ext/src/common/plugin-api-rpc.ts). The interfaces should be suffixed with `Ext` and `Main` correspondingly (e.g. `LanguagesMain` and `LanguagesExt`). From 92f47202fd1acae00b4a2429b9c522baa5098999 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 8 Oct 2024 10:29:55 +0200 Subject: [PATCH 413/441] Replace `nsfw` with `@parcel/watcher` (#14194) --- .../src/generator/webpack-generator.ts | 4 +- .../application-manager/src/rebuild.ts | 1 - .../native-webpack-plugin/package.json | 1 + .../src/native-webpack-plugin.ts | 17 +- .../private-ext-scripts/theia-ts-clean.js | 11 +- .../src/tests/theia-explorer-view.test.ts | 5 +- package.json | 1 - packages/core/README.md | 6 +- packages/core/README_TEMPLATE.md | 4 +- packages/core/package.json | 4 +- .../core/shared/@parcel/watcher/index.d.ts | 2 + packages/core/shared/@parcel/watcher/index.js | 1 + packages/core/shared/nsfw/index.d.ts | 2 - packages/core/shared/nsfw/index.js | 1 - .../core/src/node/logger-cli-contribution.ts | 18 +- .../src/browser/file-tree/file-tree-model.ts | 20 +-- .../src/browser/filesystem-preferences.ts | 3 +- .../src/node/filesystem-backend-module.ts | 54 +++--- .../src/node/filesystem-watcher-client.ts | 4 +- .../{nsfw-watcher => parcel-watcher}/index.ts | 6 +- .../parcel-filesystem-service.ts} | 134 +++++++-------- .../parcel-filesystem-watcher.spec.ts} | 51 +++--- .../parcel-options.ts} | 8 +- .../filesystem/src/typings/nsfw/index.d.ts | 18 -- yarn.lock | 156 +++++++++++++----- 25 files changed, 285 insertions(+), 247 deletions(-) create mode 100644 packages/core/shared/@parcel/watcher/index.d.ts create mode 100644 packages/core/shared/@parcel/watcher/index.js delete mode 100644 packages/core/shared/nsfw/index.d.ts delete mode 100644 packages/core/shared/nsfw/index.js rename packages/filesystem/src/node/{nsfw-watcher => parcel-watcher}/index.ts (90%) rename packages/filesystem/src/node/{nsfw-watcher/nsfw-filesystem-service.ts => parcel-watcher/parcel-filesystem-service.ts} (76%) rename packages/filesystem/src/node/{nsfw-watcher/nsfw-filesystem-watcher.spec.ts => parcel-watcher/parcel-filesystem-watcher.spec.ts} (82%) rename packages/filesystem/src/node/{nsfw-watcher/nsfw-options.ts => parcel-watcher/parcel-options.ts} (80%) delete mode 100644 packages/filesystem/src/typings/nsfw/index.d.ts diff --git a/dev-packages/application-manager/src/generator/webpack-generator.ts b/dev-packages/application-manager/src/generator/webpack-generator.ts index c02fae28273e5..9cad52452cfb5 100644 --- a/dev-packages/application-manager/src/generator/webpack-generator.ts +++ b/dev-packages/application-manager/src/generator/webpack-generator.ts @@ -362,7 +362,7 @@ const production = mode === 'production'; const commonJsLibraries = {}; for (const [entryPointName, entryPointPath] of Object.entries({ ${this.ifPackage('@theia/plugin-ext', "'backend-init-theia': '@theia/plugin-ext/lib/hosted/node/scanners/backend-init-theia',")} - ${this.ifPackage('@theia/filesystem', "'nsfw-watcher': '@theia/filesystem/lib/node/nsfw-watcher',")} + ${this.ifPackage('@theia/filesystem', "'parcel-watcher': '@theia/filesystem/lib/node/parcel-watcher',")} ${this.ifPackage('@theia/plugin-ext-vscode', "'plugin-vscode-init': '@theia/plugin-ext-vscode/lib/node/plugin-vscode-init',")} ${this.ifPackage('@theia/api-provider-sample', "'gotd-api-init': '@theia/api-provider-sample/lib/plugin/gotd-api-init',")} ${this.ifPackage('@theia/git', "'git-locator-host': '@theia/git/lib/node/git-locator/git-locator-host',")} @@ -486,6 +486,8 @@ const config = { module: /express/ }, { module: /cross-spawn/ + }, { + module: /@parcel\\/watcher/ } ] }; diff --git a/dev-packages/application-manager/src/rebuild.ts b/dev-packages/application-manager/src/rebuild.ts index cbb0e59e72403..5b0e8010f090c 100644 --- a/dev-packages/application-manager/src/rebuild.ts +++ b/dev-packages/application-manager/src/rebuild.ts @@ -32,7 +32,6 @@ type NodeABI = string | number; export const DEFAULT_MODULES = [ 'node-pty', - 'nsfw', 'native-keymap', 'find-git-repositories', 'drivelist', diff --git a/dev-packages/native-webpack-plugin/package.json b/dev-packages/native-webpack-plugin/package.json index cf2394dc898af..cf9c289074dcb 100644 --- a/dev-packages/native-webpack-plugin/package.json +++ b/dev-packages/native-webpack-plugin/package.json @@ -29,6 +29,7 @@ "watch": "theiaext watch" }, "dependencies": { + "detect-libc": "^2.0.2", "tslib": "^2.6.2", "webpack": "^5.76.0" } diff --git a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts index eafa1a01ac386..18ae43c186138 100644 --- a/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts +++ b/dev-packages/native-webpack-plugin/src/native-webpack-plugin.ts @@ -24,6 +24,7 @@ const REQUIRE_RIPGREP = '@vscode/ripgrep'; const REQUIRE_VSCODE_WINDOWS_CA_CERTS = '@vscode/windows-ca-certs'; const REQUIRE_BINDINGS = 'bindings'; const REQUIRE_KEYMAPPING = './build/Release/keymapping'; +const REQUIRE_PARCEL_WATCHER = './build/Release/watcher.node'; export interface NativeWebpackPluginOptions { out: string; @@ -72,7 +73,8 @@ export class NativeWebpackPlugin { [REQUIRE_RIPGREP]: ripgrepFile, [REQUIRE_BINDINGS]: bindingsFile, [REQUIRE_KEYMAPPING]: keymappingFile, - [REQUIRE_VSCODE_WINDOWS_CA_CERTS]: windowsCaCertsFile + [REQUIRE_VSCODE_WINDOWS_CA_CERTS]: windowsCaCertsFile, + [REQUIRE_PARCEL_WATCHER]: findNativeWatcherFile() }; }); compiler.hooks.normalModuleFactory.tap( @@ -154,6 +156,19 @@ export class NativeWebpackPlugin { } } +function findNativeWatcherFile(): string { + let name = `@parcel/watcher-${process.platform}-${process.arch}`; + if (process.platform === 'linux') { + const { MUSL, family } = require('detect-libc'); + if (family === MUSL) { + name += '-musl'; + } else { + name += '-glibc'; + } + } + return require.resolve(name); +} + async function buildFile(root: string, name: string, content: string): Promise { const tmpFile = path.join(root, name); await fs.promises.writeFile(tmpFile, content); diff --git a/dev-packages/private-ext-scripts/theia-ts-clean.js b/dev-packages/private-ext-scripts/theia-ts-clean.js index 62c3ba9e751c6..9bdaa0c860c3e 100755 --- a/dev-packages/private-ext-scripts/theia-ts-clean.js +++ b/dev-packages/private-ext-scripts/theia-ts-clean.js @@ -21,7 +21,7 @@ const _glob = require('glob'); const debug = require('debug')('ts-clean'); const fs = require('fs'); -const nsfw = require('nsfw'); +const parcelWatcher = require('@parcel/watcher'); const path = require('path'); const util = require('util'); const yargs = require('yargs'); @@ -121,13 +121,11 @@ async function tsClean() { */ async function tsCleanWatch(src, dst, dry) { await tsCleanRun(src, dst, dry); - const watcher = await nsfw(src, async events => { + await parcelWatcher.subscribe(src, async (_err, events) => { for (const event of events) { let absolute; - if (event.action === nsfw.actions.DELETED) { - absolute = path.resolve(event.directory, event.file); - } else if (event.action === nsfw.actions.RENAMED) { - absolute = path.resolve(event.directory, event.oldFile); + if (event.type === 'delete') { + absolute = event.path; } else { continue; } @@ -143,7 +141,6 @@ async function tsCleanWatch(src, dst, dry) { })); } }); - await watcher.start(); } /** diff --git a/examples/playwright/src/tests/theia-explorer-view.test.ts b/examples/playwright/src/tests/theia-explorer-view.test.ts index 1705244609988..f3362f3f212b3 100644 --- a/examples/playwright/src/tests/theia-explorer-view.test.ts +++ b/examples/playwright/src/tests/theia-explorer-view.test.ts @@ -183,7 +183,8 @@ test.describe('Theia Explorer View', () => { expect(await explorer.existsDirectoryNode('sampleDirectoryCompact/nestedFolder1/nestedFolder2', true /* compact */)).toBe(true); }); - test('should delete nested folder "sampleDirectoryCompact/nestedFolder1/nestedFolder2"', async () => { + // TODO These tests only seems to fail on Ubuntu - it's not clear why + test.skip('should delete nested folder "sampleDirectoryCompact/nestedFolder1/nestedFolder2"', async () => { const fileStatElements = await explorer.visibleFileStatNodes(); expect(await explorer.existsDirectoryNode('sampleDirectoryCompact/nestedFolder1/nestedFolder2', true /* compact */)).toBe(true); await explorer.deleteNode('sampleDirectoryCompact/nestedFolder1/nestedFolder2', true /* confirm */, 'nestedFolder2' /* nodeSegmentLabel */); @@ -192,7 +193,7 @@ test.describe('Theia Explorer View', () => { expect(updatedFileStatElements.length).toBe(fileStatElements.length - 1); }); - test('should delete compact folder "sampleDirectoryCompact/nestedFolder1"', async () => { + test.skip('should delete compact folder "sampleDirectoryCompact/nestedFolder1"', async () => { const fileStatElements = await explorer.visibleFileStatNodes(); expect(await explorer.existsDirectoryNode('sampleDirectoryCompact/nestedFolder1', true /* compact */)).toBe(true); await explorer.deleteNode('sampleDirectoryCompact/nestedFolder1', true /* confirm */, 'sampleDirectoryCompact' /* nodeSegmentLabel */); diff --git a/package.json b/package.json index 157c3aca44982..abe2eea97009a 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "lerna": "^7.1.1", "mkdirp": "^0.5.0", "node-gyp": "^9.0.0", - "nsfw": "^2.2.4", "nyc": "^15.0.0", "puppeteer": "19.7.2", "puppeteer-core": "19.7.2", diff --git a/packages/core/README.md b/packages/core/README.md index 438d5b4d9ead9..af80e14c75ef5 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -98,11 +98,11 @@ export class SomeClass { - `react-virtuoso` (from [`react-virtuoso@^2.17.0`](https://www.npmjs.com/package/react-virtuoso)) - `vscode-languageserver-protocol` (from [`vscode-languageserver-protocol@^3.17.2`](https://www.npmjs.com/package/vscode-languageserver-protocol)) - `vscode-uri` (from [`vscode-uri@^2.1.1`](https://www.npmjs.com/package/vscode-uri)) + - `@parcel/watcher` (from [`@parcel/watcher@^2.4.1`](https://www.npmjs.com/package/@parcel/watcher)) - `dompurify` (from [`dompurify@^2.2.9`](https://www.npmjs.com/package/dompurify)) - `express` (from [`express@^4.16.3`](https://www.npmjs.com/package/express)) - `lodash.debounce` (from [`lodash.debounce@^4.0.8`](https://www.npmjs.com/package/lodash.debounce)) - `lodash.throttle` (from [`lodash.throttle@^4.1.1`](https://www.npmjs.com/package/lodash.throttle)) - - `nsfw` (from [`nsfw@^2.2.4`](https://www.npmjs.com/package/nsfw)) - `markdown-it` (from [`markdown-it@^12.3.2`](https://www.npmjs.com/package/markdown-it)) - `react` (from [`react@^18.2.0`](https://www.npmjs.com/package/react)) - `ws` (from [`ws@^8.17.1`](https://www.npmjs.com/package/ws)) @@ -138,8 +138,8 @@ existing loggers. However, each log message specifies from which logger it comes from, which can give an idea, without having to read the code: ``` -root INFO [nsfw-watcher: 10734] Started watching: /Users/captain.future/git/theia/CONTRIBUTING.md -^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^ +root INFO [parcel-watcher: 10734] Started watching: /Users/captain.future/git/theia/CONTRIBUTING.md +^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^ ``` Where `root` is the name of the logger and `INFO` is the log level. These are optionally followed by the name of a child process and the process ID. diff --git a/packages/core/README_TEMPLATE.md b/packages/core/README_TEMPLATE.md index fc9f696af0a92..76a0887aef570 100644 --- a/packages/core/README_TEMPLATE.md +++ b/packages/core/README_TEMPLATE.md @@ -107,8 +107,8 @@ existing loggers. However, each log message specifies from which logger it comes from, which can give an idea, without having to read the code: ``` -root INFO [nsfw-watcher: 10734] Started watching: /Users/captain.future/git/theia/CONTRIBUTING.md -^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^ +root INFO [parcel-watcher: 10734] Started watching: /Users/captain.future/git/theia/CONTRIBUTING.md +^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^ ``` Where `root` is the name of the logger and `INFO` is the log level. These are optionally followed by the name of a child process and the process ID. diff --git a/packages/core/package.json b/packages/core/package.json index d86b2ab085578..a786863531a2d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -6,6 +6,7 @@ "typings": "lib/common/index.d.ts", "dependencies": { "@babel/runtime": "^7.10.0", + "@parcel/watcher": "^2.4.1", "@phosphor/algorithm": "1", "@phosphor/commands": "1", "@phosphor/coreutils": "1", @@ -57,7 +58,6 @@ "lodash.throttle": "^4.1.1", "markdown-it": "^12.3.2", "msgpackr": "^1.10.2", - "nsfw": "^2.2.4", "p-debounce": "^2.1.0", "perfect-scrollbar": "^1.3.0", "react": "^18.2.0", @@ -119,11 +119,11 @@ "vscode-uri" ], "export =": [ + "@parcel/watcher as parcelWatcher", "dompurify as DOMPurify", "express", "lodash.debounce as debounce", "lodash.throttle as throttle", - "nsfw", "markdown-it as markdownit", "react as React", "ws as WebSocket", diff --git a/packages/core/shared/@parcel/watcher/index.d.ts b/packages/core/shared/@parcel/watcher/index.d.ts new file mode 100644 index 0000000000000..70c4fecbff0ae --- /dev/null +++ b/packages/core/shared/@parcel/watcher/index.d.ts @@ -0,0 +1,2 @@ +import parcelWatcher = require('@parcel/watcher'); +export = parcelWatcher; diff --git a/packages/core/shared/@parcel/watcher/index.js b/packages/core/shared/@parcel/watcher/index.js new file mode 100644 index 0000000000000..cc9f177038079 --- /dev/null +++ b/packages/core/shared/@parcel/watcher/index.js @@ -0,0 +1 @@ +module.exports = require('@parcel/watcher'); diff --git a/packages/core/shared/nsfw/index.d.ts b/packages/core/shared/nsfw/index.d.ts deleted file mode 100644 index d354af1682797..0000000000000 --- a/packages/core/shared/nsfw/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -import nsfw = require('nsfw'); -export = nsfw; diff --git a/packages/core/shared/nsfw/index.js b/packages/core/shared/nsfw/index.js deleted file mode 100644 index fd3d1ddea09d5..0000000000000 --- a/packages/core/shared/nsfw/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('nsfw'); diff --git a/packages/core/src/node/logger-cli-contribution.ts b/packages/core/src/node/logger-cli-contribution.ts index 956d0d4d83946..0df9fc73afe0e 100644 --- a/packages/core/src/node/logger-cli-contribution.ts +++ b/packages/core/src/node/logger-cli-contribution.ts @@ -19,7 +19,7 @@ import { injectable } from 'inversify'; import { LogLevel } from '../common/logger'; import { CliContribution } from './cli'; import * as fs from 'fs-extra'; -import * as nsfw from 'nsfw'; +import { subscribe } from '@parcel/watcher'; import { Event, Emitter } from '../common/event'; import * as path from 'path'; @@ -89,13 +89,17 @@ export class LogLevelCliContribution implements CliContribution { } } - protected watchLogConfigFile(filename: string): Promise { - return nsfw(filename, async (events: nsfw.FileChangeEvent[]) => { + protected async watchLogConfigFile(filename: string): Promise { + await subscribe(filename, async (err, events) => { + if (err) { + console.log(`Error during log file watching ${filename}: ${err}`); + return; + } try { for (const event of events) { - switch (event.action) { - case nsfw.actions.CREATED: - case nsfw.actions.MODIFIED: + switch (event.type) { + case 'create': + case 'update': await this.slurpLogConfigFile(filename); this.logConfigChangedEvent.fire(undefined); break; @@ -104,8 +108,6 @@ export class LogLevelCliContribution implements CliContribution { } catch (e) { console.error(`Error reading log config file ${filename}: ${e}`); } - }).then((watcher: nsfw.NSFW) => { - watcher.start(); }); } diff --git a/packages/filesystem/src/browser/file-tree/file-tree-model.ts b/packages/filesystem/src/browser/file-tree/file-tree-model.ts index 05dead78f8d69..48ac295c646fc 100644 --- a/packages/filesystem/src/browser/file-tree/file-tree-model.ts +++ b/packages/filesystem/src/browser/file-tree/file-tree-model.ts @@ -21,7 +21,7 @@ import { FileStatNode, DirNode, FileNode } from './file-tree'; import { LocationService } from '../location'; import { LabelProvider } from '@theia/core/lib/browser/label-provider'; import { FileService } from '../file-service'; -import { FileOperationError, FileOperationResult, FileChangesEvent, FileChangeType, FileChange, FileOperation, FileOperationEvent } from '../../common/files'; +import { FileOperationError, FileOperationResult, FileChangesEvent, FileChangeType, FileChange } from '../../common/files'; import { MessageService } from '@theia/core/lib/common/message-service'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { FileSystemUtils } from '../../common'; @@ -45,7 +45,6 @@ export class FileTreeModel extends CompressedTreeModel implements LocationServic protected override init(): void { super.init(); this.toDispose.push(this.fileService.onDidFilesChange(changes => this.onFilesChanged(changes))); - this.toDispose.push(this.fileService.onDidRunOperation(event => this.onDidMove(event))); } get location(): URI | undefined { @@ -92,23 +91,6 @@ export class FileTreeModel extends CompressedTreeModel implements LocationServic } } - /** - * to workaround https://github.com/Axosoft/nsfw/issues/42 - */ - protected onDidMove(event: FileOperationEvent): void { - if (!event.isOperation(FileOperation.MOVE)) { - return; - } - if (event.resource.parent.toString() === event.target.resource.parent.toString()) { - // file rename - return; - } - this.refreshAffectedNodes([ - event.resource, - event.target.resource - ]); - } - protected onFilesChanged(changes: FileChangesEvent): void { if (!this.refreshAffectedNodes(this.getAffectedUris(changes)) && this.isRootAffected(changes)) { this.refresh(); diff --git a/packages/filesystem/src/browser/filesystem-preferences.ts b/packages/filesystem/src/browser/filesystem-preferences.ts index 2a363946610b1..27a35de410054 100644 --- a/packages/filesystem/src/browser/filesystem-preferences.ts +++ b/packages/filesystem/src/browser/filesystem-preferences.ts @@ -46,8 +46,7 @@ export const filesystemPreferenceSchema: PreferenceSchema = { }, default: { '**/.git/objects/**': true, - '**/.git/subtree-cache/**': true, - '**/node_modules/**': true + '**/.git/subtree-cache/**': true }, scope: 'resource' }, diff --git a/packages/filesystem/src/node/filesystem-backend-module.ts b/packages/filesystem/src/node/filesystem-backend-module.ts index abb8ea9773789..c59ba09a8bf3a 100644 --- a/packages/filesystem/src/node/filesystem-backend-module.ts +++ b/packages/filesystem/src/node/filesystem-backend-module.ts @@ -19,9 +19,9 @@ import { ContainerModule, interfaces } from '@theia/core/shared/inversify'; import { ConnectionHandler, RpcConnectionHandler, ILogger } from '@theia/core/lib/common'; import { FileSystemWatcherServer, FileSystemWatcherService } from '../common/filesystem-watcher-protocol'; import { FileSystemWatcherServerClient } from './filesystem-watcher-client'; -import { NsfwFileSystemWatcherService, NsfwFileSystemWatcherServerOptions } from './nsfw-watcher/nsfw-filesystem-service'; +import { ParcelFileSystemWatcherService, ParcelFileSystemWatcherServerOptions } from './parcel-watcher/parcel-filesystem-service'; import { NodeFileUploadService } from './node-file-upload-service'; -import { NsfwOptions } from './nsfw-watcher/nsfw-options'; +import { ParcelWatcherOptions } from './parcel-watcher/parcel-options'; import { DiskFileSystemProvider } from './disk-file-system-provider'; import { remoteFileSystemPath, RemoteFileSystemServer, RemoteFileSystemClient, FileSystemProviderServer, RemoteFileSystemProxyFactory @@ -32,16 +32,16 @@ import { BackendApplicationContribution, IPCConnectionProvider } from '@theia/co import { RpcProxyFactory, ConnectionErrorHandler } from '@theia/core'; import { FileSystemWatcherServiceDispatcher } from './filesystem-watcher-dispatcher'; -export const NSFW_SINGLE_THREADED = process.argv.includes('--no-cluster'); -export const NSFW_WATCHER_VERBOSE = process.argv.includes('--nsfw-watcher-verbose'); +export const WATCHER_SINGLE_THREADED = process.argv.includes('--no-cluster'); +export const WATCHER_VERBOSE = process.argv.includes('--watcher-verbose'); -export const NsfwFileSystemWatcherServiceProcessOptions = Symbol('NsfwFileSystemWatcherServiceProcessOptions'); +export const FileSystemWatcherServiceProcessOptions = Symbol('FileSystemWatcherServiceProcessOptions'); /** - * Options to control the way the `NsfwFileSystemWatcherService` process is spawned. + * Options to control the way the `ParcelFileSystemWatcherService` process is spawned. */ -export interface NsfwFileSystemWatcherServiceProcessOptions { +export interface FileSystemWatcherServiceProcessOptions { /** - * Path to the script that will run the `NsfwFileSystemWatcherService` in a new process. + * Path to the script that will run the `ParcelFileSystemWatcherService` in a new process. */ entryPoint: string; } @@ -66,41 +66,41 @@ export default new ContainerModule(bind => { }); export function bindFileSystemWatcherServer(bind: interfaces.Bind): void { - bind(NsfwOptions).toConstantValue({}); + bind(ParcelWatcherOptions).toConstantValue({}); bind(FileSystemWatcherServiceDispatcher).toSelf().inSingletonScope(); bind(FileSystemWatcherServerClient).toSelf(); bind(FileSystemWatcherServer).toService(FileSystemWatcherServerClient); - bind(NsfwFileSystemWatcherServiceProcessOptions).toDynamicValue(ctx => ({ - entryPoint: path.join(__dirname, 'nsfw-watcher'), + bind(FileSystemWatcherServiceProcessOptions).toDynamicValue(ctx => ({ + entryPoint: path.join(__dirname, 'parcel-watcher'), })).inSingletonScope(); - bind(NsfwFileSystemWatcherServerOptions).toDynamicValue(ctx => { + bind(ParcelFileSystemWatcherServerOptions).toDynamicValue(ctx => { const logger = ctx.container.get(ILogger); - const nsfwOptions = ctx.container.get(NsfwOptions); + const watcherOptions = ctx.container.get(ParcelWatcherOptions); return { - nsfwOptions, - verbose: NSFW_WATCHER_VERBOSE, + parcelOptions: watcherOptions, + verbose: WATCHER_VERBOSE, info: (message, ...args) => logger.info(message, ...args), error: (message, ...args) => logger.error(message, ...args), }; }).inSingletonScope(); bind(FileSystemWatcherService).toDynamicValue( - ctx => NSFW_SINGLE_THREADED - ? createNsfwFileSystemWatcherService(ctx) - : spawnNsfwFileSystemWatcherServiceProcess(ctx) + ctx => WATCHER_SINGLE_THREADED + ? createParcelFileSystemWatcherService(ctx) + : spawnParcelFileSystemWatcherServiceProcess(ctx) ).inSingletonScope(); } /** * Run the watch server in the current process. */ -export function createNsfwFileSystemWatcherService(ctx: interfaces.Context): FileSystemWatcherService { - const options = ctx.container.get(NsfwFileSystemWatcherServerOptions); +export function createParcelFileSystemWatcherService(ctx: interfaces.Context): FileSystemWatcherService { + const options = ctx.container.get(ParcelFileSystemWatcherServerOptions); const dispatcher = ctx.container.get(FileSystemWatcherServiceDispatcher); - const server = new NsfwFileSystemWatcherService(options); + const server = new ParcelFileSystemWatcherService(options); server.setClient(dispatcher); return server; } @@ -109,21 +109,21 @@ export function createNsfwFileSystemWatcherService(ctx: interfaces.Context): Fil * Run the watch server in a child process. * Return a proxy forwarding calls to the child process. */ -export function spawnNsfwFileSystemWatcherServiceProcess(ctx: interfaces.Context): FileSystemWatcherService { - const options = ctx.container.get(NsfwFileSystemWatcherServiceProcessOptions); +export function spawnParcelFileSystemWatcherServiceProcess(ctx: interfaces.Context): FileSystemWatcherService { + const options = ctx.container.get(FileSystemWatcherServiceProcessOptions); const dispatcher = ctx.container.get(FileSystemWatcherServiceDispatcher); - const serverName = 'nsfw-watcher'; + const serverName = 'parcel-watcher'; const logger = ctx.container.get(ILogger); - const nsfwOptions = ctx.container.get(NsfwOptions); + const watcherOptions = ctx.container.get(ParcelWatcherOptions); const ipcConnectionProvider = ctx.container.get(IPCConnectionProvider); const proxyFactory = new RpcProxyFactory(); const serverProxy = proxyFactory.createProxy(); // We need to call `.setClient` before listening, else the JSON-RPC calls won't go through. serverProxy.setClient(dispatcher); const args: string[] = [ - `--nsfwOptions=${JSON.stringify(nsfwOptions)}` + `--watchOptions=${JSON.stringify(watcherOptions)}` ]; - if (NSFW_WATCHER_VERBOSE) { + if (WATCHER_VERBOSE) { args.push('--verbose'); } ipcConnectionProvider.listen({ diff --git a/packages/filesystem/src/node/filesystem-watcher-client.ts b/packages/filesystem/src/node/filesystem-watcher-client.ts index ce44d8099fedd..e720b9adaca9f 100644 --- a/packages/filesystem/src/node/filesystem-watcher-client.ts +++ b/packages/filesystem/src/node/filesystem-watcher-client.ts @@ -18,10 +18,8 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import { FileSystemWatcherServer, WatchOptions, FileSystemWatcherClient, FileSystemWatcherService } from '../common/filesystem-watcher-protocol'; import { FileSystemWatcherServiceDispatcher } from './filesystem-watcher-dispatcher'; -export const NSFW_WATCHER = 'nsfw-watcher'; - /** - * Wraps the NSFW singleton service for each frontend. + * Wraps the watcher singleton service for each frontend. */ @injectable() export class FileSystemWatcherServerClient implements FileSystemWatcherServer { diff --git a/packages/filesystem/src/node/nsfw-watcher/index.ts b/packages/filesystem/src/node/parcel-watcher/index.ts similarity index 90% rename from packages/filesystem/src/node/nsfw-watcher/index.ts rename to packages/filesystem/src/node/parcel-watcher/index.ts index 92920b9aaa48b..0edbb8a2a0277 100644 --- a/packages/filesystem/src/node/nsfw-watcher/index.ts +++ b/packages/filesystem/src/node/parcel-watcher/index.ts @@ -17,7 +17,7 @@ import * as yargs from '@theia/core/shared/yargs'; import { RpcProxyFactory } from '@theia/core'; import { FileSystemWatcherServiceClient } from '../../common/filesystem-watcher-protocol'; -import { NsfwFileSystemWatcherService } from './nsfw-filesystem-service'; +import { ParcelFileSystemWatcherService } from './parcel-filesystem-service'; import { IPCEntryPoint } from '@theia/core/lib/node/messaging/ipc-protocol'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -30,7 +30,7 @@ const options: { alias: 'v', type: 'boolean' }) - .option('nsfwOptions', { + .option('watchOptions', { alias: 'o', type: 'string', coerce: JSON.parse @@ -38,7 +38,7 @@ const options: { .argv as any; export default (connection => { - const server = new NsfwFileSystemWatcherService(options); + const server = new ParcelFileSystemWatcherService(options); const factory = new RpcProxyFactory(server); server.setClient(factory.createProxy()); factory.listen(connection); diff --git a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts b/packages/filesystem/src/node/parcel-watcher/parcel-filesystem-service.ts similarity index 76% rename from packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts rename to packages/filesystem/src/node/parcel-watcher/parcel-filesystem-service.ts index ebc947846e804..bd0c352c607e3 100644 --- a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-service.ts +++ b/packages/filesystem/src/node/parcel-watcher/parcel-filesystem-service.ts @@ -14,7 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import nsfw = require('@theia/core/shared/nsfw'); import path = require('path'); import { promises as fsp } from 'fs'; import { IMinimatch, Minimatch } from 'minimatch'; @@ -24,19 +23,20 @@ import { } from '../../common/filesystem-watcher-protocol'; import { FileChangeCollection } from '../file-change-collection'; import { Deferred, timeout } from '@theia/core/lib/common/promise-util'; +import { subscribe, Options, AsyncSubscription, Event } from '@theia/core/shared/@parcel/watcher'; -export interface NsfwWatcherOptions { +export interface ParcelWatcherOptions { ignored: IMinimatch[] } -export const NsfwFileSystemWatcherServerOptions = Symbol('NsfwFileSystemWatcherServerOptions'); -export interface NsfwFileSystemWatcherServerOptions { +export const ParcelFileSystemWatcherServerOptions = Symbol('ParcelFileSystemWatcherServerOptions'); +export interface ParcelFileSystemWatcherServerOptions { verbose: boolean; // eslint-disable-next-line @typescript-eslint/no-explicit-any info: (message: string, ...args: any[]) => void; // eslint-disable-next-line @typescript-eslint/no-explicit-any error: (message: string, ...args: any[]) => void; - nsfwOptions: nsfw.Options; + parcelOptions: Options; } /** @@ -54,7 +54,7 @@ export const WatcherDisposal = Symbol('WatcherDisposal'); * Once there are no more references the handle * will wait for some time before destroying its resources. */ -export class NsfwWatcher { +export class ParcelWatcher { protected static debugIdSequence = 0; @@ -63,12 +63,12 @@ export class NsfwWatcher { /** * Used for debugging to keep track of the watchers. */ - protected debugId = NsfwWatcher.debugIdSequence++; + protected debugId = ParcelWatcher.debugIdSequence++; /** - * When this field is set, it means the nsfw instance was successfully started. + * When this field is set, it means the watcher instance was successfully started. */ - protected nsfw: nsfw.NSFW | undefined; + protected watcher: AsyncSubscription | undefined; /** * When the ref count hits zero, we schedule this watch handle to be disposed. @@ -93,7 +93,7 @@ export class NsfwWatcher { * Ensures that events are processed in the order they are emitted, * despite being processed async. */ - protected nsfwEventProcessingQueue: Promise = Promise.resolve(); + protected parcelEventProcessingQueue: Promise = Promise.resolve(); /** * Resolves once this handle disposed itself and its resources. Never throws. @@ -115,9 +115,9 @@ export class NsfwWatcher { /** Filesystem path to be watched. */ readonly fsPath: string, /** Watcher-specific options */ - readonly watcherOptions: NsfwWatcherOptions, - /** Logging and Nsfw options */ - protected readonly nsfwFileSystemWatchServerOptions: NsfwFileSystemWatcherServerOptions, + readonly watcherOptions: ParcelWatcherOptions, + /** Logging and parcel watcher options */ + protected readonly parcelFileSystemWatchServerOptions: ParcelFileSystemWatcherServerOptions, /** The client to forward events to. */ protected readonly fileSystemWatcherClient: FileSystemWatcherServiceClient, /** Amount of time in ms to wait once this handle is not referenced anymore. */ @@ -207,7 +207,7 @@ export class NsfwWatcher { /** * When starting a watcher, we'll first check and wait for the path to exists - * before running an NSFW watcher. + * before running a parcel watcher. */ protected async start(): Promise { while (await fsp.stat(this.fsPath).then(() => false, () => true)) { @@ -215,71 +215,61 @@ export class NsfwWatcher { this.assertNotDisposed(); } this.assertNotDisposed(); - const watcher = await this.createNsfw(); + const watcher = await this.createWatcher(); this.assertNotDisposed(); - await watcher.start(); this.debug('STARTED', `disposed=${this.disposed}`); // The watcher could be disposed while it was starting, make sure to check for this: if (this.disposed) { - await this.stopNsfw(watcher); + await this.stopWatcher(watcher); throw WatcherDisposal; } - this.nsfw = watcher; + this.watcher = watcher; } /** - * Given a started nsfw instance, gracefully shut it down. + * Given a started parcel watcher instance, gracefully shut it down. */ - protected async stopNsfw(watcher: nsfw.NSFW): Promise { - await watcher.stop() + protected async stopWatcher(watcher: AsyncSubscription): Promise { + await watcher.unsubscribe() .then(() => 'success=true', error => error) .then(status => this.debug('STOPPED', status)); } - protected async createNsfw(): Promise { - const fsPath = await fsp.realpath(this.fsPath); - return nsfw(fsPath, events => this.handleNsfwEvents(events), { - ...this.nsfwFileSystemWatchServerOptions.nsfwOptions, - // The errorCallback is called whenever NSFW crashes *while* watching. - // See https://github.com/atom/github/issues/342 - errorCallback: error => { - console.error(`NSFW service error on "${fsPath}":`, error); + protected async createWatcher(): Promise { + let fsPath = await fsp.realpath(this.fsPath); + if ((await fsp.stat(fsPath)).isFile()) { + fsPath = path.dirname(fsPath); + } + return subscribe(fsPath, (err, events) => { + if (err) { + console.error(`Watcher service error on "${fsPath}":`, err); this._dispose(); this.fireError(); - // Make sure to call user's error handling code: - if (this.nsfwFileSystemWatchServerOptions.nsfwOptions.errorCallback) { - this.nsfwFileSystemWatchServerOptions.nsfwOptions.errorCallback(error); - } - }, + return; + } + this.handleWatcherEvents(events); + }, { + ...this.parcelFileSystemWatchServerOptions.parcelOptions }); } - protected handleNsfwEvents(events: nsfw.FileChangeEvent[]): void { + protected handleWatcherEvents(events: Event[]): void { // Only process events if someone is listening. if (this.isInUse()) { - // This callback is async, but nsfw won't wait for it to finish before firing the next one. + // This callback is async, but parcel won't wait for it to finish before firing the next one. // We will use a lock/queue to make sure everything is processed in the order it arrives. - this.nsfwEventProcessingQueue = this.nsfwEventProcessingQueue.then(async () => { + this.parcelEventProcessingQueue = this.parcelEventProcessingQueue.then(async () => { const fileChangeCollection = new FileChangeCollection(); - await Promise.all(events.map(async event => { - if (event.action === nsfw.actions.RENAMED) { - const [oldPath, newPath] = await Promise.all([ - this.resolveEventPath(event.directory, event.oldFile), - this.resolveEventPath(event.newDirectory, event.newFile), - ]); - this.pushFileChange(fileChangeCollection, FileChangeType.DELETED, oldPath); - this.pushFileChange(fileChangeCollection, FileChangeType.ADDED, newPath); - } else { - const filePath = await this.resolveEventPath(event.directory, event.file!); - if (event.action === nsfw.actions.CREATED) { - this.pushFileChange(fileChangeCollection, FileChangeType.ADDED, filePath); - } else if (event.action === nsfw.actions.DELETED) { - this.pushFileChange(fileChangeCollection, FileChangeType.DELETED, filePath); - } else if (event.action === nsfw.actions.MODIFIED) { - this.pushFileChange(fileChangeCollection, FileChangeType.UPDATED, filePath); - } + for (const event of events) { + const filePath = event.path; + if (event.type === 'create') { + this.pushFileChange(fileChangeCollection, FileChangeType.ADDED, filePath); + } else if (event.type === 'delete') { + this.pushFileChange(fileChangeCollection, FileChangeType.DELETED, filePath); + } else if (event.type === 'update') { + this.pushFileChange(fileChangeCollection, FileChangeType.UPDATED, filePath); } - })); + } const changes = fileChangeCollection.values(); // If all changes are part of the ignored files, the collection will be empty. if (changes.length > 0) { @@ -293,7 +283,7 @@ export class NsfwWatcher { } protected async resolveEventPath(directory: string, file: string): Promise { - // nsfw already resolves symlinks, the paths should be clean already: + // parcel already resolves symlinks, the paths should be clean already: return path.resolve(directory, file); } @@ -344,9 +334,9 @@ export class NsfwWatcher { if (!this.disposed) { this.disposed = true; this.deferredDisposalDeferred.reject(WatcherDisposal); - if (this.nsfw) { - this.stopNsfw(this.nsfw); - this.nsfw = undefined; + if (this.watcher) { + this.stopWatcher(this.watcher); + this.watcher = undefined; } this.debug('DISPOSED'); } @@ -354,12 +344,12 @@ export class NsfwWatcher { // eslint-disable-next-line @typescript-eslint/no-explicit-any protected info(prefix: string, ...params: any[]): void { - this.nsfwFileSystemWatchServerOptions.info(`${prefix} NsfwWatcher(${this.debugId} at "${this.fsPath}"):`, ...params); + this.parcelFileSystemWatchServerOptions.info(`${prefix} ParcelWatcher(${this.debugId} at "${this.fsPath}"):`, ...params); } // eslint-disable-next-line @typescript-eslint/no-explicit-any protected debug(prefix: string, ...params: any[]): void { - if (this.nsfwFileSystemWatchServerOptions.verbose) { + if (this.parcelFileSystemWatchServerOptions.verbose) { this.info(prefix, ...params); } } @@ -370,20 +360,20 @@ export class NsfwWatcher { * * This watcherId will map to this handle type which keeps track of the clientId that made the request. */ -export interface NsfwWatcherHandle { +export interface PacelWatcherHandle { clientId: number; - watcher: NsfwWatcher; + watcher: ParcelWatcher; } -export class NsfwFileSystemWatcherService implements FileSystemWatcherService { +export class ParcelFileSystemWatcherService implements FileSystemWatcherService { protected client: FileSystemWatcherServiceClient | undefined; protected watcherId = 0; - protected readonly watchers = new Map(); - protected readonly watcherHandles = new Map(); + protected readonly watchers = new Map(); + protected readonly watcherHandles = new Map(); - protected readonly options: NsfwFileSystemWatcherServerOptions; + protected readonly options: ParcelFileSystemWatcherServerOptions; /** * `this.client` is undefined until someone sets it. @@ -393,9 +383,9 @@ export class NsfwFileSystemWatcherService implements FileSystemWatcherService { onError: event => this.client?.onError(event), }; - constructor(options?: Partial) { + constructor(options?: Partial) { this.options = { - nsfwOptions: {}, + parcelOptions: {}, verbose: false, info: (message, ...args) => console.info(message, ...args), error: (message, ...args) => console.error(message, ...args), @@ -430,12 +420,12 @@ export class NsfwFileSystemWatcherService implements FileSystemWatcherService { return watcherId; } - protected createWatcher(clientId: number, fsPath: string, options: WatchOptions): NsfwWatcher { - const watcherOptions: NsfwWatcherOptions = { + protected createWatcher(clientId: number, fsPath: string, options: WatchOptions): ParcelWatcher { + const watcherOptions: ParcelWatcherOptions = { ignored: options.ignored .map(pattern => new Minimatch(pattern, { dot: true })), }; - return new NsfwWatcher(clientId, fsPath, watcherOptions, this.options, this.maybeClient); + return new ParcelWatcher(clientId, fsPath, watcherOptions, this.options, this.maybeClient); } async unwatchFileChanges(watcherId: number): Promise { diff --git a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.spec.ts b/packages/filesystem/src/node/parcel-watcher/parcel-filesystem-watcher.spec.ts similarity index 82% rename from packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.spec.ts rename to packages/filesystem/src/node/parcel-watcher/parcel-filesystem-watcher.spec.ts index 6e9b9e90298f9..35ec061ca1e7b 100644 --- a/packages/filesystem/src/node/nsfw-watcher/nsfw-filesystem-watcher.spec.ts +++ b/packages/filesystem/src/node/parcel-watcher/parcel-filesystem-watcher.spec.ts @@ -21,16 +21,16 @@ import * as fs from '@theia/core/shared/fs-extra'; import * as assert from 'assert'; import URI from '@theia/core/lib/common/uri'; import { FileUri } from '@theia/core/lib/node'; -import { NsfwFileSystemWatcherService } from './nsfw-filesystem-service'; +import { ParcelFileSystemWatcherService } from './parcel-filesystem-service'; import { DidFilesChangedParams, FileChange, FileChangeType } from '../../common/filesystem-watcher-protocol'; const expect = chai.expect; const track = temp.track(); -describe('nsfw-filesystem-watcher', function (): void { +describe('parcel-filesystem-watcher', function (): void { let root: URI; - let watcherService: NsfwFileSystemWatcherService; + let watcherService: ParcelFileSystemWatcherService; let watcherId: number; this.timeout(100000); @@ -38,7 +38,7 @@ describe('nsfw-filesystem-watcher', function (): void { beforeEach(async () => { let tempPath = temp.mkdirSync('node-fs-root'); // Sometimes tempPath will use some Windows 8.3 short name in its path. This is a problem - // since NSFW always returns paths with long names. We need to convert here. + // since parcel always returns paths with long names. We need to convert here. // See: https://stackoverflow.com/a/34473971/7983255 if (process.platform === 'win32') { tempPath = cp.execSync(`powershell "(Get-Item -LiteralPath '${tempPath}').FullName"`, { @@ -46,9 +46,9 @@ describe('nsfw-filesystem-watcher', function (): void { }).trim(); } root = FileUri.create(fs.realpathSync(tempPath)); - watcherService = createNsfwFileSystemWatcherService(); + watcherService = createParcelFileSystemWatcherService(); watcherId = await watcherService.watchFileChanges(0, root.toString()); - await sleep(2000); + await sleep(200); }); afterEach(async () => { @@ -76,15 +76,15 @@ describe('nsfw-filesystem-watcher', function (): void { fs.mkdirSync(FileUri.fsPath(root.resolve('foo'))); expect(fs.statSync(FileUri.fsPath(root.resolve('foo'))).isDirectory()).to.be.true; - await sleep(2000); + await sleep(200); fs.mkdirSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))); expect(fs.statSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))).isDirectory()).to.be.true; - await sleep(2000); + await sleep(200); fs.writeFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'baz'); expect(fs.readFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'utf8')).to.be.equal('baz'); - await sleep(2000); + await sleep(200); assert.deepStrictEqual([...actualUris], expectedUris); }); @@ -106,20 +106,20 @@ describe('nsfw-filesystem-watcher', function (): void { fs.mkdirSync(FileUri.fsPath(root.resolve('foo'))); expect(fs.statSync(FileUri.fsPath(root.resolve('foo'))).isDirectory()).to.be.true; - await sleep(2000); + await sleep(200); fs.mkdirSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))); expect(fs.statSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))).isDirectory()).to.be.true; - await sleep(2000); + await sleep(200); fs.writeFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'baz'); expect(fs.readFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'utf8')).to.be.equal('baz'); - await sleep(2000); + await sleep(200); assert.deepStrictEqual(actualUris.size, 0); }); - it('Renaming should emit a DELETED change followed by ADDED', async function (): Promise { + it('Renaming should emit a DELETED and ADDED event', async function (): Promise { const file_txt = root.resolve('file.txt'); const FILE_txt = root.resolve('FILE.txt'); const changes: FileChange[] = []; @@ -131,41 +131,34 @@ describe('nsfw-filesystem-watcher', function (): void { FileUri.fsPath(file_txt), 'random content\n' ); - await sleep(1000); + await sleep(200); await fs.promises.rename( FileUri.fsPath(file_txt), FileUri.fsPath(FILE_txt) ); - await sleep(1000); + await sleep(200); + // The order of DELETED and ADDED is not deterministic try { expect(changes).deep.eq([ // initial file creation change event: { type: FileChangeType.ADDED, uri: file_txt.toString() }, // rename change events: { type: FileChangeType.DELETED, uri: file_txt.toString() }, - { type: FileChangeType.ADDED, uri: FILE_txt.toString() } + { type: FileChangeType.ADDED, uri: FILE_txt.toString() }, ]); - } catch (error) { - // TODO: remove this try/catch once the bug on macOS is fixed. - // See https://github.com/Axosoft/nsfw/issues/146 - if (process.platform !== 'darwin') { - throw error; - } - // On macOS we only get ADDED events for some reason + } catch { expect(changes).deep.eq([ // initial file creation change event: { type: FileChangeType.ADDED, uri: file_txt.toString() }, // rename change events: - { type: FileChangeType.ADDED, uri: file_txt.toString() }, - { type: FileChangeType.ADDED, uri: FILE_txt.toString() } + { type: FileChangeType.ADDED, uri: FILE_txt.toString() }, + { type: FileChangeType.DELETED, uri: file_txt.toString() }, ]); - // Mark the test case as skipped so it stands out that the bogus branch got tested - this.skip(); } }); - function createNsfwFileSystemWatcherService(): NsfwFileSystemWatcherService { - return new NsfwFileSystemWatcherService({ + function createParcelFileSystemWatcherService(): ParcelFileSystemWatcherService { + return new ParcelFileSystemWatcherService({ verbose: true }); } diff --git a/packages/filesystem/src/node/nsfw-watcher/nsfw-options.ts b/packages/filesystem/src/node/parcel-watcher/parcel-options.ts similarity index 80% rename from packages/filesystem/src/node/nsfw-watcher/nsfw-options.ts rename to packages/filesystem/src/node/parcel-watcher/parcel-options.ts index f658c4bb4e7ed..b52c0794553cc 100644 --- a/packages/filesystem/src/node/nsfw-watcher/nsfw-options.ts +++ b/packages/filesystem/src/node/parcel-watcher/parcel-options.ts @@ -14,10 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import * as nsfw from '@theia/core/shared/nsfw'; +import { Options } from '@theia/core/shared/@parcel/watcher'; /** - * Inversify service identifier allowing extensions to override options passed to nsfw by the file watcher. + * Inversify service identifier allowing extensions to override options passed to parcel by the file watcher. */ -export const NsfwOptions = Symbol('NsfwOptions'); -export type NsfwOptions = nsfw.Options; +export const ParcelWatcherOptions = Symbol('ParcelWatcherOptions'); +export type ParcelWatcherOptions = Options; diff --git a/packages/filesystem/src/typings/nsfw/index.d.ts b/packages/filesystem/src/typings/nsfw/index.d.ts deleted file mode 100644 index 0573effb0d4c1..0000000000000 --- a/packages/filesystem/src/typings/nsfw/index.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2018 Ericsson and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** - -// eslint-disable-next-line spaced-comment -/// diff --git a/yarn.lock b/yarn.lock index 29d43d8b58818..55b27c141074e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1517,6 +1517,66 @@ dependencies: "@octokit/openapi-types" "^18.0.0" +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== + +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== + +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== + +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== + +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== + +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== + +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== + +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== + +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== + +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== + +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== + +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== + "@parcel/watcher@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" @@ -1525,6 +1585,29 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" +"@parcel/watcher@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + "@phosphor/algorithm@1", "@phosphor/algorithm@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@phosphor/algorithm/-/algorithm-1.2.0.tgz#4a19aa59261b7270be696672dc3f0663f7bef152" @@ -3532,6 +3615,13 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -4719,6 +4809,11 @@ detect-libc@^2.0.0, detect-libc@^2.0.1: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== +detect-libc@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -5750,6 +5845,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" @@ -8108,6 +8210,14 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -8571,10 +8681,10 @@ node-addon-api@^4.3.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== -node-addon-api@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" - integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-api-version@^0.1.4: version "0.1.4" @@ -8876,13 +8986,6 @@ npmlog@^6.0.0, npmlog@^6.0.2: gauge "^4.0.3" set-blocking "^2.0.0" -nsfw@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-2.2.4.tgz#4ed94544a63fc843b7e3ccff6668dce13d27a33a" - integrity sha512-sTRNa7VYAiy5ARP8etIBfkIfxU0METW40UinDnv0epQMe1pzj285HdXKRKkdrV3rRzMNcuNZn2foTNszV0x+OA== - dependencies: - node-addon-api "^5.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -11097,7 +11200,7 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11115,15 +11218,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11189,7 +11283,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11210,13 +11304,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12427,7 +12514,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12445,15 +12532,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 0f15ab60a240ae5acb0e2a9b67f8862b9e4bc960 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Tue, 8 Oct 2024 10:41:07 +0200 Subject: [PATCH 414/441] Ensure notebook document event registration (#14242) --- .../notebooks/notebook-documents-and-editors-main.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index 11285a9f55101..c1405980a9b64 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -195,14 +195,16 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain addedEditors: delta.addedEditors.map(NotebooksAndEditorsMain.asEditorAddData), }; - // send to extension FIRST - await this.proxy.$acceptDocumentsAndEditorsDelta(dto); - - // handle internally + // Handle internally first + // In case the plugin wants to perform documents edits immediately + // we want to make sure that all events have already been setup this.notebookEditorsMain.handleEditorsRemoved(delta.removedEditors); this.notebookDocumentsMain.handleNotebooksRemoved(delta.removedDocuments); this.notebookDocumentsMain.handleNotebooksAdded(delta.addedDocuments); this.notebookEditorsMain.handleEditorsAdded(delta.addedEditors); + + // Send to plugin last + await this.proxy.$acceptDocumentsAndEditorsDelta(dto); } private static isDeltaEmpty(delta: NotebookAndEditorDelta): boolean { From 8f0f4b1328f43ff9de3d23317d8f796033a71208 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 8 Oct 2024 11:38:34 +0200 Subject: [PATCH 415/441] fixed notebook editor staying current until another editor is selected (#14262) Signed-off-by: Jonah Iden --- .../service/notebook-cell-editor-service.ts | 24 ++++++++++++++++--- .../service/notebook-editor-widget-service.ts | 20 ++++++++++++++++ .../browser/editors-and-documents-main.ts | 2 +- .../notebook-documents-and-editors-main.ts | 7 +++--- .../src/plugin/notebook/notebooks.ts | 2 +- 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/service/notebook-cell-editor-service.ts b/packages/notebook/src/browser/service/notebook-cell-editor-service.ts index e1d338a4b7602..53268eb62b4ec 100644 --- a/packages/notebook/src/browser/service/notebook-cell-editor-service.ts +++ b/packages/notebook/src/browser/service/notebook-cell-editor-service.ts @@ -15,12 +15,17 @@ // ***************************************************************************** import { Emitter, URI } from '@theia/core'; -import { injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor'; +import { NotebookEditorWidgetService } from './notebook-editor-widget-service'; +import { CellUri } from '../../common'; @injectable() export class NotebookCellEditorService { + @inject(NotebookEditorWidgetService) + protected readonly notebookEditorWidgetService: NotebookEditorWidgetService; + protected onDidChangeCellEditorsEmitter = new Emitter(); readonly onDidChangeCellEditors = this.onDidChangeCellEditorsEmitter.event; @@ -31,6 +36,17 @@ export class NotebookCellEditorService { protected currentCellEditors: Map = new Map(); + @postConstruct() + protected init(): void { + this.notebookEditorWidgetService.onDidChangeCurrentEditor(editor => { + // if defocus notebook editor or another notebook editor is focused, clear the active cell + if (!editor || (this.currentActiveCell && CellUri.parse(this.currentActiveCell.uri)?.notebook.toString() !== editor?.model?.uri.toString())) { + this.currentActiveCell = undefined; + this.onDidChangeFocusedCellEditorEmitter.fire(undefined); + } + }); + } + get allCellEditors(): SimpleMonacoEditor[] { return Array.from(this.currentCellEditors.values()); } @@ -46,8 +62,10 @@ export class NotebookCellEditorService { } editorFocusChanged(editor?: SimpleMonacoEditor): void { - this.currentActiveCell = editor; - this.onDidChangeFocusedCellEditorEmitter.fire(editor); + if (editor) { + this.currentActiveCell = editor; + this.onDidChangeFocusedCellEditorEmitter.fire(editor); + } } getActiveCell(): SimpleMonacoEditor | undefined { diff --git a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts index b585834ce6013..c54faa00db166 100644 --- a/packages/notebook/src/browser/service/notebook-editor-widget-service.ts +++ b/packages/notebook/src/browser/service/notebook-editor-widget-service.ts @@ -45,13 +45,23 @@ export class NotebookEditorWidgetService { protected readonly onDidChangeFocusedEditorEmitter = new Emitter(); readonly onDidChangeFocusedEditor = this.onDidChangeFocusedEditorEmitter.event; + protected readonly onDidChangeCurrentEditorEmitter = new Emitter(); + readonly onDidChangeCurrentEditor = this.onDidChangeCurrentEditorEmitter.event; + focusedEditor?: NotebookEditorWidget = undefined; + currentEditor?: NotebookEditorWidget = undefined; + @postConstruct() protected init(): void { this.applicationShell.onDidChangeActiveWidget(event => { this.notebookEditorFocusChanged(event.newValue as NotebookEditorWidget, event.newValue instanceof NotebookEditorWidget); }); + this.applicationShell.onDidChangeCurrentWidget(event => { + if (event.newValue instanceof NotebookEditorWidget || event.oldValue instanceof NotebookEditorWidget) { + this.currentNotebookEditorChanged(event.newValue); + } + }); } // --- editor management @@ -98,4 +108,14 @@ export class NotebookEditorWidgetService { } } + currentNotebookEditorChanged(newEditor: unknown): void { + if (newEditor instanceof NotebookEditorWidget) { + this.currentEditor = newEditor; + this.onDidChangeCurrentEditorEmitter.fire(newEditor); + } else if (this.currentEditor?.isDisposed || !this.currentEditor?.isVisible) { + this.currentEditor = undefined; + this.onDidChangeCurrentEditorEmitter.fire(undefined); + } + } + } diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index f88d72dc27b0b..18af7ae744aed 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -250,7 +250,7 @@ class EditorAndDocumentStateComputer implements Disposable { this.toDispose.push(this.cellEditorService.onDidChangeCellEditors(() => this.update())); - this.toDispose.push(this.notebookWidgetService.onDidChangeFocusedEditor(() => { + this.toDispose.push(this.notebookWidgetService.onDidChangeCurrentEditor(() => { this.currentState = this.currentState && new EditorAndDocumentState( this.currentState.documents, this.currentState.editors, diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts index c1405980a9b64..06d20cab9b225 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-documents-and-editors-main.ts @@ -55,7 +55,6 @@ class NotebookAndEditorState { const documentDelta = diffSets(before.documents, after.documents); const editorDelta = diffMaps(before.textEditors, after.textEditors); - const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined; const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors); return { @@ -63,7 +62,7 @@ class NotebookAndEditorState { removedDocuments: documentDelta.removed.map(e => e.uri.toComponents()), addedEditors: editorDelta.added, removedEditors: editorDelta.removed.map(removed => removed.id), - newActiveEditor: newActiveEditor, + newActiveEditor: after.activeEditor, visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0 ? undefined : [...after.visibleEditors].map(editor => editor[0]) @@ -114,7 +113,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain // this.WidgetManager.onActiveEditorChanged(() => this.updateState(), this, this.disposables); this.notebookEditorService.onDidAddNotebookEditor(async editor => this.handleEditorAdd(editor), this, this.disposables); this.notebookEditorService.onDidRemoveNotebookEditor(async editor => this.handleEditorRemove(editor), this, this.disposables); - this.notebookEditorService.onDidChangeFocusedEditor(async editor => this.updateState(editor), this, this.disposables); + this.notebookEditorService.onDidChangeCurrentEditor(async editor => this.updateState(editor), this, this.disposables); } dispose(): void { @@ -223,7 +222,7 @@ export class NotebooksAndEditorsMain implements NotebookDocumentsAndEditorsMain if (delta.visibleEditors?.length) { return false; } - if (delta.newActiveEditor) { + if (delta.newActiveEditor !== undefined) { return false; } return true; diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 5470495d402ec..d31d9554ca2ac 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -341,7 +341,7 @@ export class NotebooksExtImpl implements NotebooksExt { }); } } - if (delta.newActiveEditor !== undefined) { + if (delta.newActiveEditor !== undefined && delta.newActiveEditor !== this.activeNotebookEditor?.id) { this.onDidChangeActiveNotebookEditorEmitter.fire(this.activeNotebookEditor?.apiEditor); } } From d999fe0315a29bc5b154067621dbc1eb180fa567 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Tue, 8 Oct 2024 11:38:50 +0200 Subject: [PATCH 416/441] Notebook webview output optimization (#14234) * basic single backlayer webview Signed-off-by: Jonah Iden * allow interacting with outputs Signed-off-by: Jonah Iden * output presentation change and more smaller improvements Signed-off-by: Jonah Iden * output height fixes Signed-off-by: Jonah Iden * output height calculation Signed-off-by: Jonah Iden * collapsing outputs Signed-off-by: Jonah Iden * fixed ouptut presentation change Signed-off-by: Jonah Iden * removed testing console logs Signed-off-by: Jonah Iden * fixed interacting with cells regardless of position focusing it Signed-off-by: Jonah Iden * fixed some errors when closing a notebook Signed-off-by: Jonah Iden * improved renderer failing Signed-off-by: Jonah Iden * fixed issue with cell height changes Signed-off-by: Jonah Iden * fixed outputs when reopening notebook editors Signed-off-by: Jonah Iden * fix iframe height Signed-off-by: Jonah Iden --------- Signed-off-by: Jonah Iden --- .../notebook-cell-actions-contribution.ts | 5 +- .../src/browser/notebook-editor-widget.tsx | 39 ++- .../browser/renderers/cell-output-webview.ts | 24 +- packages/notebook/src/browser/style/index.css | 44 ++- .../browser/view-model/notebook-cell-model.ts | 13 + .../view-model/notebook-cell-output-model.ts | 8 - .../src/browser/view-model/notebook-model.ts | 6 +- .../browser/view/notebook-cell-list-view.tsx | 57 +++- .../browser/view/notebook-code-cell-view.tsx | 166 +++++----- .../view/notebook-markdown-cell-view.tsx | 13 +- .../browser/editors-and-documents-main.ts | 6 +- .../notebooks/notebook-kernels-main.ts | 4 +- .../renderers/cell-output-webview.tsx | 244 +++++++++----- .../renderers/output-webview-internal.ts | 310 +++++++++++++----- .../renderers/webview-communication.ts | 85 ++++- .../browser/plugin-ext-frontend-module.ts | 6 +- 16 files changed, 735 insertions(+), 295 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 8cfcd68531f32..3b497ca0ff50d 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -34,6 +34,7 @@ import { NotebookCommands } from './notebook-actions-contribution'; import { changeCellType } from './cell-operations'; import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service'; import { NotebookService } from '../service/notebook-service'; +import { NOTEBOOK_EDITOR_ID_PREFIX } from '../notebook-editor-widget'; export namespace NotebookCellCommands { /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */ @@ -371,7 +372,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command }], true) )); commands.registerCommand(NotebookCellCommands.CHANGE_OUTPUT_PRESENTATION_COMMAND, this.editableCellCommandHandler( - (_, __, output) => output?.requestOutputPresentationUpdate() + (notebook, cell, output) => { + this.notebookEditorWidgetService.getNotebookEditor(NOTEBOOK_EDITOR_ID_PREFIX + notebook.uri.toString())?.requestOuputPresentationChange(cell.handle, output); + } )); const insertCommand = (type: CellKind, index: number | 'above' | 'below', focusContainer: boolean): CommandHandler => this.editableCellCommandHandler(() => diff --git a/packages/notebook/src/browser/notebook-editor-widget.tsx b/packages/notebook/src/browser/notebook-editor-widget.tsx index 3451d76615e8d..1d8828e4048ed 100644 --- a/packages/notebook/src/browser/notebook-editor-widget.tsx +++ b/packages/notebook/src/browser/notebook-editor-widget.tsx @@ -18,7 +18,7 @@ import * as React from '@theia/core/shared/react'; import { CommandRegistry, MenuModelRegistry, URI } from '@theia/core'; import { ReactWidget, Navigatable, SaveableSource, Message, DelegatingSaveable, lock, unlock, animationFrame } from '@theia/core/lib/browser'; import { ReactNode } from '@theia/core/shared/react'; -import { CellKind } from '../common'; +import { CellKind, NotebookCellsChangeType } from '../common'; import { CellRenderer as CellRenderer, NotebookCellListView } from './view/notebook-cell-list-view'; import { NotebookCodeCellRenderer } from './view/notebook-code-cell-view'; import { NotebookMarkdownCellRenderer } from './view/notebook-markdown-cell-view'; @@ -35,6 +35,8 @@ import { NotebookViewportService } from './view/notebook-viewport-service'; import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution'; import { NotebookFindWidget } from './view/notebook-find-widget'; import debounce = require('lodash/debounce'); +import { CellOutputWebview, CellOutputWebviewFactory } from './renderers/cell-output-webview'; +import { NotebookCellOutputModel } from './view-model/notebook-cell-output-model'; const PerfectScrollbar = require('react-perfect-scrollbar'); export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory'); @@ -44,6 +46,9 @@ export function createNotebookEditorWidgetContainer(parent: interfaces.Container child.bind(NotebookEditorProps).toConstantValue(props); + const cellOutputWebviewFactory: CellOutputWebviewFactory = parent.get(CellOutputWebviewFactory); + child.bind(CellOutputWebview).toConstantValue(cellOutputWebviewFactory()); + child.bind(NotebookContextManager).toSelf().inSingletonScope(); child.bind(NotebookMainToolbarRenderer).toSelf().inSingletonScope(); child.bind(NotebookCellToolbarFactory).toSelf().inSingletonScope(); @@ -104,6 +109,9 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa @inject(NotebookViewportService) protected readonly viewportService: NotebookViewportService; + @inject(CellOutputWebview) + protected readonly cellOutputWebview: CellOutputWebview; + protected readonly onDidChangeModelEmitter = new Emitter(); readonly onDidChangeModel = this.onDidChangeModelEmitter.event; @@ -173,11 +181,18 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]); model.setSelectedCell(model.cells[0]); } + model.onDidChangeContent(changeEvents => { + const cellEvent = changeEvents.filter(event => event.kind === NotebookCellsChangeType.Move || event.kind === NotebookCellsChangeType.ModelChange); + if (cellEvent.length > 0) { + this.cellOutputWebview.cellsChanged(cellEvent); + } + }); }); } protected async waitForData(): Promise { this._model = await this.props.notebookData; + this.cellOutputWebview.init(this._model, this); this.saveable.delegate = this._model; this.toDispose.push(this._model); this.toDispose.push(this._model.onDidChangeContent(() => { @@ -261,12 +276,15 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.viewportService.onScroll(e)}> - +
      + {this.cellOutputWebview.render()} + +
      ; @@ -282,6 +300,12 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa this.notebookEditorService.removeNotebookEditor(this); } + requestOuputPresentationChange(cellHandle: number, output?: NotebookCellOutputModel): void { + if (output) { + this.cellOutputWebview.requestOutputPresentationUpdate(cellHandle, output); + } + } + postKernelMessage(message: unknown): void { this.onDidPostKernelMessageEmitter.fire(message); } @@ -307,6 +331,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa } override dispose(): void { + this.cellOutputWebview.dispose(); this.notebookContextManager.dispose(); this.onDidChangeModelEmitter.dispose(); this.onDidPostKernelMessageEmitter.dispose(); diff --git a/packages/notebook/src/browser/renderers/cell-output-webview.ts b/packages/notebook/src/browser/renderers/cell-output-webview.ts index 67c86fcc88437..e838f143df911 100644 --- a/packages/notebook/src/browser/renderers/cell-output-webview.ts +++ b/packages/notebook/src/browser/renderers/cell-output-webview.ts @@ -14,20 +14,38 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Disposable } from '@theia/core'; -import { NotebookCellModel } from '../view-model/notebook-cell-model'; +import { Disposable, Event } from '@theia/core'; import { NotebookModel } from '../view-model/notebook-model'; +import { NotebookEditorWidget } from '../notebook-editor-widget'; +import { NotebookContentChangedEvent } from '../notebook-types'; +import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model'; +import { NotebookCellModel } from '../view-model/notebook-cell-model'; export const CellOutputWebviewFactory = Symbol('outputWebviewFactory'); +export const CellOutputWebview = Symbol('outputWebview'); -export type CellOutputWebviewFactory = (cell: NotebookCellModel, notebook: NotebookModel) => Promise; +export type CellOutputWebviewFactory = () => Promise; + +export interface OutputRenderEvent { + cellHandle: number; + outputId: string; + outputHeight: number; +} export interface CellOutputWebview extends Disposable { readonly id: string; + init(notebook: NotebookModel, editor: NotebookEditorWidget): void; + render(): React.ReactNode; + setCellHeight(cell: NotebookCellModel, height: number): void; + cellsChanged(cellEvent: NotebookContentChangedEvent[]): void; + onDidRenderOutput: Event + + requestOutputPresentationUpdate(cellHandle: number, output: NotebookCellOutputModel): void; + attachWebview(): void; isAttached(): boolean } diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index f16a935aa5f7f..e557a7983e7d8 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -20,10 +20,24 @@ } .theia-notebook-cell-list { + position: absolute; + top: 0; + width: 100%; overflow-y: auto; list-style: none; padding-left: 0px; background-color: var(--theia-notebook-editorBackground); + z-index: 0; + pointer-events: none; +} + + +.theia-notebook-cell-output-webview { + padding: 5px 0px; + margin: 0px 15px 0px 50px; + width: calc(100% - 60px); + position: absolute; + z-index: 0; } .theia-notebook-cell { @@ -69,7 +83,8 @@ /* Rendered Markdown Content */ .theia-notebook-markdown-content { - padding: 8px 16px 8px 36px; + pointer-events: all; + padding: 8px 16px 8px 0px; font-size: var(--theia-notebook-markdown-size); } @@ -87,9 +102,13 @@ padding-bottom: 0; } +.theia-notebook-markdown-sidebar { + width: 35px; +} + /* Markdown cell edit mode */ .theia-notebook-cell-content:has(.theia-notebook-markdown-editor-container>.theia-notebook-cell-editor) { - margin-left: 36px; + pointer-events: all; margin-right: var(--theia-notebook-cell-editor-margin-right); outline: 1px solid var(--theia-notebook-cellBorderColor); } @@ -108,6 +127,7 @@ } .theia-notebook-cell-editor-container { + pointer-events: all; width: calc(100% - 46px); flex: 1; outline: 1px solid var(--theia-notebook-cellBorderColor); @@ -149,6 +169,7 @@ } .theia-notebook-cell-toolbar { + pointer-events: all; border: 1px solid var(--theia-notebook-cellToolbarSeparator); display: flex; position: absolute; @@ -161,11 +182,15 @@ display: flex; flex-direction: column; padding: 2px; - background-color: var(--theia-editor-background); flex-grow: 1; } .theia-notebook-cell-sidebar { + pointer-events: all; + display: flex; +} + +.theia-notebook-cell-sidebar-actions { display: flex; flex-direction: column; } @@ -194,6 +219,7 @@ } .theia-notebook-cell-divider { + pointer-events: all; height: 25px; width: 100%; } @@ -303,19 +329,19 @@ margin: 1px 0 0 4px; } -.theia-notebook-cell-output-webview { - padding: 5px 0px; - margin: 0px 15px 0px 9px; - width: 100%; -} - .theia-notebook-cell-drop-indicator { height: 2px; background-color: var(--theia-notebook-focusedCellBorder); width: 100%; } +.theia-notebook-collapsed-output-container { + width: 0; + overflow: visible; +} + .theia-notebook-collapsed-output { + text-wrap: nowrap; padding: 4px 8px; color: var(--theia-foreground); margin-left: 30px; diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index f9ce7ca5d11b6..bf3b8837a4cc2 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -129,6 +129,9 @@ export class NotebookCellModel implements NotebookCell, Disposable { protected onDidRequestCenterEditorEmitter = new Emitter(); readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event; + protected onDidCellHeightChangeEmitter = new Emitter(); + readonly onDidCellHeightChange = this.onDidCellHeightChangeEmitter.event; + @inject(NotebookCellModelProps) protected readonly props: NotebookCellModelProps; @@ -251,6 +254,16 @@ export class NotebookCellModel implements NotebookCell, Disposable { } } + protected _cellheight: number = 0; + get cellHeight(): number { + return this._cellheight; + } + + set cellHeight(height: number) { + this.onDidCellHeightChangeEmitter.fire(height); + this._cellheight = height; + } + @postConstruct() protected init(): void { this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op)); diff --git a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts index 846d5efb5e00f..525473b601b33 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-output-model.ts @@ -23,9 +23,6 @@ export class NotebookCellOutputModel implements Disposable { private didChangeDataEmitter = new Emitter(); readonly onDidChangeData = this.didChangeDataEmitter.event; - private requestOutputPresentationChangeEmitter = new Emitter(); - readonly onRequestOutputPresentationChange = this.requestOutputPresentationChangeEmitter.event; - get outputId(): string { return this.rawOutput.outputId; } @@ -54,11 +51,6 @@ export class NotebookCellOutputModel implements Disposable { dispose(): void { this.didChangeDataEmitter.dispose(); - this.requestOutputPresentationChangeEmitter.dispose(); - } - - requestOutputPresentationUpdate(): void { - this.requestOutputPresentationChangeEmitter.fire(); } getData(): CellOutput { diff --git a/packages/notebook/src/browser/view-model/notebook-model.ts b/packages/notebook/src/browser/view-model/notebook-model.ts index 78faa0cdaeeda..a80ce44e4e4ea 100644 --- a/packages/notebook/src/browser/view-model/notebook-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-model.ts @@ -509,10 +509,14 @@ export class NotebookModel implements Saveable, Disposable { return true; } - protected getCellIndexByHandle(handle: number): number { + getCellIndexByHandle(handle: number): number { return this.cells.findIndex(c => c.handle === handle); } + getCellByHandle(handle: number): NotebookCellModel | undefined { + return this.cells.find(c => c.handle === handle); + } + protected isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata): boolean { const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); for (const key of keys) { diff --git a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx index 5a156328e7859..7753a74e97f3b 100644 --- a/packages/notebook/src/browser/view/notebook-cell-list-view.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-list-view.tsx @@ -26,9 +26,19 @@ import { NotebookContextManager } from '../service/notebook-context-manager'; export interface CellRenderer { render(notebookData: NotebookModel, cell: NotebookCellModel, index: number): React.ReactNode + renderSidebar(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode renderDragImage(cell: NotebookCellModel): HTMLElement } +export function observeCellHeight(ref: HTMLDivElement | null, cell: NotebookCellModel): void { + if (ref) { + cell.cellHeight = ref?.getBoundingClientRect().height ?? 0; + new ResizeObserver(entries => + cell.cellHeight = ref?.getBoundingClientRect().height ?? 0 + ).observe(ref); + } +} + interface CellListProps { renderers: Map; notebookModel: NotebookModel; @@ -109,7 +119,7 @@ export class NotebookCellListView extends React.Component + return
        this.onDragStart(e)}> {this.props.notebookModel.cells .map((cell, index) => @@ -119,13 +129,8 @@ export class NotebookCellListView extends React.Component this.onAddNewCell(commandId, index)} onDrop={e => this.onDrop(e, index)} onDragOver={e => this.onDragOver(e, cell, 'top')} /> - {this.shouldRenderDragOverIndicator(cell, 'top') && } +
      • { - this.setState({ ...this.state, selectedCell: cell }); - this.props.notebookModel.setSelectedCell(cell, false); - }} - onDragStart={e => this.onDragStart(e, index, cell)} onDragEnd={e => { NotebookCellListView.dragGhost?.remove(); this.setState({ ...this.state, dragOverIndicator: undefined }); @@ -134,6 +139,7 @@ export class NotebookCellListView extends React.Component this.onDrop(e, index)} draggable={true} tabIndex={-1} + data-cell-handle={cell.handle} ref={ref => { if (ref && cell === this.state.selectedCell && this.state.scrollIntoView) { ref.scrollIntoView({ block: 'nearest' }); @@ -141,8 +147,16 @@ export class NotebookCellListView extends React.Component -
        + }} + onClick={e => { + this.setState({ ...this.state, selectedCell: cell }); + this.props.notebookModel.setSelectedCell(cell, false); + }} + > +
        +
        + {this.renderCellSidebar(cell)} +
        {this.renderCellContent(cell, index)}
        @@ -152,7 +166,7 @@ export class NotebookCellListView extends React.Component - {this.shouldRenderDragOverIndicator(cell, 'bottom') && } + ) } @@ -173,13 +187,30 @@ export class NotebookCellListView extends React.Component, index: number, cell: NotebookCellModel): void { + renderCellSidebar(cell: NotebookCellModel): React.ReactNode { + const renderer = this.props.renderers.get(cell.cellKind); + if (!renderer) { + throw new Error(`No renderer found for cell type ${cell.cellKind}`); + } + return renderer.renderSidebar(this.props.notebookModel, cell); + } + + protected onDragStart(event: React.DragEvent): void { event.stopPropagation(); if (!this.isEnabled()) { event.preventDefault(); return; } + const cellHandle = (event.target as HTMLLIElement).getAttribute('data-cell-handle'); + + if (!cellHandle) { + throw new Error('Cell handle not found in element for cell drag event'); + } + + const index = this.props.notebookModel.getCellIndexByHandle(parseInt(cellHandle)); + const cell = this.props.notebookModel.cells[index]; + NotebookCellListView.dragGhost = document.createElement('div'); NotebookCellListView.dragGhost.classList.add('theia-notebook-drag-ghost-image'); NotebookCellListView.dragGhost.appendChild(this.props.renderers.get(cell.cellKind)?.renderDragImage(cell) ?? document.createElement('div')); @@ -274,6 +305,6 @@ export function NotebookCellDivider({ isVisible, onAddNewCell, onDrop, onDragOve
      • ; } -function CellDropIndicator(): React.JSX.Element { - return
        ; +function CellDropIndicator(props: { visible: boolean }): React.JSX.Element { + return
        ; } diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 24fb8630d5da9..537776e03adaf 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -17,12 +17,11 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor'; -import { CellOutputWebviewFactory, CellOutputWebview } from '../renderers/cell-output-webview'; import { NotebookRendererRegistry } from '../notebook-renderer-registry'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { NotebookModel } from '../view-model/notebook-model'; import { CellEditor } from './notebook-cell-editor'; -import { CellRenderer } from './notebook-cell-list-view'; +import { CellRenderer, observeCellHeight } from './notebook-cell-list-view'; import { NotebookCellToolbarFactory } from './notebook-cell-toolbar-factory'; import { NotebookCellActionContribution, NotebookCellCommands } from '../contributions/notebook-cell-actions-contribution'; import { CellExecution, NotebookExecutionStateService } from '../service/notebook-execution-state-service'; @@ -36,6 +35,7 @@ import { NotebookOptionsService } from '../service/notebook-options'; import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent'; import { NotebookCellEditorService } from '../service/notebook-cell-editor-service'; +import { CellOutputWebview } from '../renderers/cell-output-webview'; @injectable() export class NotebookCodeCellRenderer implements CellRenderer { @@ -45,9 +45,6 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(NotebookRendererRegistry) protected readonly notebookRendererRegistry: NotebookRendererRegistry; - @inject(CellOutputWebviewFactory) - protected readonly cellOutputWebviewFactory: CellOutputWebviewFactory; - @inject(NotebookCellToolbarFactory) protected readonly notebookCellToolbarFactory: NotebookCellToolbarFactory; @@ -75,40 +72,39 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(MarkdownRenderer) protected readonly markdownRenderer: MarkdownRenderer; + @inject(CellOutputWebview) + protected readonly outputWebview: CellOutputWebview; + render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { - return
        -
        -
        - {this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, cell, { - contextMenuArgs: () => [cell], commandArgs: () => [notebookModel, cell] - }) - } - -
        -
        - - cell.requestFocusEditor()} /> -
        + return
        observeCellHeight(ref, cell)}> +
        + + cell.requestFocusEditor()} />
        -
        - - this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.OUTPUT_SIDEBAR_MENU, cell, { - contextMenuArgs: () => [notebookModel, cell, cell.outputs[0]] - }) - } /> -
        ; } + renderSidebar(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { + return
        + + + this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.OUTPUT_SIDEBAR_MENU, cell, { + contextMenuArgs: () => [notebookModel, cell, cell.outputs[0]] + }) + } /> +
        ; + + } + renderDragImage(cell: NotebookCellModel): HTMLElement { const dragImage = document.createElement('div'); dragImage.className = 'theia-notebook-drag-image'; @@ -151,6 +147,37 @@ export class NotebookCodeCellRenderer implements CellRenderer { } +export interface NotebookCodeCellSidebarProps { + cell: NotebookCellModel; + notebook: NotebookModel; + notebookCellToolbarFactory: NotebookCellToolbarFactory +} + +export class NotebookCodeCellSidebar extends React.Component { + + protected toDispose = new DisposableCollection(); + + constructor(props: NotebookCodeCellSidebarProps) { + super(props); + + this.toDispose.push(props.cell.onDidCellHeightChange(() => this.forceUpdate())); + } + + override componentWillUnmount(): void { + this.toDispose.dispose(); + } + + override render(): React.ReactNode { + return
        + {this.props.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, this.props.cell, { + contextMenuArgs: () => [this.props.cell], commandArgs: () => [this.props.notebook, this.props.cell] + }) + } + +
        ; + } +} + export interface NotebookCodeCellStatusProps { notebook: NotebookModel; cell: NotebookCellModel; @@ -260,79 +287,42 @@ export class NotebookCodeCellStatus extends React.Component React.ReactNode; } export class NotebookCodeCellOutputs extends React.Component { - protected outputsWebview: CellOutputWebview | undefined; - protected outputsWebviewPromise: Promise | undefined; - protected toDispose = new DisposableCollection(); - constructor(props: NotebookCellOutputProps) { - super(props); - } + protected outputHeight: number = 0; override async componentDidMount(): Promise { - const { cell, notebook, outputWebviewFactory } = this.props; - this.toDispose.push(cell.onDidChangeOutputs(() => this.updateOutputs())); - this.toDispose.push(cell.onDidChangeOutputVisibility(visible => { - if (!visible && this.outputsWebview) { - this.outputsWebview?.dispose(); - this.outputsWebview = undefined; - this.outputsWebviewPromise = undefined; + const { cell } = this.props; + this.toDispose.push(cell.onDidChangeOutputs(() => this.forceUpdate())); + this.toDispose.push(this.props.cell.onDidChangeOutputVisibility(() => this.forceUpdate())); + this.toDispose.push(this.props.outputWebview.onDidRenderOutput(event => { + if (event.cellHandle === this.props.cell.handle) { + this.outputHeight = event.outputHeight; this.forceUpdate(); - } else { - this.updateOutputs(); } })); - if (cell.outputs.length > 0) { - this.outputsWebviewPromise = outputWebviewFactory(cell, notebook).then(webview => { - this.outputsWebview = webview; - this.forceUpdate(); - return webview; - }); - } } - protected async updateOutputs(): Promise { - const { cell, notebook, outputWebviewFactory } = this.props; - if (!this.outputsWebviewPromise && cell.outputs.length > 0) { - this.outputsWebviewPromise = outputWebviewFactory(cell, notebook).then(webview => { - this.outputsWebview = webview; - this.forceUpdate(); - return webview; - }); - this.forceUpdate(); - } else if (this.outputsWebviewPromise && cell.outputs.length === 0 && cell.internalMetadata.runEndTime) { - (await this.outputsWebviewPromise).dispose(); - this.outputsWebview = undefined; - this.outputsWebviewPromise = undefined; - this.forceUpdate(); - } - } - - override async componentDidUpdate(): Promise { - if (!(await this.outputsWebviewPromise)?.isAttached()) { - (await this.outputsWebviewPromise)?.attachWebview(); - } - } - - override async componentWillUnmount(): Promise { + override componentWillUnmount(): void { this.toDispose.dispose(); - (await this.outputsWebviewPromise)?.dispose(); } override render(): React.ReactNode { - return this.outputsWebview && this.props.cell.outputVisible ? - <> + if (!this.props.cell.outputs?.length) { + return <>; + } + if (this.props.cell.outputVisible) { + return
        {this.props.renderSidebar()} - {this.outputsWebview.render()} - : - this.props.cell.outputs?.length ? {nls.localizeByDefault('Outputs are collapsed')} : <>; - +
        ; + } + return
        {nls.localizeByDefault('Outputs are collapsed')}
        ; } } diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index 68d2dbbe139c4..e24ff32f838b7 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -18,7 +18,7 @@ import * as React from '@theia/core/shared/react'; import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; import { NotebookModel } from '../view-model/notebook-model'; -import { CellRenderer } from './notebook-cell-list-view'; +import { CellRenderer, observeCellHeight } from './notebook-cell-list-view'; import { NotebookCellModel } from '../view-model/notebook-cell-model'; import { CellEditor } from './notebook-cell-editor'; import { inject, injectable } from '@theia/core/shared/inversify'; @@ -63,6 +63,10 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { notebookCellEditorService={this.notebookCellEditorService} />; } + renderSidebar(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { + return
        ; + } + renderDragImage(cell: NotebookCellModel): HTMLElement { const dragImage = document.createElement('div'); dragImage.style.width = this.notebookContextManager.context?.clientWidth + 'px'; @@ -135,7 +139,7 @@ function MarkdownCell({ } return editMode ? - (
        + (
        observeCellHeight(ref, cell)}> ) : (
        cell.requestEdit()} - ref={node => node?.replaceChildren(...markdownContent)} + ref={node => { + node?.replaceChildren(...markdownContent); + observeCellHeight(node, cell); + }} />); } diff --git a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts index 18af7ae744aed..00a104e4a30d7 100644 --- a/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts +++ b/packages/plugin-ext/src/main/browser/editors-and-documents-main.ts @@ -337,8 +337,10 @@ class EditorAndDocumentStateComputer implements Disposable { } for (const editor of this.cellEditorService.allCellEditors) { - const editorSnapshot = new EditorSnapshot(editor); - editors.set(editorSnapshot.id, editorSnapshot); + if (editor.getControl()?.getModel()) { + const editorSnapshot = new EditorSnapshot(editor); + editors.set(editorSnapshot.id, editorSnapshot); + } }; const newState = new EditorAndDocumentState(models, editors, activeId); diff --git a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts index 8572ebee91f17..fe21cfeaa8ba2 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/notebook-kernels-main.ts @@ -25,13 +25,13 @@ import { CellExecuteUpdateDto, CellExecutionCompleteDto, MAIN_RPC_CONTEXT, Noteb import { RPCProtocol } from '../../../common/rpc-protocol'; import { CellExecution, NotebookEditorWidgetService, NotebookExecutionStateService, - NotebookKernelChangeEvent, NotebookKernelService, NotebookService + NotebookKernelChangeEvent, NotebookKernelService, NotebookService, NotebookKernel as NotebookKernelServiceKernel } from '@theia/notebook/lib/browser'; import { interfaces } from '@theia/core/shared/inversify'; import { NotebookKernelSourceAction } from '@theia/notebook/lib/common'; import { NotebookDto } from './notebook-dto'; -abstract class NotebookKernel { +abstract class NotebookKernel implements NotebookKernelServiceKernel { private readonly onDidChangeEmitter = new Emitter(); private readonly preloads: { uri: URI; provides: readonly string[] }[]; readonly onDidChange: Event = this.onDidChangeEmitter.event; diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx index 745c77dfb231e..9100d5aba52a0 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/cell-output-webview.tsx @@ -19,34 +19,33 @@ *--------------------------------------------------------------------------------------------*/ import * as React from '@theia/core/shared/react'; -import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify'; +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; import { generateUuid } from '@theia/core/lib/common/uuid'; import { NotebookRendererMessagingService, CellOutputWebview, NotebookRendererRegistry, - NotebookEditorWidgetService, NotebookCellOutputsSplice, NOTEBOOK_EDITOR_ID_PREFIX, NotebookKernelService, NotebookEditorWidget + NotebookEditorWidgetService, NotebookKernelService, NotebookEditorWidget, + OutputRenderEvent, + NotebookCellOutputsSplice, + NotebookContentChangedEvent } from '@theia/notebook/lib/browser'; -import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; import { WebviewWidget } from '../../webview/webview'; import { Message, WidgetManager } from '@theia/core/lib/browser'; import { outputWebviewPreload, PreloadContext } from './output-webview-internal'; import { WorkspaceTrustService } from '@theia/workspace/lib/browser'; -import { ChangePreferredMimetypeMessage, FromWebviewMessage, OutputChangedMessage } from './webview-communication'; -import { CellUri } from '@theia/notebook/lib/common'; -import { Disposable, DisposableCollection, nls, QuickPickService } from '@theia/core'; -import { NotebookCellOutputModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-output-model'; +import { CellsChangedMessage, CellsMoved, CellsSpliced, ChangePreferredMimetypeMessage, FromWebviewMessage, OutputChangedMessage } from './webview-communication'; +import { Disposable, DisposableCollection, Emitter, QuickPickService, nls } from '@theia/core'; import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { NotebookOptionsService, NotebookOutputOptions } from '@theia/notebook/lib/browser/service/notebook-options'; +import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; +import { NotebookCellsChangeType } from '@theia/notebook/lib/common'; +import { NotebookCellOutputModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-output-model'; -const CellModel = Symbol('CellModel'); -const Notebook = Symbol('NotebookModel'); export const AdditionalNotebookCellOutputCss = Symbol('AdditionalNotebookCellOutputCss'); -export function createCellOutputWebviewContainer(ctx: interfaces.Container, cell: NotebookCellModel, notebook: NotebookModel): interfaces.Container { +export function createCellOutputWebviewContainer(ctx: interfaces.Container): interfaces.Container { const child = ctx.createChild(); - child.bind(CellModel).toConstantValue(cell); - child.bind(Notebook).toConstantValue(notebook); child.bind(AdditionalNotebookCellOutputCss).toConstantValue(DEFAULT_NOTEBOOK_OUTPUT_CSS); - child.bind(CellOutputWebviewImpl).toSelf().inSingletonScope(); + child.bind(CellOutputWebviewImpl).toSelf(); return child; } @@ -191,18 +190,16 @@ tbody th { } `; +interface CellOutputUpdate extends NotebookCellOutputsSplice { + cellHandle: number +} + @injectable() export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @inject(NotebookRendererMessagingService) protected readonly messagingService: NotebookRendererMessagingService; - @inject(CellModel) - protected readonly cell: NotebookCellModel; - - @inject(Notebook) - protected readonly notebook: NotebookModel; - @inject(WidgetManager) protected readonly widgetManager: WidgetManager; @@ -227,36 +224,63 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { @inject(NotebookOptionsService) protected readonly notebookOptionsService: NotebookOptionsService; + // returns the output Height + protected readonly onDidRenderOutputEmitter = new Emitter(); + readonly onDidRenderOutput = this.onDidRenderOutputEmitter.event; + + protected notebook: NotebookModel; + protected options: NotebookOutputOptions; readonly id = generateUuid(); protected editor: NotebookEditorWidget | undefined; - protected readonly elementRef = React.createRef(); - protected outputPresentationListeners: DisposableCollection = new DisposableCollection(); + protected element?: HTMLDivElement; // React.createRef(); protected webviewWidget: WebviewWidget; protected toDispose = new DisposableCollection(); - @postConstruct() - protected async init(): Promise { - this.editor = this.notebookEditorWidgetService.getNotebookEditor(NOTEBOOK_EDITOR_ID_PREFIX + CellUri.parse(this.cell.uri)?.notebook); + protected isDisposed = false; + + async init(notebook: NotebookModel, editor: NotebookEditorWidget): Promise { + this.notebook = notebook; + this.editor = editor; this.options = this.notebookOptionsService.computeOutputOptions(); this.toDispose.push(this.notebookOptionsService.onDidChangeOutputOptions(options => { this.options = options; this.updateStyles(); })); - this.toDispose.push(this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange))); - this.toDispose.push(this.cell.onDidChangeOutputItems(output => { - this.updateOutput({ start: this.cell.outputs.findIndex(o => o.outputId === output.outputId), deleteCount: 1, newOutputs: [output] }); - })); + this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); + // this.webviewWidget.parent = this.editor ?? null; + this.webviewWidget.setContentOptions({ + allowScripts: true, + // eslint-disable-next-line max-len + // list taken from https://github.com/microsoft/vscode/blob/a27099233b956dddc2536d4a0d714ab36266d897/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts#L762-L774 + enableCommandUris: [ + 'github-issues.authNow', + 'workbench.extensions.search', + 'workbench.action.openSettings', + '_notebook.selectKernel', + 'jupyter.viewOutput', + 'workbench.action.openLargeOutput', + 'cellOutput.enableScrolling', + ], + }); + this.webviewWidget.setHTML(await this.createWebviewContent()); + + this.notebook.onDidAddOrRemoveCell(e => { + if (e.newCellIds) { + const newCells = e.newCellIds.map(id => this.notebook.cells.find(cell => cell.handle === id)).filter(cell => !!cell) as NotebookCellModel[]; + newCells.forEach(cell => this.attachCellAndOutputListeners(cell)); + } + }); + this.notebook.cells.forEach(cell => this.attachCellAndOutputListeners(cell)); if (this.editor) { this.toDispose.push(this.editor.onDidPostKernelMessage(message => { - // console.log('from extension customKernelMessage ', JSON.stringify(message)); this.webviewWidget.sendMessage({ type: 'customKernelMessage', message @@ -264,7 +288,6 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { })); this.toDispose.push(this.editor.onPostRendererMessage(messageObj => { - // console.log('from extension customRendererMessage ', JSON.stringify(messageObj)); this.webviewWidget.sendMessage({ type: 'customRendererMessage', ...messageObj @@ -273,110 +296,184 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } - this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id }); - this.webviewWidget.parent = this.editor ?? null; - this.webviewWidget.setContentOptions({ - allowScripts: true, - // eslint-disable-next-line max-len - // list taken from https://github.com/microsoft/vscode/blob/a27099233b956dddc2536d4a0d714ab36266d897/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts#L762-L774 - enableCommandUris: [ - 'github-issues.authNow', - 'workbench.extensions.search', - 'workbench.action.openSettings', - '_notebook.selectKernel', - 'jupyter.viewOutput', - 'workbench.action.openLargeOutput', - 'cellOutput.enableScrolling', - ], - }); - this.webviewWidget.setHTML(await this.createWebviewContent()); - this.webviewWidget.onMessage((message: FromWebviewMessage) => { this.handleWebviewMessage(message); }); } + attachCellAndOutputListeners(cell: NotebookCellModel): void { + this.toDispose.push(cell.onDidChangeOutputs(outputChange => this.updateOutputs([{ + newOutputs: outputChange.newOutputs, + start: outputChange.start, + deleteCount: outputChange.deleteCount, + cellHandle: cell.handle + }]))); + this.toDispose.push(cell.onDidChangeOutputItems(output => { + const oldOutputIndex = cell.outputs.findIndex(o => o.outputId === output.outputId); + this.updateOutputs([{ + cellHandle: cell.handle, + newOutputs: [output], + start: oldOutputIndex, + deleteCount: 1 + }]); + })); + this.toDispose.push(cell.onDidCellHeightChange(height => this.setCellHeight(cell, height))); + this.toDispose.push(cell.onDidChangeOutputVisibility(visible => { + this.webviewWidget.sendMessage({ + type: 'outputVisibilityChanged', + cellHandle: cell.handle, + visible + }); + })); + } + render(): React.JSX.Element { - return
        ; + return
        { + if (element) { + this.element = element; + this.attachWebview(); + } + }}>
        ; } attachWebview(): void { - if (this.elementRef.current) { + if (this.element) { this.webviewWidget.processMessage(new Message('before-attach')); - this.elementRef.current.appendChild(this.webviewWidget.node); + this.element.appendChild(this.webviewWidget.node); this.webviewWidget.processMessage(new Message('after-attach')); this.webviewWidget.setIframeHeight(0); } } isAttached(): boolean { - return this.elementRef.current?.contains(this.webviewWidget.node) ?? false; + return this.element?.contains(this.webviewWidget.node) ?? false; } - updateOutput(update: NotebookCellOutputsSplice): void { + updateOutputs(updates: CellOutputUpdate[]): void { if (this.webviewWidget.isHidden) { this.webviewWidget.show(); } - this.outputPresentationListeners.dispose(); - this.outputPresentationListeners = new DisposableCollection(); - for (const output of this.cell.outputs) { - this.outputPresentationListeners.push(output.onRequestOutputPresentationChange(() => this.requestOutputPresentationUpdate(output))); - } - const updateOutputMessage: OutputChangedMessage = { type: 'outputChanged', - newOutputs: update.newOutputs.map(output => ({ - id: output.outputId, - items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), - metadata: output.metadata - })), - deleteStart: update.start, - deleteCount: update.deleteCount + changes: updates.map(update => ({ + cellHandle: update.cellHandle, + newOutputs: update.newOutputs.map(output => ({ + id: output.outputId, + items: output.outputs.map(item => ({ mime: item.mime, data: item.data.buffer })), + metadata: output.metadata + })), + start: update.start, + deleteCount: update.deleteCount + })) }; this.webviewWidget.sendMessage(updateOutputMessage); } - private async requestOutputPresentationUpdate(output: NotebookCellOutputModel): Promise { + cellsChanged(cellEvents: NotebookContentChangedEvent[]): void { + const changes: Array = []; + + for (const event of cellEvents) { + if (event.kind === NotebookCellsChangeType.Move) { + changes.push(...event.cells.map((cell, i) => ({ + type: 'cellMoved', + cellHandle: event.cells[0].handle, + toIndex: event.newIdx + i, + } as CellsMoved))); + } else if (event.kind === NotebookCellsChangeType.ModelChange) { + changes.push(...event.changes.map(change => ({ + type: 'cellsSpliced', + start: change.start, + deleteCount: change.deleteCount, + newCells: change.newItems.map(cell => cell.handle) + } as CellsSpliced))); + } + } + + this.webviewWidget.sendMessage({ + type: 'cellsChanged', + changes: changes.filter(e => e) + } as CellsChangedMessage); + } + + setCellHeight(cell: NotebookCellModel, height: number): void { + if (!this.isDisposed) { + this.webviewWidget.sendMessage({ + type: 'cellHeightUpdate', + cellHandle: cell.handle, + cellKind: cell.cellKind, + height + }); + } + } + + async requestOutputPresentationUpdate(cellHandle: number, output: NotebookCellOutputModel): Promise { const selectedMime = await this.quickPickService.show( output.outputs.map(item => ({ label: item.mime })), { description: nls.localizeByDefault('Select mimetype to render for current output') }); if (selectedMime) { this.webviewWidget.sendMessage({ type: 'changePreferredMimetype', + cellHandle, outputId: output.outputId, mimeType: selectedMime.label } as ChangePreferredMimetypeMessage); } } - private handleWebviewMessage(message: FromWebviewMessage): void { + protected handleWebviewMessage(message: FromWebviewMessage): void { if (!this.editor) { throw new Error('No editor found for cell output webview'); } switch (message.type) { case 'initialized': - this.updateOutput({ newOutputs: this.cell.outputs, start: 0, deleteCount: 0 }); + this.updateOutputs(this.notebook.cells.map(cell => ({ + cellHandle: cell.handle, + newOutputs: cell.outputs, + start: 0, + deleteCount: 0 + }))); this.updateStyles(); break; case 'customRendererMessage': - // console.log('from webview customRendererMessage ', message.rendererId, '', JSON.stringify(message.message)); this.messagingService.getScoped(this.editor.id).postMessage(message.rendererId, message.message); break; case 'didRenderOutput': - this.webviewWidget.setIframeHeight(message.contentHeight + 5); + this.webviewWidget.setIframeHeight(message.bodyHeight); + this.onDidRenderOutputEmitter.fire({ + cellHandle: message.cellHandle, + outputId: message.outputId, + outputHeight: message.outputHeight + }); break; case 'did-scroll-wheel': this.editor.node.getElementsByClassName('theia-notebook-viewport')[0].children[0].scrollBy(message.deltaX, message.deltaY); break; case 'customKernelMessage': - // console.log('from webview customKernelMessage ', JSON.stringify(message.message)); this.editor.recieveKernelMessage(message.message); break; case 'inputFocusChanged': this.editor?.outputInputFocusChanged(message.focused); + break; + case 'cellFocusChanged': + const selectedCell = this.notebook.getCellByHandle(message.cellHandle); + if (selectedCell) { + this.notebook.setSelectedCell(selectedCell); + } + break; + case 'cellHeightRequest': + const cellHeight = this.notebook.getCellByHandle(message.cellHandle)?.cellHeight ?? 0; + this.webviewWidget.sendMessage({ + type: 'cellHeightUpdate', + cellHandle: message.cellHandle, + height: cellHeight + }); + break; + case 'bodyHeightChange': + this.webviewWidget.setIframeHeight(message.height); + break; } } @@ -445,8 +542,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable { } dispose(): void { + this.isDisposed = true; this.toDispose.dispose(); - this.outputPresentationListeners.dispose(); - this.webviewWidget.dispose(); } } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts index ea348cc8d269b..c52a416b046b6 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/output-webview-internal.ts @@ -74,6 +74,12 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { const theia = acquireVsCodeApi(); const renderFallbackErrorName = 'vscode.fallbackToNextRenderer'; + document.body.style.overflow = 'hidden'; + const container = document.createElement('div'); + container.id = 'container'; + container.classList.add('widgetarea'); + document.body.appendChild(container); + function createEmitter(listenerChange: (listeners: Set>) => void = () => undefined): EmitterLike { const listeners = new Set>(); return { @@ -138,19 +144,124 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { return module.activate(createKernelContext()); } - class Output { + class OutputCell { + readonly element: HTMLElement; + readonly outputElements: OutputContainer[] = []; + + constructor(public cellHandle: number, cellIndex?: number) { + this.element = document.createElement('div'); + this.element.style.outline = '0'; + + this.element.id = `cellHandle${cellHandle}`; + this.element.classList.add('cell_container'); + + this.element.addEventListener('focusin', e => { + theia.postMessage({ type: 'cellFocusChanged', cellHandle: cellHandle }); + }); + + if (cellIndex && cellIndex < container.children.length) { + container.insertBefore(this.element, container.children[cellIndex]); + } else { + container.appendChild(this.element); + } + this.element = this.element; + + theia.postMessage({ type: 'cellHeightRequest', cellHandle: cellHandle }); + } + + public dispose(): void { + this.element.remove(); + } + + calcTotalOutputHeight(): number { + return this.outputElements.reduce((acc, output) => acc + output.element.clientHeight, 0) + 5; + } + + createOutputElement(index: number, output: webviewCommunication.Output, items: rendererApi.OutputItem[]): OutputContainer { + let outputContainer = this.outputElements.find(o => o.outputId === output.id); + if (!outputContainer) { + outputContainer = new OutputContainer(output, items, this); + this.element.appendChild(outputContainer.containerElement); + this.outputElements.splice(index, 0, outputContainer); + } + + return outputContainer; + } + + public clearOutputs(start: number, deleteCount: number): void { + for (const output of this.outputElements.splice(start, deleteCount)) { + output?.clear(); + output.containerElement.remove(); + } + } + + public show(outputId: string, top: number): void { + const outputContainer = this.outputElements.find(o => o.outputId === outputId); + if (!outputContainer) { + return; + } + } + + public hide(): void { + this.element.style.visibility = 'hidden'; + } + + public updateCellHeight(cellKind: number, height: number): void { + let additionalHeight = 54.5; + additionalHeight -= cells[0] === this ? 2.5 : 0; // first cell + additionalHeight -= this.outputElements.length ? 0 : 5.5; // no outputs + this.element.style.paddingTop = `${height + additionalHeight}px`; + } + + public outputVisibilityChanged(visible: boolean): void { + this.outputElements.forEach(output => { + output.element.style.display = visible ? 'initial' : 'none'; + }); + if (visible) { + this.element.getElementsByClassName('output-hidden')?.[0].remove(); + } else { + const outputHiddenElement = document.createElement('div'); + outputHiddenElement.classList.add('output-hidden'); + outputHiddenElement.style.height = '16px'; + this.element.appendChild(outputHiddenElement); + } + } + + // public updateScroll(request: webviewCommunication.IContentWidgetTopRequest): void { + // this.element.style.top = `${request.cellTop}px`; + + // const outputElement = this.outputElements.get(request.outputId); + // if (outputElement) { + // outputElement.updateScroll(request.outputOffset); + + // if (request.forceDisplay && outputElement.element) { + // // TODO @rebornix @mjbvz, there is a misalignment here. + // // We set output visibility on cell container, other than output container or output node itself. + // outputElement.element.style.visibility = ''; + // } + // } + + // if (request.forceDisplay) { + // this.element.style.visibility = ''; + // } + } + + const cells: OutputCell[] = []; + + class OutputContainer { readonly outputId: string; + readonly cellId: string; renderedItem?: rendererApi.OutputItem; allItems: rendererApi.OutputItem[]; renderer: Renderer; element: HTMLElement; - container: HTMLElement; + containerElement: HTMLElement; - constructor(output: webviewCommunication.Output, items: rendererApi.OutputItem[]) { - this.createHtmlElement(output.id); + constructor(output: webviewCommunication.Output, items: rendererApi.OutputItem[], private cell: OutputCell) { this.outputId = output.id; + this.createHtmlElement(); this.allItems = items; } @@ -169,27 +280,23 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { this.element.innerHTML = ''; } - private createHtmlElement(id: string): void { - // Recreates the output container structure used in VS Code - this.container = document.createElement('div'); - this.container.id = 'container'; - this.container.classList.add('widgetarea'); - const cellContainer = document.createElement('div'); - cellContainer.classList.add('cell_container'); - cellContainer.id = id; - this.container.appendChild(cellContainer); - const outputContainer = document.createElement('div'); - outputContainer.classList.add('output-container'); - cellContainer.appendChild(outputContainer); + preferredMimeTypeChange(mimeType: string): void { + this.containerElement.remove(); + this.createHtmlElement(); + this.cell.element.appendChild(this.containerElement); + renderers.render(this.cell, this, mimeType, undefined, new AbortController().signal); + } + + private createHtmlElement(): void { + this.containerElement = document.createElement('div'); + this.containerElement.classList.add('output-container'); this.element = document.createElement('div'); - this.element.id = id; + this.element.id = this.outputId; this.element.classList.add('output'); - outputContainer.appendChild(this.element); - document.body.appendChild(this.container); + this.containerElement.appendChild(this.element); } - } - const outputs: Output[] = []; + } class Renderer { @@ -330,7 +437,8 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { this.renderers.get(rendererId)?.disposeOutputItem(outputId); } - public async render(output: Output, preferredMimeType: string | undefined, preferredRendererId: string | undefined, signal: AbortSignal): Promise { + public async render(cell: OutputCell, output: OutputContainer, preferredMimeType: string | undefined, + preferredRendererId: string | undefined, signal: AbortSignal): Promise { const item = output.findItemToRender(preferredMimeType); const primaryRenderer = this.findRenderer(preferredRendererId, item); if (!primaryRenderer) { @@ -341,7 +449,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { // Try primary renderer first if (!(await this.doRender(item, output.element, primaryRenderer, signal)).continue) { output.renderer = primaryRenderer; - this.onRenderCompleted(); + this.onRenderCompleted(cell, output); return; } @@ -360,7 +468,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { if (renderer) { if (!(await this.doRender(additionalItem, output.element, renderer, signal)).continue) { output.renderer = renderer; - this.onRenderCompleted(); + this.onRenderCompleted(cell, output); return; // We rendered successfully } } @@ -371,28 +479,39 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { this.showRenderError(item, output.element, 'No fallback renderers found or all fallback renderers failed.'); } - private onRenderCompleted(): void { + private onRenderCompleted(cell: OutputCell, output: OutputContainer): void { // we need to check for all images are loaded. Otherwise we can't determine the correct height of the output const images = Array.from(document.images); if (images.length > 0) { - Promise.all(images.filter(img => !img.complete).map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))).then(() => { - theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight }); - new ResizeObserver(() => - theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight })) - .observe(document.body); - }); + Promise.all(images + .filter(img => !img.complete && !img.dataset.waiting) + .map(img => { + img.dataset.waiting = 'true'; // mark to avoid overriding onload a second time + return new Promise(resolve => { img.onload = img.onerror = resolve; }); + })).then(() => { + this.sendDidRenderMessage(cell, output); + new ResizeObserver(() => this.sendDidRenderMessage(cell, output)).observe(cell.element); + }); } else { - theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight }); - new ResizeObserver(() => - theia.postMessage({ type: 'didRenderOutput', contentHeight: document.body.clientHeight })) - .observe(document.body); + this.sendDidRenderMessage(cell, output); + new ResizeObserver(() => this.sendDidRenderMessage(cell, output)).observe(cell.element); } } + private sendDidRenderMessage(cell: OutputCell, output: OutputContainer): void { + theia.postMessage({ + type: 'didRenderOutput', + cellHandle: cell.cellHandle, + outputId: output.outputId, + outputHeight: cell.calcTotalOutputHeight(), + bodyHeight: document.body.clientHeight + }); + } + private async doRender(item: rendererApi.OutputItem, element: HTMLElement, renderer: Renderer, signal: AbortSignal): Promise<{ continue: boolean }> { try { - (await renderer.getOrLoad())?.renderOutputItem(item, element, signal); + await (await renderer.getOrLoad())?.renderOutputItem(item, element, signal); return { continue: false }; // We rendered successfully } catch (e) { if (signal.aborted) { @@ -482,40 +601,66 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { await Promise.all(ctx.staticPreloadsData.map(preload => kernelPreloads.load(preload))); - function clearOutput(output: Output): void { - output.clear(); - output.container.remove(); - } + async function outputsChanged(changedEvent: webviewCommunication.OutputChangedMessage): Promise { + for (const cellChange of changedEvent.changes) { + let cell = cells.find(c => c.cellHandle === cellChange.cellHandle); + if (!cell) { + cell = new OutputCell(cellChange.cellHandle); + cells.push(cell); + } - function outputsChanged(changedEvent: webviewCommunication.OutputChangedMessage): void { - for (const output of outputs.splice(changedEvent.deleteStart ?? 0, changedEvent.deleteCount ?? 0)) { - clearOutput(output); - } + cell.clearOutputs(cellChange.start, cellChange.deleteCount); - for (const outputData of changedEvent.newOutputs ?? []) { - const apiItems: rendererApi.OutputItem[] = outputData.items.map((item, index) => ({ - id: `${outputData.id}-${index}`, - mime: item.mime, - metadata: outputData.metadata, - data(): Uint8Array { - return item.data; - }, - text(): string { - return new TextDecoder().decode(this.data()); - }, - json(): unknown { - return JSON.parse(this.text()); - }, - blob(): Blob { - return new Blob([this.data()], { type: this.mime }); - }, + for (const outputData of cellChange.newOutputs ?? []) { + const apiItems: rendererApi.OutputItem[] = outputData.items.map((item, index) => ({ + id: `${outputData.id}-${index}`, + mime: item.mime, + metadata: outputData.metadata, + data(): Uint8Array { + return item.data; + }, + text(): string { + return new TextDecoder().decode(this.data()); + }, + json(): unknown { + return JSON.parse(this.text()); + }, + blob(): Blob { + return new Blob([this.data()], { type: this.mime }); + }, + + })); + const output = cell.createOutputElement(cellChange.start, outputData, apiItems); - })); + await renderers.render(cell, output, undefined, undefined, new AbortController().signal); - const output = new Output(outputData, apiItems); - outputs.push(output); + theia.postMessage({ + type: 'didRenderOutput', + cellHandle: cell.cellHandle, + outputId: outputData.id, + outputHeight: document.getElementById(output.outputId)?.clientHeight ?? 0, + bodyHeight: document.body.clientHeight + }); + + } + } + } - renderers.render(output, undefined, undefined, new AbortController().signal); + function cellsChanged(changes: (webviewCommunication.CellsMoved | webviewCommunication.CellsSpliced)[]): void { + for (const change of changes) { + if (change.type === 'cellMoved') { + const currentIndex = cells.findIndex(c => c.cellHandle === change.cellHandle); + const cell = cells[currentIndex]; + cells.splice(change.toIndex, 0, cells.splice(currentIndex, 1)[0]); + if (change.toIndex < cells.length - 1) { + container.insertBefore(cell.element, container.children[change.toIndex + (change.toIndex > currentIndex ? 1 : 0)]); + } else { + container.appendChild(cell.element); + } + } else if (change.type === 'cellsSpliced') { + const deltedCells = cells.splice(change.start, change.deleteCount, ...change.newCells.map((cellHandle, i) => new OutputCell(cellHandle, change.start + i))); + deltedCells.forEach(cell => cell.dispose()); + } } } @@ -565,6 +710,7 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { window.addEventListener('message', async rawEvent => { const event = rawEvent as ({ data: webviewCommunication.ToWebviewMessage }); + let cellHandle: number | undefined; switch (event.data.type) { case 'updateRenderers': renderers.updateRendererData(event.data.rendererData); @@ -572,27 +718,28 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { case 'outputChanged': outputsChanged(event.data); break; + case 'cellsChanged': + cellsChanged(event.data.changes); + break; case 'customRendererMessage': renderers.getRenderer(event.data.rendererId)?.receiveMessage(event.data.message); break; case 'changePreferredMimetype': + cellHandle = event.data.cellHandle; const mimeType = event.data.mimeType; - outputs.forEach(output => { - output.element.innerHTML = ''; - renderers.render(output, mimeType, undefined, new AbortController().signal); - }); + cells.find(c => c.cellHandle === cellHandle) + ?.outputElements.forEach(o => o.preferredMimeTypeChange(mimeType)); break; case 'customKernelMessage': onDidReceiveKernelMessage.fire(event.data.message); break; - case 'preload': { + case 'preload': const resources = event.data.resources; for (const uri of resources) { kernelPreloads.load(uri); } break; - } - case 'notebookStyles': { + case 'notebookStyles': const documentStyle = window.document.documentElement.style; for (let i = documentStyle.length - 1; i >= 0; i--) { @@ -609,7 +756,17 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { documentStyle.setProperty(`--${name}`, value); } break; - } + case 'cellHeightUpdate': + cellHandle = event.data.cellHandle; + const cell = cells.find(c => c.cellHandle === cellHandle); + if (cell) { + cell.updateCellHeight(event.data.cellKind, event.data.height); + } + break; + case 'outputVisibilityChanged': + cellHandle = event.data.cellHandle; + cells.find(c => c.cellHandle === cellHandle)?.outputVisibilityChanged(event.data.visible); + break; } }); window.addEventListener('wheel', handleWheel); @@ -632,5 +789,12 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise { window.addEventListener('focusout', (event: FocusEvent) => focusChange(event, false)); + new ResizeObserver(() => { + theia.postMessage({ + type: 'bodyHeightChange', + height: document.body.clientHeight + } as webviewCommunication.BodyHeightChange); + }).observe(document.body); + theia.postMessage({ type: 'initialized' }); } diff --git a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts index 442c7baf86ef1..040ef038832f5 100644 --- a/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts +++ b/packages/plugin-ext/src/main/browser/notebooks/renderers/webview-communication.ts @@ -36,15 +36,21 @@ export interface UpdateRenderersMessage { readonly rendererData: readonly RendererMetadata[]; } +export interface CellOutputChange { + readonly cellHandle: number; + readonly newOutputs?: Output[]; + readonly start: number; + readonly deleteCount: number; +} + export interface OutputChangedMessage { readonly type: 'outputChanged'; - readonly newOutputs?: Output[]; - readonly deleteStart?: number; - readonly deleteCount?: number; + changes: CellOutputChange[]; } export interface ChangePreferredMimetypeMessage { readonly type: 'changePreferredMimetype'; + readonly cellHandle: number; readonly outputId: string; readonly mimeType: string; } @@ -64,13 +70,53 @@ export interface notebookStylesMessage { styles: Record; } +export interface CellHeigthsMessage { + type: 'cellHeigths'; + cellHeigths: Record; +} + +export interface CellsMoved { + type: 'cellMoved'; + cellHandle: number; + toIndex: number; +} + +export interface CellsSpliced { + type: 'cellsSpliced'; + start: number; + deleteCount: number; + newCells: number[]; +} + +export interface CellsChangedMessage { + type: 'cellsChanged'; + changes: Array; +} + +export interface CellHeightUpdateMessage { + type: 'cellHeightUpdate'; + cellKind: number; + cellHandle: number; + height: number; +} + +export interface OutputVisibilityChangedMessage { + type: 'outputVisibilityChanged'; + cellHandle: number; + visible: boolean; +} + export type ToWebviewMessage = UpdateRenderersMessage | OutputChangedMessage | ChangePreferredMimetypeMessage | CustomRendererMessage | KernelMessage | PreloadMessage - | notebookStylesMessage; + | notebookStylesMessage + | CellHeigthsMessage + | CellHeightUpdateMessage + | CellsChangedMessage + | OutputVisibilityChangedMessage; export interface WebviewInitialized { readonly type: 'initialized'; @@ -78,7 +124,10 @@ export interface WebviewInitialized { export interface OnDidRenderOutput { readonly type: 'didRenderOutput'; - contentHeight: number; + cellHandle: number; + outputId: string; + outputHeight: number; + bodyHeight: number; } export interface WheelMessage { @@ -92,7 +141,30 @@ export interface InputFocusChange { readonly focused: boolean; } -export type FromWebviewMessage = WebviewInitialized | OnDidRenderOutput | WheelMessage | CustomRendererMessage | KernelMessage | InputFocusChange; +export interface CellOuputFocus { + readonly type: 'cellFocusChanged'; + readonly cellHandle: number; +} + +export interface CellHeightRequest { + readonly type: 'cellHeightRequest'; + readonly cellHandle: number; +} + +export interface BodyHeightChange { + readonly type: 'bodyHeightChange'; + readonly height: number; +} + +export type FromWebviewMessage = WebviewInitialized + | OnDidRenderOutput + | WheelMessage + | CustomRendererMessage + | KernelMessage + | InputFocusChange + | CellOuputFocus + | CellHeightRequest + | BodyHeightChange; export interface Output { id: string @@ -104,4 +176,3 @@ export interface OutputItem { readonly mime: string; readonly data: Uint8Array; } - diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index 5e592c30c397b..ef2530a437d21 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -88,8 +88,6 @@ import { LanguagePackService, languagePackServicePath } from '../../common/langu import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { CellOutputWebviewFactory } from '@theia/notebook/lib/browser'; import { CellOutputWebviewImpl, createCellOutputWebviewContainer } from './notebooks/renderers/cell-output-webview'; -import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model'; -import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model'; import { ArgumentProcessorContribution } from './command-registry-main'; import { WebviewSecondaryWindowSupport } from './webview/webview-secondary-window-support'; import { CustomEditorUndoRedoHandler } from './custom-editors/custom-editor-undo-redo-handler'; @@ -286,8 +284,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { return provider.createProxy(languagePackServicePath); }).inSingletonScope(); - bind(CellOutputWebviewFactory).toFactory(ctx => async (cell: NotebookCellModel, notebook: NotebookModel) => - createCellOutputWebviewContainer(ctx.container, cell, notebook).getAsync(CellOutputWebviewImpl) + bind(CellOutputWebviewFactory).toFactory(ctx => () => + createCellOutputWebviewContainer(ctx.container).get(CellOutputWebviewImpl) ); bindContributionProvider(bind, ArgumentProcessorContribution); From 91c254dc6404b01e8d82b33ddcf66b8fc14674db Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Wed, 9 Oct 2024 10:26:24 +0200 Subject: [PATCH 417/441] Notebook: Split cell command implementation (#14212) --- .../notebook-cell-actions-contribution.ts | 65 ++++++++++++++++--- .../browser/view-model/notebook-cell-model.ts | 14 ++++ .../src/browser/view/notebook-cell-editor.tsx | 5 ++ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 3b497ca0ff50d..87d656cb7cb44 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -28,12 +28,14 @@ import { import { ContextKeyService } from '@theia/core/lib/browser/context-key-service'; import { NotebookExecutionService } from '../service/notebook-execution-service'; import { NotebookCellOutputModel } from '../view-model/notebook-cell-output-model'; -import { CellEditType, CellKind } from '../../common'; +import { CellData, CellEditType, CellKind } from '../../common'; import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service'; import { NotebookCommands } from './notebook-actions-contribution'; import { changeCellType } from './cell-operations'; import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service'; import { NotebookService } from '../service/notebook-service'; +import { Selection } from '@theia/monaco-editor-core/esm/vs/editor/common/core/selection'; +import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; import { NOTEBOOK_EDITOR_ID_PREFIX } from '../notebook-editor-widget'; export namespace NotebookCellCommands { @@ -56,7 +58,7 @@ export namespace NotebookCellCommands { }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ export const SPLIT_CELL_COMMAND = Command.toDefaultLocalizedCommand({ - id: 'notebook.cell.split-cell', + id: 'notebook.cell.split', iconClass: codicon('split-vertical'), }); /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */ @@ -214,12 +216,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command order: '20' }); - // menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { - // commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id, - // icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass, - // label: nls.localizeByDefault('Split Cell'), - // order: '20' - // }); + menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { + commandId: NotebookCellCommands.SPLIT_CELL_COMMAND.id, + icon: NotebookCellCommands.SPLIT_CELL_COMMAND.iconClass, + label: nls.localizeByDefault('Split Cell'), + order: '20' + }); menus.registerMenuAction(NotebookCellActionContribution.ACTION_MENU, { commandId: NotebookCellCommands.DELETE_COMMAND.id, @@ -294,7 +296,47 @@ export class NotebookCellActionContribution implements MenuContribution, Command }] , true); })); - commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND); + commands.registerCommand(NotebookCellCommands.SPLIT_CELL_COMMAND, this.editableCellCommandHandler( + async (notebookModel, cell) => { + // selection (0,0,0,0) should also be used in !cell.editing mode, but `cell.editing` + // is not properly implemented for Code cells. + const cellSelection: Range = cell.selection ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } }; + const textModel = await cell.resolveTextModel(); + + // Create new cell with the text after the cursor + const splitOffset = textModel.offsetAt({ + line: cellSelection.start.line, + character: cellSelection.start.character + }); + const newCell: CellData = { + cellKind: cell.cellKind, + language: cell.language, + outputs: [], + source: textModel.getText().substring(splitOffset), + }; + + // add new cell below + const index = notebookModel.cells.indexOf(cell); + notebookModel.applyEdits([{ editType: CellEditType.Replace, index: index + 1, count: 0, cells: [newCell] }], true); + + // update current cell text (undo-able) + const selection = new Selection(cellSelection.start.line + 1, cellSelection.start.character + 1, cellSelection.end.line + 1, cellSelection.end.character + 1); + const endPosition = textModel.positionAt(textModel.getText().length); + const deleteOp = { + range: { + startLineNumber: selection.startLineNumber, + startColumn: selection.startColumn, + endLineNumber: endPosition.line + 1, + endColumn: endPosition.character + 1 + }, + // eslint-disable-next-line no-null/no-null + text: null + }; + // Create a new undo/redo stack entry + textModel.textEditorModel.pushStackElement(); + textModel.textEditorModel.pushEditOperations([selection], [deleteOp], () => [selection]); + }) + ); commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND, this.editableCellCommandHandler( (notebookModel, cell) => { @@ -511,6 +553,11 @@ export class NotebookCellActionContribution implements MenuContribution, Command keybinding: 'M', when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`, }, + { + command: NotebookCellCommands.SPLIT_CELL_COMMAND.id, + keybinding: KeyCode.createKeyCode({ first: Key.MINUS, modifiers: [KeyModifier.CtrlCmd, KeyModifier.Shift] }).toString(), + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`, + } ); } } diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index bf3b8837a4cc2..3c11012e48732 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -62,6 +62,10 @@ export interface NotebookCell { metadata: NotebookCellMetadata; internalMetadata: NotebookCellInternalMetadata; text: string; + /** + * The selection of the cell. Zero-based line/character coordinates. + */ + selection: Range | undefined; onDidChangeOutputs?: Event; onDidChangeOutputItems?: Event; onDidChangeLanguage: Event; @@ -254,6 +258,16 @@ export class NotebookCellModel implements NotebookCell, Disposable { } } + protected _selection: Range | undefined = undefined; + + get selection(): Range | undefined { + return this._selection; + } + + set selection(selection: Range | undefined) { + this._selection = selection; + } + protected _cellheight: number = 0; get cellHeight(): number { return this._cellheight; diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 1a42c7669ad38..22475efb0ecde 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -212,6 +212,11 @@ export class CellEditor extends React.Component { this.toDispose.push(this.editor.getControl().onDidChangeCursorSelection(e => { const selectedText = this.editor!.getControl().getModel()!.getValueInRange(e.selection); + // TODO handle secondary selections + this.props.cell.selection = { + start: { line: e.selection.startLineNumber - 1, character: e.selection.startColumn - 1 }, + end: { line: e.selection.endLineNumber - 1, character: e.selection.endColumn - 1 } + }; this.props.notebookModel.selectedText = selectedText; })); this.toDispose.push(this.editor.getControl().onDidChangeCursorPosition(e => { From 88fb3f6e8b4ae44e17d70e2ef6e2815cd8ef831c Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 9 Oct 2024 13:40:14 +0200 Subject: [PATCH 418/441] Pin Ubuntu CI versions to 22.04 (#14275) --- .github/workflows/ci-cd.yml | 4 ++-- .github/workflows/license-check.yml | 2 +- .github/workflows/performance-tests.yml | 2 +- .github/workflows/playwright.yml | 4 ++-- .github/workflows/production-smoke-test.yml | 4 ++-- .github/workflows/publish-gh-pages.yml | 2 +- .github/workflows/publish-next.yml | 2 +- .github/workflows/publish-release.yml | 2 +- .github/workflows/set-milestone-on-pr.yml | 2 +- .github/workflows/translation.yml | 2 +- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 8118098760580..4cf7f4872f462 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -13,7 +13,7 @@ jobs: lint: name: Lint - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-2019, ubuntu-latest, macos-14] + os: [windows-2019, ubuntu-22.04, macos-14] node: [18.x, 20.x] runs-on: ${{ matrix.os }} diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 763f083f96ced..b299055bd4415 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ubuntu-22.04] node: ['20.x'] java: ['11'] diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 52245cb0a2ca2..559c580fa6d02 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -7,7 +7,7 @@ jobs: build-and-test-performance: name: Performance Tests - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 30 steps: diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index d5c3cc49420f1..adae15c5f5ee9 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,9 +13,9 @@ on: jobs: build-and-test-playwright: - name: Playwright Tests (ubuntu-latest, Node.js 18.x) + name: Playwright Tests (ubuntu-22.04, Node.js 18.x) - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: diff --git a/.github/workflows/production-smoke-test.yml b/.github/workflows/production-smoke-test.yml index 3eed7632c6f6a..ab80575b9e67a 100644 --- a/.github/workflows/production-smoke-test.yml +++ b/.github/workflows/production-smoke-test.yml @@ -11,9 +11,9 @@ on: jobs: build-and-test-playwright: - name: Smoke Test for Browser Example Production Build on ubuntu-latest with Node.js 18.x + name: Smoke Test for Browser Example Production Build on ubuntu-22.04 with Node.js 18.x - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: diff --git a/.github/workflows/publish-gh-pages.yml b/.github/workflows/publish-gh-pages.yml index 78fecb218d7ac..6eae492ec3289 100644 --- a/.github/workflows/publish-gh-pages.yml +++ b/.github/workflows/publish-gh-pages.yml @@ -7,7 +7,7 @@ jobs: publish: name: Publish to NPM and GitHub pages needs: build - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 # The current approach is silly. We should be smarter and use `actions/upload-artifact` and `actions/download-artifact` instead of rebuilding # everything from scratch again. (git checkout, Node.js install, yarn, etc.) It was not possible to share artifacts on Travis CI without an diff --git a/.github/workflows/publish-next.yml b/.github/workflows/publish-next.yml index c744bfeab0647..6ae2e3e9c79ad 100644 --- a/.github/workflows/publish-next.yml +++ b/.github/workflows/publish-next.yml @@ -8,7 +8,7 @@ on: workflow_dispatch jobs: publish: name: Perform Publishing - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: - name: Checkout diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index b6d214b80ec72..7ed5e983851ce 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -18,7 +18,7 @@ on: jobs: publish: name: Perform Publishing - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: - name: Checkout diff --git a/.github/workflows/set-milestone-on-pr.yml b/.github/workflows/set-milestone-on-pr.yml index b44da2fce9250..2a040fdacaf95 100644 --- a/.github/workflows/set-milestone-on-pr.yml +++ b/.github/workflows/set-milestone-on-pr.yml @@ -22,7 +22,7 @@ on: jobs: set-milestone: if: github.event.pull_request.merged == true - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 diff --git a/.github/workflows/translation.yml b/.github/workflows/translation.yml index 531695725c166..e4a21d3858995 100644 --- a/.github/workflows/translation.yml +++ b/.github/workflows/translation.yml @@ -5,7 +5,7 @@ on: workflow_dispatch jobs: translation: name: Translation Update - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 timeout-minutes: 60 steps: From f1dc1d33d8654ffc02624b5b1869e34955ebe3ee Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 9 Oct 2024 14:58:11 +0200 Subject: [PATCH 419/441] Update rimraf to 5 (#14273) * Update rimraf to 5 fixed #14272 Signed-off-by: Jonas Helming --- examples/playwright/package.json | 2 +- package.json | 2 +- packages/filesystem/package.json | 3 +- .../src/node/download/file-download-cache.ts | 8 +-- .../git/src/browser/git-scm-provider.spec.ts | 9 +-- yarn.lock | 70 ++++++++++++++++--- 6 files changed, 68 insertions(+), 26 deletions(-) diff --git a/examples/playwright/package.json b/examples/playwright/package.json index 3d42b8481bf3a..3d2e3ba098051 100644 --- a/examples/playwright/package.json +++ b/examples/playwright/package.json @@ -39,7 +39,7 @@ "allure-commandline": "^2.23.1", "allure-playwright": "^2.5.0", "cross-env": "^7.0.3", - "rimraf": "^2.6.1", + "rimraf": "^5.0.0", "typescript": "~5.4.5" }, "publishConfig": { diff --git a/package.json b/package.json index abe2eea97009a..aebf999126bd5 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "puppeteer": "19.7.2", "puppeteer-core": "19.7.2", "puppeteer-to-istanbul": "1.4.0", - "rimraf": "^2.6.1", + "rimraf": "^5.0.0", "sinon": "^12.0.0", "temp": "^0.9.1", "tslint": "^5.12.0", diff --git a/packages/filesystem/package.json b/packages/filesystem/package.json index bcdd37c09bc2f..e870dcc25eff1 100644 --- a/packages/filesystem/package.json +++ b/packages/filesystem/package.json @@ -6,7 +6,6 @@ "@theia/core": "1.54.0", "@types/body-parser": "^1.17.0", "@types/multer": "^1.4.7", - "@types/rimraf": "^2.0.2", "@types/tar-fs": "^1.16.1", "async-mutex": "^0.3.1", "body-parser": "^1.18.3", @@ -14,7 +13,7 @@ "http-status-codes": "^1.3.0", "minimatch": "^5.1.0", "multer": "1.4.4-lts.1", - "rimraf": "^2.6.2", + "rimraf": "^5.0.0", "stat-mode": "^1.0.0", "tar-fs": "^1.16.2", "trash": "^7.2.0", diff --git a/packages/filesystem/src/node/download/file-download-cache.ts b/packages/filesystem/src/node/download/file-download-cache.ts index d1bb3285958b8..067e41458b59f 100644 --- a/packages/filesystem/src/node/download/file-download-cache.ts +++ b/packages/filesystem/src/node/download/file-download-cache.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable, inject } from '@theia/core/shared/inversify'; import { ILogger } from '@theia/core/lib/common/logger'; -import * as rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; export interface DownloadStorageItem { file: string; @@ -70,10 +70,8 @@ export class FileDownloadCache { } protected deleteRecursively(pathToDelete: string): void { - rimraf(pathToDelete, error => { - if (error) { - this.logger.warn(`An error occurred while deleting the temporary data from the disk. Cannot clean up: ${pathToDelete}.`, error); - } + rimraf(pathToDelete).catch(error => { + this.logger.warn(`An error occurred while deleting the temporary data from the disk. Cannot clean up: ${pathToDelete}.`, error); }); } diff --git a/packages/git/src/browser/git-scm-provider.spec.ts b/packages/git/src/browser/git-scm-provider.spec.ts index a3255b6b63ffb..a3b3cfb51a905 100644 --- a/packages/git/src/browser/git-scm-provider.spec.ts +++ b/packages/git/src/browser/git-scm-provider.spec.ts @@ -31,7 +31,7 @@ import { expect } from 'chai'; import * as fs from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; -import * as rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; import * as sinon from 'sinon'; import { Git, GitFileStatus, Repository } from '../common'; import { DugiteGit } from '../node/dugite-git'; @@ -109,12 +109,7 @@ describe('GitScmProvider', () => { }); afterEach(async () => { - await new Promise((resolve, reject) => rimraf(FileUri.fsPath(repository.localUri), error => { - if (error) { - reject(error); - } - resolve(); - })); + await rimraf(FileUri.fsPath(repository.localUri)); }); it('should unstage all the changes', async () => { diff --git a/yarn.lock b/yarn.lock index 55b27c141074e..049028db042e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2039,7 +2039,7 @@ dependencies: "@types/node" "*" -"@types/glob@*", "@types/glob@^8.1.0": +"@types/glob@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== @@ -2298,14 +2298,6 @@ dependencies: "@types/node" "*" -"@types/rimraf@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.5.tgz#368fb04d59630b727fc05a74d2ca557f64a8ef98" - integrity sha512-YyP+VfeaqAyFmXoTh3HChxOQMyjByRMsHU7kc5KOJkSlXudhMhQIALbYV7rHh/l8d2lX3VUQzprrcAgWdRuU8g== - dependencies: - "@types/glob" "*" - "@types/node" "*" - "@types/route-parser@^0.1.1": version "0.1.7" resolved "https://registry.yarnpkg.com/@types/route-parser/-/route-parser-0.1.7.tgz#76d324537c9f0aaf65c96588c6ab5f3b84ae1505" @@ -6359,6 +6351,18 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" +glob@^10.3.7: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -7339,6 +7343,15 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.7" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" @@ -7984,6 +7997,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -8320,6 +8338,13 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -8414,6 +8439,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -9432,6 +9462,11 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + pacote@^15.2.0: version "15.2.0" resolved "https://registry.yarnpkg.com/pacote/-/pacote-15.2.0.tgz#0f0dfcc3e60c7b39121b2ac612bf8596e95344d3" @@ -9603,6 +9638,14 @@ path-scurry@^1.10.1, path-scurry@^1.6.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -10478,7 +10521,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -10499,6 +10542,13 @@ rimraf@^4.4.1: dependencies: glob "^9.2.0" +rimraf@^5.0.0: + version "5.0.10" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" + integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== + dependencies: + glob "^10.3.7" + rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" From 6b7ceb79038adc0f58912266b663da7d96123a19 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Wed, 9 Oct 2024 15:38:50 +0200 Subject: [PATCH 420/441] Playwright API for Notebooks (#14098) --- .github/workflows/playwright.yml | 17 ++ examples/browser/package.json | 4 +- examples/playwright/.gitignore | 1 + .../configs/playwright.ci.config.ts | 8 +- examples/playwright/src/index.ts | 3 + .../notebook-files/.theia/settings.json | 3 + .../resources/notebook-files/sample.ipynb | 18 ++ .../src/tests/theia-notebook-editor.test.ts | 209 ++++++++++++++++ .../src/tests/theia-quick-command.test.ts | 6 + .../playwright/src/theia-monaco-editor.ts | 32 ++- .../playwright/src/theia-notebook-cell.ts | 232 ++++++++++++++++++ .../playwright/src/theia-notebook-editor.ts | 171 +++++++++++++ .../playwright/src/theia-notebook-toolbar.ts | 53 ++++ .../playwright/src/theia-preference-view.ts | 16 +- .../src/theia-quick-command-palette.ts | 10 + package.json | 7 +- .../browser/view/notebook-cell-toolbar.tsx | 2 +- .../browser/view/notebook-main-toolbar.tsx | 8 +- 18 files changed, 788 insertions(+), 12 deletions(-) create mode 100644 examples/playwright/src/tests/resources/notebook-files/.theia/settings.json create mode 100644 examples/playwright/src/tests/resources/notebook-files/sample.ipynb create mode 100644 examples/playwright/src/tests/theia-notebook-editor.test.ts create mode 100644 examples/playwright/src/theia-notebook-cell.ts create mode 100644 examples/playwright/src/theia-notebook-editor.ts create mode 100644 examples/playwright/src/theia-notebook-toolbar.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index adae15c5f5ee9..536b87c786875 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -33,11 +33,18 @@ jobs: with: python-version: "3.11" + - name: Install IPython Kernel + shell: bash + run: | + python3 -m pip install ipykernel==6.15.2 + python3 -m ipykernel install --user + - name: Build Browser shell: bash run: | yarn global add node-gyp yarn --skip-integrity-check --network-timeout 100000 + yarn download:plugins yarn browser build env: NODE_OPTIONS: --max_old_space_size=4096 @@ -51,3 +58,13 @@ jobs: - name: Test (playwright) shell: bash run: yarn --cwd examples/playwright ui-tests-ci + + - name: Archive test results + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 #v4 + if: ${{ !cancelled() }} + with: + name: playwright-test-results + path: | + examples/playwright/test-results/ + examples/playwright/playwright-report/ + retention-days: 2 diff --git a/examples/browser/package.json b/examples/browser/package.json index f121e2b09e290..a8a39d1189d7d 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -8,7 +8,8 @@ "config": { "applicationName": "Theia Browser Example", "preferences": { - "files.enableTrash": false + "files.enableTrash": false, + "security.workspace.trust.enabled": false }, "reloadOnReconnect": true } @@ -19,6 +20,7 @@ } } }, + "theiaPluginsDir": "../../plugins", "dependencies": { "@theia/ai-chat": "1.54.0", "@theia/ai-chat-ui": "1.54.0", diff --git a/examples/playwright/.gitignore b/examples/playwright/.gitignore index 86ac98af48d1c..4fc8f9cdad72e 100644 --- a/examples/playwright/.gitignore +++ b/examples/playwright/.gitignore @@ -1,3 +1,4 @@ allure-results test-results +playwright-report .tmp.cfg diff --git a/examples/playwright/configs/playwright.ci.config.ts b/examples/playwright/configs/playwright.ci.config.ts index 4fa758b32a3c9..2ba404cb693ad 100644 --- a/examples/playwright/configs/playwright.ci.config.ts +++ b/examples/playwright/configs/playwright.ci.config.ts @@ -21,7 +21,13 @@ const ciConfig: PlaywrightTestConfig = { ...baseConfig, workers: 1, retries: 2, - reporter: [['list'], ['allure-playwright'], ['github']] + reporter: [ + ['list'], + ['github'], + ['html', { open: 'never' }], + ], + timeout: 30 * 1000, // Overwrite baseConfig timeout + preserveOutput: 'always' }; export default ciConfig; diff --git a/examples/playwright/src/index.ts b/examples/playwright/src/index.ts index 5dd9ebe6a9a75..68ba7d9d673f2 100644 --- a/examples/playwright/src/index.ts +++ b/examples/playwright/src/index.ts @@ -26,6 +26,9 @@ export * from './theia-menu-item'; export * from './theia-menu'; export * from './theia-notification-indicator'; export * from './theia-notification-overlay'; +export * from './theia-notebook-cell'; +export * from './theia-notebook-editor'; +export * from './theia-notebook-toolbar'; export * from './theia-output-channel'; export * from './theia-output-view'; export * from './theia-page-object'; diff --git a/examples/playwright/src/tests/resources/notebook-files/.theia/settings.json b/examples/playwright/src/tests/resources/notebook-files/.theia/settings.json new file mode 100644 index 0000000000000..8bb69fd4f5289 --- /dev/null +++ b/examples/playwright/src/tests/resources/notebook-files/.theia/settings.json @@ -0,0 +1,3 @@ +{ + "files.autoSave": "off" +} diff --git a/examples/playwright/src/tests/resources/notebook-files/sample.ipynb b/examples/playwright/src/tests/resources/notebook-files/sample.ipynb new file mode 100644 index 0000000000000..709d82cff5441 --- /dev/null +++ b/examples/playwright/src/tests/resources/notebook-files/sample.ipynb @@ -0,0 +1,18 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/playwright/src/tests/theia-notebook-editor.test.ts b/examples/playwright/src/tests/theia-notebook-editor.test.ts new file mode 100644 index 0000000000000..29b77f594479d --- /dev/null +++ b/examples/playwright/src/tests/theia-notebook-editor.test.ts @@ -0,0 +1,209 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PlaywrightWorkerArgs, expect, test } from '@playwright/test'; +import { TheiaApp } from '../theia-app'; +import { TheiaAppLoader, TheiaPlaywrightTestConfig } from '../theia-app-loader'; +import { TheiaNotebookCell } from '../theia-notebook-cell'; +import { TheiaNotebookEditor } from '../theia-notebook-editor'; +import { TheiaWorkspace } from '../theia-workspace'; +import path = require('path'); + +// See .github/workflows/playwright.yml for preferred python version +const preferredKernel = process.env.CI ? 'Python 3.11' : 'Python 3'; + +test.describe('Theia Notebook Editor interaction', () => { + + let app: TheiaApp; + let editor: TheiaNotebookEditor; + + test.beforeAll(async ({ playwright, browser }) => { + app = await loadApp({ playwright, browser }); + }); + + test.beforeEach(async ({ playwright, browser }) => { + editor = await app.openEditor('sample.ipynb', TheiaNotebookEditor); + }); + + test.afterAll(async () => { + await app.page.close(); + }); + + test.afterEach(async () => { + if (editor) { + await editor.closeWithoutSave(); + } + }); + + test('kernels are installed', async () => { + const kernels = await editor.availableKernels(); + const msg = `Available kernels:\n ${kernels.join('\n')}`; + console.log(msg); // Print available kernels, useful when running in CI. + expect(kernels.length, msg).toBeGreaterThan(0); + + const py3kernel = kernels.filter(kernel => kernel.match(new RegExp(`^${preferredKernel}`))); + expect(py3kernel.length, msg).toBeGreaterThan(0); + }); + + test('should select a kernel', async () => { + await editor.selectKernel(preferredKernel); + const selectedKernel = await editor.selectedKernel(); + expect(selectedKernel).toMatch(new RegExp(`^${preferredKernel}`)); + }); + + test('should add a new code cell', async () => { + await editor.addCodeCell(); + const cells = await editor.cells(); + expect(cells.length).toBe(2); + expect(await cells[1].mode()).toBe('python'); + }); + + test('should add a new markdown cell', async () => { + await editor.addMarkdownCell(); + await (await editor.cells())[1].addEditorText('print("markdown")'); + + const cells = await editor.cells(); + expect(cells.length).toBe(2); + expect(await cells[1].mode()).toBe('markdown'); + expect(await cells[1].editorText()).toBe('print("markdown")'); + }); + + test('should execute all cells', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("Hallo Notebook!")'); + + await editor.addCodeCell(); + const secondCell = (await editor.cells())[1]; + await secondCell.addEditorText('print("Bye Notebook!")'); + + await editor.executeAllCells(); + + expect(await cell.outputText()).toBe('Hallo Notebook!'); + expect(await secondCell.outputText()).toBe('Bye Notebook!'); + }); + + test('should split cell', async () => { + const cell = await firstCell(editor); + /* + Add cell text: + print("Line-1") + print("Line-2") + */ + await cell.addEditorText('print("Line-1")\nprint("Line-2")'); + + /* + Set cursor: + print("Line-1") + <|>print("Line-2") + */ + const line = await cell.editor.lineByLineNumber(1); + await line?.waitForElementState('visible'); + await line?.click(); + await line?.press('ArrowRight'); + + // split cell + await cell.splitCell(); + + // expect two cells with text "print("Line-1")" and "print("Line-2")" + expect(await editor.cells()).toHaveLength(2); + expect(await (await editor.cells())[0].editorText()).toBe('print("Line-1")'); + expect(await (await editor.cells())[1].editorText()).toBe('print("Line-2")'); + }); +}); + +test.describe('Theia Notebook Cell interaction', () => { + + let app: TheiaApp; + let editor: TheiaNotebookEditor; + + test.beforeAll(async ({ playwright, browser }) => { + app = await loadApp({ playwright, browser }); + }); + + test.afterAll(async () => { + await app.page.close(); + }); + + test.beforeEach(async () => { + editor = await app.openEditor('sample.ipynb', TheiaNotebookEditor); + const selectedKernel = await editor.selectedKernel(); + if (selectedKernel?.match(new RegExp(`^${preferredKernel}`)) === null) { + await editor.selectKernel(preferredKernel); + } + }); + + test.afterEach(async () => { + if (editor) { + await editor.closeWithoutSave(); + } + }); + + test('should write text in a code cell', async () => { + const cell = await firstCell(editor); + // assume the first cell is a code cell + expect(await cell.isCodeCell()).toBe(true); + + await cell.addEditorText('print("Hallo")'); + const cellText = await cell.editorText(); + expect(cellText).toBe('print("Hallo")'); + }); + + test('should write multi-line text in a code cell', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("Hallo")\nprint("Notebook")'); + + const cellText = await cell.editorText(); + expect(cellText).toBe('print("Hallo")\nprint("Notebook")'); + }); + + test('Execute code cell and read output', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("Hallo Notebook!")'); + await cell.execute(); + + const cellOutput = await cell.outputText(); + expect(cellOutput).toBe('Hallo Notebook!'); + }); + + test('Check execution count matches', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("Hallo Notebook!")'); + await cell.execute(); + await cell.execute(); + await cell.execute(); + + expect(await cell.executionCount()).toBe('3'); + }); + +}); + +async function firstCell(editor: TheiaNotebookEditor): Promise { + return (await editor.cells())[0]; +} + +async function loadApp(args: TheiaPlaywrightTestConfig & PlaywrightWorkerArgs): Promise { + const workingDir = path.resolve(); + // correct WS path. When running from IDE the path is playwright/configs with CLI it's playwright/ + const prefix = workingDir.endsWith('playwright/configs') ? '../' : ''; + const ws = new TheiaWorkspace([prefix + 'src/tests/resources/notebook-files']); + const app = await TheiaAppLoader.load(args, ws); + // auto-save are disabled using settings.json file + // see examples/playwright/src/tests/resources/notebook-files/.theia/settings.json + + // NOTE: Workspace trust is disabled in examples/browser/package.json using default preferences. + // If workspace trust check is on, python extension will not be able to explore Python installations. + return app; +} diff --git a/examples/playwright/src/tests/theia-quick-command.test.ts b/examples/playwright/src/tests/theia-quick-command.test.ts index 6a11a248cec6f..f24ee87522df0 100644 --- a/examples/playwright/src/tests/theia-quick-command.test.ts +++ b/examples/playwright/src/tests/theia-quick-command.test.ts @@ -77,4 +77,10 @@ test.describe('Theia Quick Command', () => { expect(await notification.isEntryVisible('Positive Integer: 6')).toBe(true); }); + test('retrieve and check visible items', async () => { + await quickCommand.type('close all tabs', false); + const listItems = await Promise.all((await quickCommand.visibleItems()).map(async item => item.textContent())); + expect(listItems).toContain('View: Close All Tabs in Main Area'); + }); + }); diff --git a/examples/playwright/src/theia-monaco-editor.ts b/examples/playwright/src/theia-monaco-editor.ts index ac0fd5290453b..7e290df0509e5 100644 --- a/examples/playwright/src/theia-monaco-editor.ts +++ b/examples/playwright/src/theia-monaco-editor.ts @@ -27,7 +27,7 @@ export class TheiaMonacoEditor extends TheiaPageObject { await this.page.waitForSelector(this.selector, { state: 'visible' }); } - protected viewElement(): Promise | null> { + protected async viewElement(): Promise | null> { return this.page.$(this.selector); } @@ -74,6 +74,36 @@ export class TheiaMonacoEditor extends TheiaPageObject { return viewElement?.waitForSelector(`.view-lines .view-line:has-text("${text}")`); } + /** + * @returns The text content of the editor. + */ + async editorText(): Promise { + const lines: string[] = []; + const linesCount = await this.numberOfLines(); + if (linesCount === undefined) { + return undefined; + } + for (let line = 1; line <= linesCount; line++) { + const lineText = await this.textContentOfLineByLineNumber(line); + if (lineText === undefined) { + break; + } + lines.push(lineText); + } + return lines.join('\n'); + } + + /** + * Adds text to the editor. + * @param text The text to add to the editor. + * @param lineNumber The line number where to add the text. Default is 1. + */ + async addEditorText(text: string, lineNumber: number = 1): Promise { + const line = await this.lineByLineNumber(lineNumber); + await line?.click(); + await this.page.keyboard.type(text); + } + protected replaceEditorSymbolsWithSpace(content: string): string | Promise { // [ ]   => \u00a0 -- NO-BREAK SPACE // [·] · => \u00b7 -- MIDDLE DOT diff --git a/examples/playwright/src/theia-notebook-cell.ts b/examples/playwright/src/theia-notebook-cell.ts new file mode 100644 index 0000000000000..22859950f63fd --- /dev/null +++ b/examples/playwright/src/theia-notebook-cell.ts @@ -0,0 +1,232 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { ElementHandle, FrameLocator, Locator } from '@playwright/test'; +import { TheiaApp } from './theia-app'; +import { TheiaMonacoEditor } from './theia-monaco-editor'; +import { TheiaPageObject } from './theia-page-object'; + +export type CellStatus = 'success' | 'error' | 'waiting'; + +/** + * Page object for a Theia notebook cell. + */ +export class TheiaNotebookCell extends TheiaPageObject { + + protected monacoEditor: TheiaEmbeddedMonacoEditor; + + constructor(protected readonly locator: Locator, protected readonly notebookEditorLocator: Locator, app: TheiaApp) { + super(app); + const editorLocator = locator.locator('div.theia-notebook-cell-editor'); + this.monacoEditor = new TheiaEmbeddedMonacoEditor(editorLocator, app); + } + + /** + * @returns The monaco editor page object of the cell. + */ + get editor(): TheiaEmbeddedMonacoEditor { + return this.monacoEditor; + } + + /** + * @returns Locator for the sidebar (left) of the cell. + */ + sidebar(): Locator { + return this.locator.locator('div.theia-notebook-cell-sidebar'); + } + + /** + * @returns Locator for the toolbar (top) of the cell. + */ + toolbar(): Locator { + return this.locator.locator('div.theia-notebook-cell-toolbar'); + } + /** + * @returns Locator for the statusbar (bottom) of the cell. + */ + statusbar(): Locator { + return this.locator.locator('div.notebook-cell-status'); + } + + /** + * @returns Locator for the status icon inside the statusbar of the cell. + */ + statusIcon(): Locator { + return this.statusbar().locator('span.notebook-cell-status-item'); + } + + /** + * @returns `true` id the cell is a code cell, `false` otherwise. + */ + async isCodeCell(): Promise { + const classAttribute = await this.mode(); + return classAttribute !== 'markdown'; + } + + /** + * @returns The mode of the cell, e.g. 'python', 'markdown', etc. + */ + async mode(): Promise { + await this.locator.waitFor({ state: 'visible' }); + const editorElement = await this.editor.locator.elementHandle(); + if (editorElement === null) { + throw new Error('Could not find editor element for the notebook cell.'); + } + const classAttribute = await editorElement.getAttribute('data-mode-id'); + if (classAttribute === null) { + throw new Error('Could not find mode attribute for the notebook cell.'); + } + return classAttribute; + } + + /** + * @returns The text content of the cell editor. + */ + async editorText(): Promise { + return this.editor.editorText(); + } + + /** + * Adds text to the editor of the cell. + * @param text The text to add to the editor. + * @param lineNumber The line number where to add the text. Default is 1. + */ + async addEditorText(text: string, lineNumber: number = 1): Promise { + await this.editor.addEditorText(text, lineNumber); + } + + /** + * @param wait If `true` waits for the cell to finish execution, otherwise returns immediately. + */ + async execute(wait = true): Promise { + const execButton = this.sidebar().locator('[id="notebook.cell.execute-cell"]'); + await execButton.waitFor({ state: 'visible' }); + await execButton.click(); + if (wait) { + // wait for the cell to finish execution + await this.waitForCellStatus('success', 'error'); + } + } + + /** + * Splits the cell into two cells by dividing the cell text on current cursor position. + */ + async splitCell(): Promise { + const execButton = this.toolbar().locator('[id="notebook.cell.split"]'); + await execButton.waitFor({ state: 'visible' }); + await execButton.click(); + } + + /** + * Waits for the cell to reach a specific status. + * @param status The status to wait for. Possible values are 'success', 'error', 'waiting'. + */ + async waitForCellStatus(...status: CellStatus[]): Promise { + await this.statusIcon().waitFor({ state: 'visible' }); + await this.statusIcon().evaluate( + (element, expect) => { + if (expect.length === 0) { + return true; + } + const classes = element.getAttribute('class'); + if (classes !== null) { + const cellStatus = classes.includes('codicon-check') ? 'success' + : classes.includes('codicon-error') ? 'error' + : 'waiting'; + return expect.includes(cellStatus); + } + return false; + }, status); + } + + /** + * @returns The status of the cell. Possible values are 'success', 'error', 'waiting'. + */ + async status(): Promise { + const statusLocator = this.statusIcon(); + const status = this.toCellStatus(await (await statusLocator.elementHandle())?.getAttribute('class') ?? ''); + return status; + } + + protected toCellStatus(classes: string): CellStatus { + return classes.includes('codicon-check') ? 'success' + : classes.includes('codicon-error') ? 'error' + : 'waiting'; + } + + /** + * @returns The execution count of the cell. + */ + async executionCount(): Promise { + const countNode = this.sidebar().locator('span.theia-notebook-code-cell-execution-order'); + await countNode.waitFor({ state: 'visible' }); + await this.waitForCellStatus('success', 'error'); + const text = await countNode.textContent(); + return text?.substring(1, text.length - 1); + } + + /** + * @returns The output text of the cell. + */ + async outputText(): Promise { + const outputContainer = await this.outputContainer(); + await outputContainer.waitFor({ state: 'visible' }); + // By default just collect all spans text. + const spansLocator: Locator = outputContainer.locator('span:not(:has(*))'); // ignore nested spans + const spanTexts = await spansLocator.evaluateAll(spans => spans.map(span => span.textContent?.trim()) + .filter(text => text !== undefined && text.length > 0)); + return spanTexts.join(''); + } + + protected async outputContainer(): Promise { + const outFrame = await this.outputFrame(); + // each cell has it's own output div with a unique id = cellHandle + const cellOutput = outFrame.locator(`div#cellHandle${await this.cellHandle()}`); + return cellOutput.locator('div.output-container'); + } + + protected async cellHandle(): Promise { + const handle = await this.locator.getAttribute('data-cell-handle'); + if (handle === null) { + throw new Error('Could not find cell handle attribute `data-cell-handle` for the notebook cell.'); + } + return handle; + } + + protected async outputFrame(): Promise { + const containerDiv = this.notebookEditorLocator.locator('div.theia-notebook-cell-output-webview'); + const webViewFrame = containerDiv.frameLocator('iframe.webview'); + await webViewFrame.locator('iframe').waitFor({ state: 'attached' }); + return webViewFrame.frameLocator('iframe'); + } + +} + +export class TheiaEmbeddedMonacoEditor extends TheiaMonacoEditor { + + constructor(public readonly locator: Locator, app: TheiaApp) { + super('', app); + } + + override async waitForVisible(): Promise { + // Use locator instead of page to find the editor element. + await this.locator.waitFor({ state: 'visible' }); + } + + protected override viewElement(): Promise | null> { + // Use locator instead of page to find the editor element. + return this.locator.elementHandle(); + } +} diff --git a/examples/playwright/src/theia-notebook-editor.ts b/examples/playwright/src/theia-notebook-editor.ts new file mode 100644 index 0000000000000..d07964edfe718 --- /dev/null +++ b/examples/playwright/src/theia-notebook-editor.ts @@ -0,0 +1,171 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { Locator } from '@playwright/test'; +import { join } from 'path'; +import { TheiaApp } from './theia-app'; +import { TheiaEditor } from './theia-editor'; +import { TheiaNotebookCell } from './theia-notebook-cell'; +import { TheiaNotebookToolbar } from './theia-notebook-toolbar'; +import { TheiaQuickCommandPalette } from './theia-quick-command-palette'; +import { TheiaToolbarItem } from './theia-toolbar-item'; +import { OSUtil, normalizeId, urlEncodePath } from './util'; + +export namespace NotebookCommands { + export const SELECT_KERNEL_COMMAND = 'notebook.selectKernel'; + export const ADD_NEW_CELL_COMMAND = 'notebook.add-new-code-cell'; + export const ADD_NEW_MARKDOWN_CELL_COMMAND = 'notebook.add-new-markdown-cell'; + export const EXECUTE_NOTEBOOK_COMMAND = 'notebook.execute'; + export const CLEAR_ALL_OUTPUTS_COMMAND = 'notebook.clear-all-outputs'; + export const EXPORT_COMMAND = 'jupyter.notebookeditor.export'; +} + +export class TheiaNotebookEditor extends TheiaEditor { + + constructor(filePath: string, app: TheiaApp) { + // shell-tab-notebook::file:// + // notebook:file:// + super({ + tabSelector: normalizeId(`#shell-tab-notebook:file://${urlEncodePath(join(app.workspace.escapedPath, OSUtil.fileSeparator, filePath))}`), + viewSelector: normalizeId(`#notebook:file://${urlEncodePath(join(app.workspace.escapedPath, OSUtil.fileSeparator, filePath))}`) + }, app); + } + + protected viewLocator(): Locator { + return this.page.locator(this.data.viewSelector); + } + + tabLocator(): Locator { + return this.page.locator(this.data.viewSelector); + } + + override async waitForVisible(): Promise { + await super.waitForVisible(); + // wait for toolbar being rendered as it takes some time to load the kernel data. + await this.notebookToolbar().waitForVisible(); + } + + /** + * @returns The main toolbar of the notebook editor. + */ + notebookToolbar(): TheiaNotebookToolbar { + return new TheiaNotebookToolbar(this.viewLocator(), this.app); + } + + /** + * @returns The name of the selected kernel. + */ + async selectedKernel(): Promise { + const kernelItem = await this.toolbarItem(NotebookCommands.SELECT_KERNEL_COMMAND); + if (!kernelItem) { + throw new Error('Select kernel toolbar item not found.'); + } + return this.notebookToolbar().locator.locator('#kernel-text').innerText(); + } + + /** + * Allows to select a kernel using toolbar item. + * @param kernelName The name of the kernel to select. + */ + async selectKernel(kernelName: string): Promise { + await this.triggerToolbarItem(NotebookCommands.SELECT_KERNEL_COMMAND); + const qInput = new TheiaQuickCommandPalette(this.app); + const widget = await this.page.waitForSelector(qInput.selector, { timeout: 5000 }); + if (widget && !await qInput.isOpen()) { + throw new Error('Failed to trigger kernel selection'); + } + await qInput.type(kernelName, true); + await qInput.hide(); + } + + async availableKernels(): Promise { + await this.triggerToolbarItem(NotebookCommands.SELECT_KERNEL_COMMAND); + const qInput = new TheiaQuickCommandPalette(this.app); + const widget = await this.page.waitForSelector(qInput.selector, { timeout: 5000 }); + if (widget && !await qInput.isOpen()) { + throw new Error('Failed to trigger kernel selection'); + } + await qInput.type('Python', false); + try { + const listItems = await Promise.all((await qInput.visibleItems()).map(async item => item.textContent())); + await this.page.keyboard.press('Enter'); + await qInput.hide(); + return listItems.filter(item => item !== null) as string[]; + } finally { + await qInput.hide(); + } + } + + /** + * Adds a new code cell to the notebook. + */ + async addCodeCell(): Promise { + const currentCellsCount = (await this.cells()).length; + await this.triggerToolbarItem(NotebookCommands.ADD_NEW_CELL_COMMAND); + await this.waitForCellCountChanged(currentCellsCount); + } + + /** + * Adds a new markdown cell to the notebook. + */ + async addMarkdownCell(): Promise { + const currentCellsCount = (await this.cells()).length; + await this.triggerToolbarItem(NotebookCommands.ADD_NEW_MARKDOWN_CELL_COMMAND); + await this.waitForCellCountChanged(currentCellsCount); + } + + protected async waitForCellCountChanged(prevCount: number): Promise { + await this.viewLocator().locator('li.theia-notebook-cell').evaluateAll( + (elements, currentCount) => elements.length !== currentCount, prevCount + ); + } + + async executeAllCells(): Promise { + await this.triggerToolbarItem(NotebookCommands.EXECUTE_NOTEBOOK_COMMAND); + } + + async clearAllOutputs(): Promise { + await this.triggerToolbarItem(NotebookCommands.CLEAR_ALL_OUTPUTS_COMMAND); + } + + async exportAs(): Promise { + await this.triggerToolbarItem(NotebookCommands.EXPORT_COMMAND); + } + + async cells(): Promise { + const cellsLocator = this.viewLocator().locator('li.theia-notebook-cell'); + const cells: Array = []; + for (const cellLocator of await cellsLocator.all()) { + await cellLocator.waitFor({ state: 'visible' }); + cells.push(new TheiaNotebookCell(cellLocator, this.viewLocator(), this.app)); + } + return cells; + } + + protected async triggerToolbarItem(id: string): Promise { + const item = await this.toolbarItem(id); + if (!item) { + throw new Error(`Toolbar item with id ${id} not found`); + } + await item.trigger(); + } + + protected async toolbarItem(id: string): Promise { + const toolBar = this.notebookToolbar(); + await toolBar.waitForVisible(); + return toolBar.toolBarItem(id); + } +} diff --git a/examples/playwright/src/theia-notebook-toolbar.ts b/examples/playwright/src/theia-notebook-toolbar.ts new file mode 100644 index 0000000000000..3349e2267b6e4 --- /dev/null +++ b/examples/playwright/src/theia-notebook-toolbar.ts @@ -0,0 +1,53 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ElementHandle, Locator } from '@playwright/test'; +import { TheiaApp } from './theia-app'; +import { TheiaToolbar } from './theia-toolbar'; + +export class TheiaNotebookToolbar extends TheiaToolbar { + public readonly locator: Locator; + + constructor(parentLocator: Locator, app: TheiaApp) { + super(app); + this.selector = 'div#notebook-main-toolbar'; + this.locator = parentLocator.locator(this.selector); + } + + protected override toolBarItemSelector(toolbarItemId = ''): string { + return `div.theia-notebook-main-toolbar-item${toolbarItemId ? `[id="${toolbarItemId}"]` : ''}`; + } + + protected override async toolbarElementHandle(): Promise | null> { + // Use locator instead of page to find the toolbar element. + return this.locator.elementHandle(); + } + + override async waitForVisible(): Promise { + // Use locator instead of page to find the toolbar element. + await this.locator.waitFor({ state: 'visible' }); + } + + override async waitUntilHidden(): Promise { + // Use locator instead of page to find the toolbar element. + await this.locator.waitFor({ state: 'hidden' }); + } + + override async waitUntilShown(): Promise { + // Use locator instead of page to find the toolbar element. + await this.locator.waitFor({ state: 'visible' }); + } +} diff --git a/examples/playwright/src/theia-preference-view.ts b/examples/playwright/src/theia-preference-view.ts index a1f701048b17b..1af41fdb55a21 100644 --- a/examples/playwright/src/theia-preference-view.ts +++ b/examples/playwright/src/theia-preference-view.ts @@ -86,8 +86,20 @@ export class TheiaPreferenceView extends TheiaView { super(TheiaSettingsViewData, app); } - override async open(preferenceScope = TheiaPreferenceScope.Workspace): Promise { - await this.app.quickCommandPalette.trigger('Preferences: Open Settings (UI)'); + /** + * @param preferenceScope The preference scope (Workspace or User) to open the view for. Default is Workspace. + * @param useMenu If true, the view will be opened via the main menu. If false, + * the view will be opened via the quick command palette. Default is using the main menu. + * @returns The TheiaPreferenceView page object instance. + */ + override async open(preferenceScope = TheiaPreferenceScope.Workspace, useMenu: boolean = true): Promise { + if (useMenu) { + const mainMenu = await this.app.menuBar.openMenu('File'); + await (await mainMenu.menuItemByNamePath('Preferences', 'Settings'))?.click(); + } else { + await this.app.quickCommandPalette.type('Preferences:'); + await this.app.quickCommandPalette.trigger('Preferences: Open Settings (UI)'); + } await this.waitForVisible(); await this.openPreferenceScope(preferenceScope); return this; diff --git a/examples/playwright/src/theia-quick-command-palette.ts b/examples/playwright/src/theia-quick-command-palette.ts index ca65b86033050..bf9ce8c00e4a1 100644 --- a/examples/playwright/src/theia-quick-command-palette.ts +++ b/examples/playwright/src/theia-quick-command-palette.ts @@ -78,4 +78,14 @@ export class TheiaQuickCommandPalette extends TheiaPageObject { } return command.$('.monaco-list-row.focused .monaco-highlighted-label'); } + + async visibleItems(): Promise[]> { + // FIXME rewrite with locators + const command = await this.page.waitForSelector(this.selector); + if (!command) { + throw new Error('No selected command found!'); + } + return command.$$('.monaco-highlighted-label'); + } + } diff --git a/package.json b/package.json index aebf999126bd5..270c3725f3ce5 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,9 @@ "theiaPlugins": { "eclipse-theia.builtin-extension-pack": "https://open-vsx.org/api/eclipse-theia/builtin-extension-pack/1.88.1/file/eclipse-theia.builtin-extension-pack-1.88.1.vsix", "EditorConfig.EditorConfig": "https://open-vsx.org/api/EditorConfig/EditorConfig/0.16.6/file/EditorConfig.EditorConfig-0.16.6.vsix", - "dbaeumer.vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.4.2/file/dbaeumer.vscode-eslint-2.4.2.vsix" + "dbaeumer.vscode-eslint": "https://open-vsx.org/api/dbaeumer/vscode-eslint/2.4.2/file/dbaeumer.vscode-eslint-2.4.2.vsix", + "ms-toolsai.jupyter": "https://open-vsx.org/api/ms-toolsai/jupyter/2024.6.0/file/ms-toolsai.jupyter-2024.6.0.vsix", + "ms-python.python": "https://open-vsx.org/api/ms-python/python/2024.12.3/file/ms-python.python-2024.12.3.vsix" }, "theiaPluginsExcludeIds": [ "ms-vscode.js-debug-companion", @@ -112,6 +114,7 @@ "vscode.github", "vscode.github-authentication", "vscode.microsoft-authentication", - "ms-vscode.references-view" + "ms-vscode.references-view", + "ms-python.vscode-pylance" ] } diff --git a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx index 12520d4199954..426878d51005b 100644 --- a/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-toolbar.tsx @@ -48,7 +48,7 @@ abstract class NotebookCellActionBar extends React.Component; + return
        ; } } diff --git a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx index f3d20a1eb63c7..59b04784d8b4e 100644 --- a/packages/notebook/src/browser/view/notebook-main-toolbar.tsx +++ b/packages/notebook/src/browser/view/notebook-main-toolbar.tsx @@ -152,17 +152,17 @@ export class NotebookMainToolbar extends React.Component + return
        {menuItems.slice(0, menuItems.length - this.calculateNumberOfHiddenItems(menuItems)).map(item => this.renderMenuItem(item))} { this.state.numberOfHiddenItems > 0 && this.renderContextMenu(e.nativeEvent, menuItems)} /> }
        this.gapElementChanged(element)} style={{ flexGrow: 1 }}>
        -
        this.props.commandRegistry.executeCommand(NotebookCommands.SELECT_KERNEL_COMMAND.id, this.props.notebookModel)}> - + {this.state.selectedKernelLabel ?? nls.localizeByDefault('Select Kernel')}
        @@ -195,7 +195,7 @@ export class NotebookMainToolbar extends React.Component { if (item.command && (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode))) { this.props.commandRegistry.executeCommand(item.command, this.props.notebookModel.uri); From 43c4fe7f8f7a198c59bf6c1cc91f65bff5f54d30 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 9 Oct 2024 16:46:09 +0200 Subject: [PATCH 421/441] Allow to order and clear AI History view (#14233) * Allow to order and clear AI History view fixed #14183 Signed-off-by: Jonas Helming --- .../common/communication-recording-service.ts | 3 + .../src/browser/ai-history-contribution.ts | 104 ++++++++++++++++-- .../src/browser/ai-history-frontend-module.ts | 3 + .../src/browser/ai-history-widget.tsx | 51 ++++++++- .../common/communication-recording-service.ts | 8 ++ 5 files changed, 159 insertions(+), 10 deletions(-) diff --git a/packages/ai-core/src/common/communication-recording-service.ts b/packages/ai-core/src/common/communication-recording-service.ts index 491d8065173e5..4ae26c47e0e2e 100644 --- a/packages/ai-core/src/common/communication-recording-service.ts +++ b/packages/ai-core/src/common/communication-recording-service.ts @@ -41,4 +41,7 @@ export interface CommunicationRecordingService { readonly onDidRecordResponse: Event; getHistory(agentId: string): CommunicationHistory; + + clearHistory(): void; + readonly onStructuralChange: Event; } diff --git a/packages/ai-history/src/browser/ai-history-contribution.ts b/packages/ai-history/src/browser/ai-history-contribution.ts index f33d71cb6793d..38d87ce4aa425 100644 --- a/packages/ai-history/src/browser/ai-history-contribution.ts +++ b/packages/ai-history/src/browser/ai-history-contribution.ts @@ -13,11 +13,13 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { FrontendApplication } from '@theia/core/lib/browser'; +import { FrontendApplication, codicon } from '@theia/core/lib/browser'; import { AIViewContribution } from '@theia/ai-core/lib/browser'; -import { injectable } from '@theia/core/shared/inversify'; +import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { AIHistoryView } from './ai-history-widget'; -import { Command, CommandRegistry } from '@theia/core'; +import { Command, CommandRegistry, Emitter } from '@theia/core'; +import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; +import { CommunicationRecordingService } from '@theia/ai-core'; export const AI_HISTORY_TOGGLE_COMMAND_ID = 'aiHistory:toggle'; export const OPEN_AI_HISTORY_VIEW = Command.toLocalizedCommand({ @@ -25,8 +27,28 @@ export const OPEN_AI_HISTORY_VIEW = Command.toLocalizedCommand({ label: 'Open AI History view', }); +export const AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY = Command.toLocalizedCommand({ + id: 'aiHistory:sortChronologically', + label: 'AI History: Sort chronologically', + iconClass: codicon('arrow-down') +}); + +export const AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY = Command.toLocalizedCommand({ + id: 'aiHistory:sortReverseChronologically', + label: 'AI History: Sort reverse chronologically', + iconClass: codicon('arrow-up') +}); + +export const AI_HISTORY_VIEW_CLEAR = Command.toLocalizedCommand({ + id: 'aiHistory:clear', + label: 'AI History: Clear History', + iconClass: codicon('clear-all') +}); + @injectable() -export class AIHistoryViewContribution extends AIViewContribution { +export class AIHistoryViewContribution extends AIViewContribution implements TabBarToolbarContribution { + @inject(CommunicationRecordingService) private recordingService: CommunicationRecordingService; + constructor() { super({ widgetId: AIHistoryView.ID, @@ -43,10 +65,78 @@ export class AIHistoryViewContribution extends AIViewContribution await this.openView(); } - override registerCommands(commands: CommandRegistry): void { - super.registerCommands(commands); - commands.registerCommand(OPEN_AI_HISTORY_VIEW, { + override registerCommands(registry: CommandRegistry): void { + super.registerCommands(registry); + registry.registerCommand(OPEN_AI_HISTORY_VIEW, { execute: () => this.openView({ activate: true }), }); + registry.registerCommand(AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY, { + isEnabled: widget => this.withHistoryWidget(widget, historyView => !historyView.isChronological), + isVisible: widget => this.withHistoryWidget(widget, historyView => !historyView.isChronological), + execute: widget => this.withHistoryWidget(widget, historyView => { + historyView.sortHistory(true); + return true; + }) + }); + registry.registerCommand(AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY, { + isEnabled: widget => this.withHistoryWidget(widget, historyView => historyView.isChronological), + isVisible: widget => this.withHistoryWidget(widget, historyView => historyView.isChronological), + execute: widget => this.withHistoryWidget(widget, historyView => { + historyView.sortHistory(false); + return true; + }) + }); + registry.registerCommand(AI_HISTORY_VIEW_CLEAR, { + isEnabled: widget => this.withHistoryWidget(widget), + isVisible: widget => this.withHistoryWidget(widget), + execute: widget => this.withHistoryWidget(widget, () => { + this.clearHistory(); + return true; + }) + }); + } + public clearHistory(): void { + this.recordingService.clearHistory(); + } + + protected withHistoryWidget( + widget: unknown = this.tryGetWidget(), + predicate: (output: AIHistoryView) => boolean = () => true + ): boolean | false { + return widget instanceof AIHistoryView ? predicate(widget) : false; + } + + protected readonly onAIHistoryWidgetStateChangedEmitter = new Emitter(); + protected readonly onAIHistoryWidgettStateChanged = this.onAIHistoryWidgetStateChangedEmitter.event; + + @postConstruct() + protected override init(): void { + super.init(); + this.widget.then(widget => { + widget.onStateChanged(() => this.onAIHistoryWidgetStateChangedEmitter.fire()); + }); + } + + registerToolbarItems(registry: TabBarToolbarRegistry): void { + registry.registerItem({ + id: AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY.id, + command: AI_HISTORY_VIEW_SORT_CHRONOLOGICALLY.id, + tooltip: 'Sort chronologically', + isVisible: widget => this.withHistoryWidget(widget), + onDidChange: this.onAIHistoryWidgettStateChanged + }); + registry.registerItem({ + id: AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY.id, + command: AI_HISTORY_VIEW_SORT_REVERSE_CHRONOLOGICALLY.id, + tooltip: 'Sort reverse chronologically', + isVisible: widget => this.withHistoryWidget(widget), + onDidChange: this.onAIHistoryWidgettStateChanged + }); + registry.registerItem({ + id: AI_HISTORY_VIEW_CLEAR.id, + command: AI_HISTORY_VIEW_CLEAR.id, + tooltip: 'Clear History of all agents', + isVisible: widget => this.withHistoryWidget(widget) + }); } } diff --git a/packages/ai-history/src/browser/ai-history-frontend-module.ts b/packages/ai-history/src/browser/ai-history-frontend-module.ts index 021fc013cabdd..460dc9a6a06a6 100644 --- a/packages/ai-history/src/browser/ai-history-frontend-module.ts +++ b/packages/ai-history/src/browser/ai-history-frontend-module.ts @@ -21,6 +21,7 @@ import { ILogger } from '@theia/core'; import { AIHistoryViewContribution } from './ai-history-contribution'; import { AIHistoryView } from './ai-history-widget'; import '../../src/browser/style/ai-history.css'; +import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; export default new ContainerModule(bind => { bind(DefaultCommunicationRecordingService).toSelf().inSingletonScope(); @@ -38,4 +39,6 @@ export default new ContainerModule(bind => { id: AIHistoryView.ID, createWidget: () => context.container.get(AIHistoryView) })).inSingletonScope(); + bind(TabBarToolbarContribution).toService(AIHistoryViewContribution); + }); diff --git a/packages/ai-history/src/browser/ai-history-widget.tsx b/packages/ai-history/src/browser/ai-history-widget.tsx index 28277426f31a1..938b92f5e1ee2 100644 --- a/packages/ai-history/src/browser/ai-history-widget.tsx +++ b/packages/ai-history/src/browser/ai-history-widget.tsx @@ -14,14 +14,21 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { Agent, AgentService, CommunicationRecordingService, CommunicationRequestEntry, CommunicationResponseEntry } from '@theia/ai-core'; -import { codicon, ReactWidget } from '@theia/core/lib/browser'; +import { codicon, ReactWidget, StatefulWidget } from '@theia/core/lib/browser'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import * as React from '@theia/core/shared/react'; import { CommunicationCard } from './ai-history-communication-card'; import { SelectComponent, SelectOption } from '@theia/core/lib/browser/widgets/select-component'; +import { deepClone, Emitter } from '@theia/core'; + +namespace AIHistoryView { + export interface State { + chronological: boolean; + } +} @injectable() -export class AIHistoryView extends ReactWidget { +export class AIHistoryView extends ReactWidget implements StatefulWidget { @inject(CommunicationRecordingService) protected recordingService: CommunicationRecordingService; @inject(AgentService) @@ -32,6 +39,10 @@ export class AIHistoryView extends ReactWidget { protected selectedAgent?: Agent; + protected _state: AIHistoryView.State = { chronological: false }; + protected readonly onStateChangedEmitter = new Emitter(); + readonly onStateChanged = this.onStateChangedEmitter.event; + constructor() { super(); this.id = AIHistoryView.ID; @@ -41,11 +52,34 @@ export class AIHistoryView extends ReactWidget { this.title.iconClass = codicon('history'); } + protected get state(): AIHistoryView.State { + return this._state; + } + + protected set state(state: AIHistoryView.State) { + this._state = state; + this.onStateChangedEmitter.fire(this._state); + } + + storeState(): object { + return this.state; + } + + restoreState(oldState: object & Partial): void { + const copy = deepClone(this.state); + if (oldState.chronological) { + copy.chronological = oldState.chronological; + } + this.state = copy; + } + @postConstruct() protected init(): void { this.update(); this.toDispose.push(this.recordingService.onDidRecordRequest(entry => this.historyContentUpdated(entry))); this.toDispose.push(this.recordingService.onDidRecordResponse(entry => this.historyContentUpdated(entry))); + this.toDispose.push(this.recordingService.onStructuralChange(() => this.update())); + this.toDispose.push(this.onStateChanged(newState => this.update())); this.selectAgent(this.agentService.getAllAgents()[0]); } @@ -82,10 +116,13 @@ export class AIHistoryView extends ReactWidget { if (!this.selectedAgent) { return
        No agent selected.
        ; } - const history = this.recordingService.getHistory(this.selectedAgent.id); + const history = [...this.recordingService.getHistory(this.selectedAgent.id)]; if (history.length === 0) { return
        No history available for the selected agent '{this.selectedAgent.name}'.
        ; } + if (!this.state.chronological) { + history.reverse(); + } return history.map(entry => ); } @@ -93,4 +130,12 @@ export class AIHistoryView extends ReactWidget { e.stopPropagation(); this.selectAgent(agent); } + + public sortHistory(chronological: boolean): void { + this.state = { ...deepClone(this.state), chronological: chronological }; + } + + get isChronological(): boolean { + return this.state.chronological === true; + } } diff --git a/packages/ai-history/src/common/communication-recording-service.ts b/packages/ai-history/src/common/communication-recording-service.ts index 9d23a6766064e..d32eb6ffc9121 100644 --- a/packages/ai-history/src/common/communication-recording-service.ts +++ b/packages/ai-history/src/common/communication-recording-service.ts @@ -29,6 +29,9 @@ export class DefaultCommunicationRecordingService implements CommunicationRecord protected onDidRecordResponseEmitter = new Emitter(); readonly onDidRecordResponse: Event = this.onDidRecordResponseEmitter.event; + protected onStructuralChangeEmitter = new Emitter(); + readonly onStructuralChange: Event = this.onStructuralChangeEmitter.event; + protected history: Map = new Map(); getHistory(agentId: string): CommunicationHistory { @@ -60,4 +63,9 @@ export class DefaultCommunicationRecordingService implements CommunicationRecord } } } + + clearHistory(): void { + this.history.clear(); + this.onStructuralChangeEmitter.fire(undefined); + } } From 04a8b3ec7895280fe7bacf5f69857d0f27841caa Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 10 Oct 2024 08:46:36 +0200 Subject: [PATCH 422/441] Orchestrator logs its own requests (#14255) * Orchestrator logs its own requests - add data field to request - add option to turn off default logging - extracted getJsonOfText - add docu for getTextOfResponse fixed #14252 Signed-off-by: Jonas Helming --- packages/ai-chat/src/common/chat-agents.ts | 70 +++++++++++-------- packages/ai-chat/src/common/chat-model.ts | 18 ++++- .../src/common/orchestrator-chat-agent.ts | 33 +++++++-- .../ai-core/src/common/language-model-util.ts | 19 ++++- 4 files changed, 104 insertions(+), 36 deletions(-) diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts index ce6bbd741a17c..d98595e282c41 100644 --- a/packages/ai-chat/src/common/chat-agents.ts +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -137,7 +137,8 @@ export abstract class AbstractChatAgent { protected defaultLanguageModelPurpose: string, public iconClass: string = 'codicon codicon-copilot', public locations: ChatAgentLocation[] = ChatAgentLocation.ALL, - public tags: String[] = ['Chat']) { + public tags: String[] = ['Chat'], + public defaultLogging: boolean = true) { } @postConstruct() @@ -152,14 +153,16 @@ export abstract class AbstractChatAgent { throw new Error('Couldn\'t find a matching language model. Please check your setup!'); } const messages = await this.getMessages(request.session); - this.recordingService.recordRequest({ - agentId: this.id, - sessionId: request.session.id, - timestamp: Date.now(), - requestId: request.id, - request: request.request.text, - messages - }); + if (this.defaultLogging) { + this.recordingService.recordRequest({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.id, + request: request.request.text, + messages + }); + } const systemMessageDescription = await this.getSystemMessageDescription(); const tools: Map = new Map(); @@ -192,13 +195,15 @@ export abstract class AbstractChatAgent { ); await this.addContentsToResponse(languageModelResponse, request); request.response.complete(); - this.recordingService.recordResponse({ - agentId: this.id, - sessionId: request.session.id, - timestamp: Date.now(), - requestId: request.response.requestId, - response: request.response.response.asString() - }); + if (this.defaultLogging) { + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + }); + } } catch (e) { this.handleError(request, e); } @@ -307,25 +312,30 @@ export abstract class AbstractStreamParsingChatAgent extends AbstractChatAgent { const contents = this.parseContents(languageModelResponse.text); request.response.response.addContents(contents); request.response.complete(); - this.recordingService.recordResponse({ - agentId: this.id, - sessionId: request.session.id, - timestamp: Date.now(), - requestId: request.response.requestId, - response: request.response.response.asString() - }); + if (this.defaultLogging) { + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + + }); + } return; } if (isLanguageModelStreamResponse(languageModelResponse)) { await this.addStreamResponse(languageModelResponse, request); request.response.complete(); - this.recordingService.recordResponse({ - agentId: this.id, - sessionId: request.session.id, - timestamp: Date.now(), - requestId: request.response.requestId, - response: request.response.response.asString() - }); + if (this.defaultLogging) { + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: request.response.requestId, + response: request.response.response.asString() + }); + } return; } this.logger.error( diff --git a/packages/ai-chat/src/common/chat-model.ts b/packages/ai-chat/src/common/chat-model.ts index c485c69c7c4c8..0decfb284da35 100644 --- a/packages/ai-chat/src/common/chat-model.ts +++ b/packages/ai-chat/src/common/chat-model.ts @@ -73,6 +73,7 @@ export interface ChatRequestModel { readonly response: ChatResponseModel; readonly message: ParsedChatRequest; readonly agentId?: string; + readonly data?: { [key: string]: unknown }; } export interface ChatProgressMessage { @@ -342,14 +343,29 @@ export class ChatRequestModelImpl implements ChatRequestModel { protected _request: ChatRequest; protected _response: ChatResponseModelImpl; protected _agentId?: string; + protected _data: { [key: string]: unknown }; - constructor(session: ChatModel, public readonly message: ParsedChatRequest, agentId?: string) { + constructor(session: ChatModel, public readonly message: ParsedChatRequest, agentId?: string, + data: { [key: string]: unknown } = {}) { // TODO accept serialized data as a parameter to restore a previously saved ChatRequestModel this._request = message.request; this._id = generateUuid(); this._session = session; this._response = new ChatResponseModelImpl(this._id, agentId); this._agentId = agentId; + this._data = data; + } + + get data(): { [key: string]: unknown } | undefined { + return this._data; + } + + addData(key: string, value: unknown): void { + this._data[key] = value; + } + + getDataByKey(key: string): unknown { + return this._data[key]; } get id(): string { diff --git a/packages/ai-chat/src/common/orchestrator-chat-agent.ts b/packages/ai-chat/src/common/orchestrator-chat-agent.ts index 9beec41a5cc30..c74195d9ca444 100644 --- a/packages/ai-chat/src/common/orchestrator-chat-agent.ts +++ b/packages/ai-chat/src/common/orchestrator-chat-agent.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { AgentSpecificVariables, getJsonOfResponse, LanguageModelResponse } from '@theia/ai-core'; +import { AgentSpecificVariables, getJsonOfText, getTextOfResponse, LanguageModelResponse } from '@theia/ai-core'; import { PromptTemplate } from '@theia/ai-core/lib/common'; @@ -22,6 +22,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { ChatAgentService } from './chat-agent-service'; import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; import { ChatRequestModelImpl, InformationalChatResponseContentImpl } from './chat-model'; +import { generateUuid } from '@theia/core'; export const orchestratorTemplate: PromptTemplate = { id: 'orchestrator-system', @@ -59,6 +60,7 @@ You must only use the \`id\` attribute of the agent, never the name. `}; export const OrchestratorChatAgentId = 'Orchestrator'; +const OrchestatorRequestIdKey = 'orchestatorRequestIdKey'; @injectable() export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implements ChatAgent { @@ -74,7 +76,7 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implem super(OrchestratorChatAgentId, [{ purpose: 'agent-selection', identifier: 'openai/gpt-4o', - }], 'agent-selection', 'codicon codicon-symbol-boolean'); + }], 'agent-selection', 'codicon codicon-symbol-boolean', undefined, undefined, false); this.name = OrchestratorChatAgentId; this.description = 'This agent analyzes the user request against the description of all available chat agents and selects the best fitting agent to answer the request \ (by using AI).The user\'s request will be directly delegated to the selected agent without further confirmation.'; @@ -88,8 +90,19 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implem @inject(ChatAgentService) protected chatAgentService: ChatAgentService; - override invoke(request: ChatRequestModelImpl): Promise { + override async invoke(request: ChatRequestModelImpl): Promise { request.response.addProgressMessage({ content: 'Determining the most appropriate agent', status: 'inProgress' }); + // We generate a dedicated ID for recording the orchestrator request/response, as we will forward the original request to another agent + const orchestartorRequestId = generateUuid(); + request.addData(OrchestatorRequestIdKey, orchestartorRequestId); + const userPrompt = request.request.text; + this.recordingService.recordRequest({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: orchestartorRequestId, + request: userPrompt, + }); return super.invoke(request); } @@ -100,8 +113,20 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent implem protected override async addContentsToResponse(response: LanguageModelResponse, request: ChatRequestModelImpl): Promise { let agentIds: string[] = []; + const responseText = await getTextOfResponse(response); + // We use the previously generated, dedicated ID to log the orchestrator response before we forward the original request + const orchestratorRequestId = request.getDataByKey(OrchestatorRequestIdKey); + if (typeof orchestratorRequestId === 'string') { + this.recordingService.recordResponse({ + agentId: this.id, + sessionId: request.session.id, + timestamp: Date.now(), + requestId: orchestratorRequestId, + response: responseText, + }); + } try { - const jsonResponse = await getJsonOfResponse(response); + const jsonResponse = await getJsonOfText(responseText); if (Array.isArray(jsonResponse)) { agentIds = jsonResponse.filter((id: string) => id !== this.id); } diff --git a/packages/ai-core/src/common/language-model-util.ts b/packages/ai-core/src/common/language-model-util.ts index d89730b583e4d..75546a0e4ac0f 100644 --- a/packages/ai-core/src/common/language-model-util.ts +++ b/packages/ai-core/src/common/language-model-util.ts @@ -14,8 +14,18 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { isLanguageModelStreamResponse, isLanguageModelTextResponse, LanguageModelResponse, ToolRequest } from './language-model'; +import { isLanguageModelParsedResponse, isLanguageModelStreamResponse, isLanguageModelTextResponse, LanguageModelResponse, ToolRequest } from './language-model'; +/** + * Retrieves the text content from a `LanguageModelResponse` object. + * + * **Important:** For stream responses, the stream can only be consumed once. Calling this function multiple times on the same stream response will return an empty string (`''`) + * on subsequent calls, as the stream will have already been consumed. + * + * @param {LanguageModelResponse} response - The response object, which may contain a text, stream, or parsed response. + * @returns {Promise} - A promise that resolves to the text content of the response. + * @throws {Error} - Throws an error if the response type is not supported or does not contain valid text content. + */ export const getTextOfResponse = async (response: LanguageModelResponse): Promise => { if (isLanguageModelTextResponse(response)) { return response.text; @@ -25,12 +35,18 @@ export const getTextOfResponse = async (response: LanguageModelResponse): Promis result += chunk.content ?? ''; } return result; + } else if (isLanguageModelParsedResponse(response)) { + return response.content; } throw new Error(`Invalid response type ${response}`); }; export const getJsonOfResponse = async (response: LanguageModelResponse): Promise => { const text = await getTextOfResponse(response); + return getJsonOfText(text); +}; + +export const getJsonOfText = (text: string): unknown => { if (text.startsWith('```json')) { const regex = /```json\s*([\s\S]*?)\s*```/g; let match; @@ -47,6 +63,7 @@ export const getJsonOfResponse = async (response: LanguageModelResponse): Promis } throw new Error('Invalid response format'); }; + export const toolRequestToPromptText = (toolRequest: ToolRequest): string => { const parameters = toolRequest.parameters; let paramsText = ''; From 0d48b2275a2a0b3538f2e9352414eb4e917e1447 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Thu, 10 Oct 2024 09:53:18 +0200 Subject: [PATCH 423/441] Optimize showing recent workspaces (#14260) --- .../src/browser/quick-open-workspace.ts | 48 ++++++------------- .../src/node/default-workspace-server.ts | 7 ++- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/packages/workspace/src/browser/quick-open-workspace.ts b/packages/workspace/src/browser/quick-open-workspace.ts index 473c27400d17a..91890ec2bf89f 100644 --- a/packages/workspace/src/browser/quick-open-workspace.ts +++ b/packages/workspace/src/browser/quick-open-workspace.ts @@ -18,10 +18,7 @@ import { injectable, inject, optional } from '@theia/core/shared/inversify'; import { QuickPickItem, LabelProvider, QuickInputService, QuickInputButton, QuickPickSeparator } from '@theia/core/lib/browser'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { WorkspaceService } from './workspace-service'; -import { WorkspacePreferences } from './workspace-preferences'; import URI from '@theia/core/lib/common/uri'; -import { FileService } from '@theia/filesystem/lib/browser/file-service'; -import { FileStat } from '@theia/filesystem/lib/common/files'; import { nls, Path } from '@theia/core/lib/common'; import { UntitledWorkspaceService } from '../common/untitled-workspace-service'; @@ -31,14 +28,11 @@ interface RecentlyOpenedPick extends QuickPickItem { @injectable() export class QuickOpenWorkspace { - protected items: Array; protected opened: boolean; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; - @inject(FileService) protected readonly fileService: FileService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; - @inject(WorkspacePreferences) protected preferences: WorkspacePreferences; @inject(EnvVariablesServer) protected readonly envServer: EnvVariablesServer; @inject(UntitledWorkspaceService) protected untitledWorkspaceService: UntitledWorkspaceService; @@ -48,45 +42,34 @@ export class QuickOpenWorkspace { }; async open(workspaces: string[]): Promise { - this.items = []; - const [homeDirUri] = await Promise.all([ - this.envServer.getHomeDirUri(), - this.workspaceService.getUntitledWorkspace() - ]); - const home = new URI(homeDirUri).path.toString(); - await this.preferences.ready; - this.items.push({ + const homeDirUri = await this.envServer.getHomeDirUri(); + const home = new URI(homeDirUri).path.fsPath(); + const items: (RecentlyOpenedPick | QuickPickSeparator)[] = [{ type: 'separator', label: nls.localizeByDefault('folders & workspaces') - }); + }]; + for (const workspace of workspaces) { const uri = new URI(workspace); - let stat: FileStat | undefined; - try { - stat = await this.fileService.resolve(uri); - } catch { } - if (this.untitledWorkspaceService.isUntitledWorkspace(uri) || !stat) { - continue; // skip the temporary workspace files or an undefined stat. + const label = uri.path.base; + if (!label || this.untitledWorkspaceService.isUntitledWorkspace(uri)) { + continue; // skip temporary workspace files & empty workspace names } - const icon = this.labelProvider.getIcon(stat); - const iconClasses = icon === '' ? undefined : [icon + ' file-icon']; - - this.items.push({ - label: uri.path.base, - description: Path.tildify(uri.path.toString(), home), - iconClasses, + items.push({ + label: label, + description: Path.tildify(uri.path.fsPath(), home), buttons: [this.removeRecentWorkspaceButton], resource: uri, execute: () => { const current = this.workspaceService.workspace; - const uriToOpen = new URI(workspace); if ((current && current.resource.toString() !== workspace) || !current) { - this.workspaceService.open(uriToOpen); + this.workspaceService.open(uri); } - }, + } }); } - this.quickInputService?.showQuickPick(this.items, { + + this.quickInputService?.showQuickPick(items, { placeholder: nls.localize( 'theia/workspace/openRecentPlaceholder', 'Type the name of the workspace you want to open'), @@ -101,7 +84,6 @@ export class QuickOpenWorkspace { } select(): void { - this.items = []; this.opened = this.workspaceService.opened; this.workspaceService.recentWorkspaces().then(workspaceRoots => { if (workspaceRoots) { diff --git a/packages/workspace/src/node/default-workspace-server.ts b/packages/workspace/src/node/default-workspace-server.ts index 10a7490fc41a5..251d8dc6f54de 100644 --- a/packages/workspace/src/node/default-workspace-server.ts +++ b/packages/workspace/src/node/default-workspace-server.ts @@ -161,7 +161,12 @@ export class DefaultWorkspaceServer implements WorkspaceServer, BackendApplicati } protected async workspaceStillExist(workspaceRootUri: string): Promise { - return fs.pathExists(FileUri.fsPath(workspaceRootUri)); + const uri = new URI(workspaceRootUri); + // Non file system workspaces cannot be checked for existence + if (uri.scheme !== 'file') { + return false; + } + return fs.pathExists(uri.path.fsPath()); } protected async getWorkspaceURIFromCli(): Promise { From 5f49a29d5f6d7bf30578c26c22ac1a997f5e450a Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 10 Oct 2024 10:26:16 +0200 Subject: [PATCH 424/441] Notebook Cell Tag Support (#14271) * added cell tag and status bar item support for notebooks --------- Signed-off-by: Jonah Iden --- .../src/browser/notebook-frontend-module.ts | 2 + .../notebook-cell-status-bar-service.ts | 94 +++++++++++++++++++ packages/notebook/src/browser/style/index.css | 19 ++++ .../browser/view/notebook-code-cell-view.tsx | 69 +++++++++++++- .../view/notebook-markdown-cell-view.tsx | 23 ++++- .../main/browser/notebooks/notebooks-main.ts | 31 +++--- .../src/plugin/notebook/notebooks.ts | 7 +- 7 files changed, 217 insertions(+), 28 deletions(-) create mode 100644 packages/notebook/src/browser/service/notebook-cell-status-bar-service.ts diff --git a/packages/notebook/src/browser/notebook-frontend-module.ts b/packages/notebook/src/browser/notebook-frontend-module.ts index b5158315668b2..5671f2dc3355f 100644 --- a/packages/notebook/src/browser/notebook-frontend-module.ts +++ b/packages/notebook/src/browser/notebook-frontend-module.ts @@ -51,6 +51,7 @@ import { NotebookOptionsService } from './service/notebook-options'; import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler'; import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution'; import { NotebookCellEditorService } from './service/notebook-cell-editor-service'; +import { NotebookCellStatusBarService } from './service/notebook-cell-status-bar-service'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookColorContribution).toSelf().inSingletonScope(); @@ -74,6 +75,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(NotebookKernelQuickPickService).toSelf().inSingletonScope(); bind(NotebookClipboardService).toSelf().inSingletonScope(); bind(NotebookCellEditorService).toSelf().inSingletonScope(); + bind(NotebookCellStatusBarService).toSelf().inSingletonScope(); bind(NotebookCellResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toService(NotebookCellResourceResolver); diff --git a/packages/notebook/src/browser/service/notebook-cell-status-bar-service.ts b/packages/notebook/src/browser/service/notebook-cell-status-bar-service.ts new file mode 100644 index 0000000000000..92fedf6014325 --- /dev/null +++ b/packages/notebook/src/browser/service/notebook-cell-status-bar-service.ts @@ -0,0 +1,94 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationToken, Command, Disposable, Emitter, Event, URI } from '@theia/core'; +import { CellStatusbarAlignment } from '../../common'; +import { ThemeColor } from '@theia/core/lib/common/theme'; +import { AccessibilityInformation } from '@theia/core/lib/common/accessibility'; +import { injectable } from '@theia/core/shared/inversify'; +import { MarkdownString } from '@theia/core/lib/common/markdown-rendering'; + +export interface NotebookCellStatusBarItem { + readonly alignment: CellStatusbarAlignment; + readonly priority?: number; + readonly text: string; + readonly color?: string | ThemeColor; + readonly backgroundColor?: string | ThemeColor; + readonly tooltip?: string | MarkdownString; + readonly command?: string | (Command & { arguments?: unknown[] }); + readonly accessibilityInformation?: AccessibilityInformation; + readonly opacity?: string; + readonly onlyShowWhenActive?: boolean; +} +export interface NotebookCellStatusBarItemList { + items: NotebookCellStatusBarItem[]; + dispose?(): void; +} + +export interface NotebookCellStatusBarItemProvider { + viewType: string; + onDidChangeStatusBarItems?: Event; + provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise; +} + +@injectable() +export class NotebookCellStatusBarService implements Disposable { + + protected readonly onDidChangeProvidersEmitter = new Emitter(); + readonly onDidChangeProviders: Event = this.onDidChangeProvidersEmitter.event; + + protected readonly onDidChangeItemsEmitter = new Emitter(); + readonly onDidChangeItems: Event = this.onDidChangeItemsEmitter.event; + + protected readonly providers: NotebookCellStatusBarItemProvider[] = []; + + registerCellStatusBarItemProvider(provider: NotebookCellStatusBarItemProvider): Disposable { + this.providers.push(provider); + let changeListener: Disposable | undefined; + if (provider.onDidChangeStatusBarItems) { + changeListener = provider.onDidChangeStatusBarItems(() => this.onDidChangeItemsEmitter.fire()); + } + + this.onDidChangeProvidersEmitter.fire(); + + return Disposable.create(() => { + changeListener?.dispose(); + const idx = this.providers.findIndex(p => p === provider); + this.providers.splice(idx, 1); + }); + } + + async getStatusBarItemsForCell(notebookUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise { + const providers = this.providers.filter(p => p.viewType === viewType || p.viewType === '*'); + return Promise.all(providers.map(async p => { + try { + return await p.provideCellStatusBarItems(notebookUri, cellIndex, token) ?? { items: [] }; + } catch (e) { + console.error(e); + return { items: [] }; + } + })); + } + + dispose(): void { + this.onDidChangeItemsEmitter.dispose(); + this.onDidChangeProvidersEmitter.dispose(); + } +} diff --git a/packages/notebook/src/browser/style/index.css b/packages/notebook/src/browser/style/index.css index e557a7983e7d8..0ad64eb41136f 100644 --- a/packages/notebook/src/browser/style/index.css +++ b/packages/notebook/src/browser/style/index.css @@ -508,3 +508,22 @@ mark.theia-find-match.theia-find-match-selected { color: var(--theia-editor-findMatchForeground); background-color: var(--theia-editor-findMatchBackground); } + +.cell-status-bar-item { + align-items: center; + display: flex; + height: 16px; + margin: 0 3px; + overflow: hidden; + padding: 0 3px; + text-overflow: clip; + white-space: pre; +} + +.cell-status-item-has-command { + cursor: pointer; +} + +.cell-status-item-has-command:hover { + background-color: var(--theia-toolbar-hoverBackground); +} diff --git a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx index 537776e03adaf..17c4902a1f507 100644 --- a/packages/notebook/src/browser/view/notebook-code-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-code-cell-view.tsx @@ -27,7 +27,7 @@ import { NotebookCellActionContribution, NotebookCellCommands } from '../contrib import { CellExecution, NotebookExecutionStateService } from '../service/notebook-execution-state-service'; import { codicon } from '@theia/core/lib/browser'; import { NotebookCellExecutionState } from '../../common'; -import { CommandRegistry, DisposableCollection, nls } from '@theia/core'; +import { CancellationToken, CommandRegistry, DisposableCollection, nls } from '@theia/core'; import { NotebookContextManager } from '../service/notebook-context-manager'; import { NotebookViewportService } from './notebook-viewport-service'; import { EditorPreferences } from '@theia/editor/lib/browser'; @@ -36,6 +36,8 @@ import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/mar import { MarkdownString } from '@theia/monaco-editor-core/esm/vs/base/common/htmlContent'; import { NotebookCellEditorService } from '../service/notebook-cell-editor-service'; import { CellOutputWebview } from '../renderers/cell-output-webview'; +import { NotebookCellStatusBarItem, NotebookCellStatusBarItemList, NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service'; +import { LabelParser } from '@theia/core/lib/browser/label-parser'; @injectable() export class NotebookCodeCellRenderer implements CellRenderer { @@ -75,6 +77,12 @@ export class NotebookCodeCellRenderer implements CellRenderer { @inject(CellOutputWebview) protected readonly outputWebview: CellOutputWebview; + @inject(NotebookCellStatusBarService) + protected readonly notebookCellStatusBarService: NotebookCellStatusBarService; + + @inject(LabelParser) + protected readonly labelParser: LabelParser; + render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode { return
        observeCellHeight(ref, cell)}>
        @@ -87,6 +95,8 @@ export class NotebookCodeCellRenderer implements CellRenderer { cell.requestFocusEditor()} />
        ; @@ -182,7 +192,9 @@ export interface NotebookCodeCellStatusProps { notebook: NotebookModel; cell: NotebookCellModel; commandRegistry: CommandRegistry; + cellStatusBarService: NotebookCellStatusBarService; executionStateService?: NotebookExecutionStateService; + labelParser: LabelParser; onClick: () => void; } @@ -195,6 +207,8 @@ export class NotebookCodeCellStatus extends React.Component { this.forceUpdate(); })); + + this.updateStatusBarItems(); + this.props.cellStatusBarService.onDidChangeItems(() => this.updateStatusBarItems()); + this.props.notebook.onContentChanged(() => this.updateStatusBarItems()); + } + + async updateStatusBarItems(): Promise { + this.statusBarItems = await this.props.cellStatusBarService.getStatusBarItemsForCell( + this.props.notebook.uri, + this.props.notebook.cells.indexOf(this.props.cell), + this.props.notebook.viewType, + CancellationToken.None); + this.forceUpdate(); } override componentWillUnmount(): void { @@ -235,6 +262,7 @@ export class NotebookCodeCellStatus extends React.Component this.props.onClick()}>
        {this.props.executionStateService && this.renderExecutionState()} + {this.statusBarItems?.length && this.renderStatusBarItems()}
        { @@ -244,7 +272,7 @@ export class NotebookCodeCellStatus extends React.Component; } - private renderExecutionState(): React.ReactNode { + protected renderExecutionState(): React.ReactNode { const state = this.state.currentExecution?.state; const { lastRunSuccess } = this.props.cell.internalMetadata; @@ -270,7 +298,7 @@ export class NotebookCodeCellStatus extends React.Component; } - private getExecutionTime(): number { + protected getExecutionTime(): number { const { runStartTime, runEndTime } = this.props.cell.internalMetadata; const { executionTime } = this.state; if (runStartTime !== undefined && runEndTime !== undefined) { @@ -279,9 +307,42 @@ export class NotebookCodeCellStatus extends React.Component + { + this.statusBarItems.flatMap((itemList, listIndex) => + itemList.items.map((item, index) => this.renderStatusBarItem(item, `${listIndex}-${index}`) + ) + ) + } + ; + } + + protected renderStatusBarItem(item: NotebookCellStatusBarItem, key: string): React.ReactNode { + const content = this.props.labelParser.parse(item.text).map(part => { + if (typeof part === 'string') { + return part; + } else { + return ; + } + }); + return
        { + if (item.command) { + if (typeof item.command === 'string') { + this.props.commandRegistry.executeCommand(item.command); + } else { + this.props.commandRegistry.executeCommand(item.command.id, ...(item.command.arguments ?? [])); + } + } + }}> + {content} +
        ; + } + } interface NotebookCellOutputProps { diff --git a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx index e24ff32f838b7..c0397fe0649e2 100644 --- a/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx +++ b/packages/notebook/src/browser/view/notebook-markdown-cell-view.tsx @@ -30,6 +30,8 @@ import { NotebookCodeCellStatus } from './notebook-code-cell-view'; import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from './notebook-find-widget'; import * as mark from 'advanced-mark.js'; import { NotebookCellEditorService } from '../service/notebook-cell-editor-service'; +import { NotebookCellStatusBarService } from '../service/notebook-cell-status-bar-service'; +import { LabelParser } from '@theia/core/lib/browser/label-parser'; @injectable() export class NotebookMarkdownCellRenderer implements CellRenderer { @@ -51,6 +53,12 @@ export class NotebookMarkdownCellRenderer implements CellRenderer { @inject(NotebookCellEditorService) protected readonly notebookCellEditorService: NotebookCellEditorService; + @inject(NotebookCellStatusBarService) + protected readonly notebookCellStatusBarService: NotebookCellStatusBarService; + + @inject(LabelParser) + protected readonly labelParser: LabelParser; + render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { return ; + notebookCellEditorService={this.notebookCellEditorService} + notebookCellStatusBarService={this.notebookCellStatusBarService} + labelParser={this.labelParser} + />; } renderSidebar(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode { @@ -86,11 +97,15 @@ interface MarkdownCellProps { notebookModel: NotebookModel; notebookContextManager: NotebookContextManager; notebookOptionsService: NotebookOptionsService; - notebookCellEditorService: NotebookCellEditorService + notebookCellEditorService: NotebookCellEditorService; + notebookCellStatusBarService: NotebookCellStatusBarService; + labelParser: LabelParser; } function MarkdownCell({ - markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, notebookOptionsService, commandRegistry, notebookCellEditorService + markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager, + notebookOptionsService, commandRegistry, notebookCellEditorService, notebookCellStatusBarService, + labelParser }: MarkdownCellProps): React.JSX.Element { const [editMode, setEditMode] = React.useState(cell.editing); let empty = false; @@ -147,6 +162,8 @@ function MarkdownCell({ fontInfo={notebookOptionsService.editorFontInfo} /> cell.requestFocusEditor()} />
        ) : (
        ; - provideCellStatusBarItems(uri: UriComponents, index: number, token: CancellationToken): Promise; -} +import { + NotebookCellStatusBarItemProvider, + NotebookCellStatusBarItemList, + NotebookCellStatusBarService +} from '@theia/notebook/lib/browser/service/notebook-cell-status-bar-service'; export class NotebooksMainImpl implements NotebooksMain { protected readonly disposables = new DisposableCollection(); protected notebookService: NotebookService; + protected cellStatusBarService: NotebookCellStatusBarService; protected readonly proxy: NotebooksExt; protected readonly notebookSerializer = new Map(); @@ -55,6 +49,7 @@ export class NotebooksMainImpl implements NotebooksMain { commands: CommandRegistryMain ) { this.notebookService = container.get(NotebookService); + this.cellStatusBarService = container.get(NotebookCellStatusBarService); const plugins = container.get(HostedPluginSupport); this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.NOTEBOOKS_EXT); @@ -111,8 +106,8 @@ export class NotebooksMainImpl implements NotebooksMain { async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise { const that = this; const provider: NotebookCellStatusBarItemProvider = { - async provideCellStatusBarItems(uri: UriComponents, index: number, token: CancellationToken): Promise { - const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, uri, index, token); + async provideCellStatusBarItems(notebookUri: URI, index: number, token: CancellationToken): Promise { + const result = await that.proxy.$provideNotebookCellStatusBarItems(handle, notebookUri.toComponents(), index, token); return { items: result?.items ?? [], dispose(): void { @@ -131,8 +126,8 @@ export class NotebooksMainImpl implements NotebooksMain { provider.onDidChangeStatusBarItems = emitter.event; } - // const disposable = this._cellStatusBarService.registerCellStatusBarItemProvider(provider); - // this.notebookCellStatusBarRegistrations.set(handle, disposable); + const disposable = this.cellStatusBarService.registerCellStatusBarItemProvider(provider); + this.notebookCellStatusBarRegistrations.set(handle, disposable); } async $unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise { diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index d31d9554ca2ac..6101c2fc97c8a 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -22,14 +22,14 @@ import { CancellationToken, Disposable, DisposableCollection, Emitter, Event, UR import { URI as TheiaURI } from '../types-impl'; import * as theia from '@theia/plugin'; import { - CommandRegistryExt, NotebookCellStatusBarListDto, NotebookDataDto, + NotebookCellStatusBarListDto, NotebookDataDto, NotebookDocumentsAndEditorsDelta, NotebookDocumentShowOptions, NotebookDocumentsMain, NotebookEditorAddData, NotebookEditorsMain, NotebooksExt, NotebooksMain, Plugin, PLUGIN_RPC_CONTEXT } from '../../common'; import { Cache } from '../../common/cache'; import { RPCProtocol } from '../../common/rpc-protocol'; import { UriComponents } from '../../common/uri-components'; -import { CommandsConverter } from '../command-registry'; +import { CommandRegistryImpl, CommandsConverter } from '../command-registry'; import * as typeConverters from '../type-converters'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { Cell, NotebookDocument } from './notebook-document'; @@ -74,10 +74,11 @@ export class NotebooksExtImpl implements NotebooksExt { constructor( rpc: RPCProtocol, - commands: CommandRegistryExt, + commands: CommandRegistryImpl, private textDocumentsAndEditors: EditorsAndDocumentsExtImpl, private textDocuments: DocumentsExtImpl, ) { + this.commandsConverter = commands.converter; this.notebookProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN); this.notebookDocumentsProxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN); this.notebookEditors = rpc.getProxy(PLUGIN_RPC_CONTEXT.NOTEBOOK_EDITORS_MAIN); From 5058c242a60247802211126eb9335f5f4d22991d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 10 Oct 2024 11:25:56 +0200 Subject: [PATCH 425/441] Start menu handler ids at one, not zero (#14282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14279 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/src/electron-browser/preload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/electron-browser/preload.ts b/packages/core/src/electron-browser/preload.ts index fb87355fc8dda..57755899242d8 100644 --- a/packages/core/src/electron-browser/preload.ts +++ b/packages/core/src/electron-browser/preload.ts @@ -35,8 +35,8 @@ const { ipcRenderer, contextBridge } = require('electron'); // a map of menuId => map handler> const commandHandlers = new Map void>>(); -let nextHandlerId = 0; -const mainMenuId = 0; +let nextHandlerId = 1; +const mainMenuId = 1; let nextMenuId = mainMenuId + 1; let openUrlHandler: ((url: string) => Promise) | undefined; From eb5b395228a757e13fc1854be79b4f2f0adf4bca Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 10 Oct 2024 11:47:47 +0200 Subject: [PATCH 426/441] blur cell on shift+enter, only update cell height when changed (#14277) * fix cell focus on shift+enter, only update cell height when changed Signed-off-by: Jonah Iden --- .../browser/view-model/notebook-cell-model.ts | 6 +++-- .../src/browser/view/notebook-cell-editor.tsx | 24 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/notebook/src/browser/view-model/notebook-cell-model.ts b/packages/notebook/src/browser/view-model/notebook-cell-model.ts index 3c11012e48732..4160bb495d74c 100644 --- a/packages/notebook/src/browser/view-model/notebook-cell-model.ts +++ b/packages/notebook/src/browser/view-model/notebook-cell-model.ts @@ -274,8 +274,10 @@ export class NotebookCellModel implements NotebookCell, Disposable { } set cellHeight(height: number) { - this.onDidCellHeightChangeEmitter.fire(height); - this._cellheight = height; + if (height !== this._cellheight) { + this.onDidCellHeightChangeEmitter.fire(height); + this._cellheight = height; + } } @postConstruct() diff --git a/packages/notebook/src/browser/view/notebook-cell-editor.tsx b/packages/notebook/src/browser/view/notebook-cell-editor.tsx index 22475efb0ecde..e03d1eb4df385 100644 --- a/packages/notebook/src/browser/view/notebook-cell-editor.tsx +++ b/packages/notebook/src/browser/view/notebook-cell-editor.tsx @@ -103,15 +103,7 @@ export class CellEditor extends React.Component { this.props.notebookContextManager.scopedStore.setContext(NOTEBOOK_CELL_CURSOR_LAST_LINE, currentLine === lineCount); })); - this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => { - let parent = this.container?.parentElement; - while (parent && !parent.classList.contains('theia-notebook-cell')) { - parent = parent.parentElement; - } - if (parent) { - parent.focus(); - } - })); + this.toDispose.push(this.props.cell.onWillBlurCellEditor(() => this.blurEditor())); this.toDispose.push(this.props.cell.onDidChangeEditorOptions(options => { this.editor?.getControl().updateOptions(options); @@ -130,7 +122,7 @@ export class CellEditor extends React.Component { this.toDispose.push(this.props.notebookModel.onDidChangeSelectedCell(e => { if (e.cell !== this.props.cell && this.editor?.getControl().hasTextFocus()) { - this.props.notebookContextManager.context?.focus(); + this.blurEditor(); } })); if (!this.props.notebookViewportService || (this.container && this.props.notebookViewportService.isElementInViewport(this.container))) { @@ -231,7 +223,7 @@ export class CellEditor extends React.Component { })); this.props.notebookCellEditorService.editorCreated(uri, this.editor); this.setMatches(); - if (cell.editing && notebookModel.selectedCell === cell) { + if (notebookModel.selectedCell === cell) { this.editor.getControl().focus(); } } @@ -278,4 +270,14 @@ export class CellEditor extends React.Component {
        ; } + protected blurEditor(): void { + let parent = this.container?.parentElement; + while (parent && !parent.classList.contains('theia-notebook-cell')) { + parent = parent.parentElement; + } + if (parent) { + parent.focus(); + } + } + } From 2be612f8a7efb90c1183ba47f0897040925b304a Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Thu, 10 Oct 2024 13:13:52 +0200 Subject: [PATCH 427/441] feat(ai): Allow customizing the LLM request settings With this change, we enable more easily customizing the LLM settings, such as `temperature`, for the LLM requests in a chat agent. Contributed on behalf of STMicroelectronics --- packages/ai-chat/src/common/chat-agents.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/ai-chat/src/common/chat-agents.ts b/packages/ai-chat/src/common/chat-agents.ts index d98595e282c41..5a2451a5f2bdb 100644 --- a/packages/ai-chat/src/common/chat-agents.ts +++ b/packages/ai-chat/src/common/chat-agents.ts @@ -279,13 +279,22 @@ export abstract class AbstractChatAgent { tools: ToolRequest[] | undefined, token: CancellationToken ): Promise { + const settings = this.getLlmSettings(); const languageModelResponse = languageModel.request({ messages, tools, + settings, }, token); return languageModelResponse; } + /** + * @returns the settings, such as `temperature`, to be used in all language model requests. Returns `undefined` by default. + */ + protected getLlmSettings(): { [key: string]: unknown; } | undefined { + return undefined; + } + protected abstract addContentsToResponse(languageModelResponse: LanguageModelResponse, request: ChatRequestModelImpl): Promise; } From c7fb4f525a3e4ededf600fef1ca71cd9fdaaeca0 Mon Sep 17 00:00:00 2001 From: Philip Langer Date: Wed, 9 Oct 2024 22:04:36 +0200 Subject: [PATCH 428/441] feat(ai): Improve styling of the chat widget * General improvements of margins and paddings * Give code a nicer border and more space * Replace aggressive buttons for code parts with more subtle icons * Format tool call result more nicely Contributed on behalf of STMicroelectronics --- .../code-part-renderer.tsx | 11 ++-- .../toolcall-part-renderer.tsx | 13 ++++- .../ai-chat-ui/src/browser/style/index.css | 53 ++++++++++++++----- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx index 55647ead854d3..8802158eb236c 100644 --- a/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/code-part-renderer.tsx @@ -128,7 +128,7 @@ const CopyToClipboardButton = (props: { code: string, clipboardService: Clipboar const copyCodeToClipboard = React.useCallback(() => { clipboardService.writeText(code); }, [code, clipboardService]); - return ; + return
        ; }; const InsertCodeAtCursorButton = (props: { code: string, editorManager: EditorManager }) => { @@ -150,7 +150,7 @@ const InsertCodeAtCursorButton = (props: { code: string, editorManager: EditorMa }]); } }, [code, editorManager]); - return ; + return
        ; }; /** @@ -174,9 +174,9 @@ export const CodeWrapper = (props: { autoSizing: true, scrollBeyondLastLine: false, scrollBeyondLastColumn: 0, - renderFinalNewline: 'on', + renderFinalNewline: 'off', maxHeight: -1, - scrollbar: { vertical: 'hidden', horizontal: 'hidden' }, + scrollbar: { vertical: 'hidden' }, codeLens: false, inlayHints: { enabled: 'off' }, hover: { enabled: false } @@ -203,6 +203,5 @@ export const CodeWrapper = (props: { editorRef.current?.resizeToFit(); - return
        ; + return
        ; }; - diff --git a/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx index e568f50386bf4..4f5bc127f285b 100644 --- a/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx +++ b/packages/ai-chat-ui/src/browser/chat-response-renderer/toolcall-part-renderer.tsx @@ -34,7 +34,7 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer Ran {response.name} -

        {response.result}

        +
        {this.tryPrettyPrintJson(response)}
        : Running [{response.name}] } @@ -42,6 +42,17 @@ export class ToolCallPartRenderer implements ChatResponsePartRenderer ( diff --git a/packages/ai-chat-ui/src/browser/style/index.css b/packages/ai-chat-ui/src/browser/style/index.css index 13bee8bba2bc2..904893f87cf24 100644 --- a/packages/ai-chat-ui/src/browser/style/index.css +++ b/packages/ai-chat-ui/src/browser/style/index.css @@ -16,8 +16,7 @@ cursor: default; display: flex; flex-direction: column; - gap: 8px; - padding: 16px 20px; + padding: 16px 20px 6px 20px; user-select: text; -webkit-user-select: text; border-bottom: 1px solid var(--theia-sideBarSectionHeader-border); @@ -110,16 +109,26 @@ div:last-child > .theia-ChatNode { background-color: var(--theia-toolbar-hoverBackground); } -.theia-ChatNode .rendered-markdown p { - margin: 0 0 16px; +.theia-ChatNode { + line-height: 1.3rem; +} + +.theia-ChatNode ul, +.theia-ChatNode ol { + padding-inline-start: 1rem; } -.theia-ChatNode:last-child .rendered-markdown > :last-child { +.theia-ChatNode li > p { + margin-top: 0; margin-bottom: 0; } -.theia-ChatNode .rendered-markdown { - line-height: 1.3rem; +.theia-ChatNode .theia-CodeWrapper { + padding: 0.5em; + background-color: var(--theia-editor-background); + border-radius: 6px; + border: var(--theia-border-width) solid var(--theia-checkbox-border); + font-size: var(--theia-code-font-size); } .chat-input-widget { @@ -219,7 +228,7 @@ div:last-child > .theia-ChatNode { .theia-CodePartRenderer-root { display: flex; flex-direction: column; - gap: 4px; + gap: 2px; border: 1px solid var(--theia-input-border); border-radius: 4px; } @@ -232,11 +241,20 @@ div:last-child > .theia-ChatNode { display: flex; justify-content: space-between; align-items: center; - padding-bottom: 4px; + padding-left: 2px; + padding-right: 2px; } -.theia-CodePartRenderer-right button { - margin-left: 4px; +.theia-CodePartRenderer-right .button { + margin-left: 2px; + width: 18px; + height: 18px; + padding: 2px; + border-radius: 5px; + cursor: pointer; +} +.theia-CodePartRenderer-right .button:hover { + background-color: var(--theia-toolbar-hoverBackground); } .theia-CodePartRenderer-separator { @@ -249,7 +267,8 @@ div:last-child > .theia-ChatNode { font-weight: normal; color: var(--theia-descriptionForeground); line-height: 20px; - margin-bottom: 6px; + margin-top: 13px; + margin-bottom: 13px; cursor: pointer; } @@ -258,6 +277,16 @@ div:last-child > .theia-ChatNode { color: var(--theia-button-background); } +.theia-toolCall details pre { + cursor: text; + line-height: 1rem; + margin-top: 0; + margin-bottom: 0; + padding: 6px; + background-color: var(--theia-editor-background); + overflow: auto; +} + .theia-ResponseNode-ProgressMessage { font-weight: normal; color: var(--theia-descriptionForeground); From 4612d3daad9c98c4165d483fb8c835851a691b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 16 Oct 2024 09:08:57 +0200 Subject: [PATCH 429/441] Add support for proposed signature for workspace.createFileSystemWatcher (#14303) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #13957 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../file-system-event-service-ext-impl.ts | 19 +-- .../src/plugin/file-system-watcher.spec.ts | 125 ++++++++++++++++++ .../plugin-ext/src/plugin/plugin-context.ts | 20 ++- packages/plugin/src/theia.d.ts | 1 + .../theia.proposed.createFileSystemWatcher.ts | 65 +++++++++ 5 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 packages/plugin-ext/src/plugin/file-system-watcher.spec.ts create mode 100644 packages/plugin/src/theia.proposed.createFileSystemWatcher.ts diff --git a/packages/plugin-ext/src/plugin/file-system-event-service-ext-impl.ts b/packages/plugin-ext/src/plugin/file-system-event-service-ext-impl.ts index 8a9398ce6c99e..8e02f2cb97c5d 100644 --- a/packages/plugin-ext/src/plugin/file-system-event-service-ext-impl.ts +++ b/packages/plugin-ext/src/plugin/file-system-event-service-ext-impl.ts @@ -48,7 +48,7 @@ type Event = vscode.Event; type IExtensionDescription = Plugin; type IWaitUntil = WaitUntilEvent; -class FileSystemWatcher implements vscode.FileSystemWatcher { +export class FileSystemWatcher implements vscode.FileSystemWatcher { private readonly _onDidCreate = new Emitter(); private readonly _onDidChange = new Emitter(); @@ -68,7 +68,8 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { return Boolean(this._config & 0b100); } - constructor(dispatcher: Event, globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) { + constructor(dispatcher: Event, globPattern: string | IRelativePattern, + ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean, excludes?: string[]) { this._config = 0; if (ignoreCreateEvents) { @@ -82,12 +83,13 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { } const parsedPattern = parse(globPattern); + const excludePatterns = excludes?.map(exclude => parse(exclude)) || []; const subscription = dispatcher(events => { if (!ignoreCreateEvents) { for (const created of events.created) { const uri = URI.revive(created); - if (parsedPattern(uri.fsPath)) { + if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) { this._onDidCreate.fire(uri); } } @@ -95,7 +97,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { if (!ignoreChangeEvents) { for (const changed of events.changed) { const uri = URI.revive(changed); - if (parsedPattern(uri.fsPath)) { + if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) { this._onDidChange.fire(uri); } } @@ -103,7 +105,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { if (!ignoreDeleteEvents) { for (const deleted of events.deleted) { const uri = URI.revive(deleted); - if (parsedPattern(uri.fsPath)) { + if (parsedPattern(uri.fsPath) && !excludePatterns.some(p => p(uri.fsPath))) { this._onDidDelete.fire(uri); } } @@ -113,7 +115,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { this._disposable = Disposable.from(this._onDidCreate, this._onDidChange, this._onDidDelete, subscription); } - dispose() { + dispose(): void { this._disposable.dispose(); } @@ -160,8 +162,9 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ // --- file events - createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher { - return new FileSystemWatcher(this._onFileSystemEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents); + createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, + ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean, excludes?: string[]): vscode.FileSystemWatcher { + return new FileSystemWatcher(this._onFileSystemEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents, excludes); } $onFileEvent(events: FileSystemEvents) { diff --git a/packages/plugin-ext/src/plugin/file-system-watcher.spec.ts b/packages/plugin-ext/src/plugin/file-system-watcher.spec.ts new file mode 100644 index 0000000000000..346abbe122b64 --- /dev/null +++ b/packages/plugin-ext/src/plugin/file-system-watcher.spec.ts @@ -0,0 +1,125 @@ +// ***************************************************************************** +// Copyright (C) 2019 Red Hat, Inc. and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import * as assert from 'assert'; +import { FileSystemWatcher } from './file-system-event-service-ext-impl'; +import { DisposableCollection, Emitter } from '@theia/core'; +import { FileSystemEvents } from '../common'; +import { URI } from './types-impl'; + +const eventSource = new Emitter(); +let disposables = new DisposableCollection(); + +function checkIgnore(ignoreCreate: number, ignoreChange: number, ignoreDelete: number): void { + const watcher = new FileSystemWatcher(eventSource.event, '**/*.js', !ignoreCreate, !ignoreChange, !ignoreDelete); + disposables.push(watcher); + const matching = URI.file('/foo/bar/zoz.js'); + + const changed: URI[] = []; + const created: URI[] = []; + const deleted: URI[] = []; + watcher.onDidChange(e => { + changed.push(e); + }); + + watcher.onDidCreate(e => { + created.push(e); + }); + + watcher.onDidDelete(e => { + deleted.push(e); + }); + + eventSource.fire({ changed: [matching], created: [matching], deleted: [matching] }); + + assert.equal(created.length, ignoreCreate); + assert.equal(deleted.length, ignoreDelete); + assert.equal(changed.length, ignoreChange); + +} + +describe('File Watcher Test', () => { + afterEach(() => { + disposables.dispose(); + disposables = new DisposableCollection(); + }); + + it('Should match files', () => { + const watcher = new FileSystemWatcher(eventSource.event, '**/*.js'); + disposables.push(watcher); + const matching = URI.file('/foo/bar/zoz.js'); + const notMatching = URI.file('/foo/bar/zoz.ts'); + const changed: URI[] = []; + const created: URI[] = []; + const deleted: URI[] = []; + watcher.onDidChange(e => { + changed.push(e); + }); + + watcher.onDidCreate(e => { + created.push(e); + }); + + watcher.onDidDelete(e => { + deleted.push(e); + }); + + const URIs = [matching, notMatching]; + eventSource.fire({ changed: URIs, created: URIs, deleted: URIs }); + assert.equal(matching.toString(), changed[0]?.toString()); + assert.equal(matching.toString(), created[0]?.toString()); + assert.equal(matching.toString(), deleted[0]?.toString()); + }); + + it('Should ignore created', () => { + checkIgnore(0, 1, 1); + }); + + it('Should ignore changed', () => { + checkIgnore(1, 0, 1); + }); + + it('Should ignore deleted', () => { + checkIgnore(1, 1, 0); + }); + + it('Should exclude files', () => { + const watcher = new FileSystemWatcher(eventSource.event, '**/*.js', false, false, false, ['**/bar/**']); + disposables.push(watcher); + const notMatching = URI.file('/foo/bar/zoz.js'); + const matching = URI.file('/foo/gux/zoz.js'); + const changed: URI[] = []; + const created: URI[] = []; + const deleted: URI[] = []; + watcher.onDidChange(e => { + changed.push(e); + }); + + watcher.onDidCreate(e => { + created.push(e); + }); + + watcher.onDidDelete(e => { + deleted.push(e); + }); + + const URIs = [matching, notMatching]; + eventSource.fire({ changed: URIs, created: URIs, deleted: URIs }); + assert.equal(matching.toString(), changed[0]?.toString()); + assert.equal(matching.toString(), created[0]?.toString()); + assert.equal(matching.toString(), deleted[0]?.toString()); + }); +}); diff --git a/packages/plugin-ext/src/plugin/plugin-context.ts b/packages/plugin-ext/src/plugin/plugin-context.ts index 61688deddeecf..e027b45d6b84c 100644 --- a/packages/plugin-ext/src/plugin/plugin-context.ts +++ b/packages/plugin-ext/src/plugin/plugin-context.ts @@ -278,6 +278,7 @@ import { NotebookDocumentsExtImpl } from './notebook/notebook-documents'; import { NotebookEditorsExtImpl } from './notebook/notebook-editors'; import { TestingExtImpl } from './tests'; import { UriExtImpl } from './uri-ext'; +import { isObject } from '@theia/core'; export function createAPIObject(rawObject: T): T { return new Proxy(rawObject, { @@ -669,6 +670,22 @@ export function createAPIFactory( onDidStartTerminalShellExecution: Event.None }; + function createFileSystemWatcher(pattern: RelativePattern, options?: theia.FileSystemWatcherOptions): theia.FileSystemWatcher; + function createFileSystemWatcher(pattern: theia.GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: + boolean, ignoreDeleteEvents?: boolean): theia.FileSystemWatcher; + function createFileSystemWatcher(pattern: RelativePattern | theia.GlobPattern, + ignoreCreateOrOptions?: theia.FileSystemWatcherOptions | boolean, ignoreChangeEventsBoolean?: boolean, ignoreDeleteEventsBoolean?: boolean): theia.FileSystemWatcher { + if (isObject(ignoreCreateOrOptions)) { + const { ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents, excludes } = (ignoreCreateOrOptions as theia.FileSystemWatcherOptions); + return createAPIObject( + extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), + ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents, excludes)); + } else { + return createAPIObject( + extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), + ignoreCreateOrOptions as boolean, ignoreChangeEventsBoolean, ignoreDeleteEventsBoolean)); + } + } const workspace: typeof theia.workspace = { get fs(): theia.FileSystem { @@ -774,8 +791,7 @@ export function createAPIFactory( // Notebook extension will create a document in openNotebookDocument() or create openNotebookDocument() return notebooksExt.getNotebookDocument(uri).apiNotebook; }, - createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): theia.FileSystemWatcher => - createAPIObject(extHostFileSystemEvent.createFileSystemWatcher(fromGlobPattern(pattern), ignoreCreate, ignoreChange, ignoreDelete)), + createFileSystemWatcher, findFiles(include: theia.GlobPattern, exclude?: theia.GlobPattern | null, maxResults?: number, token?: CancellationToken): PromiseLike { return workspaceExt.findFiles(include, exclude, maxResults, token); }, diff --git a/packages/plugin/src/theia.d.ts b/packages/plugin/src/theia.d.ts index fe8d5f6200d28..c98c3ccef5691 100644 --- a/packages/plugin/src/theia.d.ts +++ b/packages/plugin/src/theia.d.ts @@ -23,6 +23,7 @@ import './theia-extra'; import './theia.proposed.canonicalUriProvider'; +import './theia.proposed.createFileSystemWatcher'; import './theia.proposed.customEditorMove'; import './theia.proposed.debugVisualization'; import './theia.proposed.diffCommand'; diff --git a/packages/plugin/src/theia.proposed.createFileSystemWatcher.ts b/packages/plugin/src/theia.proposed.createFileSystemWatcher.ts new file mode 100644 index 0000000000000..17d4d641266b7 --- /dev/null +++ b/packages/plugin/src/theia.proposed.createFileSystemWatcher.ts @@ -0,0 +1,65 @@ +// ***************************************************************************** +// Copyright (C) 2024 STMicroelectronics and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// code copied and modified from https://github.com/microsoft/vscode/blob/release/1.93/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts + +declare module '@theia/plugin' { + + export interface FileSystemWatcherOptions { + + /** + * Ignore when files have been created. + */ + readonly ignoreCreateEvents?: boolean; + + /** + * Ignore when files have been changed. + */ + readonly ignoreChangeEvents?: boolean; + + /** + * Ignore when files have been deleted. + */ + readonly ignoreDeleteEvents?: boolean; + + /** + * An optional set of glob patterns to exclude from watching. + * Glob patterns are always matched relative to the watched folder. + */ + readonly excludes: string[]; + } + + export namespace workspace { + + /** + * A variant of {@link workspace.createFileSystemWatcher} that optionally allows to specify + * a set of glob patterns to exclude from watching. + * + * It provides the following advantages over the other {@link workspace.createFileSystemWatcher} + * method: + * - the configured excludes from `files.watcherExclude` setting are NOT applied + * - requests for recursive file watchers inside the opened workspace are NOT ignored + * - the watcher is ONLY notified for events from this request and not from any other watcher + * + * As such, this method is prefered in cases where you want full control over the watcher behavior + * without being impacted by settings or other watchers that are installed. + */ + export function createFileSystemWatcher(pattern: RelativePattern, options?: FileSystemWatcherOptions): FileSystemWatcher; + } +} From 5447948460f42fa6243fe53db40a5ca5e134a9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 16 Oct 2024 09:17:07 +0200 Subject: [PATCH 430/441] Upgrade puppeteer to 23.1.0. (#14261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14141 Newer versions have an issue with headless mode. Also fixes test flakiness with the new version. Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- dev-packages/cli/package.json | 4 +- dev-packages/cli/src/run-test.ts | 1 + dev-packages/cli/src/theia.ts | 10 +- .../api-tests/src/explorer-open-close.spec.js | 18 - examples/api-tests/src/find-replace.spec.js | 19 - examples/api-tests/src/scm.spec.js | 12 +- examples/api-tests/src/typescript.spec.js | 40 +- package.json | 4 +- .../core/src/browser/widgets/react-widget.tsx | 4 +- yarn.lock | 414 ++++++++++++++---- 10 files changed, 394 insertions(+), 132 deletions(-) diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 5dde36d20d869..27b9cce50b361 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -51,8 +51,8 @@ "log-update": "^4.0.0", "mocha": "^10.1.0", "patch-package": "^8.0.0", - "puppeteer": "19.7.2", - "puppeteer-core": "19.7.2", + "puppeteer": "23.1.0", + "puppeteer-core": "23.1.0", "puppeteer-to-istanbul": "1.4.0", "temp": "^0.9.1", "tslib": "^2.6.2", diff --git a/dev-packages/cli/src/run-test.ts b/dev-packages/cli/src/run-test.ts index 2895d63481fae..57a6e3aecc6b9 100644 --- a/dev-packages/cli/src/run-test.ts +++ b/dev-packages/cli/src/run-test.ts @@ -84,4 +84,5 @@ export default async function runTest(options: TestOptions): Promise { ? `http://[${address}]:${port}` : `http://${address}:${port}`; await testPage.goto(url); + await testPage.bringToFront(); } diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index 0a57af1895cbf..6adca0f1412f2 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -588,6 +588,10 @@ async function theiaCli(): Promise { if (!process.env.THEIA_CONFIG_DIR) { process.env.THEIA_CONFIG_DIR = temp.track().mkdirSync('theia-test-config-dir'); } + const args = ['--no-sandbox']; + if (!testInspect) { + args.push('--headless=old'); + } await runTest({ start: () => new Promise((resolve, reject) => { const serverProcess = manager.start(toStringArray(theiaArgs)); @@ -596,11 +600,13 @@ async function theiaCli(): Promise { serverProcess.on('close', (code, signal) => reject(`Server process exited unexpectedly: ${code ?? signal}`)); }), launch: { - args: ['--no-sandbox'], + args: args, // eslint-disable-next-line no-null/no-null defaultViewport: null, // view port can take available space instead of 800x600 default devtools: testInspect, - executablePath: executablePath() + headless: testInspect ? false : 'shell', + executablePath: executablePath(), + protocolTimeout: 600000 }, files: { extension: testExtension, diff --git a/examples/api-tests/src/explorer-open-close.spec.js b/examples/api-tests/src/explorer-open-close.spec.js index d7c9d1f77bf6c..b7195e77f49f9 100644 --- a/examples/api-tests/src/explorer-open-close.spec.js +++ b/examples/api-tests/src/explorer-open-close.spec.js @@ -123,7 +123,6 @@ describe('Explorer and Editor - open and close', function () { async function openEditor() { await editorManager.open(fileUri, { mode: 'activate' }); - await waitLanguageServerReady(); const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor); assert.isDefined(activeEditor); assert.equal(activeEditor.uri.resolveToAbsolute().toString(), fileUri.resolveToAbsolute().toString()); @@ -135,21 +134,4 @@ describe('Explorer and Editor - open and close', function () { assert.isUndefined(activeEditor); } - async function waitLanguageServerReady() { - // quite a bit of jitter in the "Initializing LS" status bar entry, - // so we want to read a few times in a row that it's done (undefined) - const MAX_N = 5 - let n = MAX_N; - while (n > 0) { - await pause(1); - if (progressStatusBarItem.currentProgress) { - n = MAX_N; - } else { - n--; - } - if (n < MAX_N) { - console.debug('n = ' + n); - } - } - } }); diff --git a/examples/api-tests/src/find-replace.spec.js b/examples/api-tests/src/find-replace.spec.js index 943f68cc7efbb..af37bfb56e0a6 100644 --- a/examples/api-tests/src/find-replace.spec.js +++ b/examples/api-tests/src/find-replace.spec.js @@ -143,28 +143,9 @@ describe('Find and Replace', function () { async function openEditor() { await editorManager.open(fileUri, { mode: 'activate' }); - await waitLanguageServerReady(); const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor); assert.isDefined(activeEditor); // @ts-ignore assert.equal(activeEditor.uri.resolveToAbsolute().toString(), fileUri.resolveToAbsolute().toString()); } - - async function waitLanguageServerReady() { - // quite a bit of jitter in the "Initializing LS" status bar entry, - // so we want to read a few times in a row that it's done (undefined) - const MAX_N = 5 - let n = MAX_N; - while (n > 0) { - await pause(1); - if (progressStatusBarItem.currentProgress) { - n = MAX_N; - } else { - n--; - } - if (n < 5) { - console.debug('n = ' + n); - } - } - } }); diff --git a/examples/api-tests/src/scm.spec.js b/examples/api-tests/src/scm.spec.js index 7ff2979f98113..d1289689aa2b2 100644 --- a/examples/api-tests/src/scm.spec.js +++ b/examples/api-tests/src/scm.spec.js @@ -14,9 +14,6 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** - - - // @ts-check describe('SCM', function () { @@ -31,6 +28,8 @@ describe('SCM', function () { const { ScmService } = require('@theia/scm/lib/browser/scm-service'); const { ScmWidget } = require('@theia/scm/lib/browser/scm-widget'); const { CommandRegistry } = require('@theia/core/lib/common'); + const { PreferenceService } = require('@theia/core/lib/browser'); + /** @type {import('inversify').Container} */ const container = window['theia'].container; @@ -40,6 +39,7 @@ describe('SCM', function () { const service = container.get(ScmService); const commandRegistry = container.get(CommandRegistry); const pluginService = container.get(HostedPluginSupport); + const preferences = container.get(PreferenceService); /** @type {ScmWidget} */ let scmWidget; @@ -81,6 +81,12 @@ describe('SCM', function () { return success; } + + before(async () => { + preferences.set('git.autoRepositoryDetection', true); + preferences.set('git.openRepositoryInParentFolders', 'always'); + }); + beforeEach(async () => { if (!pluginService.getPlugin(gitPluginId)) { throw new Error(gitPluginId + ' should be started'); diff --git a/examples/api-tests/src/typescript.spec.js b/examples/api-tests/src/typescript.spec.js index ac22ca97abb03..5ee12992dd52a 100644 --- a/examples/api-tests/src/typescript.spec.js +++ b/examples/api-tests/src/typescript.spec.js @@ -16,7 +16,7 @@ // @ts-check describe('TypeScript', function () { - this.timeout(30_000); + this.timeout(200_000); const { assert } = chai; const { timeout } = require('@theia/core/lib/common/promise-util'); @@ -94,6 +94,24 @@ describe('TypeScript', function () { await preferences.set('files.autoSave', originalAutoSaveValue); }) + async function waitLanguageServerReady() { + // quite a bit of jitter in the "Initializing LS" status bar entry, + // so we want to read a few times in a row that it's done (undefined) + const MAX_N = 5 + let n = MAX_N; + while (n > 0) { + await timeout(1000); + if (progressStatusBarItem.currentProgress) { + n = MAX_N; + } else { + n--; + } + if (n < 5) { + console.debug('n = ' + n); + } + } + } + /** * @param {Uri.default} uri * @param {boolean} preview @@ -106,8 +124,8 @@ describe('TypeScript', function () { // wait till tsserver is running, see: // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103 await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile')); - // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911 - await waitForAnimation(() => !progressStatusBarItem.currentProgress); + + waitLanguageServerReady(); return /** @type {MonacoEditor} */ (editor); } @@ -388,6 +406,14 @@ describe('TypeScript', function () { assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isTrue(contextKeyService.match('suggestWidgetVisible')); + + const suggestController = editor.getControl().getContribution('editor.contrib.suggestController'); + + waitForAnimation(() => { + const content = nodeAsString(suggestController ? ['_widget']?.['_value']?.['element']?.['domNode']); + return !content.includes('loading'); + }); + // May need a couple extra "Enter" being sent for the suggest to be accepted keybindings.dispatchKeyDown('Enter'); await waitForAnimation(() => { @@ -398,7 +424,7 @@ describe('TypeScript', function () { return false; } return true; - }, 5000, 'Suggest widget has not been dismissed despite attempts to accept suggestion'); + }, 20000, 'Suggest widget has not been dismissed despite attempts to accept suggestion'); assert.isTrue(contextKeyService.match('editorTextFocus')); assert.isFalse(contextKeyService.match('suggestWidgetVisible')); @@ -542,6 +568,7 @@ describe('TypeScript', function () { }); it('editor.action.showHover', async function () { + const editor = await openEditor(demoFileUri); // class |DemoClass); editor.getControl().setPosition({ lineNumber: 8, column: 7 }); @@ -558,6 +585,11 @@ describe('TypeScript', function () { assert.isTrue(contextKeyService.match('editorHoverVisible')); assert.isTrue(contextKeyService.match('editorTextFocus')); + waitForAnimation(() => { + const content = nodeAsString(hover['_contentWidget']?.['_widget']?.['_hover']?.['contentsDomNode']); + return !content.includes('loading'); + }); + assert.deepEqual(nodeAsString(hover['_contentWidget']?.['_widget']?.['_hover']?.['contentsDomNode']).trim(), ` DIV { DIV { diff --git a/package.json b/package.json index 270c3725f3ce5..04f0c7ae15d84 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,8 @@ "mkdirp": "^0.5.0", "node-gyp": "^9.0.0", "nyc": "^15.0.0", - "puppeteer": "19.7.2", - "puppeteer-core": "19.7.2", + "puppeteer": "23.1.0", + "puppeteer-core": "23.1.0", "puppeteer-to-istanbul": "1.4.0", "rimraf": "^5.0.0", "sinon": "^12.0.0", diff --git a/packages/core/src/browser/widgets/react-widget.tsx b/packages/core/src/browser/widgets/react-widget.tsx index b1b300be35588..38fe93ce26f6b 100644 --- a/packages/core/src/browser/widgets/react-widget.tsx +++ b/packages/core/src/browser/widgets/react-widget.tsx @@ -38,7 +38,9 @@ export abstract class ReactWidget extends BaseWidget { protected override onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); - this.nodeRoot.render({this.render()}); + if (!this.isDisposed) { + this.nodeRoot.render({this.render()}); + } } /** diff --git a/yarn.lock b/yarn.lock index 049028db042e9..3a4dbbdb9d4f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1719,6 +1719,20 @@ dependencies: playwright "1.41.2" +"@puppeteer/browsers@2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.3.1.tgz#238200dbdce5c00ae28c8f2a55ac053c3be71668" + integrity sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA== + dependencies: + debug "^4.3.6" + extract-zip "^2.0.1" + progress "^2.0.3" + proxy-agent "^6.4.0" + semver "^7.6.3" + tar-fs "^3.0.6" + unbzip2-stream "^1.4.3" + yargs "^17.7.2" + "@sigstore/bundle@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" @@ -1844,6 +1858,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@tufjs/canonical-json@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz#eade9fd1f537993bc1f0949f3aea276ecc4fab31" @@ -2908,6 +2927,13 @@ agent-base@^7.0.2: dependencies: debug "^4.3.4" +agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -3301,6 +3327,13 @@ ast-types@0.9.6: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" integrity sha512-qEdtR2UH78yyHX/AUNfXmJTlM48XoFZKBdwi1nzkI1mJL21cmbu0cvjxjpkXJ5NENMq42H+hNs8VLJcqXLerBQ== +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + ast-types@^0.9.2: version "0.9.14" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.14.tgz#d34ba5dffb9d15a44351fd2a9d82e4ab2838b5ba" @@ -3388,6 +3421,11 @@ azure-devops-node-api@^11.0.1: tunnel "0.0.6" typed-rest-client "^1.8.4" +b4a@^1.6.4, b4a@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + babel-loader@^8.2.2: version "8.3.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" @@ -3444,6 +3482,40 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.0.tgz#305b511e262ffd8b9d5616b056464f8e1b3329cc" + integrity sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A== + +bare-fs@^2.1.1: + version "2.3.5" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.5.tgz#05daa8e8206aeb46d13c2fe25a2cd3797b0d284a" + integrity sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^2.0.0" + +bare-os@^2.1.0: + version "2.4.4" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.4.4.tgz#01243392eb0a6e947177bb7c8a45123d45c9b1a9" + integrity sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.3.tgz#594104c829ef660e43b5589ec8daef7df6cedb3e" + integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA== + dependencies: + bare-os "^2.1.0" + +bare-stream@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.3.0.tgz#5bef1cab8222517315fca1385bd7f08dff57f435" + integrity sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA== + dependencies: + b4a "^1.6.6" + streamx "^2.20.0" + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3461,6 +3533,11 @@ basic-auth@^2.0.1: dependencies: safe-buffer "5.1.2" +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -3998,12 +4075,14 @@ chrome-trace-event@^1.0.2: resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== -chromium-bidi@0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.4.tgz#44f25d4fa5d2f3debc3fc3948d0657194cac4407" - integrity sha512-4BX5cSaponuvVT1+SbLYTOAgDoVtX/Khoc9UsbFJ/AsPVUeFAM3RiIDFI6XFhLYMi9WmVJqh1ZH+dRpNKkKwiQ== +chromium-bidi@0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.6.4.tgz#627d76bae2819d59b61a413babe9664e0a16b71d" + integrity sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ== dependencies: - mitt "3.0.0" + mitt "3.0.1" + urlpattern-polyfill "10.0.0" + zod "3.23.8" ci-info@^3.2.0, ci-info@^3.6.1, ci-info@^3.7.0: version "3.9.0" @@ -4426,16 +4505,6 @@ corser@^2.0.1: resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ== -cosmiconfig@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" - integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== - dependencies: - import-fresh "^3.2.1" - js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - cosmiconfig@^8.2.0: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" @@ -4446,6 +4515,16 @@ cosmiconfig@^8.2.0: parse-json "^5.2.0" path-type "^4.0.0" +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + cpu-features@~0.0.9: version "0.0.9" resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.9.tgz#5226b92f0f1c63122b0a3eb84cb8335a4de499fc" @@ -4474,13 +4553,6 @@ cross-env@^7.0.3: dependencies: cross-spawn "^7.0.1" -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - cross-spawn@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" @@ -4550,6 +4622,11 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== + data-urls@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" @@ -4597,6 +4674,13 @@ debug@^3.0.1, debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.6: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -4761,6 +4845,15 @@ define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4811,10 +4904,10 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -devtools-protocol@0.0.1094867: - version "0.0.1094867" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz#2ab93908e9376bd85d4e0604aa2651258f13e374" - integrity sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ== +devtools-protocol@0.0.1312386: + version "0.0.1312386" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz#5ab824d6f1669ec6c6eb0fba047e73601d969052" + integrity sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA== diff-sequences@^29.6.3: version "29.6.3" @@ -5344,6 +5437,17 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-import-resolver-node@^0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" @@ -5509,7 +5613,7 @@ espree@^7.3.0, espree@^7.3.1: acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" -esprima@^4.0.0: +esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5704,7 +5808,7 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -extract-zip@2.0.1, extract-zip@^2.0.1: +extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -5720,6 +5824,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.2.5, fast-glob@^3.2.9: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" @@ -6039,7 +6148,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.1.0, fs-extra@^11.1.1: +fs-extra@^11.1.0, fs-extra@^11.1.1, fs-extra@^11.2.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== @@ -6253,6 +6362,16 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" + git-raw-commits@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-3.0.0.tgz#5432f053a9744f67e8db03dbc48add81252cfdeb" @@ -6652,6 +6771,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -6693,7 +6820,7 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -6709,6 +6836,14 @@ https-proxy-agent@^7.0.2: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -8024,7 +8159,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: +lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== @@ -8452,10 +8587,10 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mitt@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" - integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" @@ -8557,7 +8692,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -8671,6 +8806,11 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + nise@^5.1.0: version "5.1.9" resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.9.tgz#0cb73b5e4499d738231a473cd89bd8afbb618139" @@ -9452,6 +9592,28 @@ p-waterfall@2.1.1: dependencies: p-reduce "^2.0.0" +pac-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58" + integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.5" + pac-resolver "^7.0.1" + socks-proxy-agent "^8.0.4" + +pac-resolver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + package-hash@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" @@ -9923,7 +10085,7 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" -progress@2.0.3, progress@^2.0.0, progress@^2.0.3: +progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -9982,7 +10144,21 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@1.1.0, proxy-from-env@^1.1.0: +proxy-agent@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" + integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.1" + https-proxy-agent "^7.0.3" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -10030,22 +10206,17 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@19.7.2: - version "19.7.2" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.7.2.tgz#deee9ef915829b6a1d1a3a008625c29eeb251161" - integrity sha512-PvI+fXqgP0uGJxkyZcX51bnzjFA73MODZOAv0fSD35yR7tvbqwtMV3/Y+hxQ0AMMwzxkEebP6c7po/muqxJvmQ== +puppeteer-core@23.1.0: + version "23.1.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-23.1.0.tgz#50703d2e27c1d73d523c25b807f6e6d95a6b1c47" + integrity sha512-SvAsu+xnLN2FMXE/59bp3s3WXp8ewqUGzVV4AQtml/2xmsciZnU/bXcCW+eETHPWQ6Agg2vTI7QzWXPpEARK2g== dependencies: - chromium-bidi "0.4.4" - cross-fetch "3.1.5" - debug "4.3.4" - devtools-protocol "0.0.1094867" - extract-zip "2.0.1" - https-proxy-agent "5.0.1" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.11.0" + "@puppeteer/browsers" "2.3.1" + chromium-bidi "0.6.4" + debug "^4.3.6" + devtools-protocol "0.0.1312386" + typed-query-selector "^2.12.0" + ws "^8.18.0" puppeteer-to-istanbul@1.4.0: version "1.4.0" @@ -10057,16 +10228,17 @@ puppeteer-to-istanbul@1.4.0: v8-to-istanbul "^1.2.1" yargs "^15.3.1" -puppeteer@19.7.2: - version "19.7.2" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.7.2.tgz#1b3ce99a093cc2f8f84dfb06f066d0757ea79d4b" - integrity sha512-4Lm7Qpe/LU95Svirei/jDLDvR5oMrl9BPGd7HMY5+Q28n+BhvKuW97gKkR+1LlI86bO8J3g8rG/Ll5kv9J1nlQ== +puppeteer@23.1.0: + version "23.1.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-23.1.0.tgz#3abe4980670f214c8edfe689012e83418f81f9aa" + integrity sha512-m+CyicDlGN1AVUeOsCa6/+KQydJzxfsPowL7fQy+VGNeaWafB0m8G5aGfXdfZztKMxzCsdz7KNNzbJPeG9wwFw== dependencies: - cosmiconfig "8.0.0" - https-proxy-agent "5.0.1" - progress "2.0.3" - proxy-from-env "1.1.0" - puppeteer-core "19.7.2" + "@puppeteer/browsers" "2.3.1" + chromium-bidi "0.6.4" + cosmiconfig "^9.0.0" + devtools-protocol "0.0.1312386" + puppeteer-core "23.1.0" + typed-query-selector "^2.12.0" qs@6.11.0: version "6.11.0" @@ -10092,6 +10264,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -10528,7 +10705,7 @@ rimraf@2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -10748,6 +10925,11 @@ semver@^7.6.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -11058,6 +11240,15 @@ socks-proxy-agent@^7.0.0: debug "^4.3.3" socks "^2.6.2" +socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" + socks@^2.3.3, socks@^2.6.2: version "2.8.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.1.tgz#22c7d9dd7882649043cba0eafb49ae144e3457af" @@ -11066,6 +11257,14 @@ socks@^2.3.3, socks@^2.6.2: ip-address "^9.0.5" smart-buffer "^4.2.0" +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -11100,7 +11299,7 @@ source-map-support@^0.5.19, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -11245,6 +11444,17 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +streamx@^2.15.0, streamx@^2.20.0: + version "2.20.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.1.tgz#471c4f8b860f7b696feb83d5b125caab2fdbb93c" + integrity sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + string-argv@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" @@ -11490,7 +11700,17 @@ tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar-fs@2.1.1, tar-fs@^2.0.0: +tar-fs@^1.16.2: + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + +tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -11500,15 +11720,16 @@ tar-fs@2.1.1, tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.1.4" -tar-fs@^1.16.2: - version "1.16.3" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" - integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== +tar-fs@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== dependencies: - chownr "^1.0.1" - mkdirp "^0.5.1" - pump "^1.0.0" - tar-stream "^1.1.2" + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" tar-fs@~2.0.1: version "2.0.1" @@ -11544,6 +11765,15 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar@6.1.11: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" @@ -11618,6 +11848,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-decoder@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.0.tgz#85f19d4d5088e0b45cd841bdfaeac458dbffeefc" + integrity sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg== + dependencies: + b4a "^1.6.4" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -11776,6 +12013,11 @@ tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.1: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -11944,6 +12186,11 @@ typed-array-length@^1.0.4: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typed-query-selector@^2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/typed-query-selector/-/typed-query-selector-2.12.0.tgz#92b65dbc0a42655fccf4aeb1a08b1dddce8af5f2" + integrity sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg== + typed-rest-client@^1.8.4: version "1.8.11" resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.8.11.tgz#6906f02e3c91e8d851579f255abf0fd60800a04d" @@ -12020,7 +12267,7 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@1.4.3, unbzip2-stream@^1.0.9: +unbzip2-stream@^1.0.9, unbzip2-stream@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== @@ -12174,6 +12421,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +urlpattern-polyfill@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz#f0a03a97bfb03cdf33553e5e79a2aadd22cac8ec" + integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg== + user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -12656,16 +12908,16 @@ write-pkg@4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@8.11.0, ws@~8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== - -ws@^8.13.0, ws@^8.17.1: +ws@^8.13.0, ws@^8.17.1, ws@^8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -12893,7 +13145,7 @@ zod-to-json-schema@^3.23.2: resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz#bc7e379c8050462538383e382964c03d8fe008f9" integrity sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw== -zod@^3.23.8: +zod@3.23.8, zod@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== From 2283719ae31ff6dda02fdcf037e913b0365ba6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 16 Oct 2024 09:44:46 +0200 Subject: [PATCH 431/441] Upgrade express to 4.21.0 (#14283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14258, #14259 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/core/README.md | 2 +- packages/core/package.json | 4 +- packages/remote/package.json | 4 +- yarn.lock | 169 +++++++++++++++++++++-------------- 4 files changed, 108 insertions(+), 71 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index af80e14c75ef5..711ad6bab1baa 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -100,7 +100,7 @@ export class SomeClass { - `vscode-uri` (from [`vscode-uri@^2.1.1`](https://www.npmjs.com/package/vscode-uri)) - `@parcel/watcher` (from [`@parcel/watcher@^2.4.1`](https://www.npmjs.com/package/@parcel/watcher)) - `dompurify` (from [`dompurify@^2.2.9`](https://www.npmjs.com/package/dompurify)) - - `express` (from [`express@^4.16.3`](https://www.npmjs.com/package/express)) + - `express` (from [`express@^4.21.0`](https://www.npmjs.com/package/express)) - `lodash.debounce` (from [`lodash.debounce@^4.0.8`](https://www.npmjs.com/package/lodash.debounce)) - `lodash.throttle` (from [`lodash.throttle@^4.1.1`](https://www.npmjs.com/package/lodash.throttle)) - `markdown-it` (from [`markdown-it@^12.3.2`](https://www.npmjs.com/package/markdown-it)) diff --git a/packages/core/package.json b/packages/core/package.json index a786863531a2d..33de3749014ac 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,7 +22,7 @@ "@types/body-parser": "^1.16.4", "@types/cookie": "^0.3.3", "@types/dompurify": "^2.2.2", - "@types/express": "^4.16.0", + "@types/express": "^4.17.21", "@types/fs-extra": "^4.0.2", "@types/lodash.debounce": "4.0.3", "@types/lodash.throttle": "^4.1.3", @@ -42,7 +42,7 @@ "dompurify": "^2.2.9", "drivelist": "^9.0.2", "es6-promise": "^4.2.4", - "express": "^4.16.3", + "express": "^4.21.0", "fast-json-stable-stringify": "^2.1.0", "file-icons-js": "~1.0.3", "font-awesome": "^4.7.0", diff --git a/packages/remote/package.json b/packages/remote/package.json index 71377a15a35d4..833bccffc3cd2 100644 --- a/packages/remote/package.json +++ b/packages/remote/package.json @@ -10,7 +10,7 @@ "decompress-tar": "^4.0.0", "decompress-targz": "^4.0.0", "decompress-unzip": "^4.0.1", - "express-http-proxy": "^1.6.3", + "express-http-proxy": "^2.1.1", "glob": "^8.1.0", "socket.io": "^4.5.3", "socket.io-client": "^4.5.3", @@ -55,7 +55,7 @@ "@theia/ext-scripts": "1.54.0", "@types/archiver": "^5.3.2", "@types/decompress": "^4.2.4", - "@types/express-http-proxy": "^1.6.3", + "@types/express-http-proxy": "^1.6.6", "@types/glob": "^8.1.0", "@types/ssh2": "^1.11.11", "@types/ssh2-sftp-client": "^9.0.0" diff --git a/yarn.lock b/yarn.lock index 3a4dbbdb9d4f4..fa8459e47d8b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2017,7 +2017,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/express-http-proxy@^1.6.3": +"@types/express-http-proxy@^1.6.6": version "1.6.6" resolved "https://registry.yarnpkg.com/@types/express-http-proxy/-/express-http-proxy-1.6.6.tgz#386c6f4c61a2d26ab8817ba1c2b2aac80e5638c9" integrity sha512-J8ZqHG76rq1UB716IZ3RCmUhg406pbWxsM3oFCFccl5xlWUPzoR4if6Og/cE4juK8emH0H9quZa5ltn6ZdmQJg== @@ -2034,7 +2034,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.16.0": +"@types/express@*", "@types/express@^4.17.21": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -3616,21 +3616,21 @@ bluebird@~3.4.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" - content-type "~1.0.4" + content-type "~1.0.5" debug "2.6.9" depd "2.0.0" destroy "1.2.0" http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" + qs "6.13.0" + raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -4445,10 +4445,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== cookie@^0.4.0, cookie@~0.4.1: version "0.4.2" @@ -5179,6 +5179,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -5753,46 +5758,46 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express-http-proxy@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/express-http-proxy/-/express-http-proxy-1.6.3.tgz#f3ef139ffd49a7962e7af0462bbcca557c913175" - integrity sha512-/l77JHcOUrDUX8V67E287VEUQT0lbm71gdGVoodnlWBziarYKgMcpqT7xvh/HM8Jv52phw8Bd8tY+a7QjOr7Yg== +express-http-proxy@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/express-http-proxy/-/express-http-proxy-2.1.1.tgz#90bd7eaee5166be968157b035eb6b499d2af2bf4" + integrity sha512-4aRQRqDQU7qNPV5av0/hLcyc0guB9UP71nCYrQEYml7YphTo8tmWf3nDZWdTJMMjFikyz9xKXaURor7Chygdwg== dependencies: debug "^3.0.1" es6-promise "^4.1.1" raw-body "^2.3.0" -express@^4.16.3: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== +express@^4.21.0: + version "4.21.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" + integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.1" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.2" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" @@ -5953,13 +5958,13 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" on-finished "2.4.1" parseurl "~1.3.3" @@ -8335,10 +8340,10 @@ meow@^8.1.2: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" @@ -9808,10 +9813,10 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== path-to-regexp@^6.2.1: version "6.2.1" @@ -10247,6 +10252,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + qs@^6.11.0, qs@^6.4.0, qs@^6.9.1: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" @@ -10291,16 +10303,6 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.2, raw-body@^2.3.0: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" @@ -10930,10 +10932,10 @@ semver@^7.6.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" depd "2.0.0" @@ -10977,15 +10979,15 @@ serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.19.0" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -11088,6 +11090,16 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -11460,7 +11472,7 @@ string-argv@^0.1.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.1.2.tgz#c5b7bc03fb2b11983ba3a72333dd0559e77e4738" integrity sha512-mBqPGEOMNJKXRo7z0keX0wlAhbBAjilUdPW13nN0PecVryZxdHIeM7TqbsSUA7VYuS00HGC6mojP7DlQzfa9ZA== -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -11478,6 +11490,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -11543,7 +11564,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -11564,6 +11585,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -12816,7 +12844,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12834,6 +12862,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From de441e2258b9f10fb491964b947e9f18bca5896c Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Wed, 16 Oct 2024 10:07:26 +0200 Subject: [PATCH 432/441] Add preference to exclude files from AI code completion (#14315) Adds the preference 'ai-features.codeCompletion.excludedFileExtensions' to filter code completion requests before they are delegated to the AICodeInlineCompletionsProvider. fixed #14313 Signed-off-by: Jonas Helming --- .../browser/ai-code-completion-preference.ts | 10 ++++ ...-code-frontend-application-contribution.ts | 49 ++++++++++++++----- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts index 3dd995bc96618..60cab4a55a59b 100644 --- a/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts +++ b/packages/ai-code-completion/src/browser/ai-code-completion-preference.ts @@ -18,6 +18,7 @@ import { PreferenceSchema } from '@theia/core/lib/browser/preferences/preference import { AI_CORE_PREFERENCES_TITLE } from '@theia/ai-core/lib/browser/ai-core-preferences'; export const PREF_AI_INLINE_COMPLETION_ENABLE = 'ai-features.codeCompletion.enableCodeCompletion'; +export const PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS = 'ai-features.codeCompletion.excludedFileExtensions'; export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'object', @@ -27,6 +28,15 @@ export const AICodeCompletionPreferencesSchema: PreferenceSchema = { type: 'boolean', description: 'Enable AI completions inline within any (Monaco) editor.', default: false + }, + [PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS]: { + title: 'Excluded File Extensions', + type: 'array', + description: 'Specify file extensions (e.g., .md, .txt) where AI completions should be disabled.', + items: { + type: 'string' + }, + default: [] } } }; diff --git a/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts index 3f430939a81e5..9d7c610979606 100644 --- a/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts +++ b/packages/ai-code-completion/src/browser/ai-code-frontend-application-contribution.ts @@ -21,7 +21,7 @@ import { inject, injectable } from '@theia/core/shared/inversify'; import { AIActivationService } from '@theia/ai-core/lib/browser'; import { Disposable } from '@theia/core'; import { AICodeInlineCompletionsProvider } from './ai-code-inline-completion-provider'; -import { PREF_AI_INLINE_COMPLETION_ENABLE } from './ai-code-completion-preference'; +import { PREF_AI_INLINE_COMPLETION_ENABLE, PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS } from './ai-code-completion-preference'; @injectable() export class AIFrontendApplicationContribution implements FrontendApplicationContribution { @@ -38,27 +38,52 @@ export class AIFrontendApplicationContribution implements FrontendApplicationCon onDidInitializeLayout(): void { this.preferenceService.ready.then(() => { - this.handlePreference(PREF_AI_INLINE_COMPLETION_ENABLE, enable => this.handleInlineCompletions(enable)); + this.handlePreferences(); }); } - protected handlePreference(name: string, handler: (enable: boolean) => Disposable): void { - const enable = this.preferenceService.get(name, false) && this.activationService.isActive; - this.toDispose.set(name, handler(enable)); + protected handlePreferences(): void { + const handler = () => this.handleInlineCompletions(); + + this.toDispose.set('inlineCompletions', handler()); this.preferenceService.onPreferenceChanged(event => { - if (event.preferenceName === name) { - this.toDispose.get(name)?.dispose(); - this.toDispose.set(name, handler(event.newValue && this.activationService.isActive)); + if (event.preferenceName === PREF_AI_INLINE_COMPLETION_ENABLE || event.preferenceName === PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS) { + this.toDispose.get('inlineCompletions')?.dispose(); + this.toDispose.set('inlineCompletions', handler()); } }); + this.activationService.onDidChangeActiveStatus(change => { - this.toDispose.get(name)?.dispose(); - this.toDispose.set(name, handler(this.preferenceService.get(name, false) && change)); + this.toDispose.get('inlineCompletions')?.dispose(); + this.toDispose.set('inlineCompletions', handler()); }); } - protected handleInlineCompletions(enable: boolean): Disposable { - return enable ? monaco.languages.registerInlineCompletionsProvider({ scheme: 'file' }, this.inlineCodeCompletionProvider) : Disposable.NULL; + protected handleInlineCompletions(): Disposable { + const enable = this.preferenceService.get(PREF_AI_INLINE_COMPLETION_ENABLE, false) && this.activationService.isActive; + + if (!enable) { + return Disposable.NULL; + } + + const excludedExtensions = this.preferenceService.get(PREF_AI_INLINE_COMPLETION_EXCLUDED_EXTENSIONS, []); + + return monaco.languages.registerInlineCompletionsProvider( + { scheme: 'file' }, + { + provideInlineCompletions: (model, position, context, token) => { + const fileName = model.uri.toString(); + + if (excludedExtensions.some(ext => fileName.endsWith(ext))) { + return { items: [] }; + } + return this.inlineCodeCompletionProvider.provideInlineCompletions(model, position, context, token); + }, + freeInlineCompletions: completions => { + this.inlineCodeCompletionProvider.freeInlineCompletions(completions); + } + } + ); } } From 8bff7225a4370a0e85c9cde5eb10f1540a98d198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Wed, 16 Oct 2024 11:40:26 +0200 Subject: [PATCH 433/441] Set menu bar less often at startup (#14295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14280 Contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- .../menu/electron-main-menu-factory.ts | 63 ++++++++++--------- .../menu/electron-menu-contribution.ts | 5 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts index 4f0b97a51a113..3925b56df82f3 100644 --- a/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts +++ b/packages/core/src/electron-browser/menu/electron-main-menu-factory.ts @@ -74,42 +74,45 @@ export type ElectronMenuItemRole = ('undo' | 'redo' | 'cut' | 'copy' | 'paste' | @injectable() export class ElectronMainMenuFactory extends BrowserMainMenuFactory { - protected _menu?: MenuDto[]; - protected _toggledCommands: Set = new Set(); + protected menu?: MenuDto[]; + protected toggledCommands: Set = new Set(); @inject(PreferenceService) protected preferencesService: PreferenceService; + setMenuBar = debounce(() => this.doSetMenuBar(), 100); + @postConstruct() postConstruct(): void { - this.preferencesService.onPreferenceChanged( - debounce(e => { - if (e.preferenceName === 'window.menuBarVisibility') { - this.setMenuBar(); - } - if (this._menu) { - for (const cmd of this._toggledCommands) { - const menuItem = this.findMenuById(this._menu, cmd); - if (menuItem) { - menuItem.checked = this.commandRegistry.isToggled(cmd); - } - } - window.electronTheiaCore.setMenu(this._menu); - } - }, 10) - ); this.keybindingRegistry.onKeybindingsChanged(() => { this.setMenuBar(); }); this.menuProvider.onDidChange(() => { this.setMenuBar(); }); + this.preferencesService.ready.then(() => { + this.preferencesService.onPreferenceChanged( + debounce(e => { + if (e.preferenceName === 'window.menuBarVisibility') { + this.doSetMenuBar(); + } + if (this.menu) { + for (const cmd of this.toggledCommands) { + const menuItem = this.findMenuById(this.menu, cmd); + if (menuItem && (!!menuItem.checked !== this.commandRegistry.isToggled(cmd))) { + menuItem.checked = !menuItem.checked; + } + } + window.electronTheiaCore.setMenu(this.menu); + } + }, 10) + ); + }); } - async setMenuBar(): Promise { - await this.preferencesService.ready; - const createdMenuBar = this.createElectronMenuBar(); - window.electronTheiaCore.setMenu(createdMenuBar); + doSetMenuBar(): void { + this.menu = this.createElectronMenuBar(); + window.electronTheiaCore.setMenu(this.menu); } createElectronMenuBar(): MenuDto[] | undefined { @@ -117,14 +120,12 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { const maxWidget = document.getElementsByClassName(MAXIMIZED_CLASS); if (preference === 'visible' || (preference === 'classic' && maxWidget.length === 0)) { const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR); - this._menu = this.fillMenuTemplate([], menuModel, [], { honorDisabled: false, rootMenuPath: MAIN_MENU_BAR }, false); + const menu = this.fillMenuTemplate([], menuModel, [], { honorDisabled: false, rootMenuPath: MAIN_MENU_BAR }, false); if (isOSX) { - this._menu.unshift(this.createOSXMenu()); + menu.unshift(this.createOSXMenu()); } - return this._menu; + return menu; } - this._menu = undefined; - // eslint-disable-next-line no-null/no-null return undefined; } @@ -208,7 +209,7 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { parentItems.push(menuItem); if (this.commandRegistry.getToggledHandler(commandId, ...args)) { - this._toggledCommands.add(commandId); + this.toggledCommands.add(commandId); } } return parentItems; @@ -273,11 +274,11 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory { // We need to check if we can execute it. if (this.menuCommandExecutor.isEnabled(menuPath, cmd, ...args)) { await this.menuCommandExecutor.executeCommand(menuPath, cmd, ...args); - if (this._menu && this.menuCommandExecutor.isVisible(menuPath, cmd, ...args)) { - const item = this.findMenuById(this._menu, cmd); + if (this.menu && this.menuCommandExecutor.isVisible(menuPath, cmd, ...args)) { + const item = this.findMenuById(this.menu, cmd); if (item) { item.checked = this.menuCommandExecutor.isToggled(menuPath, cmd, ...args); - window.electronTheiaCore.setMenu(this._menu); + window.electronTheiaCore.setMenu(this.menu); } } } diff --git a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts index 0b77749c37bf3..9023dc2612021 100644 --- a/packages/core/src/electron-browser/menu/electron-menu-contribution.ts +++ b/packages/core/src/electron-browser/menu/electron-menu-contribution.ts @@ -29,7 +29,6 @@ import { WindowService } from '../../browser/window/window-service'; import { WindowTitleService } from '../../browser/window/window-title-service'; import '../../../src/electron-browser/menu/electron-menu-style.css'; -import { MenuDto } from '../../electron-common/electron-api'; import { ThemeService } from '../../browser/theming'; import { ThemeChangeEvent } from '../../common/theme'; @@ -203,7 +202,7 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme } } - protected setMenu(app: FrontendApplication, electronMenu: MenuDto[] | undefined = this.factory.createElectronMenuBar()): void { + protected setMenu(app: FrontendApplication): void { if (!isOSX) { this.hideTopPanel(app); if (this.titleBarStyle === 'custom' && !this.menuBar) { @@ -211,7 +210,7 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme return; } } - window.electronTheiaCore.setMenu(electronMenu); + this.factory.setMenuBar(); } protected createCustomTitleBar(app: FrontendApplication): void { From 7e51b89a7b5ac790399ec8ca3df4f620f69a7b77 Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Wed, 16 Oct 2024 15:13:38 +0200 Subject: [PATCH 434/441] feat: add support for llamafile as ai model provider (#14281) Support llm execution via llamafiles. - The configured llamafiles are stored in the preferences - As a user you have to provide: - a custom modelname - the file uri to the llamafile - the port to run the llama.cpp server on - Llamafiles can be started and stopped via Theia commands - The llamafile output is send to a new output channel -llamafile The current implementation does not support tools/functions so that agents like the workspace-agent don't work well with models provided via llamafiles. fixes #14286 --- examples/browser/package.json | 1 + examples/browser/tsconfig.json | 3 + examples/electron/package.json | 1 + examples/electron/tsconfig.json | 3 + packages/ai-llamafile/.eslintrc.js | 10 ++ packages/ai-llamafile/README.md | 57 +++++++++ packages/ai-llamafile/package.json | 50 ++++++++ .../browser/llamafile-command-contribution.ts | 92 +++++++++++++++ ...afile-frontend-application-contribution.ts | 59 ++++++++++ .../src/browser/llamafile-frontend-module.ts | 45 ++++++++ .../src/browser/llamafile-preferences.ts | 60 ++++++++++ .../src/common/llamafile-language-model.ts | 102 ++++++++++++++++ .../src/common/llamafile-manager.ts | 50 ++++++++ .../src/node/llamafile-backend-module.ts | 32 +++++ .../src/node/llamafile-manager-impl.ts | 109 ++++++++++++++++++ packages/ai-llamafile/src/package.spec.ts | 27 +++++ packages/ai-llamafile/tsconfig.json | 22 ++++ tsconfig.json | 3 + 18 files changed, 726 insertions(+) create mode 100644 packages/ai-llamafile/.eslintrc.js create mode 100644 packages/ai-llamafile/README.md create mode 100644 packages/ai-llamafile/package.json create mode 100644 packages/ai-llamafile/src/browser/llamafile-command-contribution.ts create mode 100644 packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts create mode 100644 packages/ai-llamafile/src/browser/llamafile-frontend-module.ts create mode 100644 packages/ai-llamafile/src/browser/llamafile-preferences.ts create mode 100644 packages/ai-llamafile/src/common/llamafile-language-model.ts create mode 100644 packages/ai-llamafile/src/common/llamafile-manager.ts create mode 100644 packages/ai-llamafile/src/node/llamafile-backend-module.ts create mode 100644 packages/ai-llamafile/src/node/llamafile-manager-impl.ts create mode 100644 packages/ai-llamafile/src/package.spec.ts create mode 100644 packages/ai-llamafile/tsconfig.json diff --git a/examples/browser/package.json b/examples/browser/package.json index a8a39d1189d7d..5cfdc9277b381 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -27,6 +27,7 @@ "@theia/ai-code-completion": "1.54.0", "@theia/ai-core": "1.54.0", "@theia/ai-history": "1.54.0", + "@theia/ai-llamafile": "1.54.0", "@theia/ai-ollama": "1.54.0", "@theia/ai-openai": "1.54.0", "@theia/ai-terminal": "1.54.0", diff --git a/examples/browser/tsconfig.json b/examples/browser/tsconfig.json index f6b8cad219535..5f39c27826132 100644 --- a/examples/browser/tsconfig.json +++ b/examples/browser/tsconfig.json @@ -23,6 +23,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-llamafile" + }, { "path": "../../packages/ai-ollama" }, diff --git a/examples/electron/package.json b/examples/electron/package.json index 04482481be284..5dcb9b9007a93 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -31,6 +31,7 @@ "@theia/ai-code-completion": "1.54.0", "@theia/ai-core": "1.54.0", "@theia/ai-history": "1.54.0", + "@theia/ai-llamafile": "1.54.0", "@theia/ai-ollama": "1.54.0", "@theia/ai-openai": "1.54.0", "@theia/ai-terminal": "1.54.0", diff --git a/examples/electron/tsconfig.json b/examples/electron/tsconfig.json index e36e0e73f4457..9199410a96828 100644 --- a/examples/electron/tsconfig.json +++ b/examples/electron/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../packages/ai-history" }, + { + "path": "../../packages/ai-llamafile" + }, { "path": "../../packages/ai-ollama" }, diff --git a/packages/ai-llamafile/.eslintrc.js b/packages/ai-llamafile/.eslintrc.js new file mode 100644 index 0000000000000..13089943582b6 --- /dev/null +++ b/packages/ai-llamafile/.eslintrc.js @@ -0,0 +1,10 @@ +/** @type {import('eslint').Linter.Config} */ +module.exports = { + extends: [ + '../../configs/build.eslintrc.json' + ], + parserOptions: { + tsconfigRootDir: __dirname, + project: 'tsconfig.json' + } +}; diff --git a/packages/ai-llamafile/README.md b/packages/ai-llamafile/README.md new file mode 100644 index 0000000000000..17a4dc5464a8e --- /dev/null +++ b/packages/ai-llamafile/README.md @@ -0,0 +1,57 @@ +# AI Llamafile Integration + +The AI Llamafile package provides an integration that allows users to manage and interact with Llamafile language models within Theia IDE. + +## Features + +- Start and stop Llamafile language servers. + +## Commands + +### Start Llamafile + +- **Command ID:** `llamafile.start` +- **Label:** `Start Llamafile` +- **Functionality:** Allows you to start a Llamafile language server by selecting from a list of configured Llamafiles. + +### Stop Llamafile + +- **Command ID:** `llamafile.stop` +- **Label:** `Stop Llamafile` +- **Functionality:** Allows you to stop a running Llamafile language server by selecting from a list of currently running Llamafiles. + +## Usage + +1. **Starting a Llamafile Language Server:** + + - Use the command palette to invoke `Start Llamafile`. + - A quick pick menu will appear with a list of configured Llamafiles. + - Select a Llamafile to start its language server. + +2. **Stopping a Llamafile Language Server:** + - Use the command palette to invoke `Stop Llamafile`. + - A quick pick menu will display a list of currently running Llamafiles. + - Select a Llamafile to stop its language server. + +## Dependencies + +This extension depends on the `@theia/ai-core` package for AI-related services and functionalities. + +## Configuration + +Make sure to configure your Llamafiles properly within the preference settings. +This setting is an array of objects, where each object defines a llamafile with a user-friendly name, the file uri, and the port to start the server on. + +Example Configuration: + +```json +{ + "ai-features.llamafile.llamafiles": [ + { + "name": "MyLlamaFile", + "uri": "file:///path/to/my.llamafile", + "port": 30000 + } + ] +} +``` diff --git a/packages/ai-llamafile/package.json b/packages/ai-llamafile/package.json new file mode 100644 index 0000000000000..9518a24ad7bd6 --- /dev/null +++ b/packages/ai-llamafile/package.json @@ -0,0 +1,50 @@ +{ + "name": "@theia/ai-llamafile", + "version": "1.54.0", + "description": "Theia - Llamafile Integration", + "dependencies": { + "@theia/ai-core": "1.54.0", + "@theia/core": "1.54.0", + "@theia/output": "1.54.0", + "tslib": "^2.6.2" + }, + "publishConfig": { + "access": "public" + }, + "theiaExtensions": [ + { + "frontend": "lib/browser/llamafile-frontend-module", + "backend": "lib/node/llamafile-backend-module" + } + ], + "keywords": [ + "theia-extension" + ], + "license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0", + "repository": { + "type": "git", + "url": "https://github.com/eclipse-theia/theia.git" + }, + "bugs": { + "url": "https://github.com/eclipse-theia/theia/issues" + }, + "homepage": "https://github.com/eclipse-theia/theia", + "files": [ + "lib", + "src" + ], + "scripts": { + "build": "theiaext build", + "clean": "theiaext clean", + "compile": "theiaext compile", + "lint": "theiaext lint", + "test": "theiaext test", + "watch": "theiaext watch" + }, + "devDependencies": { + "@theia/ext-scripts": "1.54.0" + }, + "nyc": { + "extends": "../../configs/nyc.json" + } +} diff --git a/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts b/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts new file mode 100644 index 0000000000000..eae616cfd94bd --- /dev/null +++ b/packages/ai-llamafile/src/browser/llamafile-command-contribution.ts @@ -0,0 +1,92 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { AICommandHandlerFactory } from '@theia/ai-core/lib/browser/ai-command-handler-factory'; +import { CommandContribution, CommandRegistry, MessageService } from '@theia/core'; +import { PreferenceService, QuickInputService } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { LlamafileEntry, LlamafileManager } from '../common/llamafile-manager'; +import { PREFERENCE_LLAMAFILE } from './llamafile-preferences'; + +export const StartLlamafileCommand = { + id: 'llamafile.start', + label: 'Start Llamafile', +}; +export const StopLlamafileCommand = { + id: 'llamafile.stop', + label: 'Stop Llamafile', +}; + +@injectable() +export class LlamafileCommandContribution implements CommandContribution { + + @inject(QuickInputService) + protected readonly quickInputService: QuickInputService; + + @inject(AICommandHandlerFactory) + protected readonly commandHandlerFactory: AICommandHandlerFactory; + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + @inject(MessageService) + protected messageService: MessageService; + + @inject(LlamafileManager) + protected llamafileManager: LlamafileManager; + + registerCommands(commandRegistry: CommandRegistry): void { + commandRegistry.registerCommand(StartLlamafileCommand, this.commandHandlerFactory({ + execute: async () => { + try { + const llamaFiles = this.preferenceService.get(PREFERENCE_LLAMAFILE); + if (llamaFiles === undefined || llamaFiles.length === 0) { + this.messageService.error('No Llamafiles configured.'); + return; + } + const options = llamaFiles.map(llamaFile => ({ label: llamaFile.name })); + const result = await this.quickInputService.showQuickPick(options); + if (result === undefined) { + return; + } + this.llamafileManager.startServer(result.label); + } catch (error) { + console.error('Something went wrong during the llamafile start.', error); + this.messageService.error(`Something went wrong during the llamafile start: ${error.message}.\nFor more information, see the console.`); + } + } + })); + commandRegistry.registerCommand(StopLlamafileCommand, this.commandHandlerFactory({ + execute: async () => { + try { + const llamaFiles = await this.llamafileManager.getStartedLlamafiles(); + if (llamaFiles === undefined || llamaFiles.length === 0) { + this.messageService.error('No Llamafiles running.'); + return; + } + const options = llamaFiles.map(llamaFile => ({ label: llamaFile })); + const result = await this.quickInputService.showQuickPick(options); + if (result === undefined) { + return; + } + this.llamafileManager.stopServer(result.label); + } catch (error) { + console.error('Something went wrong during the llamafile stop.', error); + this.messageService.error(`Something went wrong during the llamafile stop: ${error.message}.\nFor more information, see the console.`); + } + } + })); + } +} diff --git a/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts b/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts new file mode 100644 index 0000000000000..c202ca12fe54e --- /dev/null +++ b/packages/ai-llamafile/src/browser/llamafile-frontend-application-contribution.ts @@ -0,0 +1,59 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { FrontendApplicationContribution, PreferenceService } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { LlamafileEntry, LlamafileManager } from '../common/llamafile-manager'; +import { PREFERENCE_LLAMAFILE } from './llamafile-preferences'; + +@injectable() +export class LlamafileFrontendApplicationContribution implements FrontendApplicationContribution { + + @inject(PreferenceService) + protected preferenceService: PreferenceService; + + @inject(LlamafileManager) + protected llamafileManager: LlamafileManager; + + private _knownLlamaFiles: Map = new Map(); + + onStart(): void { + this.preferenceService.ready.then(() => { + const llamafiles = this.preferenceService.get(PREFERENCE_LLAMAFILE, []); + this.llamafileManager.addLanguageModels(llamafiles); + llamafiles.forEach(model => this._knownLlamaFiles.set(model.name, model)); + + this.preferenceService.onPreferenceChanged(event => { + if (event.preferenceName === PREFERENCE_LLAMAFILE) { + // only new models which are actual LLamaFileEntries + const newModels = event.newValue.filter((llamafileEntry: unknown) => LlamafileEntry.is(llamafileEntry)) as LlamafileEntry[]; + + const llamafilesToAdd = newModels.filter(llamafile => + !this._knownLlamaFiles.has(llamafile.name) || !LlamafileEntry.equals(this._knownLlamaFiles.get(llamafile.name)!, llamafile)); + + const llamafileIdsToRemove = [...this._knownLlamaFiles.values()].filter(llamafile => + !newModels.find(a => LlamafileEntry.equals(a, llamafile))).map(a => a.name); + + this.llamafileManager.removeLanguageModels(llamafileIdsToRemove); + llamafileIdsToRemove.forEach(model => this._knownLlamaFiles.delete(model)); + + this.llamafileManager.addLanguageModels(llamafilesToAdd); + llamafilesToAdd.forEach(model => this._knownLlamaFiles.set(model.name, model)); + } + }); + }); + } +} diff --git a/packages/ai-llamafile/src/browser/llamafile-frontend-module.ts b/packages/ai-llamafile/src/browser/llamafile-frontend-module.ts new file mode 100644 index 0000000000000..5deeb0c18d982 --- /dev/null +++ b/packages/ai-llamafile/src/browser/llamafile-frontend-module.ts @@ -0,0 +1,45 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { CommandContribution } from '@theia/core'; +import { FrontendApplicationContribution, RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser'; +import { ContainerModule } from '@theia/core/shared/inversify'; +import { OutputChannelManager, OutputChannelSeverity } from '@theia/output/lib/browser/output-channel'; +import { LlamafileManager, LlamafileManagerPath, LlamafileServerManagerClient } from '../common/llamafile-manager'; +import { LlamafileCommandContribution } from './llamafile-command-contribution'; +import { LlamafileFrontendApplicationContribution } from './llamafile-frontend-application-contribution'; +import { bindAILlamafilePreferences } from './llamafile-preferences'; + +export default new ContainerModule(bind => { + bind(FrontendApplicationContribution).to(LlamafileFrontendApplicationContribution).inSingletonScope(); + bind(CommandContribution).to(LlamafileCommandContribution).inSingletonScope(); + bind(LlamafileManager).toDynamicValue(ctx => { + const connection = ctx.container.get(RemoteConnectionProvider); + const outputChannelManager = ctx.container.get(OutputChannelManager); + const client: LlamafileServerManagerClient = { + error: (llamafileName, message) => { + const channel = outputChannelManager.getChannel(`${llamafileName}-llamafile`); + channel.appendLine(message, OutputChannelSeverity.Error); + }, + log: (llamafileName, message) => { + const channel = outputChannelManager.getChannel(`${llamafileName}-llamafile`); + channel.appendLine(message, OutputChannelSeverity.Info); + } + }; + return connection.createProxy(LlamafileManagerPath, client); + }).inSingletonScope(); + + bindAILlamafilePreferences(bind); +}); diff --git a/packages/ai-llamafile/src/browser/llamafile-preferences.ts b/packages/ai-llamafile/src/browser/llamafile-preferences.ts new file mode 100644 index 0000000000000..fcc3255725ab7 --- /dev/null +++ b/packages/ai-llamafile/src/browser/llamafile-preferences.ts @@ -0,0 +1,60 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser'; +import { interfaces } from '@theia/core/shared/inversify'; + +export const AI_LLAMAFILE_PREFERENCES_TITLE = '✨ AI LlamaFile'; +export const PREFERENCE_LLAMAFILE = 'ai-features.llamafile.llamafiles'; + +export const aiLlamafilePreferencesSchema: PreferenceSchema = { + type: 'object', + properties: { + [PREFERENCE_LLAMAFILE]: { + title: AI_LLAMAFILE_PREFERENCES_TITLE, + markdownDescription: '❗ This setting allows you to add llamafiles.\ + \n\ + You need to provide a user friendly `name`, the file `uri` to the llamafile and the `port` to use.\ + \n\ + In order to start your llamafile you have to call the "Start Llamafile" command where you can then select the llamafile to start.\ + \n\ + If you modify an entry, e.g. change the port and the server was already running, then it will be stopped and you have to manually start it again.', + type: 'array', + default: [], + items: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'The model name to use for this Llamafile.' + }, + uri: { + type: 'string', + description: 'The file uri to the Llamafile.' + }, + port: { + type: 'number', + description: 'The port to use to start the server.' + } + } + } + } + } +}; + +export function bindAILlamafilePreferences(bind: interfaces.Bind): void { + bind(PreferenceContribution).toConstantValue({ schema: aiLlamafilePreferencesSchema }); +} diff --git a/packages/ai-llamafile/src/common/llamafile-language-model.ts b/packages/ai-llamafile/src/common/llamafile-language-model.ts new file mode 100644 index 0000000000000..6f1b039013879 --- /dev/null +++ b/packages/ai-llamafile/src/common/llamafile-language-model.ts @@ -0,0 +1,102 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { LanguageModel, LanguageModelRequest, LanguageModelResponse, LanguageModelStreamResponsePart } from '@theia/ai-core'; + +export class LlamafileLanguageModel implements LanguageModel { + + readonly providerId = 'llamafile'; + readonly vendor: string = 'Mozilla'; + + constructor(readonly name: string, readonly uri: string, readonly port: number) { + } + + get id(): string { + return this.name; + } + + async request(request: LanguageModelRequest): Promise { + try { + let prompt = request.messages.map(message => { + switch (message.actor) { + case 'user': + return `User: ${message.query}`; + case 'ai': + return `Llama: ${message.query}`; + case 'system': + return `${message.query.replace(/\n\n/g, '\n')}`; + } + }).join('\n'); + prompt += '\nLlama:'; + const response = await fetch(`http://localhost:${this.port}/completion`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + prompt: prompt, + n_predict: 200, + stream: true, + stop: ['', 'Llama:', 'User:', '<|eot_id|>'], + cache_prompt: true, + }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + if (!response.body) { + throw new Error('Response body is undefined'); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + return { + stream: { + [Symbol.asyncIterator](): AsyncIterator { + return { + async next(): Promise> { + const { value, done } = await reader.read(); + if (done) { + return { value: undefined, done: true }; + } + const read = decoder.decode(value, { stream: true }); + const chunk = read.split('\n').filter(l => l.length !== 0).reduce((acc, line) => { + try { + const parsed = JSON.parse(line.substring(6)); + acc += parsed.content; + return acc; + } catch (error) { + console.error('Error parsing JSON:', error); + return acc; + } + }, ''); + return { value: { content: chunk }, done: false }; + } + }; + } + } + }; + } catch (error) { + console.error('Error:', error); + return { + text: `Error: ${error}` + }; + } + } + +} diff --git a/packages/ai-llamafile/src/common/llamafile-manager.ts b/packages/ai-llamafile/src/common/llamafile-manager.ts new file mode 100644 index 0000000000000..561f3acdee95b --- /dev/null +++ b/packages/ai-llamafile/src/common/llamafile-manager.ts @@ -0,0 +1,50 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +export const LlamafileManager = Symbol('LlamafileManager'); + +export const LlamafileManagerPath = '/services/llamafilemanager'; + +export interface LlamafileManager { + startServer(name: string): Promise; + stopServer(name: string): void; + getStartedLlamafiles(): Promise; + setClient(client: LlamafileServerManagerClient): void; + addLanguageModels(llamaFiles: LlamafileEntry[]): Promise; + removeLanguageModels(modelIds: string[]): void; +} +export interface LlamafileServerManagerClient { + log(llamafileName: string, message: string): void; + error(llamafileName: string, message: string): void; +} + +export interface LlamafileEntry { + name: string; + uri: string; + port: number; +} + +export namespace LlamafileEntry { + export function equals(a: LlamafileEntry, b: LlamafileEntry): boolean { + return a.name === b.name && a.uri === b.uri && a.port === b.port; + } + export function is(entry: unknown): entry is LlamafileEntry { + // eslint-disable-next-line no-null/no-null + return typeof entry === 'object' && entry !== null + && 'name' in entry && typeof entry.name === 'string' + && 'uri' in entry && typeof entry.uri === 'string' + && 'port' in entry && typeof entry.port === 'number'; + } +} diff --git a/packages/ai-llamafile/src/node/llamafile-backend-module.ts b/packages/ai-llamafile/src/node/llamafile-backend-module.ts new file mode 100644 index 0000000000000..83ab2c182bd00 --- /dev/null +++ b/packages/ai-llamafile/src/node/llamafile-backend-module.ts @@ -0,0 +1,32 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { ContainerModule } from '@theia/core/shared/inversify'; +import { LlamafileManagerImpl } from './llamafile-manager-impl'; +import { LlamafileManager, LlamafileServerManagerClient, LlamafileManagerPath } from '../common/llamafile-manager'; +import { ConnectionHandler, RpcConnectionHandler } from '@theia/core'; + +export default new ContainerModule(bind => { + bind(LlamafileManager).to(LlamafileManagerImpl).inSingletonScope(); + bind(ConnectionHandler).toDynamicValue(ctx => new RpcConnectionHandler( + LlamafileManagerPath, + client => { + const service = ctx.container.get(LlamafileManager); + service.setClient(client); + return service; + } + )).inSingletonScope(); +}); diff --git a/packages/ai-llamafile/src/node/llamafile-manager-impl.ts b/packages/ai-llamafile/src/node/llamafile-manager-impl.ts new file mode 100644 index 0000000000000..3d726457f9faa --- /dev/null +++ b/packages/ai-llamafile/src/node/llamafile-manager-impl.ts @@ -0,0 +1,109 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** +import { LanguageModelRegistry } from '@theia/ai-core'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { basename, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { LlamafileLanguageModel } from '../common/llamafile-language-model'; +import { LlamafileEntry, LlamafileManager, LlamafileServerManagerClient } from '../common/llamafile-manager'; + +@injectable() +export class LlamafileManagerImpl implements LlamafileManager { + + @inject(LanguageModelRegistry) + protected languageModelRegistry: LanguageModelRegistry; + + private processMap: Map = new Map(); + private client: LlamafileServerManagerClient; + + async addLanguageModels(llamaFiles: LlamafileEntry[]): Promise { + for (const llamafile of llamaFiles) { + const model = await this.languageModelRegistry.getLanguageModel(llamafile.name); + if (model) { + if (!(model instanceof LlamafileLanguageModel)) { + console.warn(`Llamafile: model ${model.id} is not an LLamafile model`); + continue; + } else { + // This can happen during the initializing of more than one frontends, changes are handled in the frontend + console.info(`Llamafile: skip creating or updating model ${llamafile.name} because it already exists.`); + } + } else { + this.languageModelRegistry.addLanguageModels([new LlamafileLanguageModel(llamafile.name, llamafile.uri, llamafile.port)]); + } + } + } + removeLanguageModels(modelIds: string[]): void { + modelIds.filter(modelId => this.isStarted(modelId)).forEach(modelId => this.stopServer(modelId)); + this.languageModelRegistry.removeLanguageModels(modelIds); + } + + async getStartedLlamafiles(): Promise { + const models = await this.languageModelRegistry.getLanguageModels(); + return models.filter(model => model instanceof LlamafileLanguageModel && this.isStarted(model.name)).map(model => model.id); + } + + async startServer(name: string): Promise { + if (!this.processMap.has(name)) { + const models = await this.languageModelRegistry.getLanguageModels(); + const llm = models.find(model => model.id === name && model instanceof LlamafileLanguageModel) as LlamafileLanguageModel | undefined; + if (llm === undefined) { + return Promise.reject(`Llamafile ${name} not found`); + } + const filePath = fileURLToPath(llm.uri); + + // Extract the directory and file name + const dir = dirname(filePath); + const fileName = basename(filePath); + const currentProcess = spawn(`./${fileName}`, ['--port', '' + llm.port, '--server', '--nobrowser'], { cwd: dir }); + this.processMap.set(name, currentProcess); + + currentProcess.stdout.on('data', (data: Buffer) => { + const output = data.toString(); + this.client.log(name, output); + }); + currentProcess.stderr.on('data', (data: Buffer) => { + const output = data.toString(); + this.client.error(name, output); + }); + currentProcess.on('close', code => { + this.client.log(name, `LlamaFile process for file ${name} exited with code ${code}`); + this.processMap.delete(name); + }); + currentProcess.on('error', error => { + this.client.error(name, `Error starting LlamaFile process for file ${name}: ${error.message}`); + this.processMap.delete(name); + }); + } + } + + stopServer(name: string): void { + if (this.processMap.has(name)) { + const currentProcess = this.processMap.get(name); + currentProcess!.kill(); + this.processMap.delete(name); + } + } + + isStarted(name: string): boolean { + return this.processMap.has(name); + } + + setClient(client: LlamafileServerManagerClient): void { + this.client = client; + } + +} diff --git a/packages/ai-llamafile/src/package.spec.ts b/packages/ai-llamafile/src/package.spec.ts new file mode 100644 index 0000000000000..ac0ffad7fa139 --- /dev/null +++ b/packages/ai-llamafile/src/package.spec.ts @@ -0,0 +1,27 @@ +// ***************************************************************************** +// Copyright (C) 2024 TypeFox GmbH and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +/* note: this bogus test file is required so that + we are able to run mocha unit tests on this + package, without having any actual unit tests in it. + This way a coverage report will be generated, + showing 0% coverage, instead of no report. + This file can be removed once we have real unit + tests in place. */ + +describe('ai-llamafile package', () => { + it('support code coverage statistics', () => true); +}); diff --git a/packages/ai-llamafile/tsconfig.json b/packages/ai-llamafile/tsconfig.json new file mode 100644 index 0000000000000..ed8ef9826cf57 --- /dev/null +++ b/packages/ai-llamafile/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../configs/base.tsconfig", + "compilerOptions": { + "composite": true, + "rootDir": "src", + "outDir": "lib" + }, + "include": [ + "src" + ], + "references": [ + { + "path": "../ai-core" + }, + { + "path": "../core" + }, + { + "path": "../output" + } + ] +} diff --git a/tsconfig.json b/tsconfig.json index ee1bdb0025812..9b9553dfdd5a1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -69,6 +69,9 @@ { "path": "packages/ai-history" }, + { + "path": "packages/ai-llamafile" + }, { "path": "packages/ai-ollama" }, From 54919bfe2c4ef0d66496df5d55b0547e83af52a5 Mon Sep 17 00:00:00 2001 From: Olaf Lessenich Date: Wed, 16 Oct 2024 13:27:40 +0200 Subject: [PATCH 435/441] Fix: use @theia/ai-core package in import Signed-off-by: Olaf Lessenich --- .../ai-chat/src/common/chat-agents-variable-contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ai-chat/src/common/chat-agents-variable-contribution.ts b/packages/ai-chat/src/common/chat-agents-variable-contribution.ts index 968a41f9af33e..72344d166fb54 100644 --- a/packages/ai-chat/src/common/chat-agents-variable-contribution.ts +++ b/packages/ai-chat/src/common/chat-agents-variable-contribution.ts @@ -23,7 +23,7 @@ import { AIVariableResolver, AIVariableService, ResolvedAIVariable -} from '../../../ai-core/src/common/variable-service'; +} from '@theia/ai-core'; import { ChatAgentService } from './chat-agent-service'; export const CHAT_AGENTS_VARIABLE: AIVariable = { From 78ce0648fd0f09beccb32385df19c3ea75dd8eb6 Mon Sep 17 00:00:00 2001 From: Jonah Iden Date: Thu, 17 Oct 2024 14:07:52 +0200 Subject: [PATCH 436/441] fix onDidChangeActiveNotebookEditorEmitter to fire correctly when editor changed (#14321) Signed-off-by: Jonah Iden --- packages/plugin-ext/src/plugin/notebook/notebooks.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/plugin-ext/src/plugin/notebook/notebooks.ts b/packages/plugin-ext/src/plugin/notebook/notebooks.ts index 6101c2fc97c8a..65487fffb55aa 100644 --- a/packages/plugin-ext/src/plugin/notebook/notebooks.ts +++ b/packages/plugin-ext/src/plugin/notebook/notebooks.ts @@ -330,6 +330,7 @@ export class NotebooksExtImpl implements NotebooksExt { if (delta.newActiveEditor === null) { // clear active notebook as current active editor is non-notebook editor this.activeNotebookEditor = undefined; + this.onDidChangeActiveNotebookEditorEmitter.fire(undefined); } else if (delta.newActiveEditor) { const activeEditor = this.editors.get(delta.newActiveEditor); if (!activeEditor) { @@ -341,8 +342,6 @@ export class NotebooksExtImpl implements NotebooksExt { newActiveEditor: null }); } - } - if (delta.newActiveEditor !== undefined && delta.newActiveEditor !== this.activeNotebookEditor?.id) { this.onDidChangeActiveNotebookEditorEmitter.fire(this.activeNotebookEditor?.apiEditor); } } From fc1b88e435bf65ad412907734b70d902c937868e Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Fri, 11 Oct 2024 15:28:51 +0200 Subject: [PATCH 437/441] feat: support custom keys for custom Open AI models The configuration for custom OpenAI models now allows specifying a unique 'apiKey' for each model, or reusing the global OpenAI API key. fixes #14288 --- packages/ai-openai/README.md | 20 +++++++++++++++++- ...penai-frontend-application-contribution.ts | 12 ++++++----- .../src/browser/openai-preferences.ts | 13 ++++++++++-- .../common/openai-language-models-manager.ts | 4 ++++ .../src/node/openai-language-model.ts | 8 +++---- .../openai-language-models-manager-impl.ts | 21 ++++++++++++------- 6 files changed, 58 insertions(+), 20 deletions(-) diff --git a/packages/ai-openai/README.md b/packages/ai-openai/README.md index 679035fe6b435..1ec7facb4a1b0 100644 --- a/packages/ai-openai/README.md +++ b/packages/ai-openai/README.md @@ -14,7 +14,25 @@ The `@theia/ai-openai` integrates OpenAI's models with Theia AI. The OpenAI API key and the models to use can be configured via preferences. -Alternatively the OpenAI API key can also be handed in via an environment variable. +Alternatively the OpenAI API key can also be handed in via the `OPENAI_API_KEY` variable. + +### Custom models + +The extension also supports OpenAI compatible models hosted on different end points. +You can configure the end points via the `ai-features.openAiCustom.customOpenAiModels` preference: + +```ts +{ + model: string + url: string + id?: string + apiKey?: string | true +} +``` + +- `model` and `url` are mandatory attributes, indicating the end point and model to use +- `id` is an optional attribute which is used in the UI to refer to this configuration +- `apiKey` is either the key to access the API served at the given URL or `true` to use the global OpenAI API key. If not given 'no-key' will be used. ## Additional Information diff --git a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts index 9295eb9cede17..b16f80aa0ef90 100644 --- a/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts +++ b/packages/ai-openai/src/browser/openai-frontend-application-contribution.ts @@ -64,7 +64,7 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio const modelsToRemove = oldModels.filter(model => !newModels.some(newModel => newModel.id === model.id)); const modelsToAddOrUpdate = newModels.filter(newModel => !oldModels.some(model => - model.id === newModel.id && model.model === newModel.model && model.url === newModel.url)); + model.id === newModel.id && model.model === newModel.model && model.url === newModel.url && model.apiKey === newModel.apiKey)); this.manager.removeLanguageModels(...modelsToRemove.map(model => model.id)); this.manager.createOrUpdateLanguageModels(...modelsToAddOrUpdate); @@ -77,21 +77,23 @@ export class OpenAiFrontendApplicationContribution implements FrontendApplicatio function createOpenAIModelDescription(modelId: string): OpenAiModelDescription { return { id: `openai/${modelId}`, - model: modelId + model: modelId, + apiKey: true }; } function createCustomModelDescriptionsFromPreferences(preferences: Partial[]): OpenAiModelDescription[] { return preferences.reduce((acc, pref) => { - if (!pref.model || !pref.url) { + if (!pref.model || !pref.url || typeof pref.model !== 'string' || typeof pref.url !== 'string') { return acc; } return [ ...acc, { - id: pref.id ?? pref.model, + id: pref.id && typeof pref.id === 'string' ? pref.id : pref.model, model: pref.model, - url: pref.url + url: pref.url, + apiKey: typeof pref.apiKey === 'string' || pref.apiKey === true ? pref.apiKey : undefined } ]; }, []); diff --git a/packages/ai-openai/src/browser/openai-preferences.ts b/packages/ai-openai/src/browser/openai-preferences.ts index 0b2eab0d4f3bf..ce937578ce623 100644 --- a/packages/ai-openai/src/browser/openai-preferences.ts +++ b/packages/ai-openai/src/browser/openai-preferences.ts @@ -43,7 +43,12 @@ export const OpenAiPreferencesSchema: PreferenceSchema = { type: 'array', title: AI_CORE_PREFERENCES_TITLE, markdownDescription: 'Integrate custom models compatible with the OpenAI API, for example via `vllm`. The required attributes are `model` and `url`.\ - Optionally, you can provide a unique `id` to identify the custom model in the UI. If none is given `model` will be used as `id`.', + \n\ + Optionally, you can\ + \n\ + - specify a unique `id` to identify the custom model in the UI. If none is given `model` will be used as `id`.\ + \n\ + - provide an `apiKey` to access the API served at the given url. Use `true` to indicate the use of the global OpenAI API key.', default: [], items: { type: 'object', @@ -59,7 +64,11 @@ export const OpenAiPreferencesSchema: PreferenceSchema = { id: { type: 'string', title: 'A unique identifier which is used in the UI to identify the custom model', - } + }, + apiKey: { + type: ['string', 'boolean'], + title: 'Either the key to access the API served at the given url or `true` to use the global OpenAI API key', + }, } } } diff --git a/packages/ai-openai/src/common/openai-language-models-manager.ts b/packages/ai-openai/src/common/openai-language-models-manager.ts index bf5ad33ca3950..363b2552dc38b 100644 --- a/packages/ai-openai/src/common/openai-language-models-manager.ts +++ b/packages/ai-openai/src/common/openai-language-models-manager.ts @@ -28,6 +28,10 @@ export interface OpenAiModelDescription { * The OpenAI API compatible endpoint where the model is hosted. If not provided the default OpenAI endpoint will be used. */ url?: string; + /** + * The key for the model. If 'true' is provided the global OpenAI API key will be used. + */ + apiKey: string | true | undefined; } export interface OpenAiLanguageModelsManager { apiKey: string | undefined; diff --git a/packages/ai-openai/src/node/openai-language-model.ts b/packages/ai-openai/src/node/openai-language-model.ts index 7692a21f8a9e4..7d47f72d99584 100644 --- a/packages/ai-openai/src/node/openai-language-model.ts +++ b/packages/ai-openai/src/node/openai-language-model.ts @@ -55,7 +55,7 @@ export class OpenAiModel implements LanguageModel { * @param model the model id as it is used by the OpenAI API * @param openAIInitializer initializer for the OpenAI client, used for each request. */ - constructor(public readonly id: string, public model: string, protected apiKey: (() => string | undefined) | undefined, public url: string | undefined) { } + constructor(public readonly id: string, public model: string, public apiKey: () => string | undefined, public url: string | undefined) { } async request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise { const openai = this.initializeOpenAi(); @@ -180,11 +180,11 @@ export class OpenAiModel implements LanguageModel { } protected initializeOpenAi(): OpenAI { - const apiKey = this.apiKey && this.apiKey(); + const apiKey = this.apiKey(); if (!apiKey && !(this.url)) { throw new Error('Please provide OPENAI_API_KEY in preferences or via environment variable'); } - // do not hand over API key to custom urls - return new OpenAI({ apiKey: this.url ? 'no-key' : apiKey, baseURL: this.url }); + // We need to hand over "some" key, even if a custom url is not key protected as otherwise the OpenAI client will throw an error + return new OpenAI({ apiKey: apiKey ?? 'no-key', baseURL: this.url }); } } diff --git a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts index d0f60856fb2ab..cfc81ba3b8adb 100644 --- a/packages/ai-openai/src/node/openai-language-models-manager-impl.ts +++ b/packages/ai-openai/src/node/openai-language-models-manager-impl.ts @@ -36,6 +36,15 @@ export class OpenAiLanguageModelsManagerImpl implements OpenAiLanguageModelsMana async createOrUpdateLanguageModels(...modelDescriptions: OpenAiModelDescription[]): Promise { for (const modelDescription of modelDescriptions) { const model = await this.languageModelRegistry.getLanguageModel(modelDescription.id); + const apiKeyProvider = () => { + if (modelDescription.apiKey === true) { + return this.apiKey; + } + if (modelDescription.apiKey) { + return modelDescription.apiKey; + } + return undefined; + }; if (model) { if (!(model instanceof OpenAiModel)) { console.warn(`Open AI: model ${modelDescription.id} is not an OpenAI model`); @@ -46,15 +55,11 @@ export class OpenAiLanguageModelsManagerImpl implements OpenAiLanguageModelsMana console.info(`Open AI: skip creating model ${modelDescription.id} because it already exists`); continue; } - if (model.url !== modelDescription.url || model.model !== modelDescription.model) { - model.url = modelDescription.url; - model.model = modelDescription.model; - } else { - // This can happen during the initializing of more than one frontends. - console.info(`Open AI: skip creating or updating model ${modelDescription.id} because it already exists and is up to date`); - } + model.url = modelDescription.url; + model.model = modelDescription.model; + model.apiKey = apiKeyProvider; } else { - this.languageModelRegistry.addLanguageModels([new OpenAiModel(modelDescription.id, modelDescription.model, () => this.apiKey, modelDescription.url)]); + this.languageModelRegistry.addLanguageModels([new OpenAiModel(modelDescription.id, modelDescription.model, apiKeyProvider, modelDescription.url)]); } } } From aaaa73f4d127e2dcb3145615d930c8563dfef41c Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Fri, 18 Oct 2024 15:16:41 +0200 Subject: [PATCH 438/441] feat: add support for custom agents (#14301) This adds support for custom agents which are basically a custom system prompt with additional metadata (id, name and description). This features allows to add very specific agents without coding. All features, like variable and functions are supported. --- .../src/browser/ai-chat-frontend-module.ts | 29 +++++++- .../src/browser/custom-agent-factory.ts | 20 +++++ ...agent-frontend-application-contribution.ts | 73 +++++++++++++++++++ .../ai-chat/src/common/chat-agent-service.ts | 15 ++++ .../ai-chat/src/common/custom-chat-agent.ts | 44 +++++++++++ packages/ai-chat/src/common/index.ts | 7 +- packages/ai-core/package.json | 2 + .../agent-configuration-widget.tsx | 8 ++ .../frontend-prompt-customization-service.ts | 61 +++++++++++++++- packages/ai-core/src/browser/style/index.css | 5 ++ packages/ai-core/src/common/agent-service.ts | 29 +++++++- packages/ai-core/src/common/prompt-service.ts | 40 ++++++++++ yarn.lock | 5 ++ 13 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 packages/ai-chat/src/browser/custom-agent-factory.ts create mode 100644 packages/ai-chat/src/browser/custom-agent-frontend-application-contribution.ts create mode 100644 packages/ai-chat/src/common/custom-chat-agent.ts diff --git a/packages/ai-chat/src/browser/ai-chat-frontend-module.ts b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts index c2231c64734aa..396bcb7331a06 100644 --- a/packages/ai-chat/src/browser/ai-chat-frontend-module.ts +++ b/packages/ai-chat/src/browser/ai-chat-frontend-module.ts @@ -14,9 +14,9 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { Agent, AIVariableContribution } from '@theia/ai-core/lib/common'; +import { Agent, AgentService, AIVariableContribution } from '@theia/ai-core/lib/common'; import { bindContributionProvider } from '@theia/core'; -import { PreferenceContribution } from '@theia/core/lib/browser'; +import { FrontendApplicationContribution, PreferenceContribution } from '@theia/core/lib/browser'; import { ContainerModule } from '@theia/core/shared/inversify'; import { ChatAgent, @@ -27,13 +27,16 @@ import { ChatService, DefaultChatAgentId } from '../common'; +import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution'; import { CommandChatAgent } from '../common/command-chat-agents'; +import { CustomChatAgent } from '../common/custom-chat-agent'; import { OrchestratorChatAgent, OrchestratorChatAgentId } from '../common/orchestrator-chat-agent'; +import { DefaultResponseContentFactory, DefaultResponseContentMatcherProvider, ResponseContentMatcherProvider } from '../common/response-content-matcher'; import { UniversalChatAgent } from '../common/universal-chat-agent'; import { aiChatPreferences } from './ai-chat-preferences'; -import { ChatAgentsVariableContribution } from '../common/chat-agents-variable-contribution'; +import { AICustomAgentsFrontendApplicationContribution } from './custom-agent-frontend-application-contribution'; import { FrontendChatServiceImpl } from './frontend-chat-service'; -import { DefaultResponseContentMatcherProvider, DefaultResponseContentFactory, ResponseContentMatcherProvider } from '../common/response-content-matcher'; +import { CustomAgentFactory } from './custom-agent-factory'; export default new ContainerModule(bind => { bindContributionProvider(bind, Agent); @@ -69,4 +72,22 @@ export default new ContainerModule(bind => { bind(ChatAgent).toService(CommandChatAgent); bind(PreferenceContribution).toConstantValue({ schema: aiChatPreferences }); + + bind(CustomChatAgent).toSelf(); + bind(CustomAgentFactory).toFactory( + ctx => (id: string, name: string, description: string, prompt: string, defaultLLM: string) => { + const agent = ctx.container.get(CustomChatAgent); + agent.id = id; + agent.name = name; + agent.description = description; + agent.prompt = prompt; + agent.languageModelRequirements = [{ + purpose: 'chat', + identifier: defaultLLM, + }]; + ctx.container.get(ChatAgentService).registerChatAgent(agent); + ctx.container.get(AgentService).registerAgent(agent); + return agent; + }); + bind(FrontendApplicationContribution).to(AICustomAgentsFrontendApplicationContribution).inSingletonScope(); }); diff --git a/packages/ai-chat/src/browser/custom-agent-factory.ts b/packages/ai-chat/src/browser/custom-agent-factory.ts new file mode 100644 index 0000000000000..9fe67f88e723e --- /dev/null +++ b/packages/ai-chat/src/browser/custom-agent-factory.ts @@ -0,0 +1,20 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { CustomChatAgent } from '../common'; + +export const CustomAgentFactory = Symbol('CustomAgentFactory'); +export type CustomAgentFactory = (id: string, name: string, description: string, prompt: string, defaultLLM: string) => CustomChatAgent; diff --git a/packages/ai-chat/src/browser/custom-agent-frontend-application-contribution.ts b/packages/ai-chat/src/browser/custom-agent-frontend-application-contribution.ts new file mode 100644 index 0000000000000..4c67a14ab508c --- /dev/null +++ b/packages/ai-chat/src/browser/custom-agent-frontend-application-contribution.ts @@ -0,0 +1,73 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { AgentService, CustomAgentDescription, PromptCustomizationService } from '@theia/ai-core'; +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { ChatAgentService } from '../common'; +import { CustomAgentFactory } from './custom-agent-factory'; + +@injectable() +export class AICustomAgentsFrontendApplicationContribution implements FrontendApplicationContribution { + @inject(CustomAgentFactory) + protected readonly customAgentFactory: CustomAgentFactory; + + @inject(PromptCustomizationService) + protected readonly customizationService: PromptCustomizationService; + + @inject(AgentService) + private readonly agentService: AgentService; + + @inject(ChatAgentService) + private readonly chatAgentService: ChatAgentService; + + private knownCustomAgents: Map = new Map(); + onStart(): void { + this.customizationService?.getCustomAgents().then(customAgents => { + customAgents.forEach(agent => { + this.customAgentFactory(agent.id, agent.name, agent.description, agent.prompt, agent.defaultLLM); + this.knownCustomAgents.set(agent.id, agent); + }); + }).catch(e => { + console.error('Failed to load custom agents', e); + }); + this.customizationService?.onDidChangeCustomAgents(() => { + this.customizationService?.getCustomAgents().then(customAgents => { + const customAgentsToAdd = customAgents.filter(agent => + !this.knownCustomAgents.has(agent.id) || !CustomAgentDescription.equals(this.knownCustomAgents.get(agent.id)!, agent)); + const customAgentIdsToRemove = [...this.knownCustomAgents.values()].filter(agent => + !customAgents.find(a => CustomAgentDescription.equals(a, agent))).map(a => a.id); + + // delete first so we don't have to deal with the case where we add and remove the same agentId + customAgentIdsToRemove.forEach(id => { + this.chatAgentService.unregisterChatAgent(id); + this.agentService.unregisterAgent(id); + this.knownCustomAgents.delete(id); + }); + customAgentsToAdd + .forEach(agent => { + this.customAgentFactory(agent.id, agent.name, agent.description, agent.prompt, agent.defaultLLM); + this.knownCustomAgents.set(agent.id, agent); + }); + }).catch(e => { + console.error('Failed to load custom agents', e); + }); + }); + } + + onStop(): void { + } +} diff --git a/packages/ai-chat/src/common/chat-agent-service.ts b/packages/ai-chat/src/common/chat-agent-service.ts index 53704d427cfd0..7c01541d44be1 100644 --- a/packages/ai-chat/src/common/chat-agent-service.ts +++ b/packages/ai-chat/src/common/chat-agent-service.ts @@ -41,6 +41,18 @@ export interface ChatAgentService { * Returns all agents, including disabled ones. */ getAllAgents(): ChatAgent[]; + + /** + * Allows to register a chat agent programmatically. + * @param agent the agent to register + */ + registerChatAgent(agent: ChatAgent): void; + + /** + * Allows to unregister a chat agent programmatically. + * @param agentId the agent id to unregister + */ + unregisterChatAgent(agentId: string): void; } @injectable() export class ChatAgentServiceImpl implements ChatAgentService { @@ -65,6 +77,9 @@ export class ChatAgentServiceImpl implements ChatAgentService { registerChatAgent(agent: ChatAgent): void { this._agents.push(agent); } + unregisterChatAgent(agentId: string): void { + this._agents = this._agents.filter(a => a.id !== agentId); + } getAgent(id: string): ChatAgent | undefined { if (!this._agentIsEnabled(id)) { diff --git a/packages/ai-chat/src/common/custom-chat-agent.ts b/packages/ai-chat/src/common/custom-chat-agent.ts new file mode 100644 index 0000000000000..52743d654dba7 --- /dev/null +++ b/packages/ai-chat/src/common/custom-chat-agent.ts @@ -0,0 +1,44 @@ +// ***************************************************************************** +// Copyright (C) 2024 EclipseSource GmbH. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 +// ***************************************************************************** + +import { AgentSpecificVariables, PromptTemplate } from '@theia/ai-core'; +import { AbstractStreamParsingChatAgent, ChatAgent, SystemMessageDescription } from './chat-agents'; +import { injectable } from '@theia/core/shared/inversify'; + +@injectable() +export class CustomChatAgent + extends AbstractStreamParsingChatAgent + implements ChatAgent { + name: string; + description: string; + readonly variables: string[] = []; + readonly functions: string[] = []; + readonly promptTemplates: PromptTemplate[] = []; + readonly agentSpecificVariables: AgentSpecificVariables[] = []; + + constructor( + ) { + super('CustomChatAgent', [{ purpose: 'chat' }], 'chat'); + } + protected override async getSystemMessageDescription(): Promise { + const resolvedPrompt = await this.promptService.getPrompt(`${this.name}_prompt`); + return resolvedPrompt ? SystemMessageDescription.fromResolvedPromptTemplate(resolvedPrompt) : undefined; + } + + set prompt(prompt: string) { + this.promptTemplates.push({ id: `${this.name}_prompt`, template: prompt }); + } +} diff --git a/packages/ai-chat/src/common/index.ts b/packages/ai-chat/src/common/index.ts index 7fdd58621cc6b..cf160ddcadf10 100644 --- a/packages/ai-chat/src/common/index.ts +++ b/packages/ai-chat/src/common/index.ts @@ -13,12 +13,13 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -export * from './chat-agent-service'; export * from './chat-agents'; +export * from './chat-agent-service'; export * from './chat-model'; -export * from './parsed-chat-request'; export * from './chat-request-parser'; export * from './chat-service'; export * from './command-chat-agents'; -export * from './universal-chat-agent'; +export * from './custom-chat-agent'; +export * from './parsed-chat-request'; export * from './orchestrator-chat-agent'; +export * from './universal-chat-agent'; diff --git a/packages/ai-core/package.json b/packages/ai-core/package.json index e40cc0906d1a3..88a834aa8ab06 100644 --- a/packages/ai-core/package.json +++ b/packages/ai-core/package.json @@ -11,6 +11,8 @@ "@theia/output": "1.54.0", "@theia/variable-resolver": "1.54.0", "@theia/workspace": "1.54.0", + "@types/js-yaml": "^4.0.9", + "js-yaml": "^4.1.0", "minimatch": "^5.1.0", "tslib": "^2.6.2" }, diff --git a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx index 43b23b8c9060a..9a8798e584986 100644 --- a/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx +++ b/packages/ai-core/src/browser/ai-configuration/agent-configuration-widget.tsx @@ -87,6 +87,7 @@ export class AIAgentConfigurationWidget extends ReactWidget { this.aiSettingsService.onDidChange(() => this.update()); this.aiConfigurationSelectionService.onDidAgentChange(() => this.update()); + this.agentService.onDidChangeAgents(() => this.update()); this.update(); } @@ -100,6 +101,9 @@ export class AIAgentConfigurationWidget extends ReactWidget { )}
      +
      + +
      {this.renderAgentDetails()} @@ -205,6 +209,10 @@ export class AIAgentConfigurationWidget extends ReactWidget { this.aiConfigurationSelectionService.selectConfigurationTab(AIVariableConfigurationWidget.ID); } + protected addCustomAgent(): void { + this.promptCustomizationService.openCustomAgentYaml(); + } + protected setActiveAgent(agent: Agent): void { this.aiConfigurationSelectionService.setActiveAgent(agent); this.update(); diff --git a/packages/ai-core/src/browser/frontend-prompt-customization-service.ts b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts index aff47785cb315..11be74b482834 100644 --- a/packages/ai-core/src/browser/frontend-prompt-customization-service.ts +++ b/packages/ai-core/src/browser/frontend-prompt-customization-service.ts @@ -17,13 +17,22 @@ import { DisposableCollection, URI, Event, Emitter } from '@theia/core'; import { OpenerService } from '@theia/core/lib/browser'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; -import { PromptCustomizationService, PromptTemplate } from '../common'; +import { PromptCustomizationService, PromptTemplate, CustomAgentDescription } from '../common'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileChangesEvent } from '@theia/filesystem/lib/common/files'; import { AICorePreferences, PREFERENCE_NAME_PROMPT_TEMPLATES } from './ai-core-preferences'; import { AgentService } from '../common/agent-service'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; +import { load, dump } from 'js-yaml'; + +const templateEntry = { + id: 'my_agent', + name: 'My Agent', + description: 'This is an example agent. Please adapt the properties to fit your needs.', + prompt: 'You are an example agent. Be nice and helpful to the user.', + defaultLLM: 'openai/gpt-4o' +}; @injectable() export class FrontendPromptCustomizationServiceImpl implements PromptCustomizationService { @@ -51,6 +60,9 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati private readonly onDidChangePromptEmitter = new Emitter(); readonly onDidChangePrompt: Event = this.onDidChangePromptEmitter.event; + private readonly onDidChangeCustomAgentsEmitter = new Emitter(); + readonly onDidChangeCustomAgents = this.onDidChangeCustomAgentsEmitter.event; + @postConstruct() protected init(): void { this.preferences.onPreferenceChanged(event => { @@ -72,6 +84,9 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati this.toDispose.push(this.fileService.watch(templateURI, { recursive: true, excludes: [] })); this.toDispose.push(this.fileService.onDidFilesChange(async (event: FileChangesEvent) => { + if (event.changes.some(change => change.resource.toString().endsWith('customAgents.yml'))) { + this.onDidChangeCustomAgentsEmitter.fire(); + } // check deleted templates for (const deletedFile of event.getDeleted()) { if (this.trackedTemplateURIs.has(deletedFile.resource.toString())) { @@ -103,6 +118,7 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati })); + this.onDidChangeCustomAgentsEmitter.fire(); const stat = await this.fileService.resolve(templateURI); if (stat.children === undefined) { return; @@ -194,4 +210,47 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati return undefined; } + async getCustomAgents(): Promise { + const customAgentYamlUri = (await this.getTemplatesDirectoryURI()).resolve('customAgents.yml'); + const yamlExists = await this.fileService.exists(customAgentYamlUri); + if (!yamlExists) { + return []; + } + const filecontent = await this.fileService.read(customAgentYamlUri, { encoding: 'utf-8' }); + try { + const doc = load(filecontent.value); + if (!Array.isArray(doc) || !doc.every(entry => CustomAgentDescription.is(entry))) { + console.debug('Invalid customAgents.yml file content'); + return []; + } + const readAgents = doc as CustomAgentDescription[]; + // make sure all agents are unique (id and name) + const uniqueAgentIds = new Set(); + const uniqueAgens: CustomAgentDescription[] = []; + readAgents.forEach(agent => { + if (uniqueAgentIds.has(agent.id)) { + return; + } + uniqueAgentIds.add(agent.id); + uniqueAgens.push(agent); + }); + return uniqueAgens; + } catch (e) { + console.debug(e.message, e); + return []; + } + } + + async openCustomAgentYaml(): Promise { + const customAgentYamlUri = (await this.getTemplatesDirectoryURI()).resolve('customAgents.yml'); + const content = dump([templateEntry]); + if (! await this.fileService.exists(customAgentYamlUri)) { + await this.fileService.createFile(customAgentYamlUri, BinaryBuffer.fromString(content)); + } else { + const fileContent = (await this.fileService.readFile(customAgentYamlUri)).value; + await this.fileService.writeFile(customAgentYamlUri, BinaryBuffer.concat([fileContent, BinaryBuffer.fromString(content)])); + } + const openHandler = await this.openerService.getOpener(customAgentYamlUri); + openHandler.open(customAgentYamlUri); + } } diff --git a/packages/ai-core/src/browser/style/index.css b/packages/ai-core/src/browser/style/index.css index b325058b20541..36cdad9c19221 100644 --- a/packages/ai-core/src/browser/style/index.css +++ b/packages/ai-core/src/browser/style/index.css @@ -88,3 +88,8 @@ border-radius: calc(var(--theia-ui-padding) * 2 / 3); background: hsla(0, 0%, 68%, 0.31); } + +.configuration-agents-add { + margin-top: 3em; + margin-left: 0; +} diff --git a/packages/ai-core/src/common/agent-service.ts b/packages/ai-core/src/common/agent-service.ts index 1038864bf81e3..7bb5b0f01a57a 100644 --- a/packages/ai-core/src/common/agent-service.ts +++ b/packages/ai-core/src/common/agent-service.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { inject, injectable, named, optional, postConstruct } from '@theia/core/shared/inversify'; -import { ContributionProvider } from '@theia/core'; +import { ContributionProvider, Emitter, Event } from '@theia/core'; import { Agent } from './agent'; import { AISettingsService } from './settings-service'; @@ -48,6 +48,24 @@ export interface AgentService { * @return true if the agent is enabled, false otherwise. */ isEnabled(agentId: string): boolean; + + /** + * Allows to register an agent programmatically. + * @param agent the agent to register + */ + registerAgent(agent: Agent): void; + + /** + * Allows to unregister an agent programmatically. + * @param agentId the agent id to unregister + */ + unregisterAgent(agentId: string): void; + + /** + * Emitted when the list of agents changes. + * This can be used to update the UI when agents are added or removed. + */ + onDidChangeAgents: Event; } @injectable() @@ -63,6 +81,9 @@ export class AgentServiceImpl implements AgentService { protected _agents: Agent[] = []; + private readonly onDidChangeAgentsEmitter = new Emitter(); + readonly onDidChangeAgents = this.onDidChangeAgentsEmitter.event; + @postConstruct() protected init(): void { this.aiSettingsService?.getSettings().then(settings => { @@ -82,6 +103,12 @@ export class AgentServiceImpl implements AgentService { registerAgent(agent: Agent): void { this._agents.push(agent); + this.onDidChangeAgentsEmitter.fire(); + } + + unregisterAgent(agentId: string): void { + this._agents = this._agents.filter(a => a.id !== agentId); + this.onDidChangeAgentsEmitter.fire(); } getAgents(): Agent[] { diff --git a/packages/ai-core/src/common/prompt-service.ts b/packages/ai-core/src/common/prompt-service.ts index 8bc55bedebd8b..44d8fb0622d66 100644 --- a/packages/ai-core/src/common/prompt-service.ts +++ b/packages/ai-core/src/common/prompt-service.ts @@ -69,6 +69,30 @@ export interface PromptService { getAllPrompts(): PromptMap; } +export interface CustomAgentDescription { + id: string; + name: string; + description: string; + prompt: string; + defaultLLM: string; +} +export namespace CustomAgentDescription { + export function is(entry: unknown): entry is CustomAgentDescription { + // eslint-disable-next-line no-null/no-null + return typeof entry === 'object' && entry !== null + && 'id' in entry && typeof entry.id === 'string' + && 'name' in entry && typeof entry.name === 'string' + && 'description' in entry && typeof entry.description === 'string' + && 'prompt' in entry + && typeof entry.prompt === 'string' + && 'defaultLLM' in entry + && typeof entry.defaultLLM === 'string'; + } + export function equals(a: CustomAgentDescription, b: CustomAgentDescription): boolean { + return a.id === b.id && a.name === b.name && a.description === b.description && a.prompt === b.prompt && a.defaultLLM === b.defaultLLM; + } +} + export const PromptCustomizationService = Symbol('PromptCustomizationService'); export interface PromptCustomizationService { /** @@ -109,6 +133,22 @@ export interface PromptCustomizationService { * Event which is fired when the prompt template is changed. */ readonly onDidChangePrompt: Event; + + /** + * Return all custom agents. + * @returns all custom agents + */ + getCustomAgents(): Promise; + + /** + * Event which is fired when custom agents are modified. + */ + readonly onDidChangeCustomAgents: Event; + + /** + * Open the custom agent yaml file. + */ + openCustomAgentYaml(): void; } @injectable() diff --git a/yarn.lock b/yarn.lock index fa8459e47d8b4..e3ca193777616 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2083,6 +2083,11 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== +"@types/js-yaml@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== + "@types/jsdom@^21.1.7": version "21.1.7" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.7.tgz#9edcb09e0b07ce876e7833922d3274149c898cfa" From 0c5f69455d9ee355b1a7ca510ffa63d2b20f0c77 Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Tue, 22 Oct 2024 14:58:48 +0200 Subject: [PATCH 439/441] Notebook: Escaping code completion pop-up disables cell edit mode (#14328) * Notebook: Escaping code completion pop-up disables cell edit mode * Added some more tests --- .github/workflows/playwright.yml | 2 +- .../src/tests/theia-notebook-editor.test.ts | 115 +++++++++++++++++- .../playwright/src/theia-monaco-editor.ts | 13 ++ .../playwright/src/theia-notebook-cell.ts | 17 ++- .../playwright/src/theia-notebook-editor.ts | 2 +- .../notebook-cell-actions-contribution.ts | 2 +- 6 files changed, 146 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 536b87c786875..96be450becff5 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -67,4 +67,4 @@ jobs: path: | examples/playwright/test-results/ examples/playwright/playwright-report/ - retention-days: 2 + retention-days: 7 diff --git a/examples/playwright/src/tests/theia-notebook-editor.test.ts b/examples/playwright/src/tests/theia-notebook-editor.test.ts index 29b77f594479d..3045d25421b87 100644 --- a/examples/playwright/src/tests/theia-notebook-editor.test.ts +++ b/examples/playwright/src/tests/theia-notebook-editor.test.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** -import { PlaywrightWorkerArgs, expect, test } from '@playwright/test'; +import { Locator, PlaywrightWorkerArgs, expect, test } from '@playwright/test'; import { TheiaApp } from '../theia-app'; import { TheiaAppLoader, TheiaPlaywrightTestConfig } from '../theia-app-loader'; import { TheiaNotebookCell } from '../theia-notebook-cell'; @@ -188,8 +188,121 @@ test.describe('Theia Notebook Cell interaction', () => { expect(await cell.executionCount()).toBe('3'); }); + test('Check arrow up and down works', async () => { + const cell = await firstCell(editor); + await editor.addCodeCell(); + const secondCell = (await editor.cells())[1]; + // second cell is selected after creation + expect(await secondCell.isSelected()).toBe(true); + // select cell above + await editor.page.keyboard.type('second cell'); + await secondCell.editor.page.keyboard.press('ArrowUp'); + expect(await cell.isSelected()).toBe(true); + + // select cell below + await cell.app.page.keyboard.press('ArrowDown'); + expect(await secondCell.isSelected()).toBe(true); + }); + + test('Check k(up)/j(down) selection works', async () => { + const cell = await firstCell(editor); + await editor.addCodeCell(); + const secondCell = (await editor.cells())[1]; + // second cell is selected after creation + expect(await secondCell.isSelected()).toBe(true); + await secondCell.selectCell(); // deselect editor focus + + // select cell above + await secondCell.editor.page.keyboard.press('k'); + expect(await cell.isSelected()).toBe(true); + + // select cell below + await cell.app.page.keyboard.press('j'); + expect(await secondCell.isSelected()).toBe(true); + }); + + test('Check x/c/v works', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("First cell")'); + await editor.addCodeCell(); + const secondCell = (await editor.cells())[1]; + await secondCell.addEditorText('print("Second cell")'); + await secondCell.selectCell(); // deselect editor focus + + // cut second cell + await secondCell.page.keyboard.press('x'); + await editor.waitForCellCountChanged(2); + expect((await editor.cells()).length).toBe(1); + + // paste second cell + await cell.selectCell(); + await cell.page.keyboard.press('v'); + await editor.waitForCellCountChanged(1); + expect((await editor.cells()).length).toBe(2); + const pastedCell = (await editor.cells())[1]; + expect(await pastedCell.isSelected()).toBe(true); + + // copy first cell + await cell.selectCell(); // deselect editor focus + await cell.page.keyboard.press('c'); + // paste copied cell + await cell.page.keyboard.press('v'); + await editor.waitForCellCountChanged(2); + expect((await editor.cells()).length).toBe(3); + expect(await (await editor.cells())[0].editorText()).toBe('print("First cell")'); + expect(await (await editor.cells())[1].editorText()).toBe('print("First cell")'); + expect(await (await editor.cells())[2].editorText()).toBe('print("Second cell")'); + }); + + test('Check LineNumber switch `l` works', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("First cell")'); + await cell.selectCell(); + await cell.page.keyboard.press('l'); + // NOTE: div.line-numbers is not visible + await cell.editor.locator.locator('.overflow-guard > div.line-numbers').waitFor({ state: 'attached' }); + }); + + test('Check Collapse output switch `o` works', async () => { + const cell = await firstCell(editor); + await cell.addEditorText('print("Check output collapse")'); + await cell.selectCell(); + await cell.execute(); // produce output + expect(await cell.outputText()).toBe('Check output collapse'); + + await cell.page.keyboard.press('o'); + await (await cell.outputContainer()).waitFor({ state: 'hidden' }); + await cell.page.keyboard.press('o'); + await (await cell.outputContainer()).waitFor({ state: 'visible' }); + + expect(await cell.outputText()).toBe('Check output collapse'); + }); + + test('Check arrow-up/arrow-down/escape with code completion', async () => { + await editor.addMarkdownCell(); + const mdCell = (await editor.cells())[1]; + await mdCell.addEditorText('h'); + + await editor.page.keyboard.press('Control+Space'); // call CC (suggestWidgetVisible=true) + await ensureCodeCompletionVisible(mdCell.editor.locator); + await editor.page.keyboard.press('Escape'); // close CC + // check the same cell still selected and not lose the edit mode + expect(await mdCell.editor.isFocused()).toBe(true); + + await editor.page.keyboard.press('Control+Space'); // call CC (suggestWidgetVisible=true) + await ensureCodeCompletionVisible(mdCell.editor.locator); + await editor.page.keyboard.press('ArrowUp'); // select next entry in CC list + await editor.page.keyboard.press('Enter'); // apply completion + // check the same cell still selected and not the second one due to 'ArrowDown' being pressed + expect(await mdCell.isSelected()).toBe(true); + + }); }); +async function ensureCodeCompletionVisible(parent: Locator): Promise { + await parent.locator('div.monaco-editor div.suggest-widget').waitFor({ timeout: 5000 }); +} + async function firstCell(editor: TheiaNotebookEditor): Promise { return (await editor.cells())[0]; } diff --git a/examples/playwright/src/theia-monaco-editor.ts b/examples/playwright/src/theia-monaco-editor.ts index 7e290df0509e5..7cfbf4b492b65 100644 --- a/examples/playwright/src/theia-monaco-editor.ts +++ b/examples/playwright/src/theia-monaco-editor.ts @@ -104,6 +104,19 @@ export class TheiaMonacoEditor extends TheiaPageObject { await this.page.keyboard.type(text); } + /** + * @returns `true` if the editor is focused, `false` otherwise. + */ + async isFocused(): Promise { + const viewElement = await this.viewElement(); + const monacoEditor = await viewElement?.$('div.monaco-editor'); + if (!monacoEditor) { + throw new Error('Couldn\'t retrieve monaco editor element.'); + } + const editorClass = await monacoEditor.getAttribute('class'); + return editorClass?.includes('focused') ?? false; + } + protected replaceEditorSymbolsWithSpace(content: string): string | Promise { // [ ]   => \u00a0 -- NO-BREAK SPACE // [·] · => \u00b7 -- MIDDLE DOT diff --git a/examples/playwright/src/theia-notebook-cell.ts b/examples/playwright/src/theia-notebook-cell.ts index 22859950f63fd..0478a680ac8c4 100644 --- a/examples/playwright/src/theia-notebook-cell.ts +++ b/examples/playwright/src/theia-notebook-cell.ts @@ -177,6 +177,14 @@ export class TheiaNotebookCell extends TheiaPageObject { return text?.substring(1, text.length - 1); } + /** + * @returns `true` if the cell is selected (blue vertical line), `false` otherwise. + */ + async isSelected(): Promise { + const markerClass = await this.locator.locator('div.theia-notebook-cell-marker').getAttribute('class'); + return markerClass?.includes('theia-notebook-cell-marker-selected') ?? false; + } + /** * @returns The output text of the cell. */ @@ -190,7 +198,14 @@ export class TheiaNotebookCell extends TheiaPageObject { return spanTexts.join(''); } - protected async outputContainer(): Promise { + /** + * Selects the cell itself not it's editor. Important for shortcut usage like copy-, cut-, paste-cell. + */ + async selectCell(): Promise { + await this.sidebar().click(); + } + + async outputContainer(): Promise { const outFrame = await this.outputFrame(); // each cell has it's own output div with a unique id = cellHandle const cellOutput = outFrame.locator(`div#cellHandle${await this.cellHandle()}`); diff --git a/examples/playwright/src/theia-notebook-editor.ts b/examples/playwright/src/theia-notebook-editor.ts index d07964edfe718..31ddbd7c3ef42 100644 --- a/examples/playwright/src/theia-notebook-editor.ts +++ b/examples/playwright/src/theia-notebook-editor.ts @@ -127,7 +127,7 @@ export class TheiaNotebookEditor extends TheiaEditor { await this.waitForCellCountChanged(currentCellsCount); } - protected async waitForCellCountChanged(prevCount: number): Promise { + async waitForCellCountChanged(prevCount: number): Promise { await this.viewLocator().locator('li.theia-notebook-cell').evaluateAll( (elements, currentCount) => elements.length !== currentCount, prevCount ); diff --git a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts index 87d656cb7cb44..38c50a753c21a 100644 --- a/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts +++ b/packages/notebook/src/browser/contributions/notebook-cell-actions-contribution.ts @@ -516,7 +516,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command { command: NotebookCellCommands.STOP_EDIT_COMMAND.id, keybinding: 'esc', - when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`, + when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && !suggestWidgetVisible`, }, { command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, From e8e2e927dbc927b3dce37b27ac9290775e7ef42c Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Thu, 24 Oct 2024 11:14:23 +0200 Subject: [PATCH 440/441] Show error message when uploading fails (#14349) --- packages/filesystem/src/browser/file-upload-service.ts | 5 +++++ packages/filesystem/src/node/node-file-upload-service.ts | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/filesystem/src/browser/file-upload-service.ts b/packages/filesystem/src/browser/file-upload-service.ts index 9447880b7daea..277ebb4837608 100644 --- a/packages/filesystem/src/browser/file-upload-service.ts +++ b/packages/filesystem/src/browser/file-upload-service.ts @@ -254,6 +254,7 @@ export class FileUploadService { } catch (error) { uploadSemaphore.cancel(); if (!isCancelled(error)) { + this.messageService.error(nls.localize('theia/filesystem/uploadFailed', 'An error occurred while uploading a file. {0}', error.message)); throw error; } } @@ -348,6 +349,10 @@ export class FileUploadService { unregister(); if (xhr.status === 200) { resolve(); + } else if (xhr.status === 500 && xhr.statusText !== xhr.response) { + // internal error with cause message + // see packages/filesystem/src/node/node-file-upload-service.ts + reject(new Error(`Internal server error: ${xhr.response}`)); } else { reject(new Error(`POST request failed: ${xhr.status} ${xhr.statusText}`)); } diff --git a/packages/filesystem/src/node/node-file-upload-service.ts b/packages/filesystem/src/node/node-file-upload-service.ts index efd063348ecde..9687ad761bd07 100644 --- a/packages/filesystem/src/node/node-file-upload-service.ts +++ b/packages/filesystem/src/node/node-file-upload-service.ts @@ -73,7 +73,13 @@ export class NodeFileUploadService implements BackendApplicationContribution { response.status(200).send(target); // ok } catch (error) { console.error(error); - response.sendStatus(500); // internal server error + if (error.message) { + // internal server error with error message as response + response.status(500).send(error.message); + } else { + // default internal server error + response.sendStatus(500); + } } } From c1e4279cf85aff0b4fdfccbe605fc391aa599e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=A4der?= Date: Thu, 24 Oct 2024 16:14:03 +0200 Subject: [PATCH 441/441] Accept a string argument in env.openExternal API (#14350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #14335 contributed on behalf of STMicroelectronics Signed-off-by: Thomas Mäder --- packages/plugin-ext/src/plugin/window-state.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/plugin-ext/src/plugin/window-state.ts b/packages/plugin-ext/src/plugin/window-state.ts index ed6d4b9cbe208..9492973d6250e 100644 --- a/packages/plugin-ext/src/plugin/window-state.ts +++ b/packages/plugin-ext/src/plugin/window-state.ts @@ -55,7 +55,13 @@ export class WindowStateExtImpl implements WindowStateExt { this.windowStateChangedEmitter.fire(this.windowStateCached); } - openUri(uri: URI): Promise { + async openUri(uriOrString: URI | string): Promise { + let uri: URI; + if (typeof uriOrString === 'string') { + uri = URI.parse(uriOrString); + } else { + uri = uriOrString; + } if (!uri.scheme.trim().length) { throw new Error('Invalid scheme - cannot be empty'); }