diff --git a/.changeset/stale-eagles-yell.md b/.changeset/stale-eagles-yell.md new file mode 100644 index 000000000000..f916b40d4eb5 --- /dev/null +++ b/.changeset/stale-eagles-yell.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/quick-edit": minor +--- + +Bump base VSCode version for Quick Edit & Playground to 1.102.1 diff --git a/.github/workflows/deploy-pages-previews.yml b/.github/workflows/deploy-pages-previews.yml index 4103eda41b33..2dcb7913f0f6 100644 --- a/.github/workflows/deploy-pages-previews.yml +++ b/.github/workflows/deploy-pages-previews.yml @@ -33,9 +33,8 @@ jobs: if: github.repository_owner == 'cloudflare' && github.head_ref != 'main' && (contains(github.event.*.labels.*.name, 'preview:chrome-devtools-patches') || contains(github.event.*.labels.*.name, 'preview:quick-edit') || contains(github.event.*.labels.*.name, 'preview:workers-playground')) timeout-minutes: 60 concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - runs-on: ubuntu-22.04 + group: ${{ github.workflow }}-${{ github.ref }}-app-previews + runs-on: macos-latest-large steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -44,6 +43,8 @@ jobs: - name: Install Dependencies uses: ./.github/actions/install-dependencies + with: + node-version: 22 - name: Build tools and libraries run: pnpm run build @@ -64,11 +65,11 @@ jobs: - name: Build and Deploy Quick Edit preview if: contains(github.event.*.labels.*.name, 'preview:quick-edit') - # Quick Edit requires yarn and VS Code build deps, so needs fairly specific logic - run: pnpm --filter quick-edit run deploy + run: pnpm --filter quick-edit run preview env: DEBIAN_FRONTEND: noninteractive CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + NODE_OPTIONS: "--max_old_space_size=30000" - name: Deploy Workers Playground preview if: contains(github.event.*.labels.*.name, 'preview:workers-playground') diff --git a/packages/quick-edit-extension/src/cfs.ts b/packages/quick-edit-extension/src/cfs.ts index 0d899d1f0137..0ad131fe2f51 100644 --- a/packages/quick-edit-extension/src/cfs.ts +++ b/packages/quick-edit-extension/src/cfs.ts @@ -194,16 +194,20 @@ declare module "*.bin" { } } } - await this.writeFile(Uri.parse(`${this.rootFolder}/${path}`), contents, { - create: true, - overwrite: true, - suppressChannelUpdate: true, - readOnly: files.readOnly, - }); + await this.writeFile( + Uri.parse(`cfs:/${this.rootFolder}/${path}`), + contents, + { + create: true, + overwrite: true, + suppressChannelUpdate: true, + readOnly: files.readOnly, + } + ); } if (this.readRoot !== null) { await this.readRoot( - await this.readDirectory(Uri.parse(`${this.rootFolder}/`)) + await this.readDirectory(Uri.parse(`cfs:/${this.rootFolder}/`)) ); } } diff --git a/packages/quick-edit-extension/vscode.d.ts b/packages/quick-edit-extension/vscode.d.ts index 1c8a513e3f4f..16cb98ca7c94 100644 --- a/packages/quick-edit-extension/vscode.d.ts +++ b/packages/quick-edit-extension/vscode.d.ts @@ -116,6 +116,24 @@ declare module 'vscode' { */ readonly languageId: string; + /** + * The file encoding of this document that will be used when the document is saved. + * + * Use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event to + * get notified when the document encoding changes. + * + * Note that the possible encoding values are currently defined as any of the following: + * 'utf8', 'utf8bom', 'utf16le', 'utf16be', 'windows1252', 'iso88591', 'iso88593', + * 'iso885915', 'macroman', 'cp437', 'windows1256', 'iso88596', 'windows1257', + * 'iso88594', 'iso885914', 'windows1250', 'iso88592', 'cp852', 'windows1251', + * 'cp866', 'cp1125', 'iso88595', 'koi8r', 'koi8u', 'iso885913', 'windows1253', + * 'iso88597', 'windows1255', 'iso88598', 'iso885910', 'iso885916', 'windows1254', + * 'iso88599', 'windows1258', 'gbk', 'gb18030', 'cp950', 'big5hkscs', 'shiftjis', + * 'eucjp', 'euckr', 'windows874', 'iso885911', 'koi8ru', 'koi8t', 'gb2312', + * 'cp865', 'cp850'. + */ + readonly encoding: string; + /** * The version number of this document (it will strictly increase after each * change, including undo/redo). @@ -157,7 +175,7 @@ declare module 'vscode' { * that the returned object is *not* live and changes to the * document are not reflected. * - * @param line A line number in [0, lineCount). + * @param line A line number in `[0, lineCount)`. * @returns A {@link TextLine line}. */ lineAt(line: number): TextLine; @@ -182,14 +200,14 @@ declare module 'vscode' { * The position will be {@link TextDocument.validatePosition adjusted}. * * @param position A position. - * @returns A valid zero-based offset. + * @returns A valid zero-based offset in UTF-16 [code units](https://developer.mozilla.org/en-US/docs/Glossary/Code_unit). */ offsetAt(position: Position): number; /** * Converts a zero-based offset to a position. * - * @param offset A zero-based offset. + * @param offset A zero-based offset into the document. This offset is in UTF-16 [code units](https://developer.mozilla.org/en-US/docs/Glossary/Code_unit). * @returns A valid {@link Position}. */ positionAt(offset: number): Position; @@ -257,6 +275,8 @@ declare module 'vscode' { /** * The zero-based character value. + * + * Character offsets are expressed using UTF-16 [code units](https://developer.mozilla.org/en-US/docs/Glossary/Code_unit). */ readonly character: number; @@ -501,13 +521,13 @@ declare module 'vscode' { * The position at which the selection starts. * This position might be before or after {@link Selection.active active}. */ - anchor: Position; + readonly anchor: Position; /** * The position of the cursor. * This position might be before or after {@link Selection.anchor anchor}. */ - active: Position; + readonly active: Position; /** * Create a selection from two positions. @@ -530,12 +550,12 @@ declare module 'vscode' { /** * A selection is reversed if its {@link Selection.anchor anchor} is the {@link Selection.end end} position. */ - isReversed: boolean; + readonly isReversed: boolean; } /** * Represents sources that can cause {@link window.onDidChangeTextEditorSelection selection change events}. - */ + */ export enum TextEditorSelectionChangeKind { /** * Selection changed due to typing in the editor. @@ -657,7 +677,11 @@ declare module 'vscode' { /** * 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, } /** @@ -893,6 +917,11 @@ declare module 'vscode' { */ export class ThemeColor { + /** + * The id of this color. + */ + readonly id: string; + /** * Creates a reference to a theme color. * @param id of the color. The available colors are listed in https://code.visualstudio.com/api/references/theme-color. @@ -937,6 +966,21 @@ declare module 'vscode' { constructor(id: string, color?: ThemeColor); } + /** + * Represents an icon in the UI. This is either an uri, separate uris for the light- and dark-themes, + * or a {@link ThemeIcon theme icon}. + */ + export type IconPath = Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; + /** * Represents theme specific rendering styles for a {@link TextEditorDecorationType text editor decoration}. */ @@ -1069,7 +1113,7 @@ declare module 'vscode' { } /** - * Represents theme specific rendeirng styles for {@link ThemableDecorationRenderOptions.before before} and + * Represents theme specific rendering styles for {@link ThemableDecorationRenderOptions.before before} and * {@link ThemableDecorationRenderOptions.after after} the content of text decorations. */ export interface ThemableDecorationAttachmentRenderOptions { @@ -1288,6 +1332,10 @@ declare module 'vscode' { * Add undo stop after making the edits. */ readonly undoStopAfter: boolean; + /** + * Keep whitespace of the {@link SnippetString.value} as is. + */ + readonly keepWhitespace?: boolean; }): Thenable; /** @@ -1352,7 +1400,7 @@ declare module 'vscode' { export interface TextEditorEdit { /** * Replace a certain text region with a new value. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. * * @param location The range this operation should remove. * @param value The new text this operation should insert after removing `location`. @@ -1361,7 +1409,7 @@ declare module 'vscode' { /** * Insert text at a location. - * You can use \r\n or \n in `value` and they will be normalized to the current {@link TextDocument document}. + * You can use `\r\n` or `\n` in `value` and they will be normalized to the current {@link TextDocument document}. * Although the equivalent text edit can be made with {@link TextEditorEdit.replace replace}, `insert` will produce a different resulting selection (it will get moved). * * @param location The position where the new text should be inserted. @@ -1530,7 +1578,7 @@ declare module 'vscode' { * ```ts * const u = URI.parse('file://server/c$/folder/file.txt') * u.authority === 'server' - * u.path === '/shares/c$/file.txt' + * u.path === '/c$/folder/file.txt' * u.fsPath === '\\server\c$\folder\file.txt' * ``` */ @@ -1847,6 +1895,9 @@ declare module 'vscode' { /** * A human-readable string which is rendered prominent. Supports rendering of {@link ThemeIcon theme icons} via * the `$()`-syntax. + * + * Note: When {@link QuickPickItem.kind kind} is set to {@link QuickPickItemKind.Default} (so a regular item + * instead of a separator), it supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax. */ label: string; @@ -1859,16 +1910,7 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the QuickPickItem. */ - iconPath?: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; + iconPath?: IconPath; /** * A human-readable string which is rendered less prominent in the same line. Supports rendering of @@ -2012,10 +2054,10 @@ declare module 'vscode' { /** * A set of file filters that are used by the dialog. Each entry is a human-readable label, - * like "TypeScript", and an array of extensions, e.g. + * like "TypeScript", and an array of extensions, for example: * ```ts * { - * 'Images': ['png', 'jpg'] + * 'Images': ['png', 'jpg'], * 'TypeScript': ['ts', 'tsx'] * } * ``` @@ -2047,10 +2089,10 @@ declare module 'vscode' { /** * A set of file filters that are used by the dialog. Each entry is a human-readable label, - * like "TypeScript", and an array of extensions, e.g. + * like "TypeScript", and an array of extensions, for example: * ```ts * { - * 'Images': ['png', 'jpg'] + * 'Images': ['png', 'jpg'], * 'TypeScript': ['ts', 'tsx'] * } * ``` @@ -2475,7 +2517,7 @@ declare module 'vscode' { static readonly SourceFixAll: CodeActionKind; /** - * Base kind for all code actions applying to the enitre notebook's scope. CodeActionKinds using + * Base kind for all code actions applying to the entire notebook's scope. CodeActionKinds using * this should always begin with `notebook.` * * This requires that new CodeActions be created for it and contributed via extensions. @@ -2493,7 +2535,7 @@ declare module 'vscode' { static readonly Notebook: CodeActionKind; /** - * Private constructor, use statix `CodeActionKind.XYZ` to derive from an existing code action kind. + * Private constructor, use static `CodeActionKind.XYZ` to derive from an existing code action kind. * * @param value The value of the kind, such as `refactor.extract.function`. */ @@ -2514,7 +2556,7 @@ declare module 'vscode' { /** * Checks if this code action kind intersects `other`. * - * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and ``"refactor.extract.function"`, + * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and `"refactor.extract.function"`, * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"`. * * @param other Kind to check. @@ -2689,7 +2731,7 @@ declare module 'vscode' { * We also support returning `Command` for legacy reasons, however all new extensions should return * `CodeAction` object instead. */ - provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(Command | T)[]>; + provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult>; /** * Given a code action fill in its {@linkcode CodeAction.edit edit}-property. Changes to @@ -3646,6 +3688,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. + * @param context Additional information about the references request. * @param token A cancellation token. * * @returns An array of locations or a thenable that resolves to such. The lack of a result can be @@ -3760,6 +3803,11 @@ declare module 'vscode' { */ snippet: SnippetString; + /** + * Whether the snippet edit should be applied with existing whitespace preserved. + */ + keepWhitespace?: boolean; + /** * Create a new snippet edit. * @@ -3866,16 +3914,7 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the edit. */ - iconPath?: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; + iconPath?: IconPath; } /** @@ -3952,7 +3991,7 @@ declare module 'vscode' { * @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. @@ -3968,7 +4007,7 @@ declare module 'vscode' { * @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. @@ -4206,7 +4245,7 @@ declare module 'vscode' { /** * Creates a semantic tokens builder. * - * @param legend A semantic tokens legent. + * @param legend A semantic tokens legend. */ constructor(legend?: SemanticTokensLegend); @@ -4679,7 +4718,7 @@ declare module 'vscode' { /** * The currently active {@linkcode SignatureHelp}. * - * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * The `activeSignatureHelp` has its {@linkcode SignatureHelp.activeSignature activeSignature} field updated based on * the user arrowing through available signatures. */ readonly activeSignatureHelp: SignatureHelp | undefined; @@ -5182,7 +5221,7 @@ declare module 'vscode' { /** * Creates a new list of inline completion items. - */ + */ constructor(items: InlineCompletionItem[]); } @@ -5352,22 +5391,22 @@ declare module 'vscode' { export class Color { /** - * The red component of this color in the range [0-1]. + * The red component of this color in the range `[0-1]`. */ readonly red: number; /** - * The green component of this color in the range [0-1]. + * The green component of this color in the range `[0-1]`. */ readonly green: number; /** - * The blue component of this color in the range [0-1]. + * The blue component of this color in the range `[0-1]`. */ readonly blue: number; /** - * The alpha component of this color in the range [0-1]. + * The alpha component of this color in the range `[0-1]`. */ readonly alpha: number; @@ -5489,7 +5528,7 @@ declare module 'vscode' { */ export enum InlayHintKind { /** - * An inlay hint that for a type annotation. + * An inlay hint that is for a type annotation. */ Type = 1, /** @@ -6066,10 +6105,88 @@ declare module 'vscode' { provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } + /** + * Identifies a {@linkcode DocumentDropEdit} or {@linkcode DocumentPasteEdit} + */ + export class DocumentDropOrPasteEditKind { + static readonly Empty: DocumentDropOrPasteEditKind; + + /** + * The root kind for basic text edits. + * + * This kind should be used for edits that insert basic text into the document. A good example of this is + * an edit that pastes the clipboard text while also updating imports in the file based on the pasted text. + * For this we could use a kind such as `text.updateImports.someLanguageId`. + * + * Even though most drop/paste edits ultimately insert text, you should not use {@linkcode Text} as the base kind + * for every edit as this is redundant. Instead a more specific kind that describes the type of content being + * inserted should be used instead. For example, if the edit adds a Markdown link, use `markdown.link` since even + * though the content being inserted is text, it's more important to know that the edit inserts Markdown syntax. + */ + static readonly Text: DocumentDropOrPasteEditKind; + + /** + * Root kind for edits that update imports in a document in addition to inserting text. + */ + static readonly TextUpdateImports: DocumentDropOrPasteEditKind; + + /** + * Use {@linkcode DocumentDropOrPasteEditKind.Empty} instead. + */ + 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; + } + /** * An edit operation applied {@link DocumentDropEditProvider on drop}. */ export class 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[]; + /** * The text or snippet to insert at the drop location. */ @@ -6082,8 +6199,10 @@ declare module 'vscode' { /** * @param insertText The text or snippet to insert at the drop location. + * @param title Human readable label that describes the edit. + * @param kind {@link DocumentDropOrPasteEditKind Kind} of the edit. */ - constructor(insertText: string | SnippetString); + constructor(insertText: string | SnippetString, title?: string, kind?: DocumentDropOrPasteEditKind); } /** @@ -6093,7 +6212,7 @@ declare module 'vscode' { * and dropping files, users can hold down `shift` to drop the file into the editor instead of opening it. * Requires `editor.dropIntoEditor.enabled` to be on. */ - export interface DocumentDropEditProvider { + export interface DocumentDropEditProvider { /** * Provide edits which inserts the content being dragged and dropped into the document. * @@ -6105,7 +6224,212 @@ declare module 'vscode' { * @returns A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; + 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 edit 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[]; + } + + + /** + * 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, + } + + /** + * Additional information about the paste operation. + */ + export interface DocumentPasteEditContext { + + /** + * Requested kind of paste edits to return. + * + * When a explicit kind if requested by {@linkcode DocumentPasteTriggerKind.PasteAs PasteAs}, providers are + * encourage to be more flexible when generating an edit of the requested kind. + */ + readonly only: DocumentDropOrPasteEditKind | undefined; + + /** + * The reason why paste edits were requested. + */ + readonly triggerKind: DocumentPasteTriggerKind; + } + + /** + * Provider invoked when the user copies or pastes in a {@linkcode TextDocument}. + */ + export interface DocumentPasteEditProvider { + + /** + * Optional method invoked after the user copies from a {@link TextEditor text editor}. + * + * 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 window. + * This means that any added metadata cannot be seen by other editor windows or by other applications. + * + * @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 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 {@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 context Additional context for the paste. + * @param token A cancellation token. + * + * @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; + + /** + * 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 {@linkcode DocumentPasteEdit.insertText} or {@linkcode 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 edit the applies a paste operation. + */ + export class DocumentPasteEdit { + + /** + * Human readable label that describes the edit. + */ + title: string; + + /** + * {@link DocumentDropOrPasteEditKind Kind} of the edit. + */ + 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; + + /** + * An optional additional edit to apply on paste. + */ + additionalEdit?: WorkspaceEdit; + + /** + * 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 possible paste edits shown to the user. + */ + 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 DocumentDropOrPasteEditKind Kind} of the edit. + */ + constructor(insertText: string | SnippetString, title: string, kind: DocumentDropOrPasteEditKind); + } + + /** + * Provides additional metadata about how a {@linkcode DocumentPasteEditProvider} works. + */ + export interface DocumentPasteProviderMetadata { + /** + * List of {@link DocumentDropOrPasteEditKind kinds} that the provider may return in {@linkcode DocumentPasteEditProvider.provideDocumentPasteEdits provideDocumentPasteEdits}. + * + * This is used to filter out providers when a specific {@link DocumentDropOrPasteEditKind kind} of edit is requested. + */ + readonly providedPasteEditKinds: readonly DocumentDropOrPasteEditKind[]; + + /** + * Mime types that {@linkcode DocumentPasteEditProvider.prepareDocumentPaste prepareDocumentPaste} may add on copy. + */ + readonly copyMimeTypes?: readonly string[]; + + /** + * 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 {@linkcode DataTransfer}. + * 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[]; } /** @@ -6275,7 +6599,9 @@ declare module 'vscode' { * If the language supports Unicode identifiers (e.g. JavaScript), it is preferable * to provide a word definition that uses exclusion of known separators. * e.g.: A regex that matches anything except known separators (and dot is allowed to occur in a floating point number): - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + * ``` + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + * ``` */ wordPattern?: RegExp; /** @@ -6361,7 +6687,7 @@ declare module 'vscode' { export enum ConfigurationTarget { /** * Global configuration - */ + */ Global = 1, /** @@ -6502,7 +6828,7 @@ declare module 'vscode' { workspaceValue?: T; /** - * The workpace-folder-specific value. + * The workspace-folder-specific value. */ workspaceFolderValue?: T; @@ -7325,13 +7651,25 @@ declare module 'vscode' { */ 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 integration 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; + /** * Send text to the terminal. The text is written to the stdin of the underlying pty process * (shell) of the terminal. * * @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(s) added are \n or \r\n, depending on the platform. This defaults to `true`. + * The character(s) added are `\n` or `\r\n`, depending on the platform. This defaults to `true`. */ sendText(text: string, shouldExecute?: boolean): void; @@ -7416,34 +7754,363 @@ declare module 'vscode' { * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html */ readonly isInteractedWith: boolean; + + /** + * The detected shell type of the {@link Terminal}. This will be `undefined` when there is + * not a clear signal as to what the shell is, or the shell is not supported yet. This + * value should change to the shell type of a sub-shell when launched (for example, running + * `bash` inside `zsh`). + * + * Note that the possible values are currently defined as any of the following: + * 'bash', 'cmd', 'csh', 'fish', 'gitbash', 'julia', 'ksh', 'node', 'nu', 'pwsh', 'python', + * 'sh', 'wsl', 'zsh'. + */ + readonly shell: string | undefined; } /** - * Provides information on a line in a terminal in order to provide links for it. + * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal. */ - export interface TerminalLinkContext { + export interface TerminalShellIntegration { /** - * This is the text from the unwrapped line in the terminal. + * 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. */ - line: string; + readonly cwd: Uri | undefined; /** - * The terminal the link belongs to. + * 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. + * } */ - terminal: Terminal; + 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 executable A command to run. + * @param args Arguments to launch the executable with. The arguments will be escaped such + * that they are interpreted as single arguments when the argument both contains whitespace + * and does not include any single quote, double quote or backtick characters. + * + * Note that this escaping is not intended to be a security measure, be careful when passing + * untrusted data to this API as strings like `$(...)` can often be used in shells to + * execute code within a string. + * + * @example + * // Execute a command in a terminal immediately after being created + * const myTerm = window.createTerminal(); + * window.onDidChangeTerminalShellIntegration(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; } /** - * A provider that enables detection and handling of links within terminals. + * A command that was executed in a terminal. */ - export interface TerminalLinkProvider { + export interface TerminalShellExecution { /** - * Provide terminal links for the given context. Note that this can be called multiple times - * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) - * that could have problems when asynchronous usage may overlap. - * @param context Information about what links are being provided for. - * @param token A cancellation token. - * @returns A list of terminal links for the given line. + * 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'); + * } + */ + 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. + */ + export 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. + */ + export interface TerminalShellIntegrationChangeEvent { + /** + * The terminal that shell integration has been activated in. + */ + readonly terminal: Terminal; + + /** + * The shell integration object. + */ + readonly shellIntegration: TerminalShellIntegration; + } + + /** + * An event signalling that an execution has started in a terminal. + */ + 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; + } + + /** + * An event signalling that an execution has ended in a terminal. + */ + 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. + * + * When this is `undefined` it can mean several things: + * + * - The shell either did not report an exit code (ie. the shell integration script is + * misbehaving) + * - The shell reported a command started before the command finished (eg. a sub-shell was + * opened). + * - The user canceled the command via ctrl+c. + * - The user pressed enter when there was no input. + * + * 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'); + * } + * } + * }); + */ + readonly exitCode: number | undefined; + } + + /** + * Provides information on a line in a terminal in order to provide links for it. + */ + export interface TerminalLinkContext { + /** + * This is the text from the unwrapped line in the terminal. + */ + line: string; + + /** + * The terminal the link belongs to. + */ + terminal: Terminal; + } + + /** + * A provider that enables detection and handling of links within terminals. + */ + export interface TerminalLinkProvider { + /** + * Provide terminal links for the given context. Note that this can be called multiple times + * even before previous calls resolve, make sure to not share global objects (eg. `RegExp`) + * that could have problems when asynchronous usage may overlap. + * @param context Information about what links are being provided for. + * @param token A cancellation token. + * @returns A list of terminal links for the given line. */ provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult; @@ -7732,8 +8399,8 @@ declare module 'vscode' { }; /** - * A storage utility for secrets. Secrets are persisted across reloads and are independent of the - * current opened {@link workspace.workspaceFolders workspace}. + * A secret storage object that stores state independent + * of the current opened {@link workspace.workspaceFolders workspace}. */ readonly secrets: SecretStorage; @@ -7775,7 +8442,7 @@ declare module 'vscode' { * {@linkcode ExtensionContext.globalState globalState} to store key value data. * * @see {@linkcode FileSystem workspace.fs} for how to read and write files and folders from - * an uri. + * a uri. */ readonly storageUri: Uri | undefined; @@ -7843,6 +8510,13 @@ declare module 'vscode' { * The current `Extension` instance. */ readonly extension: Extension; + + /** + * An object that keeps information about how this extension can use language models. + * + * @see {@link LanguageModelChat.sendRequest} + */ + readonly languageModelAccessInformation: LanguageModelAccessInformation; } /** @@ -7899,8 +8573,10 @@ declare module 'vscode' { } /** - * Represents a storage utility for secrets, information that is - * sensitive. + * Represents a storage utility for secrets (or any information that is sensitive) + * that will be stored encrypted. The implementation of the secret storage will + * be different on each platform and the secrets will not be synced across + * machines. */ export interface SecretStorage { /** @@ -8318,7 +8994,7 @@ declare module 'vscode' { * @param args The command arguments. * @param options Optional options for the started the shell. */ - constructor(command: string | ShellQuotedString, args: (string | ShellQuotedString)[], options?: ShellExecutionOptions); + constructor(command: string | ShellQuotedString, args: Array, options?: ShellExecutionOptions); /** * The shell command line. Is `undefined` if created with a command and arguments. @@ -8328,12 +9004,12 @@ declare module 'vscode' { /** * The shell command. Is `undefined` if created with a full command line. */ - command: string | ShellQuotedString; + command: string | ShellQuotedString | undefined; /** * The shell args. Is `undefined` if created with a full command line. */ - args: (string | ShellQuotedString)[]; + args: Array | undefined; /** * The shell options used when the command line is executed in a shell. @@ -8535,7 +9211,7 @@ declare module 'vscode' { * * This interface is not intended to be implemented. */ - interface TaskStartEvent { + export interface TaskStartEvent { /** * The task item representing the task that got started. */ @@ -8547,7 +9223,7 @@ declare module 'vscode' { * * This interface is not intended to be implemented. */ - interface TaskEndEvent { + export interface TaskEndEvent { /** * The task item representing the task that finished. */ @@ -9339,7 +10015,7 @@ declare module 'vscode' { /** * A panel that contains a webview. */ - interface WebviewPanel { + export interface WebviewPanel { /** * Identifies the type of the webview panel, such as `'markdown.preview'`. */ @@ -9469,7 +10145,7 @@ declare module 'vscode' { * * @param T Type of the webview's state. */ - interface WebviewPanelSerializer { + export interface WebviewPanelSerializer { /** * Restore a webview panel from its serialized `state`. * @@ -9560,7 +10236,7 @@ declare module 'vscode' { * * @param T Type of the webview's state. */ - interface WebviewViewResolveContext { + export interface WebviewViewResolveContext { /** * Persisted state from the webview content. * @@ -9598,7 +10274,7 @@ declare module 'vscode' { */ export interface WebviewViewProvider { /** - * Revolves a webview view. + * Resolves a webview view. * * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is * first loaded or when the user hides and then shows a view again. @@ -9650,7 +10326,7 @@ declare module 'vscode' { * Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a `CustomDocument` is * managed by the editor. When no more references remain to a `CustomDocument`, it is disposed of. */ - interface CustomDocument { + export interface CustomDocument { /** * The associated uri for this document. */ @@ -9670,7 +10346,7 @@ declare module 'vscode' { * * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. */ - interface CustomDocumentEditEvent { + export interface CustomDocumentEditEvent { /** * The document that the edit is for. @@ -9709,7 +10385,7 @@ declare module 'vscode' { * * @see {@linkcode CustomEditorProvider.onDidChangeCustomDocument}. */ - interface CustomDocumentContentChangeEvent { + export interface CustomDocumentContentChangeEvent { /** * The document that the change is for. */ @@ -9719,7 +10395,7 @@ declare module 'vscode' { /** * A backup for an {@linkcode CustomDocument}. */ - interface CustomDocumentBackup { + export interface CustomDocumentBackup { /** * Unique identifier for the backup. * @@ -9739,7 +10415,7 @@ declare module 'vscode' { /** * Additional information used to implement {@linkcode CustomDocumentBackup}. */ - interface CustomDocumentBackupContext { + export interface CustomDocumentBackupContext { /** * Suggested file location to write the new backup. * @@ -9755,7 +10431,7 @@ declare module 'vscode' { /** * Additional information about the opening custom document. */ - interface CustomDocumentOpenContext { + export interface CustomDocumentOpenContext { /** * The id of the backup to restore the document from or `undefined` if there is no backup. * @@ -10295,6 +10971,12 @@ declare module 'vscode' { * 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; } /** @@ -10440,13 +11122,32 @@ declare module 'vscode' { */ export const onDidChangeTerminalState: Event; + /** + * 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; + /** * Represents the current window's state. */ export const state: WindowState; /** - * An {@link Event} which fires when the focus state of the current window + * An {@link Event} which fires when the focus or activity state of the current window * changes. The value of the event represents whether the window is focused. */ export const onDidChangeWindowState: Event; @@ -11099,8 +11800,8 @@ declare module 'vscode' { canSelectMany?: boolean; /** - * An optional interface to implement drag and drop in the tree view. - */ + * An optional interface to implement drag and drop in the tree view. + */ dragAndDropController?: TreeDragAndDropController; /** @@ -11240,7 +11941,7 @@ declare module 'vscode' { * A map containing a mapping of the mime type of the corresponding transferred data. * * Drag and drop controllers that implement {@link TreeDragAndDropController.handleDrag `handleDrag`} can add additional mime types to the - * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from + * data transfer. These additional mime types will only be included in the `handleDrop` when the drag was initiated from * an element in the same drag and drop controller. */ export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { @@ -11314,6 +12015,8 @@ declare module 'vscode' { * When the user starts dragging items from this `DragAndDropController`, `handleDrag` will be called. * Extensions can use `handleDrag` to add their {@link DataTransferItem `DataTransferItem`} items to the drag and drop. * + * Mime types added in `handleDrag` won't be available outside the application. + * * When the items are dropped on **another tree item** in **the same tree**, your `DataTransferItem` objects * will be preserved. Use the recommended mime type for the tree (`application/vnd.code.tree.`) to add * tree objects in a data transfer. See the documentation for `DataTransferItem` for how best to take advantage of this. @@ -11333,8 +12036,8 @@ declare module 'vscode' { * * Extensions should fire {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} for any elements that need to be refreshed. * - * @param dataTransfer The data transfer items of the source of the drag. * @param target The target tree element that the drop is occurring on. When undefined, the target is the root. + * @param dataTransfer The data transfer items of the source of the drag. * @param token A cancellation token indicating that the drop has been cancelled. */ handleDrop?(target: T | undefined, dataTransfer: DataTransfer, token: CancellationToken): Thenable | void; @@ -11361,8 +12064,8 @@ declare module 'vscode' { */ export interface TreeCheckboxChangeEvent { /** - * The items that were checked or unchecked. - */ + * The items that were checked or unchecked. + */ readonly items: ReadonlyArray<[T, TreeItemCheckboxState]>; } @@ -11402,8 +12105,8 @@ declare module 'vscode' { readonly onDidChangeVisibility: Event; /** - * An event to signal that an element or root has either been checked or unchecked. - */ + * An event to signal that an element or root has either been checked or unchecked. + */ readonly onDidChangeCheckboxState: Event>; /** @@ -11446,15 +12149,15 @@ declare module 'vscode' { /** * If true, then the element will be selected. */ - select?: boolean; + readonly select?: boolean; /** * If true, then the element will be focused. */ - focus?: boolean; + readonly focus?: boolean; /** * If true, then the element will be expanded. If a number is passed, then up to that number of levels of children will be expanded */ - expand?: boolean | number; + readonly expand?: boolean | number; }): Thenable; } @@ -11541,16 +12244,7 @@ declare module 'vscode' { * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. * When a file or folder {@link ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). */ - iconPath?: string | Uri | { - /** - * The icon path for the light theme. - */ - light: string | Uri; - /** - * The icon path for the dark theme. - */ - dark: string | Uri; - } | ThemeIcon; + iconPath?: string | IconPath; /** * A human-readable string which is rendered less prominent. @@ -11680,8 +12374,8 @@ declare module 'vscode' { } /** - * Checkbox state of the tree item - */ + * Checkbox state of the tree item + */ export enum TreeItemCheckboxState { /** * Determines an item is unchecked @@ -11737,7 +12431,7 @@ declare module 'vscode' { * until `Terminal.show` is called. The typical usage for this is when you need to run * something that may need interactivity but only want to tell the user about it when * interaction is needed. Note that the terminals will still be exposed to all extensions - * as normal. + * as normal. The hidden terminals will not be restored when the workspace is next opened. */ hideFromUser?: boolean; @@ -11751,16 +12445,7 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; + iconPath?: IconPath; /** * The icon {@link ThemeColor} for the terminal. @@ -11770,8 +12455,8 @@ declare module 'vscode' { color?: ThemeColor; /** - * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. - */ + * The {@link TerminalLocation} or {@link TerminalEditorLocationOptions} or {@link TerminalSplitLocationOptions} for the terminal. + */ location?: TerminalLocation | TerminalEditorLocationOptions | TerminalSplitLocationOptions; /** @@ -11799,16 +12484,7 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; + iconPath?: IconPath; /** * The icon {@link ThemeColor} for the terminal. @@ -11832,7 +12508,7 @@ declare module 'vscode' { /** * Defines the interface of a terminal pty, enabling extensions to control a terminal. */ - interface Pseudoterminal { + export interface Pseudoterminal { /** * An event that when fired will write data to the terminal. Unlike * {@link Terminal.sendText} which sends text to the underlying child @@ -12416,7 +13092,7 @@ declare module 'vscode' { buttons: readonly QuickInputButton[]; /** - * An event signaling when a button in the title bar was triggered. + * An event signaling when a top level button (buttons stored in {@link buttons}) was triggered. * This event does not fire for buttons on a {@link QuickPickItem}. */ readonly onDidTriggerButton: Event; @@ -12549,17 +13225,7 @@ declare module 'vscode' { /** * Icon for the button. */ - readonly iconPath: Uri | { - /** - * The icon path for the light theme. - */ - light: Uri; - /** - * The icon path for the dark theme. - */ - dark: Uri; - } | ThemeIcon; - + readonly iconPath: IconPath; /** * An optional tooltip. */ @@ -12651,7 +13317,7 @@ declare module 'vscode' { /** * The reason why the document was changed. * Is `undefined` if the reason is not known. - */ + */ readonly reason: TextDocumentChangeReason | undefined; } @@ -13152,17 +13818,13 @@ declare module 'vscode' { * for file changes recursively. * * Additional paths can be added for file watching by providing a {@link RelativePattern} with - * a `base` path to watch. If the `pattern` is complex (e.g. contains `**` or path segments), - * the path will be watched recursively and otherwise will be watched non-recursively (i.e. only - * changes to the first level of the path will be reported). - * - * *Note* that requests for recursive file watchers for a `base` path that is inside the opened - * workspace are ignored given all opened {@link workspace.workspaceFolders workspace folders} are - * watched for file changes recursively by default. Non-recursive file watchers however are always - * supported, even inside the opened workspace because they allow to bypass the configured settings - * for excludes (`files.watcherExclude`). If you need to watch in a location that is typically - * excluded (for example `node_modules` or `.git` folder), then you can use a non-recursive watcher - * in the workspace for this purpose. + * a `base` path to watch. If the path is a folder and the `pattern` is complex (e.g. contains + * `**` or path segments), it will be watched recursively and otherwise will be watched + * non-recursively (i.e. only changes to the first level of the path will be reported). + * + * *Note* that paths that do not exist in the file system will be monitored with a delay until + * created and then watched depending on the parameters provided. If a watched path is deleted, + * the watcher will suspend and not report any events until the path is created again. * * If possible, keep the use of recursive watchers to a minimum because recursive file watching * is quite resource intense. @@ -13177,26 +13839,13 @@ declare module 'vscode' { * * *Note* that file events from recursive file watchers may be excluded based on user configuration. * The setting `files.watcherExclude` helps to reduce the overhead of file events from folders - * that are known to produce many file changes at once (such as `node_modules` folders). As such, + * that are known to produce many file changes at once (such as `.git` folders). As such, * it is highly recommended to watch with simple patterns that do not require recursive watchers * where the exclude settings are ignored and you have full control over the events. * * *Note* that symbolic links are not automatically followed for file watching unless the path to * watch itself is a symbolic link. * - * *Note* that file changes for the path to be watched may not be delivered when the path itself - * changes. For example, when watching a path `/Users/somename/Desktop` and the path itself is - * being deleted, the watcher may not report an event and may not work anymore from that moment on. - * The underlying behaviour depends on the path that is provided for watching: - * * if the path is within any of the workspace folders, deletions are tracked and reported unless - * excluded via `files.watcherExclude` setting - * * if the path is equal to any of the workspace folders, deletions are not tracked - * * if the path is outside of any of the workspace folders, deletions are not tracked - * - * If you are interested in being notified when the watched path itself is being deleted, you have - * to watch it's parent folder. Make sure to use a simple `pattern` (such as putting the name of the - * folder) to not accidentally watch all sibling folders recursively. - * * *Note* that the file paths that are reported for having changed may have a different path casing * compared to the actual casing on disk on case-insensitive platforms (typically macOS and Windows * but not Linux). We allow a user to open a workspace folder with any desired path casing and try @@ -13286,6 +13935,29 @@ declare module 'vscode' { */ 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. * @@ -13338,7 +14010,29 @@ declare module 'vscode' { * @param uri Identifies the resource to open. * @returns A promise that resolves to a {@link TextDocument document}. */ - export function openTextDocument(uri: Uri): Thenable; + export function openTextDocument(uri: Uri, options?: { + /** + * The {@link TextDocument.encoding encoding} of the document to use + * for decoding the underlying buffer to text. If omitted, the encoding + * will be guessed based on the file content and/or the editor settings + * unless the document is already opened. + * + * Opening a text document that was already opened with a different encoding + * has the potential of changing the text contents of the text document. + * Specifically, when the encoding results in a different set of characters + * than the previous encoding. As such, an error is thrown for dirty documents + * when the specified encoding is different from the encoding of the document. + * + * See {@link TextDocument.encoding} for more information about valid + * values for encoding. Using an unsupported encoding will fallback to the + * default encoding for the document. + * + * *Note* that if you open a document with an encoding that does not + * support decoding the underlying bytes, content may be replaced with + * substitution characters as appropriate. + */ + readonly encoding?: string; + }): Thenable; /** * A short-hand for `openTextDocument(Uri.file(path))`. @@ -13347,7 +14041,29 @@ declare module 'vscode' { * @param path A path of a file on disk. * @returns A promise that resolves to a {@link TextDocument document}. */ - export function openTextDocument(path: string): Thenable; + export function openTextDocument(path: string, options?: { + /** + * The {@link TextDocument.encoding encoding} of the document to use + * for decoding the underlying buffer to text. If omitted, the encoding + * will be guessed based on the file content and/or the editor settings + * unless the document is already opened. + * + * Opening a text document that was already opened with a different encoding + * has the potential of changing the text contents of the text document. + * Specifically, when the encoding results in a different set of characters + * than the previous encoding. As such, an error is thrown for dirty documents + * when the specified encoding is different from the encoding of the document. + * + * See {@link TextDocument.encoding} for more information about valid + * values for encoding. Using an unsupported encoding will fallback to the + * default encoding for the document. + * + * *Note* that if you open a document with an encoding that does not + * support decoding the underlying bytes, content may be replaced with + * substitution characters as appropriate. + */ + readonly encoding?: string; + }): Thenable; /** * Opens an untitled text document. The editor will prompt the user for a file @@ -13366,6 +14082,14 @@ declare module 'vscode' { * The initial contents of the document. */ content?: string; + /** + * The {@link TextDocument.encoding encoding} of the document. + * + * See {@link TextDocument.encoding} for more information about valid + * values for encoding. Using an unsupported encoding will fallback to the + * default encoding for the document. + */ + readonly encoding?: string; }): Thenable; /** @@ -13635,8 +14359,9 @@ declare module 'vscode' { readonly isCaseSensitive?: boolean; /** * Whether the file system provider is readonly, no modifications like write, delete, create are possible. + * If a {@link MarkdownString} is given, it will be shown as the reason why the file system is readonly. */ - readonly isReadonly?: boolean; + readonly isReadonly?: boolean | MarkdownString; }): Disposable; /** @@ -13648,13 +14373,139 @@ declare module 'vscode' { * Event that fires when the current workspace has been trusted. */ export const onDidGrantWorkspaceTrust: Event; + + /** + * Decodes the content from a `Uint8Array` to a `string`. You MUST + * provide the entire content at once to ensure that the encoding + * can properly apply. Do not use this method to decode content + * in chunks, as that may lead to incorrect results. + * + * Will pick an encoding based on settings and the content of the + * buffer (for example byte order marks). + * + * *Note* that if you decode content that is unsupported by the + * encoding, the result may contain substitution characters as + * appropriate. + * + * @throws This method will throw an error when the content is binary. + * + * @param content The text content to decode as a `Uint8Array`. + * @returns A thenable that resolves to the decoded `string`. + */ + export function decode(content: Uint8Array): Thenable; + + /** + * Decodes the content from a `Uint8Array` to a `string` using the + * provided encoding. You MUST provide the entire content at once + * to ensure that the encoding can properly apply. Do not use this + * method to decode content in chunks, as that may lead to incorrect + * results. + * + * *Note* that if you decode content that is unsupported by the + * encoding, the result may contain substitution characters as + * appropriate. + * + * @throws This method will throw an error when the content is binary. + * + * @param content The text content to decode as a `Uint8Array`. + * @param options Additional context for picking the encoding. + * @returns A thenable that resolves to the decoded `string`. + */ + export function decode(content: Uint8Array, options: { + /** + * Allows to explicitly pick the encoding to use. + * See {@link TextDocument.encoding} for more information + * about valid values for encoding. + * Using an unsupported encoding will fallback to the + * default configured encoding. + */ + readonly encoding: string; + }): Thenable; + + /** + * Decodes the content from a `Uint8Array` to a `string`. You MUST + * provide the entire content at once to ensure that the encoding + * can properly apply. Do not use this method to decode content + * in chunks, as that may lead to incorrect results. + * + * The encoding is picked based on settings and the content + * of the buffer (for example byte order marks). + * + * *Note* that if you decode content that is unsupported by the + * encoding, the result may contain substitution characters as + * appropriate. + * + * @throws This method will throw an error when the content is binary. + * + * @param content The content to decode as a `Uint8Array`. + * @param options Additional context for picking the encoding. + * @returns A thenable that resolves to the decoded `string`. + */ + export function decode(content: Uint8Array, options: { + /** + * The URI that represents the file if known. This information + * is used to figure out the encoding related configuration + * for the file if any. + */ + readonly uri: Uri; + }): Thenable; + + /** + * Encodes the content of a `string` to a `Uint8Array`. + * + * Will pick an encoding based on settings. + * + * @param content The content to decode as a `string`. + * @returns A thenable that resolves to the encoded `Uint8Array`. + */ + export function encode(content: string): Thenable; + + /** + * Encodes the content of a `string` to a `Uint8Array` using the + * provided encoding. + * + * @param content The content to decode as a `string`. + * @param options Additional context for picking the encoding. + * @returns A thenable that resolves to the encoded `Uint8Array`. + */ + export function encode(content: string, options: { + /** + * Allows to explicitly pick the encoding to use. + * See {@link TextDocument.encoding} for more information + * about valid values for encoding. + * Using an unsupported encoding will fallback to the + * default configured encoding. + */ + readonly encoding: string; + }): Thenable; + + /** + * Encodes the content of a `string` to a `Uint8Array`. + * + * The encoding is picked based on settings. + * + * @param content The content to decode as a `string`. + * @param options Additional context for picking the encoding. + * @returns A thenable that resolves to the encoded `Uint8Array`. + */ + export function encode(content: string, options: { + /** + * The URI that represents the file if known. This information + * is used to figure out the encoding related configuration + * for the file if any. + */ + readonly uri: Uri; + }): Thenable; } /** - * The configuration scope which can be a - * a 'resource' or a languageId or both or - * a '{@link TextDocument}' or - * a '{@link WorkspaceFolder}' + * The configuration scope which can be: + * - a {@link Uri} representing a resource + * - a {@link TextDocument} representing an open text document + * - a {@link WorkspaceFolder} representing a workspace folder + * - an object containing: + * - `uri`: an optional {@link Uri} of a text document + * - `languageId`: the language identifier of a text document */ export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { /** @@ -14239,12 +15090,44 @@ declare module 'vscode' { /** * Registers a new {@link DocumentDropEditProvider}. * + * Multiple drop providers can be registered for a language. When dropping content into an editor, all + * registered providers for the editor's language will be invoked based on the mimetypes they handle + * as specified by their {@linkcode DocumentDropEditProviderMetadata}. + * + * Each provider can return one or more {@linkcode DocumentDropEdit DocumentDropEdits}. The edits are sorted + * using the {@linkcode DocumentDropEdit.yieldTo} property. By default the first edit will be applied. If there + * are any additional edits, these will be shown to the user as selectable drop options in the drop widget. + * * @param selector A selector that defines the documents this provider applies to. * @param provider A drop provider. + * @param metadata Additional metadata about the provider. + * + * @returns A {@linkcode Disposable} that unregisters this provider when disposed of. + */ + export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider, metadata?: DocumentDropEditProviderMetadata): Disposable; + + /** + * Registers a new {@linkcode DocumentPasteEditProvider}. + * + * Multiple providers can be registered for a language. All registered providers for a language will be invoked + * for copy and paste operations based on their handled mimetypes as specified by the {@linkcode DocumentPasteProviderMetadata}. + * + * For {@link DocumentPasteEditProvider.prepareDocumentPaste copy operations}, changes to the {@linkcode DataTransfer} + * made by each provider will be merged into a single {@linkcode DataTransfer} that is used to populate the clipboard. + * + * For {@link DocumentPasteEditProvider.providerDocumentPasteEdits paste operations}, each provider will be invoked + * and can return one or more {@linkcode DocumentPasteEdit DocumentPasteEdits}. The edits are sorted using + * the {@linkcode DocumentPasteEdit.yieldTo} property. By default the first edit will be applied + * and the rest of the edits will be shown to the user as selectable paste options in the paste widget. * - * @returns A {@link Disposable} that unregisters this provider when disposed of. + * @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 {@linkcode Disposable} that unregisters this provider when disposed of. */ - export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider): Disposable; + export function registerDocumentPasteEditProvider(selector: DocumentSelector, provider: DocumentPasteEditProvider, metadata: DocumentPasteProviderMetadata): Disposable; + /** * Set a {@link LanguageConfiguration language configuration} for a language. @@ -15338,7 +16221,7 @@ declare module 'vscode' { * * @param rendererId The renderer ID to communicate with * @returns A new notebook renderer messaging object. - */ + */ export function createRendererMessaging(rendererId: string): NotebookRendererMessaging; } @@ -15500,10 +16383,30 @@ declare module 'vscode' { hideWhenEmpty?: boolean; /** - * This group's collection of - * {@link SourceControlResourceState source control resource states}. - */ - resourceStates: SourceControlResourceState[]; + * Context value of the resource group. This can be used to contribute resource group specific actions. + * For example, if a resource group is given a context value of `exportable`, when contributing actions to `scm/resourceGroup/context` + * using `menus` extension point, you can specify context value for key `scmResourceGroupState` in `when` expressions, like `scmResourceGroupState == exportable`. + * ```json + * "contributes": { + * "menus": { + * "scm/resourceGroup/context": [ + * { + * "command": "extension.export", + * "when": "scmResourceGroupState == exportable" + * } + * ] + * } + * } + * ``` + * This will show action `extension.export` only for resource groups with `contextValue` equal to `exportable`. + */ + contextValue?: string; + + /** + * This group's collection of + * {@link SourceControlResourceState source control resource states}. + */ + resourceStates: SourceControlResourceState[]; /** * Dispose this source control resource group. @@ -15587,7 +16490,7 @@ declare module 'vscode' { } /** - * Namespace for source control mangement. + * Namespace for source control management. */ export namespace scm { @@ -15901,7 +16804,7 @@ declare module 'vscode' { export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; /** - * A debug adaper factory that creates {@link DebugAdapterDescriptor debug adapter descriptors}. + * A debug adapter factory that creates {@link DebugAdapterDescriptor debug adapter descriptors}. */ export interface DebugAdapterDescriptorFactory { /** @@ -15955,7 +16858,7 @@ declare module 'vscode' { } /** - * A debug adaper factory that creates {@link DebugAdapterTracker debug adapter trackers}. + * A debug adapter factory that creates {@link DebugAdapterTracker debug adapter trackers}. */ export interface DebugAdapterTrackerFactory { /** @@ -16147,6 +17050,13 @@ declare module 'vscode' { * 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; } /** @@ -16166,6 +17076,50 @@ declare module 'vscode' { Dynamic = 2 } + /** + * Represents a thread in a debug session. + */ + export class DebugThread { + /** + * Debug session for thread. + */ + readonly session: DebugSession; + + /** + * ID of the associated thread in the debug protocol. + */ + readonly threadId: number; + + /** + * @hidden + */ + private constructor(session: DebugSession, threadId: number); + } + + /** + * Represents a stack frame in a debug session. + */ + export class DebugStackFrame { + /** + * Debug session for thread. + */ + readonly session: DebugSession; + + /** + * ID of the associated thread in the debug protocol. + */ + readonly threadId: number; + /** + * ID of the stack frame in the debug protocol. + */ + readonly frameId: number; + + /** + * @hidden + */ + private constructor(session: DebugSession, threadId: number, frameId: number); + } + /** * Namespace for debug functionality. */ @@ -16216,6 +17170,19 @@ declare module 'vscode' { */ export const onDidChangeBreakpoints: Event; + /** + * The currently focused thread or stack frame, or `undefined` if no + * thread or stack is focused. A thread can be focused any time there is + * an active debug session, while a stack frame can only be focused when + * a session is paused and the call stack has been retrieved. + */ + export const activeStackItem: DebugThread | DebugStackFrame | undefined; + + /** + * An event which fires when the {@link debug.activeStackItem} has changed. + */ + export const onDidChangeActiveStackItem: Event; + /** * Register a {@link DebugConfigurationProvider debug configuration provider} for a specific debug type. * The optional {@link DebugConfigurationProviderTriggerKind triggerKind} can be used to specify when the `provideDebugConfigurations` method of the provider is triggered. @@ -16276,7 +17243,7 @@ declare module 'vscode' { /** * Add breakpoints. * @param breakpoints The breakpoints to add. - */ + */ export function addBreakpoints(breakpoints: readonly Breakpoint[]): void; /** @@ -16408,9 +17375,10 @@ declare module 'vscode' { /** * The range the comment thread is located within the document. The thread icon will be shown - * at the last line of the range. + * at the last line of the range. When set to undefined, the comment will be associated with the + * file, and not a specific range. */ - range: Range; + range: Range | undefined; /** * The ordered comments of the thread. @@ -16427,7 +17395,7 @@ declare module 'vscode' { * Whether the thread supports reply. * Defaults to true. */ - canReply: boolean; + canReply: boolean | CommentAuthorInformation; /** * Context value of the comment thread. This can be used to contribute thread specific actions. @@ -16579,6 +17547,21 @@ declare module 'vscode' { text: string; } + /** + * The ranges a CommentingRangeProvider enables commenting on. + */ + export interface CommentingRanges { + /** + * Enables comments to be added to a file without a specific range. + */ + enableFileComments: boolean; + + /** + * The ranges which allow new comment threads creation. + */ + ranges?: Range[]; + } + /** * Commenting range provider for a {@link CommentController comment controller}. */ @@ -16586,7 +17569,7 @@ declare module 'vscode' { /** * Provide a list of ranges which allow new comment threads creation or null for a given document */ - provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; + provideCommentingRanges(document: TextDocument, token: CancellationToken): ProviderResult; } /** @@ -16708,9 +17691,9 @@ declare module 'vscode' { } /** - * Optional options to be used when calling {@link authentication.getSession} with the flag `forceNewSession`. + * Optional options to be used when calling {@link authentication.getSession} with interactive options `forceNewSession` & `createIfNone`. */ - export interface AuthenticationForceNewSessionOptions { + export interface AuthenticationGetSessionPresentationOptions { /** * An optional message that will be displayed to the user when we ask to re-authenticate. Providing additional context * as to why you are asking a user to re-authenticate can help increase the odds that they will accept. @@ -16718,6 +17701,12 @@ declare module 'vscode' { detail?: string; } + /** + * Optional options to be used when calling {@link authentication.getSession} with the flag `forceNewSession`. + * @deprecated Use {@link AuthenticationGetSessionPresentationOptions} instead. + */ + export type AuthenticationForceNewSessionOptions = AuthenticationGetSessionPresentationOptions; + /** * Options to be used when getting an {@link AuthenticationSession} from an {@link AuthenticationProvider}. */ @@ -16747,6 +17736,8 @@ declare module 'vscode' { * on the accounts activity bar icon. An entry for the extension will be added under the menu to sign in. This * allows quietly prompting the user to sign in. * + * If you provide options, you will also see the dialog but with the additional context provided. + * * If there is a matching session but the extension has not been granted access to it, setting this to true * will also result in an immediate modal dialog, and false will add a numbered badge to the accounts icon. * @@ -16754,7 +17745,7 @@ declare module 'vscode' { * * Note: you cannot use this option with {@link AuthenticationGetSessionOptions.silent silent}. */ - createIfNone?: boolean; + createIfNone?: boolean | AuthenticationGetSessionPresentationOptions; /** * Whether we should attempt to reauthenticate even if there is already a session available. @@ -16762,12 +17753,14 @@ declare module 'vscode' { * If true, a modal dialog will be shown asking the user to sign in again. This is mostly used for scenarios * where the token needs to be re minted because it has lost some authorization. * + * If you provide options, you will also see the dialog but with the additional context provided. + * * If there are no existing sessions and forceNewSession is true, it will behave identically to * {@link AuthenticationGetSessionOptions.createIfNone createIfNone}. * * This defaults to false. */ - forceNewSession?: boolean | AuthenticationForceNewSessionOptions; + forceNewSession?: boolean | AuthenticationGetSessionPresentationOptions | AuthenticationForceNewSessionOptions; /** * Whether we should show the indication to sign in in the Accounts menu. @@ -16780,6 +17773,11 @@ declare module 'vscode' { * Note: you cannot use this option with any other options that prompt the user like {@link AuthenticationGetSessionOptions.createIfNone 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; } /** @@ -16814,17 +17812,17 @@ declare module 'vscode' { /** * Whether it is possible to be signed into multiple accounts at once with this provider. * If not specified, will default to false. - */ + */ readonly supportsMultipleAccounts?: boolean; } /** - * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. - */ + * An {@link Event} which fires when an {@link AuthenticationSession} is added, removed, or changed. + */ export interface AuthenticationProviderAuthenticationSessionsChangeEvent { /** * The {@link AuthenticationSession AuthenticationSessions} of the {@link AuthenticationProvider} that have been added. - */ + */ readonly added: readonly AuthenticationSession[] | undefined; /** @@ -16840,6 +17838,18 @@ declare module 'vscode' { 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. */ @@ -16854,9 +17864,10 @@ declare module 'vscode' { * 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. @@ -16869,9 +17880,10 @@ declare module 'vscode' { * 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. @@ -16902,7 +17914,7 @@ declare module 'vscode' { * @param options The {@link AuthenticationGetSessionOptions} to use * @returns A thenable that resolves to an authentication session */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** */createIfNone: true }): Thenable; + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** */createIfNone: true | AuthenticationGetSessionPresentationOptions }): Thenable; /** * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not @@ -16917,7 +17929,7 @@ declare module 'vscode' { * @param options The {@link AuthenticationGetSessionOptions} to use * @returns A thenable that resolves to an authentication session */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** literal-type defines return type */forceNewSession: true | AuthenticationForceNewSessionOptions }): Thenable; + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** literal-type defines return type */forceNewSession: true | AuthenticationGetSessionPresentationOptions | AuthenticationForceNewSessionOptions }): Thenable; /** * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not @@ -16934,6 +17946,20 @@ declare module 'vscode' { */ 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} which fires when the authentication sessions of an authentication provider have * been added, removed, or changed. @@ -17051,7 +18077,7 @@ declare module 'vscode' { * @param id Identifier for the controller, must be globally unique. * @param label A human-readable label for the controller. * @returns An instance of the {@link TestController}. - */ + */ export function createTestController(id: string, label: string): TestController; } @@ -17119,9 +18145,18 @@ declare module 'vscode' { * the generic "run all" button, then the default profile for * {@link TestRunProfileKind.Run} will be executed, although the * user can configure this. + * + * Changes the user makes in their default profiles will be reflected + * in this property after a {@link onDidChangeDefault} event. */ isDefault: boolean; + /** + * Fired when a user has changed whether this is a default profile. The + * event contains the new value of {@link isDefault} + */ + onDidChangeDefault: Event; + /** * Whether this profile supports continuous running of requests. If so, * then {@link TestRunRequest.continuous} may be set to `true`. Defaults @@ -17162,6 +18197,40 @@ declare module 'vscode' { */ 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. + */ + loadDetailedCoverage?: (testRun: TestRun, fileCoverage: FileCoverage, token: CancellationToken) => Thenable; + + /** + * An extension-provided function that provides detailed statement and + * function-level coverage for a single test in a file. This is the per-test + * sibling of {@link TestRunProfile.loadDetailedCoverage}, called only if + * a test item is provided in {@link FileCoverage.includesTests} and only + * for files where such data is reported. + * + * Often {@link TestRunProfile.loadDetailedCoverage} will be called first + * when a user opens a file, and then this method will be called if they + * drill down into specific per-test coverage information. This method + * should then return coverage data only for statements and declarations + * executed by the specific test during the run. + * + * The {@link FileCoverage} object passed to this function is the same + * instance emitted on {@link TestRun.addCoverage} calls associated with this profile. + * + * @param testRun The test run that generated the coverage data. + * @param fileCoverage The file coverage object to load detailed coverage for. + * @param fromTestItem The test item to request coverage information for. + * @param token A cancellation token that indicates the operation should be cancelled. + */ + loadDetailedCoverageForTest?: (testRun: TestRun, fileCoverage: FileCoverage, fromTestItem: TestItem, token: CancellationToken) => Thenable; + /** * Deletes the run profile. */ @@ -17296,7 +18365,7 @@ declare module 'vscode' { * runs which may still be ongoing, will be marked as outdated and deprioritized * in the editor's UI. * - * @param item Item to mark as outdated. If undefined, all the controller's items are marked outdated. + * @param items Item to mark as outdated. If undefined, all the controller's items are marked outdated. */ invalidateTestResults(items?: TestItem | readonly TestItem[]): void; @@ -17351,13 +18420,22 @@ declare module 'vscode' { */ readonly continuous?: boolean; + /** + * Controls how test Test Results view is focused. If true, the editor + * will keep the maintain the user's focus. If false, the editor will + * prefer to move focus into the Test Results view, although + * this may be configured by users. + */ + readonly preserveFocus: boolean; + /** * @param include Array of specific tests to run, or undefined to run all tests * @param exclude An array of tests to exclude from the run. * @param profile The run profile used for this request. * @param continuous Whether to run tests continuously as source changes. + * @param preserveFocus Whether to preserve the user's focus when the run is started */ - constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean); + constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean, preserveFocus?: boolean); } /** @@ -17441,11 +18519,22 @@ declare module 'vscode' { */ appendOutput(output: string, location?: Location, test?: TestItem): void; + /** + * Adds coverage for a file in the run. + */ + 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; } /** @@ -17584,6 +18673,34 @@ declare module 'vscode' { error: string | MarkdownString | undefined; } + /** + * A stack frame found in the {@link TestMessage.stackTrace}. + */ + export class TestMessageStackFrame { + /** + * The location of this stack frame. This should be provided as a URI if the + * location of the call frame can be accessed by the editor. + */ + uri?: Uri; + + /** + * Position of the stack frame within the file. + */ + position?: Position; + + /** + * The name of the stack frame, typically a method or function name. + */ + label: string; + + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor(label: string, uri?: Uri, position?: Position); + } + /** * Message associated with the test state. Can be linked to a specific * source range -- useful for assertion failures, for example. @@ -17640,6 +18757,11 @@ declare module 'vscode' { */ contextValue?: string; + /** + * The stack trace associated with the message or failure. + */ + stackTrace?: TestMessageStackFrame[]; + /** * Creates a new TestMessage that will present as a diff in the editor. * @param message Message to display to the user. @@ -17655,6 +18777,186 @@ declare module 'vscode' { 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; + + /** + * A list of {@link TestItem test cases} that generated coverage in this + * file. If set, then {@link TestRunProfile.loadDetailedCoverageForTest} + * should also be defined in order to retrieve detailed coverage information. + */ + includesTests?: TestItem[]; + + /** + * 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 + * @param includesTests Test cases included in this coverage report, see {@link FileCoverage.includesTests} + */ + constructor( + uri: Uri, + statementCoverage: TestCoverageCount, + branchCoverage?: TestCoverageCount, + declarationCoverage?: TestCoverageCount, + includesTests?: TestItem[], + ); + } + + /** + * 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; + /** * The tab represents a single text based resource. */ @@ -17955,7 +19257,7 @@ declare module 'vscode' { readonly value: T; /** - * Creates a new telementry trusted value. + * Creates a new telemetry trusted value. * * @param value A value to trust */ @@ -17963,7 +19265,7 @@ declare module 'vscode' { } /** - * A telemetry logger which can be used by extensions to log usage and error telementry. + * A telemetry logger which can be used by extensions to log usage and error telemetry. * * A logger wraps around an {@link TelemetrySender sender} but it guarantees that * - user settings to disable or tweak telemetry are respected, and that @@ -18089,6 +19391,1396 @@ declare module 'vscode' { */ readonly additionalCommonProperties?: Record; } + + /** + * 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[]; + + /** + * The list of tools were attached to this request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + + /** + * @hidden + */ + private constructor(prompt: string, command: string | undefined, references: ChatPromptReference[], participant: string, toolReferences: ChatLanguageModelToolReference[]); + } + + /** + * 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 context Extra context passed to a participant. + * @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?: IconPath; + + /** + * 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 have the same properties as the result that was + * previously returned from this chat participant's handler. + */ + 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 list of tools that the user attached to their request. + * + * When a tool reference is present, the chat participant should make a chat request using + * {@link LanguageModelChatToolMode.Required} to force the language model to generate input for the tool. Then, the + * participant can use {@link lm.invokeTool} to use the tool attach the result to its request for the user's prompt. The + * tool may contribute useful extra context for the user's request. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + * This associates the tool invocation to a chat session. + */ + readonly toolInvocationToken: ChatParticipantToolToken; + + /** + * This is the model that is currently selected in the UI. Extensions can use this or use {@link lm.selectChatModels} to + * pick another model. Don't hold onto this past the lifetime of the request. + */ + readonly model: LanguageModelChat; + } + + /** + * 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 or location. + * @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?: IconPath): 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?: IconPath; + + /** + * Create a new ChatResponseReferencePart. + * @param value A uri or location + * @param iconPath Icon for the reference shown in UI + */ + constructor(value: Uri | Location, iconPath?: IconPath); + } + + /** + * 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 + */ + 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 | Array, 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 | Array, name?: string): LanguageModelChatMessage; + + /** + * The role of this message. + */ + role: LanguageModelChatMessageRole; + + /** + * A string or heterogeneous array of things that a message can contain as content. Some parts may be message-type + * specific for some models. + */ + content: Array; + + /** + * 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 | Array, name?: string); + } + + /** + * Represents a language model response. + * + * @see {@link ChatRequest} + */ + export interface LanguageModelChatResponse { + + /** + * An async iterable that is a stream of text and tool-call parts forming the overall response. A + * {@link LanguageModelTextPart} is part of the assistant's response to be shown to the user. A + * {@link LanguageModelToolCallPart} is a request from the language model to call a tool. The latter will + * only be returned if tools were passed in the request via {@link LanguageModelChatRequestOptions.tools}. The + * `unknown`-type is used as a placeholder for future parts, like image data parts. + * + * *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.stream) { + * if (chunk instanceof LanguageModelTextPart) { + * console.log("TEXT", chunk); + * } else if (chunk instanceof LanguageModelToolCallPart) { + * console.log("TOOL CALL", chunk); + * } + * } + * + * } catch(e) { + * // stream ended with an error + * console.error(e); + * } + * ``` + */ + stream: AsyncIterable; + + /** + * This is equivalent to filtering everything except for text parts from a {@link LanguageModelChatResponse.stream}. + * + * @see {@link LanguageModelChatResponse.stream} + */ + 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 an 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!_ Extensions 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`} + * + * An extension can make use of language model tool calling by passing a set of tools to + * {@link LanguageModelChatRequestOptions.tools}. The language model will return a {@link LanguageModelToolCallPart} and + * the extension can invoke the tool and make another request with the result. + * + * @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 looked up in the respective documentation. + */ + modelOptions?: { [name: string]: any }; + + /** + * An optional list of tools that are available to the language model. These could be registered tools available via + * {@link lm.tools}, or private tools that are just implemented within the calling extension. + * + * If the LLM requests to call one of these tools, it will return a {@link LanguageModelToolCallPart} in + * {@link LanguageModelChatResponse.stream}. It's the caller's responsibility to invoke the tool. If it's a tool + * registered in {@link lm.tools}, that means calling {@link lm.invokeTool}. + * + * Then, the tool result can be provided to the LLM by creating an Assistant-type {@link LanguageModelChatMessage} with a + * {@link LanguageModelToolCallPart}, followed by a User-type message with a {@link LanguageModelToolResultPart}. + */ + tools?: LanguageModelChatTool[]; + + /** + * The tool-selecting mode to use. {@link LanguageModelChatToolMode.Auto} by default. + */ + toolMode?: LanguageModelChatToolMode; + } + + /** + * McpStdioServerDefinition represents an MCP server available by running + * a local process and operating on its stdin and stdout streams. The process + * will be spawned as a child process of the extension host and by default + * will not run in a shell environment. + */ + export class McpStdioServerDefinition { + /** + * The human-readable name of the server. + */ + readonly label: string; + + /** + * The working directory used to start the server. + */ + cwd?: Uri; + + /** + * The command used to start the server. Node.js-based servers may use + * `process.execPath` to use the editor's version of Node.js to run the script. + */ + command: string; + + /** + * Additional command-line arguments passed to the server. + */ + args: string[]; + + /** + * Optional additional environment information for the server. Variables + * in this environment will overwrite or remove (if null) the default + * environment variables of the editor's extension host. + */ + env: Record; + + /** + * Optional version identification for the server. If this changes, the + * editor will indicate that tools have changed and prompt to refresh them. + */ + version?: string; + + /** + * @param label The human-readable name of the server. + * @param command The command used to start the server. + * @param args Additional command-line arguments passed to the server. + * @param env Optional additional environment information for the server. + * @param version Optional version identification for the server. + */ + constructor(label: string, command: string, args?: string[], env?: Record, version?: string); + } + + /** + * McpHttpServerDefinition represents an MCP server available using the + * Streamable HTTP transport. + */ + export class McpHttpServerDefinition { + /** + * The human-readable name of the server. + */ + readonly label: string; + + /** + * The URI of the server. The editor will make a POST request to this URI + * to begin each session. + */ + uri: Uri; + + /** + * Optional additional heads included with each request to the server. + */ + headers: Record; + + /** + * Optional version identification for the server. If this changes, the + * editor will indicate that tools have changed and prompt to refresh them. + */ + version?: string; + + /** + * @param label The human-readable name of the server. + * @param uri The URI of the server. + * @param headers Optional additional heads included with each request to the server. + */ + constructor(label: string, uri: Uri, headers?: Record, version?: string); + } + + /** + * Definitions that describe different types of Model Context Protocol servers, + * which can be returned from the {@link McpServerDefinitionProvider}. + */ + export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition; + + /** + * A type that can provide Model Context Protocol server definitions. This + * should be registered using {@link lm.registerMcpServerDefinitionProvider} + * during extension activation. + */ + export interface McpServerDefinitionProvider { + /** + * Optional event fired to signal that the set of available servers has changed. + */ + readonly onDidChangeMcpServerDefinitions?: Event; + + /** + * Provides available MCP servers. The editor will call this method eagerly + * to ensure the availability of servers for the language model, and so + * extensions should not take actions which would require user + * interaction, such as authentication. + * + * @param token A cancellation token. + * @returns An array of MCP available MCP servers + */ + provideMcpServerDefinitions(token: CancellationToken): ProviderResult; + + /** + * This function will be called when the editor needs to start a MCP server. + * At this point, the extension may take any actions which may require user + * interaction, such as authentication. Any non-`readonly` property of the + * server may be modified, and the extension should return the resolved server. + * + * The extension may return undefined to indicate that the server + * should not be started, or throw an error. If there is a pending tool + * call, the editor will cancel it and return an error message to the + * language model. + * + * @param server The MCP server to resolve + * @param token A cancellation token. + * @returns The resolved server or thenable that resolves to such. This may + * be the given `server` definition with non-readonly properties filled in. + */ + resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult; + } + + /** + * Namespace for language model related functionality. + */ + export namespace lm { + + /** + * An event that is fired when the set of available chat models changes. + */ + 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! + */ + export function selectChatModels(selector?: LanguageModelChatSelector): Thenable; + + /** + * Register a LanguageModelTool. The tool must also be registered in the package.json `languageModelTools` contribution + * point. A registered tool is available in the {@link lm.tools} list for any extension to see. But in order for it to + * be seen by a language model, it must be passed in the list of available tools in {@link LanguageModelChatRequestOptions.tools}. + * @returns A {@link Disposable} that unregisters the tool when disposed. + */ + export function registerTool(name: string, tool: LanguageModelTool): Disposable; + + /** + * A list of all available tools that were registered by all extensions using {@link lm.registerTool}. They can be called + * with {@link lm.invokeTool} with input that match their declared `inputSchema`. + */ + export const tools: readonly LanguageModelToolInformation[]; + + /** + * Invoke a tool listed in {@link lm.tools} by name with the given input. The input will be validated against + * the schema declared by the tool + * + * A tool can be invoked by a chat participant, in the context of handling a chat request, or globally by any extension in + * any custom flow. + * + * In the former case, the caller shall pass the + * {@link LanguageModelToolInvocationOptions.toolInvocationToken toolInvocationToken}, which comes with the a + * {@link ChatRequest.toolInvocationToken chat request}. This makes sure the chat UI shows the tool invocation for the + * correct conversation. + * + * A tool {@link LanguageModelToolResult result} is an array of {@link LanguageModelTextPart text-} and + * {@link LanguageModelPromptTsxPart prompt-tsx}-parts. If the tool caller is using `@vscode/prompt-tsx`, it can + * incorporate the response parts into its prompt using a `ToolResult`. If not, the parts can be passed along to the + * {@link LanguageModelChat} via a user message with a {@link LanguageModelToolResultPart}. + * + * If a chat participant wants to preserve tool results for requests across multiple turns, it can store tool results in + * the {@link ChatResult.metadata} returned from the handler and retrieve them on the next turn from + * {@link ChatResponseTurn.result}. + * + * @param name The name of the tool to call. + * @param options The options to use when invoking the tool. + * @param token A cancellation token. See {@link CancellationTokenSource} for how to create one. + * @returns The result of the tool invocation. + */ + export function invokeTool(name: string, options: LanguageModelToolInvocationOptions, token?: CancellationToken): Thenable; + + /** + * Registers a provider that publishes Model Context Protocol servers for the editor to + * consume. This allows MCP servers to be dynamically provided to the editor in + * addition to those the user creates in their configuration files. + * + * Before calling this method, extensions must register the `contributes.mcpServerDefinitionProviders` + * extension point with the corresponding {@link id}, for example: + * + * ```js + * "contributes": { + * "mcpServerDefinitionProviders": [ + * { + * "id": "cool-cloud-registry.mcp-servers", + * "label": "Cool Cloud Registry", + * } + * ] + * } + * ``` + * + * When a new McpServerDefinitionProvider is available, the editor will present a 'refresh' + * action to the user to discover new servers. To enable this flow, extensions should + * call `registerMcpServerDefinitionProvider` during activation. + * @param id The ID of the provider, which is unique to the extension. + * @param provider The provider to register + * @returns A disposable that unregisters the provider when disposed. + */ + export function registerMcpServerDefinitionProvider(id: string, provider: McpServerDefinitionProvider): Disposable; + } + + /** + * 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; + } + + /** + * A tool that is available to the language model via {@link LanguageModelChatRequestOptions}. A language model uses all the + * properties of this interface to decide which tool to call, and how to call it. + */ + export interface LanguageModelChatTool { + /** + * The name of the tool. + */ + name: string; + + /** + * The description of the tool. + */ + description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + inputSchema?: object | undefined; + } + + /** + * A tool-calling mode for the language model to use. + */ + export enum LanguageModelChatToolMode { + /** + * The language model can choose to call a tool or generate a message. Is the default. + */ + Auto = 1, + + /** + * The language model must call one of the provided tools. Note- some models only support a single tool when using this + * mode. + */ + Required = 2 + } + + /** + * A language model response part indicating a tool call, returned from a {@link LanguageModelChatResponse}, and also can be + * included as a content part on a {@link LanguageModelChatMessage}, to represent a previous tool call in a chat request. + */ + export class LanguageModelToolCallPart { + /** + * The ID of the tool call. This is a unique identifier for the tool call within the chat request. + */ + callId: string; + + /** + * The name of the tool to call. + */ + name: string; + + /** + * The input with which to call the tool. + */ + input: object; + + /** + * Create a new LanguageModelToolCallPart. + * + * @param callId The ID of the tool call. + * @param name The name of the tool to call. + * @param input The input with which to call the tool. + */ + constructor(callId: string, name: string, input: object); + } + + /** + * The result of a tool call. This is the counterpart of a {@link LanguageModelToolCallPart tool call} and + * it can only be included in the content of a User message + */ + export class LanguageModelToolResultPart { + /** + * The ID of the tool call. + * + * *Note* that this should match the {@link LanguageModelToolCallPart.callId callId} of a tool call part. + */ + callId: string; + + /** + * The value of the tool result. + */ + content: Array; + + /** + * @param callId The ID of the tool call. + * @param content The content of the tool result. + */ + constructor(callId: string, content: Array); + } + + /** + * A language model response part containing a piece of text, returned from a {@link LanguageModelChatResponse}. + */ + export class LanguageModelTextPart { + /** + * The text content of the part. + */ + value: string; + + /** + * Construct a text part with the given content. + * @param value The text content of the part. + */ + constructor(value: string); + } + + /** + * A language model response part containing a PromptElementJSON from `@vscode/prompt-tsx`. + * @see {@link LanguageModelToolResult} + */ + export class LanguageModelPromptTsxPart { + /** + * The value of the part. + */ + value: unknown; + + /** + * Construct a prompt-tsx part with the given content. + * @param value The value of the part, the result of `renderPromptElementJSON` from `@vscode/prompt-tsx`. + */ + constructor(value: unknown); + } + + /** + * A result returned from a tool invocation. If using `@vscode/prompt-tsx`, this result may be rendered using a `ToolResult`. + */ + export class LanguageModelToolResult { + /** + * A list of tool result content parts. Includes `unknown` because this list may be extended with new content types in + * the future. + * @see {@link lm.invokeTool}. + */ + content: Array; + + /** + * Create a LanguageModelToolResult + * @param content A list of tool result content parts + */ + constructor(content: Array); + } + + /** + * A token that can be passed to {@link lm.invokeTool} when invoking a tool inside the context of handling a chat request. + */ + export type ChatParticipantToolToken = never; + + /** + * Options provided for tool invocation. + */ + export interface LanguageModelToolInvocationOptions { + /** + * An opaque object that ties a tool invocation to a chat request from a {@link ChatParticipant chat participant}. + * + * The _only_ way to get a valid tool invocation token is using the provided {@link ChatRequest.toolInvocationToken toolInvocationToken} + * from a chat request. In that case, a progress bar will be automatically shown for the tool invocation in the chat response view, and if + * the tool requires user confirmation, it will show up inline in the chat view. + * + * If the tool is being invoked outside of a chat request, `undefined` should be passed instead, and no special UI except for + * confirmations will be shown. + * + * *Note* that a tool that invokes another tool during its invocation, can pass along the `toolInvocationToken` that it received. + */ + toolInvocationToken: ChatParticipantToolToken | undefined; + + /** + * The input with which to invoke the tool. The input must match the schema defined in + * {@link LanguageModelToolInformation.inputSchema} + */ + input: T; + + /** + * Options to hint at how many tokens the tool should return in its response, and enable the tool to count tokens + * accurately. + */ + tokenizationOptions?: LanguageModelToolTokenizationOptions; + } + + /** + * Options related to tokenization for a tool invocation. + */ + export interface LanguageModelToolTokenizationOptions { + /** + * If known, the maximum number of tokens the tool should emit in its result. + */ + tokenBudget: number; + + /** + * Count the number of tokens in a message using the model specific tokenizer-logic. + * @param text A string. + * @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, token?: CancellationToken): Thenable; + } + + /** + * Information about a registered tool available in {@link lm.tools}. + */ + export interface LanguageModelToolInformation { + /** + * A unique name for the tool. + */ + readonly name: string; + + /** + * A description of this tool that may be passed to a language model. + */ + readonly description: string; + + /** + * A JSON schema for the input this tool accepts. + */ + readonly inputSchema: object | undefined; + + /** + * A set of tags, declared by the tool, that roughly describe the tool's capabilities. A tool user may use these to filter + * the set of tools to just ones that are relevant for the task at hand. + */ + readonly tags: readonly string[]; + } + + /** + * Options for {@link LanguageModelTool.prepareInvocation}. + */ + export interface LanguageModelToolInvocationPrepareOptions { + /** + * The input that the tool is being invoked with. + */ + input: T; + } + + /** + * A tool that can be invoked by a call to a {@link LanguageModelChat}. + */ + export interface LanguageModelTool { + /** + * Invoke the tool with the given input and return a result. + * + * The provided {@link LanguageModelToolInvocationOptions.input} has been validated against the declared schema. + */ + invoke(options: LanguageModelToolInvocationOptions, token: CancellationToken): ProviderResult; + + /** + * Called once before a tool is invoked. It's recommended to implement this to customize the progress message that appears + * while the tool is running, and to provide a more useful message with context from the invocation input. Can also + * signal that a tool needs user confirmation before running, if appropriate. + * + * * *Note 1:* Must be free of side-effects. + * * *Note 2:* A call to `prepareInvocation` is not necessarily followed by a call to `invoke`. + */ + prepareInvocation?(options: LanguageModelToolInvocationPrepareOptions, token: CancellationToken): ProviderResult; + } + + /** + * When this is returned in {@link PreparedToolInvocation}, the user will be asked to confirm before running the tool. These + * messages will be shown with buttons that say "Continue" and "Cancel". + */ + export interface LanguageModelToolConfirmationMessages { + /** + * The title of the confirmation message. + */ + title: string; + + /** + * The body of the confirmation message. + */ + message: string | MarkdownString; + } + + /** + * The result of a call to {@link LanguageModelTool.prepareInvocation}. + */ + export interface PreparedToolInvocation { + /** + * A customized progress message to show while the tool runs. + */ + invocationMessage?: string | MarkdownString; + + /** + * The presence of this property indicates that the user should be asked to confirm before running the tool. The user + * should be asked for confirmation for any tool that has a side-effect or may potentially be dangerous. + */ + confirmationMessages?: LanguageModelToolConfirmationMessages; + } + + /** + * A reference to a tool that the user manually attached to their request, either using the `#`-syntax inline, or as an + * attachment via the paperclip button. + */ + export interface ChatLanguageModelToolReference { + /** + * The tool name. Refers to a tool listed in {@link lm.tools}. + */ + readonly name: 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 be used to modify the prompt + * as-is. + */ + readonly range?: [start: number, end: number]; + } } /** diff --git a/packages/quick-edit-extension/vscode.proposed.fileSearchProvider.d.ts b/packages/quick-edit-extension/vscode.proposed.fileSearchProvider.d.ts index bf7bc5ecba37..8dcfd99852b5 100644 --- a/packages/quick-edit-extension/vscode.proposed.fileSearchProvider.d.ts +++ b/packages/quick-edit-extension/vscode.proposed.fileSearchProvider.d.ts @@ -13,6 +13,12 @@ declare module 'vscode' { export interface FileSearchQuery { /** * The search pattern to match against file paths. + * To be correctly interpreted by Quick Open, this is interpreted in a relaxed way. The picker will apply its own highlighting and scoring on the results. + * + * Tips for matching in Quick Open: + * With the pattern, the picker will use the file name and file paths to score each entry. The score will determine the ordering and filtering. + * The scoring prioritizes prefix and substring matching. Then, it checks and it checks whether the pattern's letters appear in the same order as in the target (file name and path). + * If a file does not match at all using our criteria, it will be omitted from Quick Open. */ pattern: string; } diff --git a/packages/quick-edit/.node-version b/packages/quick-edit/.node-version index 4a58985bb483..8fdd954df983 100644 --- a/packages/quick-edit/.node-version +++ b/packages/quick-edit/.node-version @@ -1 +1 @@ -18.18 +22 \ No newline at end of file diff --git a/packages/quick-edit/.nvmrc b/packages/quick-edit/.nvmrc index 4a58985bb483..8fdd954df983 100644 --- a/packages/quick-edit/.nvmrc +++ b/packages/quick-edit/.nvmrc @@ -1 +1 @@ -18.18 +22 \ No newline at end of file diff --git a/packages/quick-edit/README.md b/packages/quick-edit/README.md index 12263b6780ec..9f1ab39103f2 100644 --- a/packages/quick-edit/README.md +++ b/packages/quick-edit/README.md @@ -1,17 +1,19 @@ # VSCode for Web -This package contains Cloudflare's fork VSCode for Web, to support web editing of Workers. This package primarily contains code and setup for _building_ VSCode for web, but it can also be used for development. +This package contains Cloudflare's fork of VSCode for Web, to support web editing of Workers. This package contains code and setup for building VSCode for Web. ## Developing -1. You must switch your NodeJS version to NodeJS 18 (using a tool like nvm). VSCode's build process requires this. For instance, if you use `nvm`, running `nvm use` would be enough to switch to the correct NodeJS version. -2. Run `pnpm install` -3. Run `pnpm run setup`, which will install dependencies, clone VSCode (currently v1.85.2), apply the patches specified in `./patches`, and symlink the top level packages within `workers-sdk`. -4. Run `pnpm run dev`. This will start various dev servers for VSCode and `quick-edit-extension`. Note, this takes a _long_ time to start up. Expect up to 3 minutes, although reloads will be much faster. You can access the VSCode dev server at `http://localhost:8788` +Currently it's not possible to run VSCode's dev server to develop patches. This is because of a limitation with the `wrangler dev` file server for Workers Assets and the number of assets that would be watched locally. ## Building -Follow steps (1), (2) and (3) from above, and then run `pnpm run custom:build` +1. You must switch your NodeJS version to NodeJS 22 (using a tool like nvm). VSCode's build process requires this. For instance, if you use `nvm`, running `nvm use` would be enough to switch to the correct NodeJS version. +2. Run `pnpm install` +3. Run `pnpm run setup`, which will install dependencies, clone VSCode (currently v1.102.1), apply the patches specified in `./patches`, and symlink the top level packages within `workers-sdk`. +4. Run `pnpm run custom:build`. It's `custom:build` rather than `build` because it's _really slow_, and shouldn't be regularly run by people building other packages in the repo. + +You should then be able to test out the local VSCode for Web instance by running `pnpm wrangler dev` at http://localhost:8787 ## Deployment @@ -34,6 +36,59 @@ If you need to add additional patches to VSCode, ensure you've run `pnpm run set ## Modifying VSCode settings -If you need to change VSCode's configuration, open the `packages/quick-edit/functions/_middleware.ts` file. +If you need to change VSCode's configuration, open the `packages/quick-edit/src/index.ts` file. The `WORKBENCH_WEB_CONFIGURATION` object contains VSCode's setup config, but the property `configurationDefaults` is most relevant, since it lets you set defaults for any VSCode settings. The format is exactly the same as VSCode's `settings.json` file. + +## Embedding VSCode + +> [!WARNING] +> Cloudflare does not officially support embedding this version of VSCode in third-party contexts—use at your own risk! This documentation is aimed at people inside Cloudflare embedding this package. + +The primary purpose of the patches we apply to VSCode is to allow the visible filesystem within VSCode to be controllable from the outside (by a web page that embeds the editor). You can refer to `packages/workers-playground/src/QuickEditor/VSCodeEditor.tsx` as a reference implementation. + +To communicate with VSCode, a web page needs to load the deployed editor in an iframe: + +```html + +``` + +The `$worker` query parameter should be set to the path of the to-be-edited Worker on the virtual filesystem. The protocol used for the virtual filesystem is `cfs`, so it should look like `cfs:/my-worker-name`. + +To load files into the VSCode filesystem and react to changes from within VSCode, you need to send over a `MessagePort`: + +```ts +import { Channel } from "@cloudflare/workers-editor-shared"; +import type { + FromQuickEditMessage, + ToQuickEditMessage, + WorkerLoadedMessage, + WrappedChannel, +} from "@cloudflare/workers-editor-shared"; + +const editor; // Acquire a reference to the iframe DOM element somehow. This will depend on the frontend framework in use. + +const channel = Channel( + new MessageChannel() +); + +editor.addEventListener("load", () => { + editor.contentWindow.postMessage("PORT", "*", [channel.remote]) +}) + +// When you have the initial contents of the Worker (loaded from an API, perhaps) +channel.postMessage({ + type: "WorkerLoaded", + body: { + // Refer to the WorkerLoaded type for details + }, +}) + +channel.onMessage(message => { + // message is of type SetEntryPoint, UpdateFile, CreateFile, or DeleteFile + // Refer to @cloudflare/workers-editor-shared for details +}) + +``` diff --git a/packages/quick-edit/build.sh b/packages/quick-edit/build.sh index c7e7f19d3a1f..7127893bf05e 100755 --- a/packages/quick-edit/build.sh +++ b/packages/quick-edit/build.sh @@ -1,22 +1,18 @@ set -eu -# The preinstall script in vscode will fail if npm_execpath is not set by yarn -# This make sure the env is unset so yarn can set it accordingly -unset npm_execpath -# We cannot run yarn without disabling the corepack check as the packageManager field is set to pnpm -SKIP_YARN_COREPACK_CHECK=0 +cd ../../vendor/vscode -# Cleanup development symlink to vscode -rm -f web/assets +# Build vscode +node --max-old-space-size=30000 ./node_modules/gulp/bin/gulp.js vscode-web-min -yarn --cwd ../../vendor/vscode +# Move the output assets to the assets direcotry for the `quick-edit` Worker +mv ../vscode-web ../../packages/quick-edit/web/assets -# Build vscode -yarn --cwd ../../vendor/vscode gulp vscode-web-min +cd ../../packages/quick-edit + +pnpm esbuild editor-files/workbench.ts --outfile=web/assets/workbench.js -mv ../../vendor/vscode-web web/assets -# Pages doesn't support uploading folders called node_modules -mv web/assets/node_modules web/assets/modules +cp editor-files/workbench.html web/assets # Build quick-edit-extension pnpm --filter quick-edit-extension run package-web diff --git a/packages/quick-edit/bundle-dts.ts b/packages/quick-edit/bundle-dts.ts index 42a822ed0147..b4fa722ca118 100644 --- a/packages/quick-edit/bundle-dts.ts +++ b/packages/quick-edit/bundle-dts.ts @@ -1,13 +1,13 @@ import { readFileSync, writeFileSync } from "node:fs"; -function readDtsFile(reference) { +function readDtsFile(reference: string) { return readFileSync( `../../vendor/vscode/extensions/node_modules/typescript/lib/lib.${reference}.d.ts`, "utf8" ); } -function writeDtsFile(reference, content) { +function writeDtsFile(reference: string, content: string) { return writeFileSync( `../../vendor/vscode/extensions/node_modules/typescript/lib/lib.${reference}.d.ts`, content @@ -16,7 +16,7 @@ function writeDtsFile(reference, content) { const importRegex = /\/\/\/ /g; -function replaceReferences(dts) { +function replaceReferences(dts: string) { return dts.replaceAll(importRegex, (_, ref) => { const innerDts = readDtsFile(ref); console.log("Including", ref, `(${innerDts.split("\n").length} lines)`); @@ -24,7 +24,7 @@ function replaceReferences(dts) { }); } -function inlineDts(dts) { +function inlineDts(dts: string) { if (!importRegex.test(dts)) { return dts; } diff --git a/packages/quick-edit/editor-files/code.d.ts b/packages/quick-edit/editor-files/code.d.ts new file mode 100644 index 000000000000..d55d10379c5c --- /dev/null +++ b/packages/quick-edit/editor-files/code.d.ts @@ -0,0 +1,8 @@ +// This isn't exact, it's just a basic attempt at matching the compiled VSCode API +declare module "*/workbench.web.main.internal.js" { + function create(target: HTMLElement, config: Record): void; + const URI: { + revive: (uri: unknown) => unknown; + parse: (uri: unknown) => unknown; + }; +} diff --git a/packages/quick-edit/editor-files/tsconfig.json b/packages/quick-edit/editor-files/tsconfig.json new file mode 100644 index 000000000000..d7ad4c297d31 --- /dev/null +++ b/packages/quick-edit/editor-files/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "dist", + "lib": ["ES2020", "DOM"], + "sourceMap": true, + "strict": true + } +} diff --git a/packages/quick-edit/editor-files/workbench.html b/packages/quick-edit/editor-files/workbench.html new file mode 100644 index 000000000000..31516c7caea8 --- /dev/null +++ b/packages/quick-edit/editor-files/workbench.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/quick-edit/editor-files/workbench.ts b/packages/quick-edit/editor-files/workbench.ts new file mode 100644 index 000000000000..39c87f8fe624 --- /dev/null +++ b/packages/quick-edit/editor-files/workbench.ts @@ -0,0 +1,97 @@ +// This is taken from the VSCode source with modifications + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + create, + URI, + // This is the path that the *compiled* VSCode output will be, relative to the final location of this file (web/assets/workbench.js) +} from "/assets/out/vs/workbench/workbench.web.main.internal.js"; + +// These types *should* be imported from the VSCode source tree, but since that's not available in +// workers-sdk it's easier to no-op them. +type IWorkspace = unknown; +type IWorkbenchConstructionOptions = unknown; +declare class IWorkspaceProvider {} +type UriComponents = unknown; + +class WorkspaceProvider implements IWorkspaceProvider { + static create( + config: IWorkbenchConstructionOptions & { + folderUri: UriComponents; + } + ) { + return new WorkspaceProvider({ folderUri: URI.revive(config.folderUri) }); + } + + private constructor(readonly workspace: IWorkspace) {} + + async open( + _workspace: IWorkspace, + _options?: { reuse?: boolean; payload?: object } + ): Promise { + return true; + } + + hasRemote(): boolean { + return true; + } +} + +function createEditor(port: MessagePort) { + // Find config by checking for DOM + const configElement = document.getElementById( + "vscode-workbench-web-configuration" + ); + const configElementAttribute = configElement + ? configElement.getAttribute("data-settings") + : undefined; + if (!configElement || !configElementAttribute) { + throw new Error("Missing web configuration element"); + } + const config: IWorkbenchConstructionOptions & { + folderUri?: UriComponents; + workspaceUri?: UriComponents; + callbackRoute: string; + } = JSON.parse(configElementAttribute); + + const searchParams = new URLSearchParams(window.location.search); + + const messagePorts = new Map(); + + // This passes the MessagePort through to the `cloudflare.quick-edit-extension` VSCode extension, which is preloaded + messagePorts.set("cloudflare.quick-edit-extension", port); + + const folderUri = searchParams.get("worker"); + + if (!folderUri) { + throw new Error("Missing target folder"); + } + + // Create workbench + create(document.body, { + ...config, + defaultLayout: { + force: true, + editors: [], + }, + settingsSyncOptions: undefined, + workspaceProvider: WorkspaceProvider.create({ + ...config, + folderUri: URI.parse(decodeURIComponent(folderUri)), + }), + messagePorts, + }); +} + +/** + * The web page that embeds this VSCode instance must provide a MessagePort used for file communication + */ +window.onmessage = (e) => { + if (e.data === "PORT" && e.ports[0]) { + createEditor(e.ports[0]); + } +}; diff --git a/packages/quick-edit/functions/_middleware.ts b/packages/quick-edit/functions/_middleware.ts deleted file mode 100644 index 551222dce95c..000000000000 --- a/packages/quick-edit/functions/_middleware.ts +++ /dev/null @@ -1,81 +0,0 @@ -export const onRequest = async ({ - env, - request, - next, -}: Parameters[0]) => { - const url = new URL(request.url); - - const isLocalDev = url.hostname === "localhost"; - - const values = { - WORKBENCH_WEB_CONFIGURATION: JSON.stringify({ - configurationDefaults: { - "workbench.colorTheme": - url.searchParams.get("theme") === "dark" - ? "Solarflare Dark" - : "Solarflare Light", - "workbench.startupEditor": "none", - "editor.minimap.autohide": true, - "files.exclude": { - "*.d.ts": true, - "jsconfig.json": true, - "package.json": true, - "wrangler.toml": true, - }, - "files.autoSave": "afterDelay", - "files.autoSaveDelay": 200, - "telemetry.telemetryLevel": "off", - "window.menuBarVisibility": "hidden", - }, - productConfiguration: { - nameShort: "Quick Edit", - nameLong: "Cloudflare Workers Quick Edit", - applicationName: "workers-quick-edit", - dataFolderName: ".quick-edit", - version: "1.76.0", - extensionEnabledApiProposals: { - "cloudflare.quick-edit-extension": [ - "fileSearchProvider", - "textSearchProvider", - "ipc", - ], - }, - }, - additionalBuiltinExtensions: [ - { - scheme: url.protocol === "https:" ? "https" : "http", - path: "/quick-edit-extension", - }, - { - scheme: url.protocol === "https:" ? "https" : "http", - path: "/solarflare-theme", - }, - ], - }).replace(/"/g, """), - WORKBENCH_AUTH_SESSION: "", - WORKBENCH_WEB_BASE_URL: "/assets", - }; - - if (url.pathname === "/") { - url.pathname = `${ - values.WORKBENCH_WEB_BASE_URL - }/out/vs/code/browser/workbench/workbench${isLocalDev ? "-dev" : ""}`; - const response = await env.ASSETS.fetch(url); - let body = await response.text(); - body = body.replaceAll( - /\{\{([^}]+)\}\}/g, - (_, key) => values[key as keyof typeof values] ?? "undefined" - ); - if (!isLocalDev) { - body = body.replace("/node_modules/", "/modules/"); - } - - return new Response(body, { - headers: { - "Content-Type": "text/html", - }, - }); - } else { - return next(); - } -}; diff --git a/packages/quick-edit/package.json b/packages/quick-edit/package.json index d94225296c5f..65c5b5b35bd5 100644 --- a/packages/quick-edit/package.json +++ b/packages/quick-edit/package.json @@ -10,19 +10,22 @@ "license": "BSD-3-Clause", "author": "workers-devprod@cloudflare.com", "scripts": { - "check:lint": "eslint functions --max-warnings=0", + "check:lint": "eslint --max-warnings=0", "check:type": "tsc", "custom:build": "./build.sh", - "deploy": "pnpm run setup && pnpm run custom:build && CLOUDFLARE_ACCOUNT_ID=e35fd947284363a46fd7061634477114 pnpm exec wrangler pages deploy --project-name quick-edit ./web", - "dev": "concurrently 'pnpm exec wrangler pages dev ./web' 'npm --prefix web/quick-edit-extension run watch-web' 'yarn --cwd ../../vendor/vscode watch' 'yarn --cwd ../../vendor/vscode watch-web'", - "setup": "rm -rf web/assets web/quick-edit-extension && ./setup.sh" + "deploy": "pnpm run setup && pnpm run custom:build && CLOUDFLARE_ACCOUNT_ID=e35fd947284363a46fd7061634477114 pnpm wrangler deploy", + "preview": "pnpm run setup && pnpm run custom:build && CLOUDFLARE_ACCOUNT_ID=e35fd947284363a46fd7061634477114 pnpm wrangler versions upload", + "setup": "./setup.sh" }, "dependencies": { "yarn": "^1.22.19" }, "devDependencies": { "@cloudflare/workers-tsconfig": "workspace:*", + "@cloudflare/workers-types": "^4.20250712.0", + "@types/node": "catalog:default", "concurrently": "^8.2.2", + "esbuild": "catalog:default", "tsx": "^3.12.8", "wrangler": "workspace:*" }, diff --git a/packages/quick-edit/patches/0001-Add-Custom-workbench-for-Cloudflare.patch b/packages/quick-edit/patches/0001-Add-Custom-workbench-for-Cloudflare.patch deleted file mode 100644 index a014d6198a4c..000000000000 --- a/packages/quick-edit/patches/0001-Add-Custom-workbench-for-Cloudflare.patch +++ /dev/null @@ -1,598 +0,0 @@ -From 16813a84f1dd335897f601d2bf7710f3d39cf796 Mon Sep 17 00:00:00 2001 -From: Samuel Macleod -Date: Mon, 3 Apr 2023 11:18:10 +0100 -Subject: [PATCH 1/9] Add Custom workbench for Cloudflare - ---- - src/vs/code/browser/workbench/workbench.ts | 522 +-------------------- - 1 file changed, 25 insertions(+), 497 deletions(-) - -diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts -index 16c18d0..b6abcd1 100644 ---- a/src/vs/code/browser/workbench/workbench.ts -+++ b/src/vs/code/browser/workbench/workbench.ts -@@ -5,441 +5,22 @@ - - import { isStandalone } from 'vs/base/browser/browser'; - import { mainWindow } from 'vs/base/browser/window'; --import { VSBuffer, decodeBase64, encodeBase64 } from 'vs/base/common/buffer'; --import { Emitter } from 'vs/base/common/event'; --import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; --import { parse } from 'vs/base/common/marshalling'; --import { Schemas } from 'vs/base/common/network'; --import { posix } from 'vs/base/common/path'; --import { isEqual } from 'vs/base/common/resources'; --import { ltrim } from 'vs/base/common/strings'; - import { URI, UriComponents } from 'vs/base/common/uri'; --import product from 'vs/platform/product/common/product'; --import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; --import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; - import type { IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/browser/web.api'; --import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; --import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; - import { create } from 'vs/workbench/workbench.web.main'; - --interface ISecretStorageCrypto { -- seal(data: string): Promise; -- unseal(data: string): Promise; --} -- --class TransparentCrypto implements ISecretStorageCrypto { -- async seal(data: string): Promise { -- return data; -- } -- -- async unseal(data: string): Promise { -- return data; -- } --} -- --const enum AESConstants { -- ALGORITHM = 'AES-GCM', -- KEY_LENGTH = 256, -- IV_LENGTH = 12, --} -- --class ServerKeyedAESCrypto implements ISecretStorageCrypto { -- private _serverKey: Uint8Array | undefined; -- -- /** Gets whether the algorithm is supported; requires a secure context */ -- public static supported() { -- return !!crypto.subtle; -- } -- -- constructor(private readonly authEndpoint: string) { } -- -- async seal(data: string): Promise { -- // Get a new key and IV on every change, to avoid the risk of reusing the same key and IV pair with AES-GCM -- // (see also: https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams#properties) -- const iv = mainWindow.crypto.getRandomValues(new Uint8Array(AESConstants.IV_LENGTH)); -- // crypto.getRandomValues isn't a good-enough PRNG to generate crypto keys, so we need to use crypto.subtle.generateKey and export the key instead -- const clientKeyObj = await mainWindow.crypto.subtle.generateKey( -- { name: AESConstants.ALGORITHM as const, length: AESConstants.KEY_LENGTH as const }, -- true, -- ['encrypt', 'decrypt'] -- ); -- -- const clientKey = new Uint8Array(await mainWindow.crypto.subtle.exportKey('raw', clientKeyObj)); -- const key = await this.getKey(clientKey); -- const dataUint8Array = new TextEncoder().encode(data); -- const cipherText: ArrayBuffer = await mainWindow.crypto.subtle.encrypt( -- { name: AESConstants.ALGORITHM as const, iv }, -- key, -- dataUint8Array -- ); -- -- // Base64 encode the result and store the ciphertext, the key, and the IV in localStorage -- // Note that the clientKey and IV don't need to be secret -- const result = new Uint8Array([...clientKey, ...iv, ...new Uint8Array(cipherText)]); -- return encodeBase64(VSBuffer.wrap(result)); -- } -- -- async unseal(data: string): Promise { -- // encrypted should contain, in order: the key (32-byte), the IV for AES-GCM (12-byte) and the ciphertext (which has the GCM auth tag at the end) -- // Minimum length must be 44 (key+IV length) + 16 bytes (1 block encrypted with AES - regardless of key size) -- const dataUint8Array = decodeBase64(data); -- -- if (dataUint8Array.byteLength < 60) { -- throw Error('Invalid length for the value for credentials.crypto'); -- } -- -- const keyLength = AESConstants.KEY_LENGTH / 8; -- const clientKey = dataUint8Array.slice(0, keyLength); -- const iv = dataUint8Array.slice(keyLength, keyLength + AESConstants.IV_LENGTH); -- const cipherText = dataUint8Array.slice(keyLength + AESConstants.IV_LENGTH); -- -- // Do the decryption and parse the result as JSON -- const key = await this.getKey(clientKey.buffer); -- const decrypted = await mainWindow.crypto.subtle.decrypt( -- { name: AESConstants.ALGORITHM as const, iv: iv.buffer }, -- key, -- cipherText.buffer -- ); -- -- return new TextDecoder().decode(new Uint8Array(decrypted)); -- } -- -- /** -- * Given a clientKey, returns the CryptoKey object that is used to encrypt/decrypt the data. -- * The actual key is (clientKey XOR serverKey) -- */ -- private async getKey(clientKey: Uint8Array): Promise { -- if (!clientKey || clientKey.byteLength !== AESConstants.KEY_LENGTH / 8) { -- throw Error('Invalid length for clientKey'); -- } -- -- const serverKey = await this.getServerKeyPart(); -- const keyData = new Uint8Array(AESConstants.KEY_LENGTH / 8); -- -- for (let i = 0; i < keyData.byteLength; i++) { -- keyData[i] = clientKey[i]! ^ serverKey[i]!; -- } -- -- return mainWindow.crypto.subtle.importKey( -- 'raw', -- keyData, -- { -- name: AESConstants.ALGORITHM as const, -- length: AESConstants.KEY_LENGTH as const, -- }, -- true, -- ['encrypt', 'decrypt'] -- ); -- } -- -- private async getServerKeyPart(): Promise { -- if (this._serverKey) { -- return this._serverKey; -- } -- -- let attempt = 0; -- let lastError: unknown | undefined; -- -- while (attempt <= 3) { -- try { -- const res = await fetch(this.authEndpoint, { credentials: 'include', method: 'POST' }); -- if (!res.ok) { -- throw new Error(res.statusText); -- } -- const serverKey = new Uint8Array(await await res.arrayBuffer()); -- if (serverKey.byteLength !== AESConstants.KEY_LENGTH / 8) { -- throw Error(`The key retrieved by the server is not ${AESConstants.KEY_LENGTH} bit long.`); -- } -- this._serverKey = serverKey; -- return this._serverKey; -- } catch (e) { -- lastError = e; -- attempt++; -- -- // exponential backoff -- await new Promise(resolve => setTimeout(resolve, attempt * attempt * 100)); -- } -- } -- -- throw lastError; -- } --} -- --export class LocalStorageSecretStorageProvider implements ISecretStorageProvider { -- private readonly _storageKey = 'secrets.provider'; -- -- private _secretsPromise: Promise> = this.load(); -- -- type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; -- -- constructor( -- private readonly crypto: ISecretStorageCrypto, -- ) { } -- -- private async load(): Promise> { -- const record = this.loadAuthSessionFromElement(); -- // Get the secrets from localStorage -- const encrypted = localStorage.getItem(this._storageKey); -- if (encrypted) { -- try { -- const decrypted = JSON.parse(await this.crypto.unseal(encrypted)); -- return { ...record, ...decrypted }; -- } catch (err) { -- // TODO: send telemetry -- console.error('Failed to decrypt secrets from localStorage', err); -- localStorage.removeItem(this._storageKey); -- } -- } -- -- return record; -- } -- -- private loadAuthSessionFromElement(): Record { -- let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; -- const authSessionElement = mainWindow.document.getElementById('vscode-workbench-auth-session'); -- const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; -- if (authSessionElementAttribute) { -- try { -- authSessionInfo = JSON.parse(authSessionElementAttribute); -- } catch (error) { /* Invalid session is passed. Ignore. */ } -- } -- -- if (!authSessionInfo) { -- return {}; -- } -- -- const record: Record = {}; -- -- // Settings Sync Entry -- record[`${product.urlProtocol}.loginAccount`] = JSON.stringify(authSessionInfo); -- -- // Auth extension Entry -- if (authSessionInfo.providerId !== 'github') { -- console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); -- return record; -- } -- -- const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); -- record[authAccount] = JSON.stringify(authSessionInfo.scopes.map(scopes => ({ -- id: authSessionInfo!.id, -- scopes, -- accessToken: authSessionInfo!.accessToken -- }))); -- -- return record; -- } -- -- async get(key: string): Promise { -- const secrets = await this._secretsPromise; -- return secrets[key]; -- } -- async set(key: string, value: string): Promise { -- const secrets = await this._secretsPromise; -- secrets[key] = value; -- this._secretsPromise = Promise.resolve(secrets); -- this.save(); -- } -- async delete(key: string): Promise { -- const secrets = await this._secretsPromise; -- delete secrets[key]; -- this._secretsPromise = Promise.resolve(secrets); -- this.save(); -- } -- -- private async save(): Promise { -- try { -- const encrypted = await this.crypto.seal(JSON.stringify(await this._secretsPromise)); -- localStorage.setItem(this._storageKey, encrypted); -- } catch (err) { -- console.error(err); -- } -- } --} -- -- --class LocalStorageURLCallbackProvider extends Disposable implements IURLCallbackProvider { -- -- private static REQUEST_ID = 0; -- -- private static QUERY_KEYS: ('scheme' | 'authority' | 'path' | 'query' | 'fragment')[] = [ -- 'scheme', -- 'authority', -- 'path', -- 'query', -- 'fragment' -- ]; -- -- private readonly _onCallback = this._register(new Emitter()); -- readonly onCallback = this._onCallback.event; -- -- private pendingCallbacks = new Set(); -- private lastTimeChecked = Date.now(); -- private checkCallbacksTimeout: unknown | undefined = undefined; -- private onDidChangeLocalStorageDisposable: IDisposable | undefined; -- -- constructor(private readonly _callbackRoute: string) { -- super(); -- } -- -- create(options: Partial = {}): URI { -- const id = ++LocalStorageURLCallbackProvider.REQUEST_ID; -- const queryParams: string[] = [`vscode-reqid=${id}`]; -- -- for (const key of LocalStorageURLCallbackProvider.QUERY_KEYS) { -- const value = options[key]; -- -- if (value) { -- queryParams.push(`vscode-${key}=${encodeURIComponent(value)}`); -- } -- } -- -- // TODO@joao remove eventually -- // https://github.com/microsoft/vscode-dev/issues/62 -- // https://github.com/microsoft/vscode/blob/159479eb5ae451a66b5dac3c12d564f32f454796/extensions/github-authentication/src/githubServer.ts#L50-L50 -- if (!(options.authority === 'vscode.github-authentication' && options.path === '/dummy')) { -- const key = `vscode-web.url-callbacks[${id}]`; -- localStorage.removeItem(key); -- -- this.pendingCallbacks.add(id); -- this.startListening(); -- } -- -- return URI.parse(mainWindow.location.href).with({ path: this._callbackRoute, query: queryParams.join('&') }); -- } -- -- private startListening(): void { -- if (this.onDidChangeLocalStorageDisposable) { -- return; -- } -- -- const fn = () => this.onDidChangeLocalStorage(); -- mainWindow.addEventListener('storage', fn); -- this.onDidChangeLocalStorageDisposable = { dispose: () => mainWindow.removeEventListener('storage', fn) }; -- } -- -- private stopListening(): void { -- this.onDidChangeLocalStorageDisposable?.dispose(); -- this.onDidChangeLocalStorageDisposable = undefined; -- } -- -- // this fires every time local storage changes, but we -- // don't want to check more often than once a second -- private async onDidChangeLocalStorage(): Promise { -- const ellapsed = Date.now() - this.lastTimeChecked; -- -- if (ellapsed > 1000) { -- this.checkCallbacks(); -- } else if (this.checkCallbacksTimeout === undefined) { -- this.checkCallbacksTimeout = setTimeout(() => { -- this.checkCallbacksTimeout = undefined; -- this.checkCallbacks(); -- }, 1000 - ellapsed); -- } -- } -- -- private checkCallbacks(): void { -- let pendingCallbacks: Set | undefined; -- -- for (const id of this.pendingCallbacks) { -- const key = `vscode-web.url-callbacks[${id}]`; -- const result = localStorage.getItem(key); -- -- if (result !== null) { -- try { -- this._onCallback.fire(URI.revive(JSON.parse(result))); -- } catch (error) { -- console.error(error); -- } -- -- pendingCallbacks = pendingCallbacks ?? new Set(this.pendingCallbacks); -- pendingCallbacks.delete(id); -- localStorage.removeItem(key); -- } -- } -- -- if (pendingCallbacks) { -- this.pendingCallbacks = pendingCallbacks; -- -- if (this.pendingCallbacks.size === 0) { -- this.stopListening(); -- } -- } -- -- this.lastTimeChecked = Date.now(); -- } --} -- - class WorkspaceProvider implements IWorkspaceProvider { - - private static QUERY_PARAM_EMPTY_WINDOW = 'ew'; -- private static QUERY_PARAM_FOLDER = 'folder'; -- private static QUERY_PARAM_WORKSPACE = 'workspace'; - - private static QUERY_PARAM_PAYLOAD = 'payload'; - - static create(config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents }) { -- let foundWorkspace = false; -- let workspace: IWorkspace; - let payload = Object.create(null); -- -- const query = new URL(document.location.href).searchParams; -- query.forEach((value, key) => { -- switch (key) { -- -- // Folder -- case WorkspaceProvider.QUERY_PARAM_FOLDER: -- if (config.remoteAuthority && value.startsWith(posix.sep)) { -- // when connected to a remote and having a value -- // that is a path (begins with a `/`), assume this -- // is a vscode-remote resource as simplified URL. -- workspace = { folderUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; -- } else { -- workspace = { folderUri: URI.parse(value) }; -- } -- foundWorkspace = true; -- break; -- -- // Workspace -- case WorkspaceProvider.QUERY_PARAM_WORKSPACE: -- if (config.remoteAuthority && value.startsWith(posix.sep)) { -- // when connected to a remote and having a value -- // that is a path (begins with a `/`), assume this -- // is a vscode-remote resource as simplified URL. -- workspace = { workspaceUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; -- } else { -- workspace = { workspaceUri: URI.parse(value) }; -- } -- foundWorkspace = true; -- break; -- -- // Empty -- case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: -- workspace = undefined; -- foundWorkspace = true; -- break; -- -- // Payload -- case WorkspaceProvider.QUERY_PARAM_PAYLOAD: -- try { -- payload = parse(value); // use marshalling#parse() to revive potential URIs -- } catch (error) { -- console.error(error); // possible invalid JSON -- } -- break; -- } -- }); -- -- // If no workspace is provided through the URL, check for config -- // attribute from server -- if (!foundWorkspace) { -- if (config.folderUri) { -- workspace = { folderUri: URI.revive(config.folderUri) }; -- } else if (config.workspaceUri) { -- workspace = { workspaceUri: URI.revive(config.workspaceUri) }; -- } -- } -- -- return new WorkspaceProvider(workspace, payload, config); -+ return new WorkspaceProvider( -+ { folderUri: URI.revive(config.folderUri)! }, -+ payload -+ ); - } - - readonly trusted = true; -@@ -447,7 +28,6 @@ class WorkspaceProvider implements IWorkspaceProvider { - private constructor( - readonly workspace: IWorkspace, - readonly payload: object, -- private readonly config: IWorkbenchConstructionOptions - ) { - } - -@@ -483,18 +63,6 @@ class WorkspaceProvider implements IWorkspaceProvider { - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW}=true`; - } - -- // Folder -- else if (isFolderToOpen(workspace)) { -- const queryParamFolder = this.encodeWorkspacePath(workspace.folderUri); -- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${queryParamFolder}`; -- } -- -- // Workspace -- else if (isWorkspaceToOpen(workspace)) { -- const queryParamWorkspace = this.encodeWorkspacePath(workspace.workspaceUri); -- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${queryParamWorkspace}`; -- } -- - // Append payload if any - if (options?.payload) { - targetHref += `&${WorkspaceProvider.QUERY_PARAM_PAYLOAD}=${encodeURIComponent(JSON.stringify(options.payload))}`; -@@ -503,66 +71,18 @@ class WorkspaceProvider implements IWorkspaceProvider { - return targetHref; - } - -- private encodeWorkspacePath(uri: URI): string { -- if (this.config.remoteAuthority && uri.scheme === Schemas.vscodeRemote) { -- -- // when connected to a remote and having a folder -- // or workspace for that remote, only use the path -- // as query value to form shorter, nicer URLs. -- // however, we still need to `encodeURIComponent` -- // to ensure to preserve special characters, such -- // as `+` in the path. -- -- return encodeURIComponent(`${posix.sep}${ltrim(uri.path, posix.sep)}`).replaceAll('%2F', '/'); -- } - -- return encodeURIComponent(uri.toString(true)); -- } - - private isSame(workspaceA: IWorkspace, workspaceB: IWorkspace): boolean { -- if (!workspaceA || !workspaceB) { -- return workspaceA === workspaceB; // both empty -- } -- -- if (isFolderToOpen(workspaceA) && isFolderToOpen(workspaceB)) { -- return isEqual(workspaceA.folderUri, workspaceB.folderUri); // same workspace -- } -- -- if (isWorkspaceToOpen(workspaceA) && isWorkspaceToOpen(workspaceB)) { -- return isEqual(workspaceA.workspaceUri, workspaceB.workspaceUri); // same workspace -- } -- -- return false; -+ return true; - } - - hasRemote(): boolean { -- if (this.workspace) { -- if (isFolderToOpen(this.workspace)) { -- return this.workspace.folderUri.scheme === Schemas.vscodeRemote; -- } -- -- if (isWorkspaceToOpen(this.workspace)) { -- return this.workspace.workspaceUri.scheme === Schemas.vscodeRemote; -- } -- } -- - return true; - } - } - --function readCookie(name: string): string | undefined { -- const cookies = document.cookie.split('; '); -- for (const cookie of cookies) { -- if (cookie.startsWith(name + '=')) { -- return cookie.substring(name.length + 1); -- } -- } -- -- return undefined; --} -- --(function () { -- -+function createEditor(port: MessagePort) { - // Find config by checking for DOM - const configElement = mainWindow.document.getElementById('vscode-workbench-web-configuration'); - const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; -@@ -570,19 +90,27 @@ function readCookie(name: string): string | undefined { - throw new Error('Missing web configuration element'); - } - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute); -- const secretStorageKeyPath = readCookie('vscode-secret-key-path'); -- const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported() -- ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto(); -+ -+ const searchParams = new URLSearchParams(window.location.search); -+ -+ const messagePorts = new Map(); -+ messagePorts.set("cloudflare.quick-edit-extension", port); - - // Create workbench - create(mainWindow.document.body, { - ...config, -- windowIndicator: config.windowIndicator ?? { label: '$(remote)', tooltip: `${product.nameShort} Web` }, -- settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, -- workspaceProvider: WorkspaceProvider.create(config), -- urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), -- secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath -- ? undefined /* with a remote without embedder-preferred storage, store on the remote */ -- : new LocalStorageSecretStorageProvider(secretStorageCrypto), -+ defaultLayout: { -+ force: true, -+ editors: [], -+ }, -+ settingsSyncOptions: undefined, -+ workspaceProvider: WorkspaceProvider.create({ ...config, folderUri: URI.parse(decodeURIComponent(searchParams.get("worker")!)) }), -+ messagePorts - }); --})(); -+} -+ -+window.onmessage = (e) => { -+ if (e.data === "PORT") { -+ createEditor(e.ports[0]!); -+ } -+}; -\ No newline at end of file --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/patches/0002-Remove-Themes-from-Setting-SubMenu.patch b/packages/quick-edit/patches/0002-Remove-Themes-from-Setting-SubMenu.patch deleted file mode 100644 index 17632c896ec7..000000000000 --- a/packages/quick-edit/patches/0002-Remove-Themes-from-Setting-SubMenu.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0ae310974f575dc69c4815249b3d7d582f77b7ab Mon Sep 17 00:00:00 2001 -From: Jacob M-G Evans -Date: Fri, 31 Mar 2023 17:11:08 -0500 -Subject: [PATCH 2/9] Remove Themes from Setting SubMenu - ---- - .../themes/browser/themes.contribution.ts | 40 +------------------ - 1 file changed, 1 insertion(+), 39 deletions(-) - -diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts -index 60a131a..ecd9363 100644 ---- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts -+++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts -@@ -5,7 +5,7 @@ - - import { localize } from 'vs/nls'; - import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; --import { MenuRegistry, MenuId, Action2, registerAction2, ISubmenuItem } from 'vs/platform/actions/common/actions'; -+import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; - import { equalsIgnoreCase } from 'vs/base/common/strings'; - import { Registry } from 'vs/platform/registry/common/platform'; - import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -@@ -719,44 +719,6 @@ registerAction2(class extends Action2 { - } - }); - --const ThemesSubMenu = new MenuId('ThemesSubMenu'); --MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { -- title: localize('themes', "Themes"), -- submenu: ThemesSubMenu, -- group: '2_configuration', -- order: 7 --}); --MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { -- title: localize({ key: 'miSelectTheme', comment: ['&& denotes a mnemonic'] }, "&&Theme"), -- submenu: ThemesSubMenu, -- group: '2_configuration', -- order: 7 --}); -- --MenuRegistry.appendMenuItem(ThemesSubMenu, { -- command: { -- id: SelectColorThemeCommandId, -- title: localize('selectTheme.label', "Color Theme") -- }, -- order: 1 --}); -- --MenuRegistry.appendMenuItem(ThemesSubMenu, { -- command: { -- id: SelectFileIconThemeCommandId, -- title: localize('themes.selectIconTheme.label', "File Icon Theme") -- }, -- order: 2 --}); -- --MenuRegistry.appendMenuItem(ThemesSubMenu, { -- command: { -- id: SelectProductIconThemeCommandId, -- title: localize('themes.selectProductIconTheme.label', "Product Icon Theme") -- }, -- order: 3 --}); -- - type DefaultThemeUpdatedNotificationReaction = 'keepNew' | 'keepOld' | 'tryNew' | 'cancel' | 'browse'; - - class DefaultThemeUpdatedNotificationContribution implements IWorkbenchContribution { --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/patches/0003-Remove-Profile-from-Setting-SubMenu.patch b/packages/quick-edit/patches/0003-Remove-Profile-from-Setting-SubMenu.patch deleted file mode 100644 index c4cb2ea85837..000000000000 --- a/packages/quick-edit/patches/0003-Remove-Profile-from-Setting-SubMenu.patch +++ /dev/null @@ -1,63 +0,0 @@ -From a523dcb033cd56f38b28a91e3e81b77f33b80b5e Mon Sep 17 00:00:00 2001 -From: Jacob M-G Evans -Date: Fri, 31 Mar 2023 17:11:28 -0500 -Subject: [PATCH 3/9] Remove Profile from Setting SubMenu - ---- - .../browser/userDataProfile.ts | 26 +------------------ - 1 file changed, 1 insertion(+), 25 deletions(-) - -diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts -index bf6cc44..fb9feec 100644 ---- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts -+++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts -@@ -7,7 +7,7 @@ import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/ - import { isWeb } from 'vs/base/common/platform'; - import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; - import { localize } from 'vs/nls'; --import { Action2, IMenuService, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; -+import { Action2, IMenuService, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; - import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; - import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; - import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -@@ -70,7 +70,6 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements - } - - private registerActions(): void { -- this.registerProfileSubMenu(); - this._register(this.registerSwitchProfileAction()); - - this.registerProfilesActions(); -@@ -86,29 +85,6 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements - this.registerHelpAction(); - } - -- private registerProfileSubMenu(): void { -- const getProfilesTitle = () => { -- return localize('profiles', "Profiles ({0})", this.userDataProfileService.currentProfile.name); -- }; -- MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { -- get title() { -- return getProfilesTitle(); -- }, -- submenu: ProfilesMenu, -- group: '2_configuration', -- order: 1, -- }); -- MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { -- get title() { -- return getProfilesTitle(); -- }, -- submenu: ProfilesMenu, -- group: '2_configuration', -- order: 1, -- when: PROFILES_ENABLEMENT_CONTEXT, -- }); -- } -- - private readonly profilesDisposable = this._register(new MutableDisposable()); - private registerProfilesActions(): void { - this.profilesDisposable.value = new DisposableStore(); --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/patches/0004-Remove-Account-Extension.patch b/packages/quick-edit/patches/0004-Remove-Account-Extension.patch deleted file mode 100644 index d046ba08e81b..000000000000 --- a/packages/quick-edit/patches/0004-Remove-Account-Extension.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 6e4c026e1ba2beb66eaa391b83cbb8662b017cf4 Mon Sep 17 00:00:00 2001 -From: Jacob M-G Evans -Date: Tue, 28 Mar 2023 14:53:02 -0500 -Subject: [PATCH 4/9] Remove Account & Extension - ---- - src/vs/workbench/browser/parts/globalCompositeBar.ts | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts -index 13432c8..6edf613 100644 ---- a/src/vs/workbench/browser/parts/globalCompositeBar.ts -+++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts -@@ -144,11 +144,11 @@ export class GlobalCompositeBar extends Disposable { - } - - private get accountsVisibilityPreference(): boolean { -- return this.storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.PROFILE, true); -+ return false; - } - - private set accountsVisibilityPreference(value: boolean) { -- this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.PROFILE, StorageTarget.USER); -+ - } - } - --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/patches/0005-Remove-nls.patch b/packages/quick-edit/patches/0005-Remove-nls.patch deleted file mode 100644 index 6bb0a02fd498..000000000000 --- a/packages/quick-edit/patches/0005-Remove-nls.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 7d3749a65d79728ce2544d437107249c390cdb1a Mon Sep 17 00:00:00 2001 -From: Workers DevProd -Date: Tue, 23 May 2023 12:09:58 +0100 -Subject: [PATCH 5/9] Remove nls - ---- - src/vs/code/browser/workbench/workbench.html | 17 +---------------- - 1 file changed, 1 insertion(+), 16 deletions(-) - -diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html -index 700231f..390736d 100644 ---- a/src/vs/code/browser/workbench/workbench.html -+++ b/src/vs/code/browser/workbench/workbench.html -@@ -41,20 +41,6 @@ - self.webPackagePaths[key] = `${baseUrl}/node_modules/${key}/${self.webPackagePaths[key]}`; - }); - -- // Set up nls if the user is not using the default language (English) -- const nlsConfig = {}; -- // Normalize locale to lowercase because translationServiceUrl is case-sensitive. -- // ref: https://github.com/microsoft/vscode/issues/187795 -- const locale = localStorage.getItem('vscode.nls.locale') || navigator.language.toLowerCase(); -- if (!locale.startsWith('en')) { -- nlsConfig['vs/nls'] = { -- availableLanguages: { -- '*': locale -- }, -- translationServiceUrl: '{{WORKBENCH_NLS_BASE_URL}}' -- }; -- } -- - require.config({ - baseUrl: `${baseUrl}/out`, - recordStats: true, -@@ -66,8 +52,7 @@ - throw new Error(`Invalid script url: ${value}`) - } - }), -- paths: self.webPackagePaths, -- ...nlsConfig -+ paths: self.webPackagePaths - }); - - -- -+ -+ - -+ -+ -+ --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/patches/0009-Hide-VSCode-UI.patch b/packages/quick-edit/patches/hide-unwanted-ui.diff similarity index 53% rename from packages/quick-edit/patches/0009-Hide-VSCode-UI.patch rename to packages/quick-edit/patches/hide-unwanted-ui.diff index afaabfb518b1..e7b757cd0e93 100644 --- a/packages/quick-edit/patches/0009-Hide-VSCode-UI.patch +++ b/packages/quick-edit/patches/hide-unwanted-ui.diff @@ -1,92 +1,128 @@ -From 833a7aaab8eec7a29be6b255ce3a90e2232269c2 Mon Sep 17 00:00:00 2001 -From: Workers DevProd -Date: Sat, 17 Feb 2024 02:10:20 +0000 -Subject: [PATCH 9/9] Hide VSCode UI - ---- - src/vs/platform/window/common/window.ts | 2 +- - .../debug/browser/debug.contribution.ts | 594 +----- - .../browser/extensions.contribution.ts | 1710 +---------------- - .../contrib/scm/browser/scm.contribution.ts | 430 +---- - src/vs/workbench/workbench.common.main.ts | 9 - - 5 files changed, 6 insertions(+), 2739 deletions(-) - diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts -index 38ad7ca..612812e 100644 +index 86ad0229037..5e1a604ee07 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts -@@ -167,7 +167,7 @@ export interface IDensitySettings { +@@ -255,7 +255,7 @@ export function hasNativeTitlebar(configurationService: IConfigurationService, t - export function getTitleBarStyle(configurationService: IConfigurationService): 'native' | 'custom' { + export function getTitleBarStyle(configurationService: IConfigurationService): TitlebarStyle { if (isWeb) { -- return 'custom'; -+ return 'native'; +- return TitlebarStyle.CUSTOM; ++ return TitlebarStyle.NATIVE; } const configuration = configurationService.getValue('window'); +diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts +index 051dc16d1e0..0ea5dae22ca 100644 +--- a/src/vs/workbench/browser/layout.ts ++++ b/src/vs/workbench/browser/layout.ts +@@ -701,6 +701,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi + initialization: initialLayoutState, + runtime: layoutRuntimeState, + }; ++ this.stateModel.setRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN, true); + + // Sidebar View Container To Restore + if (this.isVisible(Parts.SIDEBAR_PART)) { +diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts +index 064faf73e15..9cea6109ea4 100644 +--- a/src/vs/workbench/browser/parts/globalCompositeBar.ts ++++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts +@@ -10,7 +10,7 @@ import { IActivityService } from '../../services/activity/common/activity.js'; + import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; + import { DisposableStore, Disposable } from '../../../base/common/lifecycle.js'; + import { IColorTheme, IThemeService } from '../../../platform/theme/common/themeService.js'; +-import { IStorageService, StorageScope, StorageTarget } from '../../../platform/storage/common/storage.js'; ++import { IStorageService, StorageScope } from '../../../platform/storage/common/storage.js'; + import { IExtensionService } from '../../services/extensions/common/extensions.js'; + import { CompositeBarActionViewItem, CompositeBarAction, IActivityHoverOptions, ICompositeBarActionViewItemOptions, ICompositeBarColors } from './compositeBarActions.js'; + import { Codicon } from '../../../base/common/codicons.js'; +@@ -731,9 +731,8 @@ function simpleActivityContextMenuActions(storageService: IStorageService, isAcc + } + + export function isAccountsActionVisible(storageService: IStorageService): boolean { +- return storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.PROFILE, true); ++ return false + } + + function setAccountsActionVisible(storageService: IStorageService, visible: boolean) { +- storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, visible, StorageScope.PROFILE, StorageTarget.USER); + } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts -index 4ff1a98..4ef7a69 100644 +index 4a98431a021..8423cd02de2 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts -@@ -3,598 +3,10 @@ +@@ -3,697 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ --import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; --import { FileAccess } from 'vs/base/common/network'; --import { isMacintosh, isWeb } from 'vs/base/common/platform'; --import { URI } from 'vs/base/common/uri'; --import 'vs/css!./media/debug.contribution'; --import 'vs/css!./media/debugHover'; --import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; --import * as nls from 'vs/nls'; --import { ICommandActionTitle, Icon } from 'vs/platform/action/common/action'; --import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; --import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; --import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; --import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; - import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; --import { Extensions as QuickAccessExtensions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess'; --import { Registry } from 'vs/platform/registry/common/platform'; --import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; --import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; --import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; --import { EditorExtensions } from 'vs/workbench/common/editor'; --import { Extensions as ViewExtensions, IViewContainersRegistry, IViewsRegistry, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; --import { BreakpointEditorContribution } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; --import { BreakpointsView } from 'vs/workbench/contrib/debug/browser/breakpointsView'; --import { CallStackEditorContribution } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; --import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'; --import { registerColors } from 'vs/workbench/contrib/debug/browser/debugColors'; --import { ADD_CONFIGURATION_ID, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_STACK_TRACE_ID, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/contrib/debug/browser/debugCommands'; --import { DebugConsoleQuickAccess } from 'vs/workbench/contrib/debug/browser/debugConsoleQuickAccess'; --import { RunToCursorAction, SelectionToReplAction, SelectionToWatchExpressionsAction } from 'vs/workbench/contrib/debug/browser/debugEditorActions'; --import { DebugEditorContribution } from 'vs/workbench/contrib/debug/browser/debugEditorContribution'; --import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; --import { DebugProgressContribution } from 'vs/workbench/contrib/debug/browser/debugProgress'; --import { StartDebugQuickAccessProvider } from 'vs/workbench/contrib/debug/browser/debugQuickAccess'; --import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; --import { DebugStatusContribution } from 'vs/workbench/contrib/debug/browser/debugStatus'; --import { DebugTitleContribution } from 'vs/workbench/contrib/debug/browser/debugTitle'; --import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; --import { DebugViewPaneContainer } from 'vs/workbench/contrib/debug/browser/debugViewlet'; --import { DisassemblyView, DisassemblyViewContribution } from 'vs/workbench/contrib/debug/browser/disassemblyView'; --import { LoadedScriptsView } from 'vs/workbench/contrib/debug/browser/loadedScriptsView'; --import { Repl } from 'vs/workbench/contrib/debug/browser/repl'; --import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; --import { ADD_TO_WATCH_ID, BREAK_WHEN_VALUE_CHANGES_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, COPY_EVALUATE_PATH_ID, COPY_VALUE_ID, SET_VARIABLE_ID, VariablesView, VIEW_MEMORY_ID } from 'vs/workbench/contrib/debug/browser/variablesView'; --import { ADD_WATCH_ID, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchExpressionsView'; --import { WelcomeView } from 'vs/workbench/contrib/debug/browser/welcomeView'; --import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, getStateLabel, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID } from 'vs/workbench/contrib/debug/common/debug'; --import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; --import { DebugLifecycle } from 'vs/workbench/contrib/debug/common/debugLifecycle'; --import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput'; --import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; --import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -- +-import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +-import { FileAccess } from '../../../../base/common/network.js'; +-import { isMacintosh, isWeb } from '../../../../base/common/platform.js'; +-import { URI } from '../../../../base/common/uri.js'; +-import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; +-import * as nls from '../../../../nls.js'; +-import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +-import { ICommandActionTitle, Icon } from '../../../../platform/action/common/action.js'; +-import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; +-import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; +-import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; +-import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; + import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +-import { KeybindingWeight, KeybindingsRegistry } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +-import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from '../../../../platform/quickinput/common/quickAccess.js'; +-import { Registry } from '../../../../platform/registry/common/platform.js'; +-import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; +-import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; +-import { FocusedViewContext } from '../../../common/contextkeys.js'; +-import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +-import { EditorExtensions } from '../../../common/editor.js'; +-import { IViewContainersRegistry, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js'; +-import { launchSchemaId } from '../../../services/configuration/common/configuration.js'; +-import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +-import { COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL } from '../../notebook/browser/contrib/notebookVariables/notebookVariableCommands.js'; +-import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_EXPRESSION_SELECTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_THREADS_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_VARIABLE_VALUE, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID, getStateLabel } from '../common/debug.js'; +-import { DebugWatchAccessibilityAnnouncer } from '../common/debugAccessibilityAnnouncer.js'; +-import { DebugContentProvider } from '../common/debugContentProvider.js'; +-import { DebugLifecycle } from '../common/debugLifecycle.js'; ++import { IDebugService } from '../common/debug.js'; + import { DebugVisualizerService, IDebugVisualizerService } from '../common/debugVisualizers.js'; +-import { DisassemblyViewInput } from '../common/disassemblyViewInput.js'; +-import { ReplAccessibilityAnnouncer } from '../common/replAccessibilityAnnouncer.js'; +-import { BreakpointEditorContribution } from './breakpointEditorContribution.js'; +-import { BreakpointsView } from './breakpointsView.js'; +-import { CallStackEditorContribution } from './callStackEditorContribution.js'; +-import { CallStackView } from './callStackView.js'; +-import { registerColors } from './debugColors.js'; +-import { ADD_CONFIGURATION_ID, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_STACK_TRACE_ID, COPY_VALUE_ID, COPY_VALUE_LABEL, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_ADDRESS_ID, COPY_ADDRESS_LABEL, TOGGLE_BREAKPOINT_ID } from './debugCommands.js'; +-import { DebugConsoleQuickAccess } from './debugConsoleQuickAccess.js'; +-import { RunToCursorAction, SelectionToReplAction, SelectionToWatchExpressionsAction } from './debugEditorActions.js'; +-import { DebugEditorContribution } from './debugEditorContribution.js'; +-import * as icons from './debugIcons.js'; +-import { DebugProgressContribution } from './debugProgress.js'; +-import { StartDebugQuickAccessProvider } from './debugQuickAccess.js'; + import { DebugService } from './debugService.js'; +-import './debugSettingMigration.js'; +-import { DebugStatusContribution } from './debugStatus.js'; +-import { DebugTitleContribution } from './debugTitle.js'; +-import { DebugToolBar } from './debugToolBar.js'; +-import { DebugViewPaneContainer } from './debugViewlet.js'; +-import { DisassemblyView, DisassemblyViewContribution } from './disassemblyView.js'; +-import { LoadedScriptsView } from './loadedScriptsView.js'; +-import './media/debug.contribution.css'; +-import './media/debugHover.css'; +-import { Repl } from './repl.js'; +-import { ReplAccessibilityHelp } from './replAccessibilityHelp.js'; +-import { ReplAccessibleView } from './replAccessibleView.js'; +-import { RunAndDebugAccessibilityHelp } from './runAndDebugAccessibilityHelp.js'; +-import { StatusBarColorProvider } from './statusbarColorProvider.js'; +-import { BREAK_WHEN_VALUE_CHANGES_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, SET_VARIABLE_ID, VIEW_MEMORY_ID, VariablesView } from './variablesView.js'; +-import { ADD_WATCH_ID, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, WatchExpressionsView } from './watchExpressionsView.js'; +-import { WelcomeView } from './welcomeView.js'; + -const debugCategory = nls.localize('debugCategory', "Debug"); -registerColors(); -registerSingleton(IDebugService, DebugService, InstantiationType.Delayed); + registerSingleton(IDebugVisualizerService, DebugVisualizerService, InstantiationType.Delayed); - -// Register Debug Workbench Contributions -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugStatusContribution, LifecyclePhase.Eventually); @@ -140,17 +176,16 @@ index 4ff1a98..4ef7a69 100644 -}; - -registerDebugCommandPaletteItem(RESTART_SESSION_ID, RESTART_LABEL); --registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize2('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE); +-registerDebugCommandPaletteItem(TERMINATE_THREAD_ID, nls.localize2('terminateThread', "Terminate Thread"), CONTEXT_IN_DEBUG_MODE, CONTEXT_TERMINATE_THREADS_SUPPORTED); -registerDebugCommandPaletteItem(STEP_OVER_ID, STEP_OVER_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(STEP_INTO_ID, STEP_INTO_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugCommandPaletteItem(STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); -registerDebugCommandPaletteItem(STEP_OUT_ID, STEP_OUT_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); --registerDebugCommandPaletteItem(PAUSE_ID, PAUSE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('running')); +-registerDebugCommandPaletteItem(PAUSE_ID, PAUSE_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.and(CONTEXT_DEBUG_STATE.isEqualTo('running'), CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG.toNegated())); -registerDebugCommandPaletteItem(DISCONNECT_ID, DISCONNECT_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED)); -registerDebugCommandPaletteItem(DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH, ContextKeyExpr.and(CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED))); -registerDebugCommandPaletteItem(STOP_ID, STOP_LABEL, CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.or(CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated(), CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED)); -registerDebugCommandPaletteItem(CONTINUE_ID, CONTINUE_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); --registerDebugCommandPaletteItem(FOCUS_REPL_ID, nls.localize2({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugFocusConsole' }, "Focus on Debug Console View")); -registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize2('jumpToCursor', "Jump to Cursor"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); -registerDebugCommandPaletteItem(JUMP_TO_CURSOR_ID, nls.localize2('SetNextStatement', "Set Next Statement"), CONTEXT_JUMP_TO_CURSOR_SUPPORTED); -registerDebugCommandPaletteItem(RunToCursorAction.ID, RunToCursorAction.LABEL, CONTEXT_DEBUGGERS_AVAILABLE); @@ -162,7 +197,7 @@ index 4ff1a98..4ef7a69 100644 -registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); -registerDebugCommandPaletteItem(NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL); -registerDebugCommandPaletteItem(PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL); --registerDebugCommandPaletteItem(SHOW_LOADED_SCRIPTS_ID, OPEN_LOADED_SCRIPTS_LABEL, CONTEXT_IN_DEBUG_MODE); +-registerDebugCommandPaletteItem(SHOW_LOADED_SCRIPTS_ID, OPEN_LOADED_SCRIPTS_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_LOADED_SCRIPTS_SUPPORTED); -registerDebugCommandPaletteItem(SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL); -registerDebugCommandPaletteItem(SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL); -registerDebugCommandPaletteItem(CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')); @@ -189,25 +224,32 @@ index 4ff1a98..4ef7a69 100644 -registerDebugViewMenuItem(MenuId.DebugCallStackContext, DISCONNECT_ID, DISCONNECT_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session'), undefined, '3_modification'); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, 21, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session'), CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED), undefined, '3_modification'); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, STOP_ID, STOP_LABEL, 30, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('session'), undefined, '3_modification'); --registerDebugViewMenuItem(MenuId.DebugCallStackContext, PAUSE_ID, PAUSE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('running'))); +-registerDebugViewMenuItem(MenuId.DebugCallStackContext, PAUSE_ID, PAUSE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), ContextKeyExpr.and(CONTEXT_DEBUG_STATE.isEqualTo('running'), CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG.toNegated()))); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, CONTINUE_ID, CONTINUE_LABEL, 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped'))); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, STEP_OVER_ID, STEP_OVER_LABEL, 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, STEP_INTO_ID, STEP_INTO_LABEL, 30, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, STEP_OUT_ID, STEP_OUT_LABEL, 40, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_DEBUG_STATE.isEqualTo('stopped')); --registerDebugViewMenuItem(MenuId.DebugCallStackContext, TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), undefined, 'termination'); +-registerDebugViewMenuItem(MenuId.DebugCallStackContext, TERMINATE_THREAD_ID, nls.localize('terminateThread', "Terminate Thread"), 10, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('thread'), CONTEXT_TERMINATE_THREADS_SUPPORTED, 'termination'); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, RESTART_FRAME_ID, nls.localize('restartFrame', "Restart Frame"), 10, ContextKeyExpr.and(CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_RESTART_FRAME_SUPPORTED), CONTEXT_STACK_FRAME_SUPPORTS_RESTART); -registerDebugViewMenuItem(MenuId.DebugCallStackContext, COPY_STACK_TRACE_ID, nls.localize('copyStackTrace', "Copy Call Stack"), 20, CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), undefined, '3_modification'); - -registerDebugViewMenuItem(MenuId.DebugVariablesContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Binary Data"), 15, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, 'inline', icons.debugInspectMemory); -- -registerDebugViewMenuItem(MenuId.DebugVariablesContext, SET_VARIABLE_ID, nls.localize('setValue', "Set Value"), 10, ContextKeyExpr.or(CONTEXT_SET_VARIABLE_SUPPORTED, ContextKeyExpr.and(CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_SET_EXPRESSION_SUPPORTED)), CONTEXT_VARIABLE_IS_READONLY.toNegated(), '3_modification'); --registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, nls.localize('copyValue', "Copy Value"), 10, undefined, undefined, '5_cutcopypaste'); --registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, nls.localize('copyAsExpression', "Copy as Expression"), 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste'); --registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, nls.localize('addToWatchExpressions', "Add to Watch"), 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands'); +-registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_VALUE_ID, COPY_VALUE_LABEL, 10, undefined, undefined, '5_cutcopypaste'); +-registerDebugViewMenuItem(MenuId.DebugVariablesContext, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste'); +-registerDebugViewMenuItem(MenuId.DebugVariablesContext, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands'); -registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_READ_ID, nls.localize('breakWhenValueIsRead', "Break on Value Read"), 200, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, undefined, 'z_commands'); -registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break on Value Change"), 210, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands'); -registerDebugViewMenuItem(MenuId.DebugVariablesContext, BREAK_WHEN_VALUE_IS_ACCESSED_ID, nls.localize('breakWhenValueIsAccessed', "Break on Value Access"), 220, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, undefined, 'z_commands'); - +-registerDebugViewMenuItem(MenuId.DebugHoverContext, VIEW_MEMORY_ID, nls.localize('viewMemory', "View Binary Data"), 15, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_IN_DEBUG_MODE, 'inline', icons.debugInspectMemory); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_VALUE_ID, COPY_VALUE_LABEL, 10, undefined, undefined, '5_cutcopypaste'); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, 20, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, '5_cutcopypaste'); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, 100, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, undefined, 'z_commands'); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_IS_READ_ID, nls.localize('breakWhenValueIsRead', "Break on Value Read"), 200, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, undefined, 'z_commands'); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_CHANGES_ID, nls.localize('breakWhenValueChanges', "Break on Value Change"), 210, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, undefined, 'z_commands'); +-registerDebugViewMenuItem(MenuId.DebugHoverContext, BREAK_WHEN_VALUE_IS_ACCESSED_ID, nls.localize('breakWhenValueIsAccessed', "Break on Value Access"), 220, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, undefined, 'z_commands'); +- -registerDebugViewMenuItem(MenuId.DebugWatchContext, ADD_WATCH_ID, ADD_WATCH_LABEL, 10, undefined, undefined, '3_modification'); -registerDebugViewMenuItem(MenuId.DebugWatchContext, EDIT_EXPRESSION_COMMAND_ID, nls.localize('editWatchExpression', "Edit Expression"), 20, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, '3_modification'); -registerDebugViewMenuItem(MenuId.DebugWatchContext, SET_EXPRESSION_COMMAND_ID, nls.localize('setValue', "Set Value"), 30, ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), CONTEXT_SET_EXPRESSION_SUPPORTED), ContextKeyExpr.and(CONTEXT_WATCH_ITEM_TYPE.isEqualTo('variable'), CONTEXT_SET_VARIABLE_SUPPORTED)), CONTEXT_VARIABLE_IS_READONLY.toNegated(), '3_modification'); @@ -216,6 +258,21 @@ index 4ff1a98..4ef7a69 100644 -registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_EXPRESSION_COMMAND_ID, nls.localize('removeWatchExpression', "Remove Expression"), 20, CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression'), undefined, 'inline', icons.watchExpressionRemove); -registerDebugViewMenuItem(MenuId.DebugWatchContext, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, 20, undefined, undefined, 'z_commands'); - +-registerDebugViewMenuItem(MenuId.NotebookVariablesContext, COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL, 20, CONTEXT_VARIABLE_VALUE); +- +-KeybindingsRegistry.registerKeybindingRule({ +- id: COPY_VALUE_ID, +- weight: KeybindingWeight.WorkbenchContrib, +- when: ContextKeyExpr.and( +- CONTEXT_EXPRESSION_SELECTED.negate(), +- ContextKeyExpr.or( +- FocusedViewContext.isEqualTo(WATCH_VIEW_ID), +- FocusedViewContext.isEqualTo(VARIABLES_VIEW_ID), +- ), +- ), +- primary: KeyMod.CtrlCmd | KeyCode.KeyC +-}); +- -// Touch Bar -if (isMacintosh) { - @@ -235,7 +292,7 @@ index 4ff1a98..4ef7a69 100644 - registerTouchBarEntry(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/continue-tb.png')); - registerTouchBarEntry(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/run-with-debugging-tb.png')); - registerTouchBarEntry(CONTINUE_ID, CONTINUE_LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/continue-tb.png')); -- registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/pause-tb.png')); +- registerTouchBarEntry(PAUSE_ID, PAUSE_LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.and(CONTEXT_DEBUG_STATE.isEqualTo('running'), CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG.toNegated())), FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/pause-tb.png')); - registerTouchBarEntry(STEP_OVER_ID, STEP_OVER_LABEL, 2, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepover-tb.png')); - registerTouchBarEntry(STEP_INTO_ID, STEP_INTO_LABEL, 3, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepinto-tb.png')); - registerTouchBarEntry(STEP_OUT_ID, STEP_OUT_LABEL, 4, CONTEXT_IN_DEBUG_MODE, FileAccess.asFileUri('vs/workbench/contrib/debug/browser/media/stepout-tb.png')); @@ -252,8 +309,7 @@ index 4ff1a98..4ef7a69 100644 -MenuRegistry.appendMenuItem(MenuId.MenubarMainMenu, { - submenu: MenuId.MenubarDebugMenu, - title: { -- value: 'Run', -- original: 'Run', +- ...nls.localize2('runMenu', "Run"), - mnemonicTitle: nls.localize({ key: 'mRun', comment: ['&& denotes a mnemonic'] }, "&&Run") - }, - order: 6 @@ -278,7 +334,7 @@ index 4ff1a98..4ef7a69 100644 - order: 2, - when: CONTEXT_DEBUGGERS_AVAILABLE -}); - +- -MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { @@ -378,6 +434,28 @@ index 4ff1a98..4ef7a69 100644 - when: CONTEXT_DEBUGGERS_AVAILABLE -}); - +-// Disassembly +- +-MenuRegistry.appendMenuItem(MenuId.DebugDisassemblyContext, { +- group: '1_edit', +- command: { +- id: COPY_ADDRESS_ID, +- title: COPY_ADDRESS_LABEL, +- }, +- order: 2, +- when: CONTEXT_DEBUGGERS_AVAILABLE +-}); +- +-MenuRegistry.appendMenuItem(MenuId.DebugDisassemblyContext, { +- group: '3_breakpoints', +- command: { +- id: TOGGLE_BREAKPOINT_ID, +- title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Breakpoint"), +- }, +- order: 2, +- when: CONTEXT_DEBUGGERS_AVAILABLE +-}); +- -// Breakpoint actions are registered from breakpointsView.ts - -// Install Debuggers @@ -406,7 +484,7 @@ index 4ff1a98..4ef7a69 100644 - id: REPL_VIEW_ID, - name: nls.localize2({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, "Debug Console"), - containerIcon: icons.debugConsoleViewIcon, -- canToggleVisibility: false, +- canToggleVisibility: true, - canMoveView: true, - when: CONTEXT_DEBUGGERS_AVAILABLE, - ctorDescriptor: new SyncDescriptor(Repl), @@ -444,14 +522,12 @@ index 4ff1a98..4ef7a69 100644 -viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize2('loadedScripts', "Loaded Scripts"), containerIcon: icons.loadedScriptsViewIcon, ctorDescriptor: new SyncDescriptor(LoadedScriptsView), order: 35, weight: 5, canToggleVisibility: true, canMoveView: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], viewContainer); - -// Register disassembly view -+import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; - +- -Registry.as(EditorExtensions.EditorPane).registerEditorPane( - EditorPaneDescriptor.create(DisassemblyView, DISASSEMBLY_VIEW_ID, nls.localize('disassembly', "Disassembly")), - [new SyncDescriptor(DisassemblyViewInput)] -); -+import { IDebugService, } from 'vs/workbench/contrib/debug/common/debug'; - +- -// Register configuration -const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); -configurationRegistry.registerConfiguration({ @@ -460,16 +536,38 @@ index 4ff1a98..4ef7a69 100644 - title: nls.localize('debugConfigurationTitle', "Debug"), - type: 'object', - properties: { +- 'debug.showVariableTypes': { +- type: 'boolean', +- description: nls.localize({ comment: ['This is the description for a setting'], key: 'showVariableTypes' }, "Show variable type in variable pane during debug session"), +- default: false +- }, - 'debug.allowBreakpointsEverywhere': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allow setting breakpoints in any file."), - default: false - }, +- 'debug.gutterMiddleClickAction': { +- type: 'string', +- enum: ['logpoint', 'conditionalBreakpoint', 'triggeredBreakpoint', 'none'], +- description: nls.localize({ comment: ['This is the description for a setting'], key: 'gutterMiddleClickAction' }, 'Controls the action to perform when clicking the editor gutter with the middle mouse button.'), +- enumDescriptions: [ +- nls.localize('debug.gutterMiddleClickAction.logpoint', "Add Logpoint."), +- nls.localize('debug.gutterMiddleClickAction.conditionalBreakpoint', "Add Conditional Breakpoint."), +- nls.localize('debug.gutterMiddleClickAction.triggeredBreakpoint', "Add Triggered Breakpoint."), +- nls.localize('debug.gutterMiddleClickAction.none', "Don't perform any action."), +- ], +- default: 'logpoint', +- }, - 'debug.openExplorerOnEnd': { - type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open the explorer view at the end of a debug session."), - default: false - }, +- 'debug.closeReadonlyTabsOnEnd': { +- type: 'boolean', +- description: nls.localize({ comment: ['This is the description for a setting'], key: 'closeReadonlyTabsOnEnd' }, "At the end of a debug session, all the read-only tabs associated with that session will be closed"), +- default: false +- }, - 'debug.inlineValues': { - type: 'string', - 'enum': ['on', 'off', 'auto'], @@ -483,7 +581,7 @@ index 4ff1a98..4ef7a69 100644 - }, - 'debug.toolBarLocation': { - enum: ['floating', 'docked', 'commandCenter', 'hidden'], -- markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, `commandCenter` (requires `{0}`), or `hidden`.", '#window.commandCenter#'), +- markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, `commandCenter` (requires {0}), or `hidden`.", '`#window.commandCenter#`'), - default: 'floating', - markdownEnumDescriptions: [ - nls.localize('debugToolBar.floating', "Show debug toolbar in all views."), @@ -554,11 +652,17 @@ index 4ff1a98..4ef7a69 100644 - description: nls.localize('debug.console.acceptSuggestionOnEnter', "Controls whether suggestions should be accepted on Enter in the Debug Console. Enter is also used to evaluate whatever is typed in the Debug Console."), - default: 'off' - }, +- 'debug.console.maximumLines': { +- type: 'number', +- description: nls.localize('debug.console.maximumLines', "Controls the maximum number of lines in the Debug Console."), +- default: 10000 +- }, - 'launch': { - type: 'object', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces."), - default: { configurations: [], compounds: [] }, -- $ref: launchSchemaId +- $ref: launchSchemaId, +- disallowConfigurationDefault: true - }, - 'debug.focusWindowOnBreak': { - type: 'boolean', @@ -613,9 +717,15 @@ index 4ff1a98..4ef7a69 100644 - description: nls.localize('debug.disassemblyView.showSourceCode', "Show Source Code in Disassembly View.") - }, - 'debug.autoExpandLazyVariables': { -- type: 'boolean', -- default: false, -- description: nls.localize('debug.autoExpandLazyVariables', "Automatically show values for variables that are lazily resolved by the debugger, such as getters.") +- type: 'string', +- enum: ['auto', 'on', 'off'], +- default: 'auto', +- enumDescriptions: [ +- nls.localize('debug.autoExpandLazyVariables.auto', "When in screen reader optimized mode, automatically expand lazy variables."), +- nls.localize('debug.autoExpandLazyVariables.on', "Always automatically expand lazy variables."), +- nls.localize('debug.autoExpandLazyVariables.off', "Never automatically expand lazy variables.") +- ], +- description: nls.localize('debug.autoExpandLazyVariables', "Controls whether variables that are lazily resolved, such as getters, are automatically resolved and expanded by the debugger.") - }, - 'debug.enableStatusBarColor': { - type: 'boolean', @@ -624,100 +734,115 @@ index 4ff1a98..4ef7a69 100644 - }, - 'debug.hideLauncherWhileDebugging': { - type: 'boolean', -- markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'debug.hideLauncherWhileDebugging' }, "Hide 'Start Debugging' control in title bar of 'Run and Debug' view while debugging is active. Only relevant when `{0}` is not `docked`.", '#debug.toolBarLocation#'), +- markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'debug.hideLauncherWhileDebugging' }, "Hide 'Start Debugging' control in title bar of 'Run and Debug' view while debugging is active. Only relevant when {0} is not `docked`.", '`#debug.toolBarLocation#`'), +- default: false +- }, +- 'debug.hideSlowPreLaunchWarning': { +- type: 'boolean', +- markdownDescription: nls.localize('debug.hideSlowPreLaunchWarning', "Hide the warning shown when a `preLaunchTask` has been running for a while."), - default: false - } - } -}); +- +-AccessibleViewRegistry.register(new ReplAccessibleView()); +-AccessibleViewRegistry.register(new ReplAccessibilityHelp()); +-AccessibleViewRegistry.register(new RunAndDebugAccessibilityHelp()); +-registerWorkbenchContribution2(ReplAccessibilityAnnouncer.ID, ReplAccessibilityAnnouncer, WorkbenchPhase.AfterRestored); +-registerWorkbenchContribution2(DebugWatchAccessibilityAnnouncer.ID, DebugWatchAccessibilityAnnouncer, WorkbenchPhase.AfterRestored); +registerSingleton(IDebugService, DebugService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts -index 4797dcb..627ffb1 100644 +index 816f372aceb..191146749c3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts -@@ -2,1717 +2,9 @@ - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. +@@ -4,2042 +4,9 @@ *--------------------------------------------------------------------------------------------*/ -- --import { localize } from 'vs/nls'; --import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; --import { Registry } from 'vs/platform/registry/common/platform'; --import { MenuRegistry, MenuId, registerAction2, Action2, ISubmenuItem, IMenuItem, IAction2Options } from 'vs/platform/actions/common/actions'; - import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; --import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, InstallOperation, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; --import { EnablementState, IExtensionManagementServerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; --import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; --import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; --import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; --import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, IExtension, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP } from 'vs/workbench/contrib/extensions/common/extensions'; --import { ReinstallAction, InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, PromptExtensionInstallFailureAction, SearchExtensionsAction, SwitchToPreReleaseVersionAction, SwitchToReleasedVersionAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; --import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; --import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; --import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, DefaultViewsContext, ExtensionsSortByContext, SearchHasTextContext } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; --import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; --import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; --import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; --import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; --import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; --import { KeymapExtensions } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; --import { areSameExtensions, getIdAndVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; --import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; --import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; --import { URI, UriComponents } from 'vs/base/common/uri'; --import { ExtensionActivationProgress } from 'vs/workbench/contrib/extensions/browser/extensionsActivationProgress'; --import { onUnexpectedError } from 'vs/base/common/errors'; --import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/browser/extensionsDependencyChecker'; --import { CancellationToken } from 'vs/base/common/cancellation'; --import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsService } from 'vs/workbench/common/views'; --import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; --import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; --import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; --import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess'; --import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from 'vs/workbench/contrib/extensions/browser/extensionsQuickAccess'; --import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; --import { CONTEXT_SYNC_ENABLEMENT } from 'vs/workbench/services/userDataSync/common/userDataSync'; --import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/browser/clipboard'; --import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; --import { MultiCommand } from 'vs/editor/browser/editorExtensions'; --import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; -+import { IExtensionsWorkbenchService, } from 'vs/workbench/contrib/extensions/common/extensions'; - import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; --import { Categories } from 'vs/platform/action/common/actionCommonCategories'; --import { IExtensionRecommendationNotificationService } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; --import { ExtensionRecommendationNotificationService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService'; --import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; --import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; --import { IHostService } from 'vs/workbench/services/host/browser/host'; --import { ResourceContextKey, WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; --import { IAction } from 'vs/base/common/actions'; --import { IWorkspaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig'; --import { Schemas } from 'vs/base/common/network'; --import { ShowRuntimeExtensionsAction } from 'vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor'; --import { ExtensionEnablementWorkspaceTrustTransitionParticipant } from 'vs/workbench/contrib/extensions/browser/extensionEnablementWorkspaceTrustTransitionParticipant'; --import { clearSearchResultsIcon, configureRecommendedIcon, extensionsViewIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; --import { EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; --import { Disposable, DisposableStore, IDisposable, isDisposable } from 'vs/base/common/lifecycle'; --import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; --import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; --import { mnemonicButtonLabel } from 'vs/base/common/labels'; --import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; --import { Promises } from 'vs/base/common/async'; --import { EditorExtensions } from 'vs/workbench/common/editor'; --import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from 'vs/workbench/services/workspaces/common/workspaceTrust'; --import { ExtensionsCompletionItemsProvider } from 'vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider'; --import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; --import { Event } from 'vs/base/common/event'; --import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; --import { UnsupportedExtensionsMigrationContrib } from 'vs/workbench/contrib/extensions/browser/unsupportedExtensionsMigrationContribution'; --import { isWeb } from 'vs/base/common/platform'; --import { ExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; --import { IStorageService } from 'vs/platform/storage/common/storage'; --import { IStringDictionary } from 'vs/base/common/collections'; --import { CONTEXT_KEYBINDINGS_EDITOR } from 'vs/workbench/contrib/preferences/common/preferences'; --import { DeprecatedExtensionsChecker } from 'vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker'; + + import './media/extensionManagement.css'; +-import { localize, localize2 } from '../../../../nls.js'; +-import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; +-import { Registry } from '../../../../platform/registry/common/platform.js'; +-import { MenuRegistry, MenuId, registerAction2, Action2, IMenuItem, IAction2Options } from '../../../../platform/actions/common/actions.js'; + import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +-import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, SortBy, FilterType, VerifyExtensionSignatureConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +-import { EnablementState, IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +-import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; +-import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +-import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +-import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP, IExtensionArg, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY, AutoRestartConfigurationKey, extensionsFilterSubMenu, DefaultViewsContext } from '../common/extensions.js'; +-import { InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction, InstallAnotherVersionAction, InstallAction } from './extensionsActions.js'; +-import { ExtensionsInput } from '../common/extensionsInput.js'; +-import { ExtensionEditor } from './extensionEditor.js'; +-import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewPaneContainer, BuiltInExtensionsContext, SearchMarketplaceExtensionsContext, RecommendedExtensionsContext, ExtensionsSortByContext, SearchHasTextContext, ExtensionsSearchValueContext } from './extensionsViewlet.js'; +-import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js'; +-import * as jsonContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; +-import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from '../common/extensionsFileTemplate.js'; +-import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; +-import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +-import { KeymapExtensions } from '../common/extensionsUtils.js'; +-import { areSameExtensions, getIdAndVersion } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; +-import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; +-import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +-import { URI, UriComponents } from '../../../../base/common/uri.js'; +-import { ExtensionActivationProgress } from './extensionsActivationProgress.js'; +-import { onUnexpectedError } from '../../../../base/common/errors.js'; +-import { ExtensionDependencyChecker } from './extensionsDependencyChecker.js'; +-import { CancellationToken } from '../../../../base/common/cancellation.js'; +-import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions } from '../../../common/views.js'; +-import { IViewsService } from '../../../services/views/common/viewsService.js'; +-import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; +-import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; +-import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +-import { IQuickAccessRegistry, Extensions } from '../../../../platform/quickinput/common/quickAccess.js'; +-import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from './extensionsQuickAccess.js'; +-import { ExtensionRecommendationsService } from './extensionRecommendationsService.js'; +-import { CONTEXT_SYNC_ENABLEMENT } from '../../../services/userDataSync/common/userDataSync.js'; +-import { CopyAction, CutAction, PasteAction } from '../../../../editor/contrib/clipboard/browser/clipboard.js'; +-import { IEditorService } from '../../../services/editor/common/editorService.js'; +-import { MultiCommand } from '../../../../editor/browser/editorExtensions.js'; +-import { IWebview } from '../../webview/browser/webview.js'; ++import { IExtensionsWorkbenchService } from '../common/extensions.js'; + import { ExtensionsWorkbenchService } from './extensionsWorkbenchService.js'; +-import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +-import { IExtensionRecommendationNotificationService } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js'; +-import { ExtensionRecommendationNotificationService } from './extensionRecommendationNotificationService.js'; +-import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +-import { IHostService } from '../../../services/host/browser/host.js'; +-import { ResourceContextKey, WorkbenchStateContext } from '../../../common/contextkeys.js'; +-import { IAction } from '../../../../base/common/actions.js'; +-import { IWorkspaceExtensionsConfigService } from '../../../services/extensionRecommendations/common/workspaceExtensionsConfig.js'; +-import { Schemas } from '../../../../base/common/network.js'; +-import { ShowRuntimeExtensionsAction } from './abstractRuntimeExtensionsEditor.js'; +-import { ExtensionEnablementWorkspaceTrustTransitionParticipant } from './extensionEnablementWorkspaceTrustTransitionParticipant.js'; +-import { clearSearchResultsIcon, configureRecommendedIcon, extensionsViewIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from './extensionsIcons.js'; +-import { EXTENSION_CATEGORIES, ExtensionType } from '../../../../platform/extensions/common/extensions.js'; +-import { Disposable, DisposableStore, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js'; +-import { IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +-import { mnemonicButtonLabel } from '../../../../base/common/labels.js'; +-import { Query } from '../common/extensionQuery.js'; +-import { EditorExtensions } from '../../../common/editor.js'; +-import { WORKSPACE_TRUST_EXTENSION_SUPPORT } from '../../../services/workspaces/common/workspaceTrust.js'; +-import { ExtensionsCompletionItemsProvider } from './extensionsCompletionItemsProvider.js'; +-import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; +-import { Event } from '../../../../base/common/event.js'; +-import { UnsupportedExtensionsMigrationContrib } from './unsupportedExtensionsMigrationContribution.js'; +-import { isNative, isWeb } from '../../../../base/common/platform.js'; +-import { ExtensionStorageService } from '../../../../platform/extensionManagement/common/extensionStorage.js'; +-import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +-import { IStringDictionary } from '../../../../base/common/collections.js'; +-import { CONTEXT_KEYBINDINGS_EDITOR } from '../../preferences/common/preferences.js'; +-import { ProgressLocation } from '../../../../platform/progress/common/progress.js'; +-import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +-import { IConfigurationMigrationRegistry, Extensions as ConfigurationMigrationExtensions } from '../../../common/configuration.js'; +-import { IProductService } from '../../../../platform/product/common/productService.js'; +-import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; +-import product from '../../../../platform/product/common/product.js'; +-import { ExtensionGalleryResourceType, ExtensionGalleryServiceUrlConfigKey, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; +-import { ILanguageModelToolsService } from '../../chat/common/languageModelToolsService.js'; +-import { SearchExtensionsTool, SearchExtensionsToolData } from '../common/searchExtensionsTool.js'; // Singletons - registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); +-registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); -registerSingleton(IExtensionRecommendationNotificationService, ExtensionRecommendationNotificationService, InstantiationType.Delayed); -registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService, InstantiationType.Eager /* Prompts recommendations in the background */); - @@ -740,11 +865,10 @@ index 4797dcb..627ffb1 100644 - new SyncDescriptor(ExtensionsInput) - ]); - -- --Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( +-export const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer( - { - id: VIEWLET_ID, -- title: { value: localize('extensions', "Extensions"), original: 'Extensions' }, +- title: localize2('extensions', "Extensions"), - openCommandActionDescriptor: { - id: VIEWLET_ID, - mnemonicTitle: localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"), @@ -758,7 +882,6 @@ index 4797dcb..627ffb1 100644 - alwaysUseContainerInfo: true, - }, ViewContainerLocation.Sidebar); - -- -Registry.as(ConfigurationExtensions.Configuration) - .registerConfiguration({ - id: 'extensions', @@ -767,17 +890,15 @@ index 4797dcb..627ffb1 100644 - type: 'object', - properties: { - 'extensions.autoUpdate': { -- enum: [true, 'onlyEnabledExtensions', 'onlySelectedExtensions', false,], +- enum: [true, 'onlyEnabledExtensions', false,], - enumItemLabels: [ - localize('all', "All Extensions"), - localize('enabled', "Only Enabled Extensions"), -- localize('selected', "Only Selected Extensions"), - localize('none', "None"), - ], - enumDescriptions: [ -- localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions except for those updates are ignored.'), -- localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions except for those updates are ignored. Disabled extensions are not updated automatically.'), -- localize('extensions.autoUpdate.selected', 'Download and install updates automatically only for selected extensions.'), +- localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions.'), +- localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions.'), - localize('extensions.autoUpdate.false', 'Extensions are not automatically updated.'), - ], - description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."), @@ -892,7 +1013,42 @@ index 4797dcb..627ffb1 100644 - type: 'boolean', - description: localize('extensionsDeferredStartupFinishedActivation', "When enabled, extensions which declare the `onStartupFinished` activation event will be activated after a timeout."), - default: false -- } +- }, +- 'extensions.experimental.issueQuickAccess': { +- type: 'boolean', +- description: localize('extensionsInQuickAccess', "When enabled, extensions can be searched for via Quick Access and report issues from there."), +- default: true +- }, +- [VerifyExtensionSignatureConfigKey]: { +- type: 'boolean', +- description: localize('extensions.verifySignature', "When enabled, extensions are verified to be signed before getting installed."), +- default: true, +- scope: ConfigurationScope.APPLICATION, +- included: isNative +- }, +- [AutoRestartConfigurationKey]: { +- type: 'boolean', +- description: localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."), +- default: false, +- included: product.quality !== 'stable' +- }, +- [ExtensionGalleryServiceUrlConfigKey]: { +- type: 'string', +- description: localize('extensions.gallery.serviceUrl', "Configure the Marketplace service URL to connect to"), +- default: '', +- scope: ConfigurationScope.APPLICATION, +- tags: ['usesOnlineServices'], +- included: false, +- policy: { +- name: 'ExtensionGalleryServiceUrl', +- minimumVersion: '1.99', +- }, +- }, +- 'extensions.supportNodeGlobalNavigator': { +- type: 'boolean', +- description: localize('extensionsSupportNodeGlobalNavigator', "When enabled, Node.js navigator object is exposed on the global scope."), +- default: false, +- }, - } - }); - @@ -900,26 +1056,26 @@ index 4797dcb..627ffb1 100644 -jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema); - -// Register Commands --CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean) => { +-CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string) => { - const extensionService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionService.local.find(e => areSameExtensions(e.identifier, { id: extensionId })); - if (extension) { -- extensionService.open(extension, { tab, preserveFocus }); +- extensionService.open(extension, { tab, preserveFocus, feature }); - } else { - throw new Error(localize('notFound', "Extension '{0}' not found.", extensionId)); - } -}); - --CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean) => { +-CommandsRegistry.registerCommand('extension.open', async (accessor: ServicesAccessor, extensionId: string, tab?: ExtensionEditorTab, preserveFocus?: boolean, feature?: string, sideByside?: boolean) => { - const extensionService = accessor.get(IExtensionsWorkbenchService); - const commandService = accessor.get(ICommandService); - - const [extension] = await extensionService.getExtensions([{ id: extensionId }], CancellationToken.None); - if (extension) { -- return extensionService.open(extension, { tab, preserveFocus }); +- return extensionService.open(extension, { tab, preserveFocus, feature, sideByside }); - } - -- return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus); +- return commandService.executeCommand('_extensions.manage', extensionId, tab, preserveFocus, feature); -}); - -CommandsRegistry.registerCommand({ @@ -955,6 +1111,15 @@ index 4797dcb..627ffb1 100644 - 'description': localize('workbench.extensions.installExtension.option.donotSync', "When enabled, VS Code do not sync this extension when Settings Sync is on."), - default: false - }, +- 'justification': { +- 'type': ['string', 'object'], +- 'description': localize('workbench.extensions.installExtension.option.justification', "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."), +- }, +- 'enable': { +- 'type': 'boolean', +- 'description': localize('workbench.extensions.installExtension.option.enable', "When enabled, the extension will be enabled if it is installed but disabled. If the extension is already enabled, this has no effect."), +- default: false +- }, - 'context': { - 'type': 'object', - 'description': localize('workbench.extensions.installExtension.option.context', "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."), @@ -964,35 +1129,48 @@ index 4797dcb..627ffb1 100644 - } - ] - }, -- handler: async (accessor, arg: string | UriComponents, options?: { installOnlyNewlyAddedFromExtensionPackVSIX?: boolean; installPreReleaseVersion?: boolean; donotSync?: boolean; context?: IStringDictionary }) => { +- handler: async ( +- accessor, +- arg: string | UriComponents, +- options?: { +- installOnlyNewlyAddedFromExtensionPackVSIX?: boolean; +- installPreReleaseVersion?: boolean; +- donotSync?: boolean; +- justification?: string | { reason: string; action: string }; +- enable?: boolean; +- context?: IStringDictionary; +- }) => { - const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); +- const extensionGalleryService = accessor.get(IExtensionGalleryService); - try { - if (typeof arg === 'string') { - const [id, version] = getIdAndVersion(arg); -- const [extension] = await extensionsWorkbenchService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None); -- if (extension) { -- const installOptions: InstallOptions = { +- const extension = extensionsWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id, uuid: version })); +- if (extension?.enablementState === EnablementState.DisabledByExtensionKind) { +- const [gallery] = await extensionGalleryService.getExtensions([{ id, preRelease: options?.installPreReleaseVersion }], CancellationToken.None); +- if (!gallery) { +- throw new Error(localize('notFound', "Extension '{0}' not found.", arg)); +- } +- await extensionManagementService.installFromGallery(gallery, { - isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */ - installPreReleaseVersion: options?.installPreReleaseVersion, - installGivenVersion: !!version, -- context: options?.context -- }; -- if (extension.gallery && extension.enablementState === EnablementState.DisabledByExtensionKind) { -- await extensionManagementService.installFromGallery(extension.gallery, installOptions); -- return; -- } -- if (version) { -- await extensionsWorkbenchService.installVersion(extension, version, installOptions); -- } else { -- await extensionsWorkbenchService.install(extension, installOptions); -- } +- context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND }, +- }); - } else { -- throw new Error(localize('notFound', "Extension '{0}' not found.", arg)); +- await extensionsWorkbenchService.install(arg, { +- version, +- installPreReleaseVersion: options?.installPreReleaseVersion, +- context: { ...options?.context, [EXTENSION_INSTALL_SOURCE_CONTEXT]: ExtensionInstallSource.COMMAND }, +- justification: options?.justification, +- enable: options?.enable, +- isMachineScoped: options?.donotSync ? true : undefined, /* do not allow syncing extensions automatically while installing through the command */ +- }, ProgressLocation.Notification); - } - } else { - const vsix = URI.revive(arg); -- await extensionsWorkbenchService.install(vsix, { installOnlyNewlyAddedFromExtensionPack: options?.installOnlyNewlyAddedFromExtensionPackVSIX }); +- await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }); - } - } catch (e) { - onUnexpectedError(e); @@ -1025,7 +1203,7 @@ index 4797dcb..627ffb1 100644 - throw new Error(localize('notInstalled', "Extension '{0}' is not installed. Make sure you use the full extension ID, including the publisher, e.g.: ms-dotnettools.csharp.", id)); - } - if (extensionToUninstall.isBuiltin) { -- throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be installed", id)); +- throw new Error(localize('builtin', "Extension '{0}' is a Built-in extension and cannot be uninstalled", id)); - } - - try { @@ -1049,15 +1227,7 @@ index 4797dcb..627ffb1 100644 - ] - }, - handler: async (accessor, query: string = '') => { -- const paneCompositeService = accessor.get(IPaneCompositePartService); -- const viewlet = await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); -- -- if (!viewlet) { -- return; -- } -- -- (viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query); -- viewlet.focus(); +- return accessor.get(IExtensionsWorkbenchService).openSearch(query); - } -}); - @@ -1083,6 +1253,11 @@ index 4797dcb..627ffb1 100644 -export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey('hasLocalServer', false); -export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey('hasRemoteServer', false); -export const CONTEXT_HAS_WEB_SERVER = new RawContextKey('hasWebServer', false); +-const CONTEXT_GALLERY_SORT_CAPABILITIES = new RawContextKey('gallerySortCapabilities', ''); +-const CONTEXT_GALLERY_FILTER_CAPABILITIES = new RawContextKey('galleryFilterCapabilities', ''); +-const CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED = new RawContextKey('galleryAllPublicRepositorySigned', false); +-const CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED = new RawContextKey('galleryAllPrivateRepositorySigned', false); +-const CONTEXT_GALLERY_HAS_EXTENSION_LINK = new RawContextKey('galleryHasExtensionLink', false); - -async function runAction(action: IAction): Promise { - try { @@ -1102,15 +1277,18 @@ index 4797dcb..627ffb1 100644 -class ExtensionsContributions extends Disposable implements IWorkbenchContribution { - - constructor( +- @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, -- @IContextKeyService contextKeyService: IContextKeyService, -- @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, +- @IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService, +- @IContextKeyService private readonly contextKeyService: IContextKeyService, +- @IViewsService private readonly viewsService: IViewsService, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, - @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IDialogService private readonly dialogService: IDialogService, - @ICommandService private readonly commandService: ICommandService, +- @IProductService private readonly productService: IProductService, - ) { - super(); - const hasGalleryContext = CONTEXT_HAS_GALLERY.bindTo(contextKeyService); @@ -1133,11 +1311,24 @@ index 4797dcb..627ffb1 100644 - hasWebServerContext.set(true); - } - +- extensionGalleryManifestService.getExtensionGalleryManifest() +- .then(extensionGalleryManifest => { +- this.registerGalleryCapabilitiesContexts(extensionGalleryManifest); +- this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(extensionGalleryManifest => this.registerGalleryCapabilitiesContexts(extensionGalleryManifest))); +- }); - this.registerGlobalActions(); - this.registerContextMenuActions(); - this.registerQuickAccessProvider(); - } - +- private async registerGalleryCapabilitiesContexts(extensionGalleryManifest: IExtensionGalleryManifest | null): Promise { +- CONTEXT_GALLERY_SORT_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.sorting?.map(s => s.name)?.join('_')}_UpdateDate_`); +- CONTEXT_GALLERY_FILTER_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.filtering?.map(s => s.name)?.join('_')}_`); +- CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPublicRepositorySigned); +- CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allPrivateRepositorySigned); +- CONTEXT_GALLERY_HAS_EXTENSION_LINK.bindTo(this.contextKeyService).set(!!(extensionGalleryManifest && getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionDetailsViewUri))); +- } +- - private registerQuickAccessProvider(): void { - if (this.extensionManagementServerService.localExtensionManagementServer - || this.extensionManagementServerService.remoteExtensionManagementServer @@ -1173,17 +1364,17 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.focusExtensionsView', -- title: { value: localize('focusExtensions', "Focus on Extensions View"), original: 'Focus on Extensions View' }, +- title: localize2('focusExtensions', 'Focus on Extensions View'), - category: ExtensionsLocalizedLabel, - f1: true, - run: async (accessor: ServicesAccessor) => { -- await accessor.get(IPaneCompositePartService).openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); +- await accessor.get(IExtensionsWorkbenchService).openSearch(''); - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installExtensions', -- title: { value: localize('installExtensions', "Install Extensions"), original: 'Install Extensions' }, +- title: localize2('installExtensions', 'Install Extensions'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, @@ -1196,7 +1387,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showRecommendedKeymapExtensions', -- title: { value: localize('showRecommendedKeymapExtensionsShort', "Keymaps"), original: 'Keymaps' }, +- title: localize2('showRecommendedKeymapExtensionsShort', 'Keymaps'), - category: PreferencesLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1209,23 +1400,23 @@ index 4797dcb..627ffb1 100644 - menuTitles: { - [MenuId.EditorTitle.id]: localize('importKeyboardShortcutsFroms', "Migrate Keyboard Shortcuts from...") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:keymaps ')) +- run: () => this.extensionsWorkbenchService.openSearch('@recommended:keymaps ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showLanguageExtensions', -- title: { value: localize('showLanguageExtensionsShort', "Language Extensions"), original: 'Language Extensions' }, +- title: localize2('showLanguageExtensionsShort', 'Language Extensions'), - category: PreferencesLocalizedLabel, - menu: { - id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended:languages ')) +- run: () => this.extensionsWorkbenchService.openSearch('@recommended:languages ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.checkForUpdates', -- title: { value: localize('checkForUpdates', "Check for Extension Updates"), original: 'Check for Extension Updates' }, +- title: localize2('checkForUpdates', 'Check for Extension Updates'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1240,69 +1431,50 @@ index 4797dcb..627ffb1 100644 - await this.extensionsWorkbenchService.checkForUpdates(); - const outdated = this.extensionsWorkbenchService.outdated; - if (outdated.length) { -- return runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@outdated ')); +- return this.extensionsWorkbenchService.openSearch('@outdated '); - } else { - return this.dialogService.info(localize('noUpdatesAvailable', "All extensions are up to date.")); - } - } - }); - -- const autoUpdateExtensionsSubMenu = new MenuId('autoUpdateExtensionsSubMenu'); -- MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { -- submenu: autoUpdateExtensionsSubMenu, -- title: localize('configure auto updating extensions', "Auto Update Extensions"), -- when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), -- group: '1_updates', -- order: 5, -- }); -- -- this.registerExtensionAction({ -- id: 'configureExtensionsAutoUpdate.all', -- title: localize('configureExtensionsAutoUpdate.all', "All Extensions"), -- toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions')), -- menu: [{ -- id: autoUpdateExtensionsSubMenu, -- order: 1, -- }], -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) -- }); -- -- this.registerExtensionAction({ -- id: 'configureExtensionsAutoUpdate.enabled', -- title: localize('configureExtensionsAutoUpdate.enabled', "Enabled Extensions"), -- toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), -- menu: [{ -- id: autoUpdateExtensionsSubMenu, -- order: 2, -- }], -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlyEnabledExtensions') -- }); -- +- const enableAutoUpdateWhenCondition = ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false); - this.registerExtensionAction({ -- id: 'configureExtensionsAutoUpdate.selected', -- title: localize('configureExtensionsAutoUpdate.selected', "Selected Extensions"), -- toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), +- id: 'workbench.extensions.action.enableAutoUpdate', +- title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'), +- category: ExtensionsLocalizedLabel, +- precondition: enableAutoUpdateWhenCondition, - menu: [{ -- id: autoUpdateExtensionsSubMenu, -- order: 2, +- id: MenuId.ViewContainerTitle, +- order: 5, +- group: '1_updates', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), enableAutoUpdateWhenCondition) +- }, { +- id: MenuId.CommandPalette, - }], -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions') +- run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(true) - }); - +- const disableAutoUpdateWhenCondition = ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, false); - this.registerExtensionAction({ -- id: 'configureExtensionsAutoUpdate.none', -- title: localize('configureExtensionsAutoUpdate.none', "None"), -- toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), +- id: 'workbench.extensions.action.disableAutoUpdate', +- title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'), +- precondition: disableAutoUpdateWhenCondition, +- category: ExtensionsLocalizedLabel, - menu: [{ -- id: autoUpdateExtensionsSubMenu, -- order: 3, +- id: MenuId.ViewContainerTitle, +- order: 5, +- group: '1_updates', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), disableAutoUpdateWhenCondition) +- }, { +- id: MenuId.CommandPalette, - }], -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) +- run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateForAllExtensions(false) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.updateAllExtensions', -- title: { value: localize('updateAll', "Update All Extensions"), original: 'Update All Extensions' }, +- title: localize2('updateAll', 'Update All Extensions'), - category: ExtensionsLocalizedLabel, - precondition: HasOutdatedExtensionsContext, - menu: [ @@ -1323,40 +1495,13 @@ index 4797dcb..627ffb1 100644 - ], - icon: installWorkspaceRecommendedIcon, - run: async () => { -- const outdated = this.extensionsWorkbenchService.outdated; -- const results = await this.extensionsWorkbenchService.updateAll(); -- results.forEach((result) => { -- if (result.error) { -- const extension: IExtension | undefined = outdated.find((extension) => areSameExtensions(extension.identifier, result.identifier)); -- if (extension) { -- runAction(this.instantiationService.createInstance(PromptExtensionInstallFailureAction, extension, extension.latestVersion, InstallOperation.Update, result.error)); -- } -- } -- }); +- await this.extensionsWorkbenchService.updateAll(); - } - }); - - this.registerExtensionAction({ -- id: 'workbench.extensions.action.disableAutoUpdate', -- title: { value: localize('disableAutoUpdate', "Disable Auto Update for All Extensions"), original: 'Disable Auto Update for All Extensions' }, -- category: ExtensionsLocalizedLabel, -- f1: true, -- precondition: CONTEXT_HAS_GALLERY, -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) -- }); -- -- this.registerExtensionAction({ -- id: 'workbench.extensions.action.enableAutoUpdate', -- title: { value: localize('enableAutoUpdate', "Enable Auto Update for All Extensions"), original: 'Enable Auto Update for All Extensions' }, -- category: ExtensionsLocalizedLabel, -- f1: true, -- precondition: CONTEXT_HAS_GALLERY, -- run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) -- }); -- -- this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAll', -- title: { value: localize('enableAll', "Enable All Extensions"), original: 'Enable All Extensions' }, +- title: localize2('enableAll', 'Enable All Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1377,7 +1522,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAllWorkspace', -- title: { value: localize('enableAllWorkspace', "Enable All Extensions for this Workspace"), original: 'Enable All Extensions for this Workspace' }, +- title: localize2('enableAllWorkspace', 'Enable All Extensions for this Workspace'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, @@ -1393,7 +1538,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAll', -- title: { value: localize('disableAll', "Disable All Installed Extensions"), original: 'Disable All Installed Extensions' }, +- title: localize2('disableAll', 'Disable All Installed Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1414,7 +1559,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAllWorkspace', -- title: { value: localize('disableAllWorkspace', "Disable All Installed Extensions for this Workspace"), original: 'Disable All Installed Extensions for this Workspace' }, +- title: localize2('disableAllWorkspace', 'Disable All Installed Extensions for this Workspace'), - category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.CommandPalette, @@ -1430,7 +1575,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, -- title: { value: localize('InstallFromVSIX', "Install from VSIX..."), original: 'Install from VSIX...' }, +- title: localize2('InstallFromVSIX', 'Install from VSIX...'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1466,35 +1611,59 @@ index 4797dcb..627ffb1 100644 - when: ContextKeyExpr.and(ResourceContextKey.Extension.isEqualTo('.vsix'), ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)), - }], - run: async (accessor: ServicesAccessor, resources: URI[] | URI) => { -- const extensionService = accessor.get(IExtensionService); - const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const hostService = accessor.get(IHostService); - const notificationService = accessor.get(INotificationService); - -- const extensions = Array.isArray(resources) ? resources : [resources]; -- await Promises.settled(extensions.map(async (vsix) => await extensionsWorkbenchService.install(vsix))) -- .then(async (extensions) => { -- for (const extension of extensions) { -- const requireReload = !(extension.local && extensionService.canAddExtension(toExtensionDescription(extension.local))); -- const message = requireReload ? localize('InstallVSIXAction.successReload', "Completed installing {0} extension from VSIX. Please reload Visual Studio Code to enable it.", extension.displayName || extension.name) -- : localize('InstallVSIXAction.success', "Completed installing {0} extension from VSIX.", extension.displayName || extension.name); -- const actions = requireReload ? [{ -- label: localize('InstallVSIXAction.reloadNow', "Reload Now"), -- run: () => hostService.reload() -- }] : []; -- notificationService.prompt( -- Severity.Info, -- message, -- actions -- ); -- } -- }); +- const vsixs = Array.isArray(resources) ? resources : [resources]; +- const result = await Promise.allSettled(vsixs.map(async (vsix) => await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }))); +- let error: Error | undefined, requireReload = false, requireRestart = false; +- for (const r of result) { +- if (r.status === 'rejected') { +- error = new Error(r.reason); +- break; +- } +- requireReload = requireReload || r.value.runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow; +- requireRestart = requireRestart || r.value.runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions; +- } +- if (error) { +- throw error; +- } +- if (requireReload) { +- notificationService.prompt( +- Severity.Info, +- vsixs.length > 1 ? localize('InstallVSIXs.successReload', "Completed installing extensions. Please reload Visual Studio Code to enable them.") +- : localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload Visual Studio Code to enable it."), +- [{ +- label: localize('InstallVSIXAction.reloadNow', "Reload Now"), +- run: () => hostService.reload() +- }] +- ); +- } +- else if (requireRestart) { +- notificationService.prompt( +- Severity.Info, +- vsixs.length > 1 ? localize('InstallVSIXs.successRestart', "Completed installing extensions. Please restart extensions to enable them.") +- : localize('InstallVSIXAction.successRestart', "Completed installing extension. Please restart extensions to enable it."), +- [{ +- label: localize('InstallVSIXAction.restartExtensions', "Restart Extensions"), +- run: () => extensionsWorkbenchService.updateRunningExtensions() +- }] +- ); +- } +- else { +- notificationService.prompt( +- Severity.Info, +- vsixs.length > 1 ? localize('InstallVSIXs.successNoReload', "Completed installing extensions.") : localize('InstallVSIXAction.successNoReload', "Completed installing extension."), +- [] +- ); +- } - } - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.installExtensionFromLocation', -- title: { value: localize('installExtensionFromLocation', "Install Extension from Location..."), original: 'Install Extension from Location...' }, +- title: localize2('installExtensionFromLocation', 'Install Extension from Location...'), - category: Categories.Developer, - menu: [{ - id: MenuId.CommandPalette, @@ -1503,22 +1672,30 @@ index 4797dcb..627ffb1 100644 - run: async (accessor: ServicesAccessor) => { - const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); - if (isWeb) { -- const quickInputService = accessor.get(IQuickInputService); -- const disposables = new DisposableStore(); -- const quickPick = disposables.add(quickInputService.createQuickPick()); -- quickPick.title = localize('installFromLocation', "Install Extension from Location"); -- quickPick.customButton = true; -- quickPick.customLabel = localize('install button', "Install"); -- quickPick.placeholder = localize('installFromLocationPlaceHolder', "Location of the web extension"); -- quickPick.ignoreFocusOut = true; -- disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(() => { -- quickPick.hide(); -- if (quickPick.value) { -- extensionManagementService.installFromLocation(URI.parse(quickPick.value)); -- } -- })); -- disposables.add(quickPick.onDidHide(() => disposables.dispose())); -- quickPick.show(); +- return new Promise((c, e) => { +- const quickInputService = accessor.get(IQuickInputService); +- const disposables = new DisposableStore(); +- const quickPick = disposables.add(quickInputService.createQuickPick()); +- quickPick.title = localize('installFromLocation', "Install Extension from Location"); +- quickPick.customButton = true; +- quickPick.customLabel = localize('install button', "Install"); +- quickPick.placeholder = localize('installFromLocationPlaceHolder', "Location of the web extension"); +- quickPick.ignoreFocusOut = true; +- disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(async () => { +- quickPick.hide(); +- if (quickPick.value) { +- try { +- await extensionManagementService.installFromLocation(URI.parse(quickPick.value)); +- } catch (error) { +- e(error); +- return; +- } +- } +- c(); +- })); +- disposables.add(quickPick.onDidHide(() => disposables.dispose())); +- quickPick.show(); +- }); - } else { - const fileDialogService = accessor.get(IFileDialogService); - const extensionLocation = await fileDialogService.showOpenDialog({ @@ -1528,14 +1705,13 @@ index 4797dcb..627ffb1 100644 - title: localize('installFromLocation', "Install Extension from Location"), - }); - if (extensionLocation?.[0]) { -- extensionManagementService.installFromLocation(extensionLocation[0]); +- await extensionManagementService.installFromLocation(extensionLocation[0]); - } - } - } - }); - -- const extensionsFilterSubMenu = new MenuId('extensionsFilterSubMenu'); -- MenuRegistry.appendMenuItem(extensionsSearchActionsMenu, { +- MenuRegistry.appendMenuItem(extensionsSearchActionsMenu, { - submenu: extensionsFilterSubMenu, - title: localize('filterExtensions', "Filter Extensions..."), - group: 'navigation', @@ -1544,28 +1720,29 @@ index 4797dcb..627ffb1 100644 - }); - - const showFeaturedExtensionsId = 'extensions.filter.featured'; +- const featuresExtensionsWhenContext = ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Featured}_`))); - this.registerExtensionAction({ - id: showFeaturedExtensionsId, -- title: { value: localize('showFeaturedExtensions', "Show Featured Extensions"), original: 'Show Featured Extensions' }, +- title: localize2('showFeaturedExtensions', 'Show Featured Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, -- when: CONTEXT_HAS_GALLERY +- when: featuresExtensionsWhenContext - }, { - id: extensionsFilterSubMenu, -- when: CONTEXT_HAS_GALLERY, +- when: featuresExtensionsWhenContext, - group: '1_predefined', - order: 1, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('featured filter', "Featured") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@featured ')) +- run: () => this.extensionsWorkbenchService.openSearch('@featured ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showPopularExtensions', -- title: { value: localize('showPopularExtensions', "Show Popular Extensions"), original: 'Show Popular Extensions' }, +- title: localize2('showPopularExtensions', 'Show Popular Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1579,12 +1756,12 @@ index 4797dcb..627ffb1 100644 - menuTitles: { - [extensionsFilterSubMenu.id]: localize('most popular filter', "Most Popular") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@popular ')) +- run: () => this.extensionsWorkbenchService.openSearch('@popular ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showRecommendedExtensions', -- title: { value: localize('showRecommendedExtensions', "Show Recommended Extensions"), original: 'Show Recommended Extensions' }, +- title: localize2('showRecommendedExtensions', 'Show Recommended Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1598,12 +1775,12 @@ index 4797dcb..627ffb1 100644 - menuTitles: { - [extensionsFilterSubMenu.id]: localize('most popular recommended', "Recommended") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recommended ')) +- run: () => this.extensionsWorkbenchService.openSearch('@recommended ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.recentlyPublishedExtensions', -- title: { value: localize('recentlyPublishedExtensions', "Show Recently Published Extensions"), original: 'Show Recently Published Extensions' }, +- title: localize2('recentlyPublishedExtensions', 'Show Recently Published Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1617,19 +1794,19 @@ index 4797dcb..627ffb1 100644 - menuTitles: { - [extensionsFilterSubMenu.id]: localize('recently published filter', "Recently Published") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@recentlyPublished ')) +- run: () => this.extensionsWorkbenchService.openSearch('@recentlyPublished ') - }); - - const extensionsCategoryFilterSubMenu = new MenuId('extensionsCategoryFilterSubMenu'); -- MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { +- MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { - submenu: extensionsCategoryFilterSubMenu, - title: localize('filter by category', "Category"), -- when: CONTEXT_HAS_GALLERY, +- when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Category}_`))), - group: '2_categories', - order: 1, - }); - -- EXTENSION_CATEGORIES.map((category, index) => { +- EXTENSION_CATEGORIES.forEach((category, index) => { - this.registerExtensionAction({ - id: `extensions.actions.searchByCategory.${category}`, - title: category, @@ -1638,13 +1815,29 @@ index 4797dcb..627ffb1 100644 - when: CONTEXT_HAS_GALLERY, - order: index, - }], -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, `@category:"${category.toLowerCase()}"`)) +- run: () => this.extensionsWorkbenchService.openSearch(`@category:"${category.toLowerCase()}"`) - }); - }); - - this.registerExtensionAction({ +- id: 'workbench.extensions.action.installedExtensions', +- title: localize2('installedExtensions', 'Show Installed Extensions'), +- category: ExtensionsLocalizedLabel, +- f1: true, +- menu: [{ +- id: extensionsFilterSubMenu, +- group: '3_installed', +- order: 1, +- }], +- menuTitles: { +- [extensionsFilterSubMenu.id]: localize('installed filter', "Installed") +- }, +- run: () => this.extensionsWorkbenchService.openSearch('@installed ') +- }); +- +- this.registerExtensionAction({ - id: 'workbench.extensions.action.listBuiltInExtensions', -- title: { value: localize('showBuiltInExtensions', "Show Built-in Extensions"), original: 'Show Built-in Extensions' }, +- title: localize2('showBuiltInExtensions', 'Show Built-in Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1652,17 +1845,17 @@ index 4797dcb..627ffb1 100644 - }, { - id: extensionsFilterSubMenu, - group: '3_installed', -- order: 2, +- order: 3, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('builtin filter', "Built-in") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@builtin ')) +- run: () => this.extensionsWorkbenchService.openSearch('@builtin ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.extensionUpdates', -- title: { value: localize('extensionUpdates', "Show Extension Updates"), original: 'Show Extension Updates' }, +- title: localize2('extensionUpdates', 'Show Extension Updates'), - category: ExtensionsLocalizedLabel, - precondition: CONTEXT_HAS_GALLERY, - f1: true, @@ -1670,17 +1863,17 @@ index 4797dcb..627ffb1 100644 - id: extensionsFilterSubMenu, - group: '3_installed', - when: CONTEXT_HAS_GALLERY, -- order: 1, +- order: 2, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('extension updates filter', "Updates") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@updates')) +- run: () => this.extensionsWorkbenchService.openSearch('@updates') - }); - - this.registerExtensionAction({ - id: LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, -- title: { value: localize('showWorkspaceUnsupportedExtensions', "Show Extensions Unsupported By Workspace"), original: 'Show Extensions Unsupported By Workspace' }, +- title: localize2('showWorkspaceUnsupportedExtensions', 'Show Extensions Unsupported By Workspace'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1688,18 +1881,18 @@ index 4797dcb..627ffb1 100644 - }, { - id: extensionsFilterSubMenu, - group: '3_installed', -- order: 5, +- order: 6, - when: ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER), - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('workspace unsupported filter', "Workspace Unsupported") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@workspaceUnsupported')) +- run: () => this.extensionsWorkbenchService.openSearch('@workspaceUnsupported') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showEnabledExtensions', -- title: { value: localize('showEnabledExtensions', "Show Enabled Extensions"), original: 'Show Enabled Extensions' }, +- title: localize2('showEnabledExtensions', 'Show Enabled Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1707,17 +1900,17 @@ index 4797dcb..627ffb1 100644 - }, { - id: extensionsFilterSubMenu, - group: '3_installed', -- order: 3, +- order: 4, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('enabled filter', "Enabled") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@enabled ')) +- run: () => this.extensionsWorkbenchService.openSearch('@enabled ') - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showDisabledExtensions', -- title: { value: localize('showDisabledExtensions', "Show Disabled Extensions"), original: 'Show Disabled Extensions' }, +- title: localize2('showDisabledExtensions', 'Show Disabled Extensions'), - category: ExtensionsLocalizedLabel, - menu: [{ - id: MenuId.CommandPalette, @@ -1725,16 +1918,16 @@ index 4797dcb..627ffb1 100644 - }, { - id: extensionsFilterSubMenu, - group: '3_installed', -- order: 4, +- order: 5, - }], - menuTitles: { - [extensionsFilterSubMenu.id]: localize('disabled filter', "Disabled") - }, -- run: () => runAction(this.instantiationService.createInstance(SearchExtensionsAction, '@disabled ')) +- run: () => this.extensionsWorkbenchService.openSearch('@disabled ') - }); - - const extensionsSortSubMenu = new MenuId('extensionsSortSubMenu'); -- MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { +- MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { - submenu: extensionsSortSubMenu, - title: localize('sorty by', "Sort By"), - when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext)), @@ -1743,35 +1936,35 @@ index 4797dcb..627ffb1 100644 - }); - - [ -- { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate() }, -- { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate() }, -- { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate() }, -- { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate() }, -- { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()) }, -- ].map(({ id, title, precondition }, index) => { +- { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.InstallCount }, +- { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.WeightedRating }, +- { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.Title }, +- { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.PublishedDate }, +- { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()), sortCapability: 'UpdateDate' }, +- ].map(({ id, title, precondition, sortCapability }, index) => { +- const sortCapabilityContext = ContextKeyExpr.regex(CONTEXT_GALLERY_SORT_CAPABILITIES.key, new RegExp(`_${sortCapability}_`)); - this.registerExtensionAction({ - id: `extensions.sort.${id}`, - title, -- precondition: precondition, +- precondition: ContextKeyExpr.and(precondition, ContextKeyExpr.regex(ExtensionsSearchValueContext.key, /^@feature:/).negate(), sortCapabilityContext), - menu: [{ - id: extensionsSortSubMenu, -- when: ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), +- when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), sortCapabilityContext), - order: index, - }], - toggled: ExtensionsSortByContext.isEqualTo(id), - run: async () => { -- const viewlet = await this.paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); -- const extensionsViewPaneContainer = viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer; -- const currentQuery = Query.parse(extensionsViewPaneContainer.searchValue || ''); -- extensionsViewPaneContainer.search(new Query(currentQuery.value, id, currentQuery.groupBy).toString()); -- extensionsViewPaneContainer.focus(); +- const extensionsViewPaneContainer = ((await this.viewsService.openViewContainer(VIEWLET_ID, true))?.getViewPaneContainer()) as IExtensionsViewPaneContainer | undefined; +- const currentQuery = Query.parse(extensionsViewPaneContainer?.searchValue ?? ''); +- extensionsViewPaneContainer?.search(new Query(currentQuery.value, id).toString()); +- extensionsViewPaneContainer?.focus(); - } - }); - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.clearExtensionsSearchResults', -- title: { value: localize('clearExtensionsSearchResults', "Clear Extensions Search Results"), original: 'Clear Extensions Search Results' }, +- title: localize2('clearExtensionsSearchResults', 'Clear Extensions Search Results'), - category: ExtensionsLocalizedLabel, - icon: clearSearchResultsIcon, - f1: true, @@ -1793,7 +1986,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.refreshExtension', -- title: { value: localize('refreshExtension', "Refresh"), original: 'Refresh' }, +- title: localize2('refreshExtension', 'Refresh'), - category: ExtensionsLocalizedLabel, - icon: refreshIcon, - f1: true, @@ -1853,17 +2046,6 @@ index 4797dcb..627ffb1 100644 - }, - run: () => runAction(this.instantiationService.createInstance(InstallSpecificVersionOfExtensionAction, InstallSpecificVersionOfExtensionAction.ID, InstallSpecificVersionOfExtensionAction.LABEL)) - }); -- -- this.registerExtensionAction({ -- id: ReinstallAction.ID, -- title: { value: ReinstallAction.LABEL, original: 'Reinstall Extension...' }, -- category: Categories.Developer, -- menu: { -- id: MenuId.CommandPalette, -- when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.or(CONTEXT_HAS_LOCAL_SERVER, CONTEXT_HAS_REMOTE_SERVER)) -- }, -- run: () => runAction(this.instantiationService.createInstance(ReinstallAction, ReinstallAction.ID, ReinstallAction.LABEL)) -- }); - } - - // Extension Context Menu @@ -1934,12 +2116,12 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showPreReleaseVersion', -- title: { value: localize('show pre-release version', "Show Pre-Release Version"), original: 'Show Pre-Release Version' }, +- title: localize2('show pre-release version', 'Show Pre-Release Version'), - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 0, -- when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) +- when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); @@ -1950,12 +2132,12 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.showReleasedVersion', -- title: { value: localize('show released version', "Show Release Version"), original: 'Show Release Version' }, +- title: localize2('show released version', 'Show Release Version'), - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 1, -- when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) +- when: ContextKeyExpr.and(ContextKeyExpr.has('inExtensionEditor'), ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.has('showPreReleaseVersion'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); @@ -1966,20 +2148,25 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: ToggleAutoUpdateForExtensionAction.ID, -- title: { value: ToggleAutoUpdateForExtensionAction.LABEL, original: 'Auto Update' }, +- title: ToggleAutoUpdateForExtensionAction.LABEL, - category: ExtensionsLocalizedLabel, +- precondition: ContextKeyExpr.and(ContextKeyExpr.or(ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.equals('isExtensionEnabled', true)), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isExtensionAllowed')), - menu: { - id: MenuId.ExtensionContext, - group: UPDATE_ACTIONS_GROUP, - order: 1, -- when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) +- when: ContextKeyExpr.and( +- ContextKeyExpr.not('inExtensionEditor'), +- ContextKeyExpr.equals('extensionStatus', 'installed'), +- ContextKeyExpr.not('isBuiltinExtension'), +- ) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { -- const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, []); +- const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction); - action.extension = extension; - return action.run(); - } @@ -1990,11 +2177,12 @@ index 4797dcb..627ffb1 100644 - id: ToggleAutoUpdatesForPublisherAction.ID, - title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' }, - category: ExtensionsLocalizedLabel, +- precondition: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), - menu: { - id: MenuId.ExtensionContext, - group: UPDATE_ACTIONS_GROUP, - order: 2, -- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { - const instantiationService = accessor.get(IInstantiationService); @@ -2009,39 +2197,45 @@ index 4797dcb..627ffb1 100644 - }); - - this.registerExtensionAction({ -- id: SwitchToPreReleaseVersionAction.ID, -- title: SwitchToPreReleaseVersionAction.TITLE, +- id: 'workbench.extensions.action.switchToPreRlease', +- title: localize('enablePreRleaseLabel', "Switch to Pre-Release Version"), +- category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, - order: 2, -- when: ContextKeyExpr.and(ContextKeyExpr.not('installedExtensionIsPreReleaseVersion'), ContextKeyExpr.not('installedExtensionIsOptedTpPreRelease'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) +- when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { +- const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { -- extensionWorkbenchService.open(extension, { showPreReleaseVersion: true }); -- await extensionWorkbenchService.install(extension, { installPreReleaseVersion: true }); +- const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); +- action.extension = extension; +- return action.run(); - } - } - }); - - this.registerExtensionAction({ -- id: SwitchToReleasedVersionAction.ID, -- title: SwitchToReleasedVersionAction.TITLE, +- id: 'workbench.extensions.action.switchToRelease', +- title: localize('disablePreRleaseLabel', "Switch to Release Version"), +- category: ExtensionsLocalizedLabel, - menu: { - id: MenuId.ExtensionContext, - group: INSTALL_ACTIONS_GROUP, -- order: 3, -- when: ContextKeyExpr.and(ContextKeyExpr.has('installedExtensionIsPreReleaseVersion'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('extensionHasReleaseVersion'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) +- order: 2, +- when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.has('galleryExtensionHasPreReleaseVersion'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.has('installedExtensionIsOptedToPreRelease'), ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) - }, - run: async (accessor: ServicesAccessor, id: string) => { +- const instantiationService = accessor.get(IInstantiationService); - const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); - const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); - if (extension) { -- extensionWorkbenchService.open(extension, { showPreReleaseVersion: false }); -- await extensionWorkbenchService.install(extension, { installPreReleaseVersion: false }); +- const action = instantiationService.createInstance(TogglePreReleaseExtensionAction); +- action.extension = extension; +- return action.run(); - } - } - }); @@ -2066,88 +2260,245 @@ index 4797dcb..627ffb1 100644 - }); - - this.registerExtensionAction({ -- id: 'workbench.extensions.action.copyExtension', -- title: { value: localize('workbench.extensions.action.copyExtension', "Copy"), original: 'Copy' }, +- id: 'workbench.extensions.action.installUnsigned', +- title: localize('install', "Install"), - menu: { - id: MenuId.ExtensionContext, -- group: '1_copy' +- group: '0_install', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('extensionIsUnsigned'), +- ContextKeyExpr.or(ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PUBLIC_REPOSITORY_SIGNED, ContextKeyExpr.not('extensionIsPrivate')), ContextKeyExpr.and(CONTEXT_GALLERY_ALL_PRIVATE_REPOSITORY_SIGNED, ContextKeyExpr.has('extensionIsPrivate')))), +- order: 1 - }, - run: async (accessor: ServicesAccessor, extensionId: string) => { -- const clipboardService = accessor.get(IClipboardService); +- const instantiationService = accessor.get(IInstantiationService); - const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] - || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { -- const name = localize('extensionInfoName', 'Name: {0}', extension.displayName); -- const id = localize('extensionInfoId', 'Id: {0}', extensionId); -- const description = localize('extensionInfoDescription', 'Description: {0}', extension.description); -- const verision = localize('extensionInfoVersion', 'Version: {0}', extension.version); -- const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', extension.publisherDisplayName); -- const link = extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${extension.url}`) : null; -- const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`; -- await clipboardService.writeText(clipboardStr); +- const action = instantiationService.createInstance(InstallAction, { installPreReleaseVersion: this.extensionManagementService.preferPreReleases }); +- action.extension = extension; +- return action.run(); - } - } - }); - - this.registerExtensionAction({ -- id: 'workbench.extensions.action.copyExtensionId', -- title: { value: localize('workbench.extensions.action.copyExtensionId', "Copy Extension ID"), original: 'Copy Extension ID' }, -- menu: { -- id: MenuId.ExtensionContext, -- group: '1_copy' -- }, -- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IClipboardService).writeText(id) -- }); -- -- this.registerExtensionAction({ -- id: 'workbench.extensions.action.configure', -- title: { value: localize('workbench.extensions.action.configure', "Extension Settings"), original: 'Extension Settings' }, +- id: 'workbench.extensions.action.installAndDonotSync', +- title: localize('install installAndDonotSync', "Install (Do not Sync)"), - menu: { - id: MenuId.ExtensionContext, -- group: '2_configure', -- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')), +- group: '0_install', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), - order: 1 - }, -- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: `@ext:${id}` }) +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- const instantiationService = accessor.get(IInstantiationService); +- const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] +- || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +- if (extension) { +- const action = instantiationService.createInstance(InstallAction, { +- installPreReleaseVersion: this.extensionManagementService.preferPreReleases, +- isMachineScoped: true, +- }); +- action.extension = extension; +- return action.run(); +- } +- } - }); - - this.registerExtensionAction({ -- id: 'workbench.extensions.action.configureKeybindings', -- title: { value: localize('workbench.extensions.action.configureKeybindings', "Extension Keyboard Shortcuts"), original: 'Extension Keyboard Shortcuts' }, +- id: 'workbench.extensions.action.installPrereleaseAndDonotSync', +- title: localize('installPrereleaseAndDonotSync', "Install Pre-Release (Do not Sync)"), - menu: { - id: MenuId.ExtensionContext, -- group: '2_configure', -- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasKeybindings')), +- group: '0_install', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.has('isPreReleaseExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), - order: 2 - }, -- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query: `@ext:${id}` }) +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- const instantiationService = accessor.get(IInstantiationService); +- const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] +- || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +- if (extension) { +- const action = instantiationService.createInstance(InstallAction, { +- isMachineScoped: true, +- preRelease: true +- }); +- action.extension = extension; +- return action.run(); +- } +- } - }); - - this.registerExtensionAction({ -- id: 'workbench.extensions.action.toggleApplyToAllProfiles', -- title: { value: localize('workbench.extensions.action.toggleApplyToAllProfiles', "Apply Extension to all Profiles"), original: `Apply Extension to all Profiles` }, -- toggled: ContextKeyExpr.has('isApplicationScopedExtension'), +- id: InstallAnotherVersionAction.ID, +- title: InstallAnotherVersionAction.LABEL, - menu: { - id: MenuId.ExtensionContext, -- group: '2_configure', -- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate()), +- group: '0_install', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('isExtensionAllowed'), ContextKeyExpr.not('extensionDisallowInstall')), - order: 3 - }, -- run: async (accessor: ServicesAccessor, id: string) => { -- const extension = this.extensionsWorkbenchService.local.find(e => areSameExtensions({ id }, e.identifier)); +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- const instantiationService = accessor.get(IInstantiationService); +- const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] +- || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; - if (extension) { -- return this.extensionsWorkbenchService.toggleApplyExtensionToAllProfiles(extension); +- return instantiationService.createInstance(InstallAnotherVersionAction, extension, false).run(); - } - } - }); - - this.registerExtensionAction({ -- id: TOGGLE_IGNORE_EXTENSION_ACTION_ID, -- title: { value: localize('workbench.extensions.action.toggleIgnoreExtension', "Sync This Extension"), original: `Sync This Extension` }, +- id: 'workbench.extensions.action.copyExtension', +- title: localize2('workbench.extensions.action.copyExtension', 'Copy'), +- menu: { +- id: MenuId.ExtensionContext, +- group: '1_copy' +- }, +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- const clipboardService = accessor.get(IClipboardService); +- const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] +- || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; +- if (extension) { +- const name = localize('extensionInfoName', 'Name: {0}', extension.displayName); +- const id = localize('extensionInfoId', 'Id: {0}', extensionId); +- const description = localize('extensionInfoDescription', 'Description: {0}', extension.description); +- const verision = localize('extensionInfoVersion', 'Version: {0}', extension.version); +- const publisher = localize('extensionInfoPublisher', 'Publisher: {0}', extension.publisherDisplayName); +- const link = extension.url ? localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link: {0}', `${extension.url}`) : null; +- const clipboardStr = `${name}\n${id}\n${description}\n${verision}\n${publisher}${link ? '\n' + link : ''}`; +- await clipboardService.writeText(clipboardStr); +- } +- } +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.copyExtensionId', +- title: localize2('workbench.extensions.action.copyExtensionId', 'Copy Extension ID'), +- menu: { +- id: MenuId.ExtensionContext, +- group: '1_copy' +- }, +- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IClipboardService).writeText(id) +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.copyLink', +- title: localize2('workbench.extensions.action.copyLink', 'Copy Link'), +- menu: { +- id: MenuId.ExtensionContext, +- group: '1_copy', +- when: ContextKeyExpr.and(ContextKeyExpr.has('isGalleryExtension'), CONTEXT_GALLERY_HAS_EXTENSION_LINK), +- }, +- run: async (accessor: ServicesAccessor, _, extension: IExtensionArg) => { +- const clipboardService = accessor.get(IClipboardService); +- if (extension.galleryLink) { +- await clipboardService.writeText(extension.galleryLink); +- } +- } +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.configure', +- title: localize2('workbench.extensions.action.configure', 'Settings'), +- menu: { +- id: MenuId.ExtensionContext, +- group: '2_configure', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasConfiguration')), +- order: 1 +- }, +- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openSettings({ jsonEditor: false, query: `@ext:${id}` }) +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.download', +- title: localize('download VSIX', "Download VSIX"), +- menu: { +- id: MenuId.ExtensionContext, +- when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')), +- order: this.productService.quality === 'stable' ? 0 : 1 +- }, +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'release'); +- } +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.downloadPreRelease', +- title: localize('download pre-release', "Download Pre-Release VSIX"), +- menu: { +- id: MenuId.ExtensionContext, +- when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')), +- order: this.productService.quality === 'stable' ? 1 : 0 +- }, +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'prerelease'); +- } +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.downloadSpecificVersion', +- title: localize('download specific version', "Download Specific Version VSIX..."), +- menu: { +- id: MenuId.ExtensionContext, +- when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')), +- order: 2 +- }, +- run: async (accessor: ServicesAccessor, extensionId: string) => { +- accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'any'); +- } +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.manageAccountPreferences', +- title: localize2('workbench.extensions.action.changeAccountPreference', "Account Preferences"), +- menu: { +- id: MenuId.ExtensionContext, +- group: '2_configure', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasAccountPreferences')), +- order: 2, +- }, +- run: (accessor: ServicesAccessor, id: string) => accessor.get(ICommandService).executeCommand('_manageAccountPreferencesForExtension', id) +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.configureKeybindings', +- title: localize2('workbench.extensions.action.configureKeybindings', 'Keyboard Shortcuts'), - menu: { - id: MenuId.ExtensionContext, - group: '2_configure', -- when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT), +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('extensionHasKeybindings')), +- order: 2 +- }, +- run: async (accessor: ServicesAccessor, id: string) => accessor.get(IPreferencesService).openGlobalKeybindingSettings(false, { query: `@ext:${id}` }) +- }); +- +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.toggleApplyToAllProfiles', +- title: localize2('workbench.extensions.action.toggleApplyToAllProfiles', "Apply Extension to all Profiles"), +- toggled: ContextKeyExpr.has('isApplicationScopedExtension'), +- menu: { +- id: MenuId.ExtensionContext, +- group: '2_configure', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.equals('isWorkspaceScopedExtension', false)), +- order: 3 +- }, +- run: async (accessor: ServicesAccessor, _: string, extensionArg: IExtensionArg) => { +- const uriIdentityService = accessor.get(IUriIdentityService); +- const extension = extensionArg.location ? this.extensionsWorkbenchService.installed.find(e => uriIdentityService.extUri.isEqual(e.local?.location, extensionArg.location)) : undefined; +- if (extension) { +- return this.extensionsWorkbenchService.toggleApplyExtensionToAllProfiles(extension); +- } +- } +- }); +- +- this.registerExtensionAction({ +- id: TOGGLE_IGNORE_EXTENSION_ACTION_ID, +- title: localize2('workbench.extensions.action.toggleIgnoreExtension', "Sync This Extension"), +- menu: { +- id: MenuId.ExtensionContext, +- group: '2_configure', +- when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), CONTEXT_SYNC_ENABLEMENT, ContextKeyExpr.equals('isWorkspaceScopedExtension', false)), - order: 4 - }, - run: async (accessor: ServicesAccessor, id: string) => { @@ -2160,7 +2511,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.ignoreRecommendation', -- title: { value: localize('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), original: `Ignore Recommendation` }, +- title: localize2('workbench.extensions.action.ignoreRecommendation', "Ignore Recommendation"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', @@ -2172,7 +2523,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.undoIgnoredRecommendation', -- title: { value: localize('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), original: `Undo Ignored Recommendation` }, +- title: localize2('workbench.extensions.action.undoIgnoredRecommendation', "Undo Ignored Recommendation"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', @@ -2184,11 +2535,11 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addExtensionToWorkspaceRecommendations', -- title: { value: localize('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), original: `Add to Workspace Recommendations` }, +- title: localize2('workbench.extensions.action.addExtensionToWorkspaceRecommendations', "Add to Workspace Recommendations"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', -- when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate()), +- when: ContextKeyExpr.and(WorkbenchStateContext.notEqualsTo('empty'), ContextKeyExpr.has('isBuiltinExtension').negate(), ContextKeyExpr.has('isExtensionWorkspaceRecommended').negate(), ContextKeyExpr.has('isUserIgnoredRecommendation').negate(), ContextKeyExpr.notEquals('extensionSource', 'resource')), - order: 2 - }, - run: (accessor: ServicesAccessor, id: string) => accessor.get(IWorkspaceExtensionsConfigService).toggleRecommendation(id) @@ -2196,7 +2547,7 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', -- title: { value: localize('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), original: `Remove from Workspace Recommendations` }, +- title: localize2('workbench.extensions.action.removeExtensionFromWorkspaceRecommendations', "Remove from Workspace Recommendations"), - menu: { - id: MenuId.ExtensionContext, - group: '3_recommendations', @@ -2208,8 +2559,8 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceRecommendations', -- title: { value: localize('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), original: `Add Extension to Workspace Recommendations` }, -- category: localize('extensions', "Extensions"), +- title: localize2('workbench.extensions.action.addToWorkspaceRecommendations', "Add Extension to Workspace Recommendations"), +- category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), @@ -2231,8 +2582,8 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceFolderRecommendations', -- title: { value: localize('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), original: `Add Extension to Workspace Folder Recommendations` }, -- category: localize('extensions', "Extensions"), +- title: localize2('workbench.extensions.action.addToWorkspaceFolderRecommendations', "Add Extension to Workspace Folder Recommendations"), +- category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), @@ -2242,8 +2593,8 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceIgnoredRecommendations', -- title: { value: localize('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), original: `Add Extension to Workspace Ignored Recommendations` }, -- category: localize('extensions', "Extensions"), +- title: localize2('workbench.extensions.action.addToWorkspaceIgnoredRecommendations', "Add Extension to Workspace Ignored Recommendations"), +- category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), @@ -2265,8 +2616,8 @@ index 4797dcb..627ffb1 100644 - - this.registerExtensionAction({ - id: 'workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', -- title: { value: localize('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), original: `Add Extension to Workspace Folder Ignored Recommendations` }, -- category: localize('extensions', "Extensions"), +- title: localize2('workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations', "Add Extension to Workspace Folder Ignored Recommendations"), +- category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('folder'), ContextKeyExpr.equals('resourceScheme', Schemas.extension)), @@ -2277,7 +2628,7 @@ index 4797dcb..627ffb1 100644 - this.registerExtensionAction({ - id: ConfigureWorkspaceRecommendedExtensionsAction.ID, - title: { value: ConfigureWorkspaceRecommendedExtensionsAction.LABEL, original: 'Configure Recommended Extensions (Workspace)' }, -- category: localize('extensions', "Extensions"), +- category: EXTENSIONS_CATEGORY, - menu: { - id: MenuId.CommandPalette, - when: WorkbenchStateContext.isEqualTo('workspace'), @@ -2285,6 +2636,39 @@ index 4797dcb..627ffb1 100644 - run: () => runAction(this.instantiationService.createInstance(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL)) - }); - +- this.registerExtensionAction({ +- id: 'workbench.extensions.action.manageTrustedPublishers', +- title: localize2('workbench.extensions.action.manageTrustedPublishers', "Manage Trusted Extension Publishers"), +- category: EXTENSIONS_CATEGORY, +- f1: true, +- run: async (accessor: ServicesAccessor) => { +- const quickInputService = accessor.get(IQuickInputService); +- const extensionManagementService = accessor.get(IWorkbenchExtensionManagementService); +- const trustedPublishers = extensionManagementService.getTrustedPublishers(); +- const trustedPublisherItems = trustedPublishers.map(publisher => ({ +- id: publisher.publisher, +- label: publisher.publisherDisplayName, +- description: publisher.publisher, +- picked: true, +- })).sort((a, b) => a.label.localeCompare(b.label)); +- const result = await quickInputService.pick(trustedPublisherItems, { +- canPickMany: true, +- title: localize('trustedPublishers', "Manage Trusted Extension Publishers"), +- placeHolder: localize('trustedPublishersPlaceholder', "Choose which publishers to trust"), +- }); +- if (result) { +- const untrustedPublishers = []; +- for (const { publisher } of trustedPublishers) { +- if (!result.some(r => r.id === publisher)) { +- untrustedPublishers.push(publisher); +- } +- } +- trustedPublishers.filter(publisher => !result.some(r => r.id === publisher.publisher)); +- extensionManagementService.untrustPublishers(...untrustedPublishers); +- } +- } +- }); +- - } - - private registerExtensionAction(extensionActionOptions: IExtensionActionOptions): IDisposable { @@ -2334,6 +2718,55 @@ index 4797dcb..627ffb1 100644 - } -} - +-class TrustedPublishersInitializer implements IWorkbenchContribution { +- constructor( +- @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, +- @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, +- @IProductService productService: IProductService, +- @IStorageService storageService: IStorageService, +- ) { +- const trustedPublishersInitStatusKey = 'trusted-publishers-init-migration'; +- if (!storageService.get(trustedPublishersInitStatusKey, StorageScope.APPLICATION)) { +- for (const profile of userDataProfilesService.profiles) { +- extensionManagementService.getInstalled(ExtensionType.User, profile.extensionsResource) +- .then(async extensions => { +- const trustedPublishers = new Map(); +- for (const extension of extensions) { +- if (!extension.publisherDisplayName) { +- continue; +- } +- const publisher = extension.manifest.publisher.toLowerCase(); +- if (productService.trustedExtensionPublishers?.includes(publisher) +- || (extension.publisherDisplayName && productService.trustedExtensionPublishers?.includes(extension.publisherDisplayName.toLowerCase()))) { +- continue; +- } +- trustedPublishers.set(publisher, { publisher, publisherDisplayName: extension.publisherDisplayName }); +- } +- if (trustedPublishers.size) { +- extensionManagementService.trustPublishers(...trustedPublishers.values()); +- } +- storageService.store(trustedPublishersInitStatusKey, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE); +- }); +- } +- } +- } +-} +- +-class ExtensionToolsContribution extends Disposable implements IWorkbenchContribution { +- +- static readonly ID = 'extensions.chat.toolsContribution'; +- +- constructor( +- @ILanguageModelToolsService toolsService: ILanguageModelToolsService, +- @IInstantiationService instantiationService: IInstantiationService, +- ) { +- super(); +- const searchExtensionsTool = instantiationService.createInstance(SearchExtensionsTool); +- this._register(toolsService.registerToolData(SearchExtensionsToolData)); +- this._register(toolsService.registerToolImplementation(SearchExtensionsToolData.id, searchExtensionsTool)); +- } +-} +- -const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(StatusUpdater, LifecyclePhase.Eventually); @@ -2345,53 +2778,176 @@ index 4797dcb..627ffb1 100644 -workbenchRegistry.registerWorkbenchContribution(ExtensionEnablementWorkspaceTrustTransitionParticipant, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(ExtensionsCompletionItemsProvider, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(UnsupportedExtensionsMigrationContrib, LifecyclePhase.Eventually); --workbenchRegistry.registerWorkbenchContribution(DeprecatedExtensionsChecker, LifecyclePhase.Eventually); +-workbenchRegistry.registerWorkbenchContribution(TrustedPublishersInitializer, LifecyclePhase.Eventually); -if (isWeb) { - workbenchRegistry.registerWorkbenchContribution(ExtensionStorageCleaner, LifecyclePhase.Eventually); -} - +-registerWorkbenchContribution2(ExtensionToolsContribution.ID, ExtensionToolsContribution, WorkbenchPhase.AfterRestored); +- - -// Running Extensions -registerAction2(ShowRuntimeExtensionsAction); +- +-Registry.as(ConfigurationMigrationExtensions.ConfigurationMigration) +- .registerConfigurationMigrations([{ +- key: AutoUpdateConfigurationKey, +- migrateFn: (value, accessor) => { +- if (value === 'onlySelectedExtensions') { +- return { value: false }; +- } +- return []; +- } +- }]); ++registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Delayed); +diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts +index 27b1e707779..d8f1ee0792e 100644 +--- a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts ++++ b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts +@@ -55,7 +55,6 @@ import { VSBuffer } from '../../../../base/common/buffer.js'; + import { IProductService } from '../../../../platform/product/common/productService.js'; + import { IOpenerService } from '../../../../platform/opener/common/opener.js'; + import { CHAT_CONFIG_MENU_ID } from '../../chat/browser/actions/chatActions.js'; +-import { VIEW_CONTAINER } from '../../extensions/browser/extensions.contribution.js'; + + // acroynms do not get localized + const category: ILocalizedString = { +@@ -738,7 +737,6 @@ export class ShowInstalledMcpServersCommand extends Action2 { + const viewsService = accessor.get(IViewsService); + const view = await viewsService.openView(InstalledMcpServersViewId, true); + if (!view) { +- await viewsService.openViewContainer(VIEW_CONTAINER.id); + await viewsService.openView(InstalledMcpServersViewId, true); + } + } +diff --git a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts +index f54a3bd4327..d73f849d00d 100644 +--- a/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts ++++ b/src/vs/workbench/contrib/mcp/browser/mcpServersView.ts +@@ -10,9 +10,9 @@ import { IListContextMenuEvent, IListRenderer } from '../../../../base/browser/u + import { Emitter, Event } from '../../../../base/common/event.js'; + import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, isDisposable } from '../../../../base/common/lifecycle.js'; + import { DelayedPagedModel, IPagedModel, PagedModel } from '../../../../base/common/paging.js'; +-import { localize, localize2 } from '../../../../nls.js'; ++import { localize } from '../../../../nls.js'; + import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +-import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; ++import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; + import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; + import { IHoverService } from '../../../../platform/hover/browser/hover.js'; + import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +@@ -23,8 +23,8 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; + import { IThemeService } from '../../../../platform/theme/common/themeService.js'; + import { getLocationBasedViewColors, ViewPane } from '../../../browser/parts/views/viewPane.js'; + import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; +-import { IViewDescriptorService, IViewsRegistry, Extensions as ViewExtensions } from '../../../common/views.js'; +-import { HasInstalledMcpServersContext, IMcpWorkbenchService, InstalledMcpServersViewId, IWorkbenchMcpServer, McpServerContainers, mcpServerIcon, McpServerInstallState } from '../common/mcpTypes.js'; ++import { IViewDescriptorService } from '../../../common/views.js'; ++import { IMcpWorkbenchService, IWorkbenchMcpServer, McpServerContainers, mcpServerIcon, McpServerInstallState } from '../common/mcpTypes.js'; + import { DropDownAction, InstallAction, InstallingLabelAction, ManageMcpServerAction } from './mcpServerActions.js'; + import { PublisherWidget, InstallCountWidget, RatingsWidget, McpServerIconWidget } from './mcpServerWidgets.js'; + import { ActionRunner, IAction, Separator } from '../../../../base/common/actions.js'; +@@ -33,14 +33,9 @@ import { IMcpGalleryService } from '../../../../platform/mcp/common/mcpManagemen + import { URI } from '../../../../base/common/uri.js'; + import { ThemeIcon } from '../../../../base/common/themables.js'; + import { IProductService } from '../../../../platform/product/common/productService.js'; +-import { Registry } from '../../../../platform/registry/common/platform.js'; + import { IWorkbenchContribution } from '../../../common/contributions.js'; +-import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +-import { DefaultViewsContext, SearchMcpServersContext } from '../../extensions/common/extensions.js'; +-import { VIEW_CONTAINER } from '../../extensions/browser/extensions.contribution.js'; + import { renderMarkdown } from '../../../../base/browser/markdownRenderer.js'; + import { MarkdownString } from '../../../../base/common/htmlContent.js'; +-import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; + + export interface McpServerListViewOptions { + showWelcomeOnEmpty?: boolean; +@@ -380,32 +375,5 @@ export class McpServersViewsContribution extends Disposable implements IWorkbenc + + constructor() { + super(); +- +- Registry.as(ViewExtensions.ViewsRegistry).registerViews([ +- { +- id: InstalledMcpServersViewId, +- name: localize2('mcp-installed', "MCP Servers - Installed"), +- ctorDescriptor: new SyncDescriptor(McpServersListView, [{ showWelcomeOnEmpty: false }]), +- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext), +- weight: 40, +- order: 4, +- canToggleVisibility: true +- }, +- { +- id: 'workbench.views.mcp.default.marketplace', +- name: localize2('mcp', "MCP Servers"), +- ctorDescriptor: new SyncDescriptor(DefaultBrowseMcpServersView, [{ showWelcomeOnEmpty: true }]), +- when: ContextKeyExpr.and(DefaultViewsContext, HasInstalledMcpServersContext.toNegated(), ChatContextKeys.Setup.hidden.negate()), +- weight: 40, +- order: 4, +- canToggleVisibility: true +- }, +- { +- id: 'workbench.views.mcp.marketplace', +- name: localize2('mcp', "MCP Servers"), +- ctorDescriptor: new SyncDescriptor(McpServersListView, [{ showWelcomeOnEmpty: true }]), +- when: ContextKeyExpr.and(SearchMcpServersContext), +- } +- ], VIEW_CONTAINER); + } + } diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts -index a927038..84e3f51 100644 +index 5e374fb135a..da493f9b655 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts -@@ -3,440 +3,12 @@ +@@ -3,654 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ --import { localize, localize2 } from 'vs/nls'; --import { Registry } from 'vs/platform/registry/common/platform'; --import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; --import { DirtyDiffWorkbenchController } from './dirtydiffDecorator'; --import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; --import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; --import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; --import { SCMActiveResourceContextKeyController, SCMStatusController } from './activity'; --import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; --import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; --import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; --import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; --import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -+import { ISCMService, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; - import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; - import { SCMService } from 'vs/workbench/contrib/scm/common/scmService'; --import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views'; --import { SCMViewPaneContainer } from 'vs/workbench/contrib/scm/browser/scmViewPaneContainer'; --import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; --import { ModesRegistry } from 'vs/editor/common/languages/modesRegistry'; --import { Codicon } from 'vs/base/common/codicons'; --import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; --import { SCMViewPane } from 'vs/workbench/contrib/scm/browser/scmViewPane'; - import { SCMViewService } from 'vs/workbench/contrib/scm/browser/scmViewService'; --import { SCMRepositoriesViewPane } from 'vs/workbench/contrib/scm/browser/scmRepositoriesViewPane'; --import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; --import { Context as SuggestContext } from 'vs/editor/contrib/suggest/browser/suggest'; --import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from 'vs/workbench/contrib/workspace/common/workspace'; - import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff'; - import { QuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiffService'; --import { getActiveElement } from 'vs/base/browser/dom'; +-import { localize, localize2 } from '../../../../nls.js'; +-import { Registry } from '../../../../platform/registry/common/platform.js'; +-import { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js'; +-import { QuickDiffWorkbenchController } from './quickDiffDecorator.js'; +-import { VIEWLET_ID, ISCMService, VIEW_PANE_ID, ISCMProvider, ISCMViewService, REPOSITORIES_VIEW_PANE_ID, HISTORY_VIEW_PANE_ID } from '../common/scm.js'; +-import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; +-import { MenuRegistry, MenuId } from '../../../../platform/actions/common/actions.js'; +-import { SCMActiveResourceContextKeyController, SCMActiveRepositoryController } from './activity.js'; +-import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +-import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from '../../../../platform/configuration/common/configurationRegistry.js'; +-import { IContextKeyService, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +-import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; +-import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; ++import { ISCMService, ISCMViewService } from '../common/scm.js'; + import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; + import { SCMService } from '../common/scmService.js'; +-import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry } from '../../../common/views.js'; +-import { SCMViewPaneContainer } from './scmViewPaneContainer.js'; +-import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +-import { ModesRegistry } from '../../../../editor/common/languages/modesRegistry.js'; +-import { Codicon } from '../../../../base/common/codicons.js'; +-import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; +-import { ContextKeys, SCMViewPane } from './scmViewPane.js'; +-import { RepositoryPicker, SCMViewService } from './scmViewService.js'; +-import { SCMRepositoriesViewPane } from './scmRepositoriesViewPane.js'; +-import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +-import { Context as SuggestContext } from '../../../../editor/contrib/suggest/browser/suggest.js'; +-import { MANAGE_TRUST_COMMAND_ID, WorkspaceTrustContext } from '../../workspace/common/workspace.js'; ++import { SCMViewService } from './scmViewService.js'; + import { IQuickDiffService } from '../common/quickDiff.js'; + import { QuickDiffService } from '../common/quickDiffService.js'; +-import { getActiveElement, isActiveElement } from '../../../../base/browser/dom.js'; +-import { SCMWorkingSetController } from './workingSet.js'; +-import { IViewsService } from '../../../services/views/common/viewsService.js'; +-import { IListService, WorkbenchList } from '../../../../platform/list/browser/listService.js'; +-import { isSCMRepository } from './util.js'; +-import { SCMHistoryViewPane } from './scmHistoryViewPane.js'; +-import { QuickDiffModelService, IQuickDiffModelService } from './quickDiffModel.js'; +-import { QuickDiffEditorController } from './quickDiffWidget.js'; +-import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; +-import { RemoteNameContext } from '../../../common/contextkeys.js'; +-import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +-import { SCMAccessibilityHelp } from './scmAccessibilityHelp.js'; +-import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +-import { SCMHistoryItemContextContribution } from './scmHistoryChatContext.js'; - -ModesRegistry.registerLanguage({ - id: 'scminput', @@ -2401,13 +2957,16 @@ index a927038..84e3f51 100644 -}); - -Registry.as(WorkbenchExtensions.Workbench) -- .registerWorkbenchContribution(DirtyDiffWorkbenchController, LifecyclePhase.Restored); +- .registerWorkbenchContribution(QuickDiffWorkbenchController, LifecyclePhase.Restored); +- +-registerEditorContribution(QuickDiffEditorController.ID, +- QuickDiffEditorController, EditorContributionInstantiation.AfterFirstRender); - -const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.sourceControl, localize('sourceControlViewIcon', 'View icon of the Source Control view.')); - -const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ - id: VIEWLET_ID, -- title: { value: localize('source control', "Source Control"), original: 'Source Control' }, +- title: localize2('source control', 'Source Control'), - ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), - storageId: 'workbench.scm.views.state', - icon: sourceControlViewIcon, @@ -2417,6 +2976,7 @@ index a927038..84e3f51 100644 -}, ViewContainerLocation.Sidebar, { doNotRegisterOpenCommand: true }); - -const viewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); +-const containerTitle = localize('source control view', "Source Control"); - -viewsRegistry.registerViewWelcomeContent(VIEW_PANE_ID, { - content: localize('no open repo', "No source control providers registered."), @@ -2433,14 +2993,37 @@ index a927038..84e3f51 100644 - when: ContextKeyExpr.and(ContextKeyExpr.equals('scm.providerCount', 0), WorkspaceTrustContext.IsEnabled, WorkspaceTrustContext.IsTrusted.toNegated()) -}); - +-viewsRegistry.registerViewWelcomeContent(HISTORY_VIEW_PANE_ID, { +- content: localize('no history items', "The selected source control provider does not have any source control history items."), +- when: ContextKeys.SCMHistoryItemCount.isEqualTo(0) +-}); +- +-viewsRegistry.registerViews([{ +- id: REPOSITORIES_VIEW_PANE_ID, +- containerTitle, +- name: localize2('scmRepositories', "Repositories"), +- singleViewPaneContainerTitle: localize('source control repositories', "Source Control Repositories"), +- ctorDescriptor: new SyncDescriptor(SCMRepositoriesViewPane), +- canToggleVisibility: true, +- hideByDefault: true, +- canMoveView: true, +- weight: 20, +- order: 0, +- when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), +- // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); +- containerIcon: sourceControlViewIcon +-}], viewContainer); +- -viewsRegistry.registerViews([{ - id: VIEW_PANE_ID, -- name: localize2('source control', "Source Control"), +- containerTitle, +- name: localize2('scmChanges', 'Changes'), +- singleViewPaneContainerTitle: containerTitle, - ctorDescriptor: new SyncDescriptor(SCMViewPane), - canToggleVisibility: true, - canMoveView: true, -- weight: 80, -- order: -999, +- weight: 40, +- order: 1, - containerIcon: sourceControlViewIcon, - openCommandActionDescriptor: { - id: viewContainer.id, @@ -2456,24 +3039,39 @@ index a927038..84e3f51 100644 -}], viewContainer); - -viewsRegistry.registerViews([{ -- id: REPOSITORIES_VIEW_PANE_ID, -- name: localize2('source control repositories', "Source Control Repositories"), -- ctorDescriptor: new SyncDescriptor(SCMRepositoriesViewPane), +- id: HISTORY_VIEW_PANE_ID, +- containerTitle, +- name: localize2('scmGraph', "Graph"), +- singleViewPaneContainerTitle: localize('source control graph', "Source Control Graph"), +- ctorDescriptor: new SyncDescriptor(SCMHistoryViewPane), - canToggleVisibility: true, -- hideByDefault: true, - canMoveView: true, -- weight: 20, -- order: -1000, -- when: ContextKeyExpr.and(ContextKeyExpr.has('scm.providerCount'), ContextKeyExpr.notEquals('scm.providerCount', 0)), -- // readonly when = ContextKeyExpr.or(ContextKeyExpr.equals('config.scm.alwaysShowProviders', true), ContextKeyExpr.and(ContextKeyExpr.notEquals('scm.providerCount', 0), ContextKeyExpr.notEquals('scm.providerCount', 1))); +- weight: 40, +- order: 2, +- when: ContextKeyExpr.and( +- ContextKeyExpr.has('scm.historyProviderCount'), +- ContextKeyExpr.notEquals('scm.historyProviderCount', 0), +- ), - containerIcon: sourceControlViewIcon -}], viewContainer); - -Registry.as(WorkbenchExtensions.Workbench) -- .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); +- .registerWorkbenchContribution(SCMActiveRepositoryController, LifecyclePhase.Restored); - -Registry.as(WorkbenchExtensions.Workbench) -- .registerWorkbenchContribution(SCMStatusController, LifecyclePhase.Restored); +- .registerWorkbenchContribution(SCMActiveResourceContextKeyController, LifecyclePhase.Restored); +- +-registerWorkbenchContribution2( +- SCMWorkingSetController.ID, +- SCMWorkingSetController, +- WorkbenchPhase.AfterRestored +-); +- +-registerWorkbenchContribution2( +- SCMHistoryItemContextContribution.ID, +- SCMHistoryItemContextContribution, +- WorkbenchPhase.AfterRestored +-); - -Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ - id: 'scm', @@ -2614,13 +3212,20 @@ index a927038..84e3f51 100644 - markdownDescription: localize('inputFontSize', "Controls the font size for the input message in pixels."), - default: 13 - }, -- 'scm.inputMaxLines': { +- 'scm.inputMaxLineCount': { - type: 'number', - markdownDescription: localize('inputMaxLines', "Controls the maximum number of lines that the input will auto-grow to."), - minimum: 1, - maximum: 50, - default: 10 - }, +- 'scm.inputMinLineCount': { +- type: 'number', +- markdownDescription: localize('inputMinLines', "Controls the minimum number of lines that the input will auto-grow from."), +- minimum: 1, +- maximum: 50, +- default: 1 +- }, - 'scm.alwaysShowRepositories': { - type: 'boolean', - markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the Source Control view."), @@ -2652,27 +3257,47 @@ index a927038..84e3f51 100644 - markdownDescription: localize('showInputActionButton', "Controls whether an action button can be shown in the Source Control input."), - default: true - }, -- 'scm.showIncomingChanges': { +- 'scm.workingSets.enabled': { +- type: 'boolean', +- description: localize('scm.workingSets.enabled', "Controls whether to store editor working sets when switching between source control history item groups."), +- default: false +- }, +- 'scm.workingSets.default': { - type: 'string', -- enum: ['always', 'never', 'auto'], +- enum: ['empty', 'current'], - enumDescriptions: [ -- localize('scm.showIncomingChanges.always', "Always show incoming changes in the Source Control view."), -- localize('scm.showIncomingChanges.never', "Never show incoming changes in the Source Control view."), -- localize('scm.showIncomingChanges.auto', "Only show incoming changes in the Source Control view when any exist."), +- localize('scm.workingSets.default.empty', "Use an empty working set when switching to a source control history item group that does not have a working set."), +- localize('scm.workingSets.default.current', "Use the current working set when switching to a source control history item group that does not have a working set.") - ], -- description: localize('scm.showIncomingChanges', "Controls whether incoming changes are shown in the Source Control view."), -- default: 'auto' +- description: localize('scm.workingSets.default', "Controls the default working set to use when switching to a source control history item group that does not have a working set."), +- default: 'current' - }, -- 'scm.showOutgoingChanges': { +- 'scm.compactFolders': { +- type: 'boolean', +- description: localize('scm.compactFolders', "Controls whether the Source Control view should render folders in a compact form. In such a form, single child folders will be compressed in a combined tree element."), +- default: true +- }, +- 'scm.graph.pageOnScroll': { +- type: 'boolean', +- description: localize('scm.graph.pageOnScroll', "Controls whether the Source Control Graph view will load the next page of items when you scroll to the end of the list."), +- default: true +- }, +- 'scm.graph.pageSize': { +- type: 'number', +- description: localize('scm.graph.pageSize', "The number of items to show in the Source Control Graph view by default and when loading more items."), +- minimum: 1, +- maximum: 1000, +- default: 50 +- }, +- 'scm.graph.badges': { - type: 'string', -- enum: ['always', 'never', 'auto'], +- enum: ['all', 'filter'], - enumDescriptions: [ -- localize('scm.showOutgoingChanges.always', "Always show outgoing changes in the Source Control view."), -- localize('scm.showOutgoingChanges.never', "Never show outgoing changes in the Source Control view."), -- localize('scm.showOutgoingChanges.auto', "Only show outgoing changes in the Source Control view when any exist."), +- localize('scm.graph.badges.all', "Show badges of all history item groups in the Source Control Graph view."), +- localize('scm.graph.badges.filter', "Show only the badges of history item groups used as a filter in the Source Control Graph view.") - ], -- description: localize('scm.showOutgoingChanges', "Controls whether outgoing changes are shown in the Source Control view."), -- default: 'auto' +- description: localize('scm.graph.badges', "Controls which badges are shown in the Source Control Graph view. The badges are shown on the right side of the graph indicating the names of history item groups."), +- default: 'filter' - } - } -}); @@ -2707,6 +3332,22 @@ index a927038..84e3f51 100644 - } -}); - +-KeybindingsRegistry.registerCommandAndKeybindingRule({ +- id: 'scm.clearInput', +- weight: KeybindingWeight.WorkbenchContrib, +- when: ContextKeyExpr.and(ContextKeyExpr.has('scmRepository'), SuggestContext.Visible.toNegated(), EditorContextKeys.hasNonEmptySelection.toNegated()), +- primary: KeyCode.Escape, +- handler: async (accessor) => { +- const scmService = accessor.get(ISCMService); +- const contextKeyService = accessor.get(IContextKeyService); +- +- const context = contextKeyService.getContext(getActiveElement()); +- const repositoryId = context.getValue('scmRepository'); +- const repository = repositoryId ? scmService.getRepository(repositoryId) : undefined; +- repository?.input.setValue('', true); +- } +-}); +- -const viewNextCommitCommand = { - description: { description: localize('scm view next commit', "Source Control: View Next Commit"), args: [] }, - weight: KeybindingWeight.WorkbenchContrib, @@ -2761,12 +3402,35 @@ index a927038..84e3f51 100644 - primary: KeyMod.Alt | KeyCode.UpArrow -}); - --CommandsRegistry.registerCommand('scm.openInIntegratedTerminal', async (accessor, provider: ISCMProvider) => { -- if (!provider || !provider.rootUri) { +-CommandsRegistry.registerCommand('scm.openInIntegratedTerminal', async (accessor, ...providers: ISCMProvider[]) => { +- if (!providers || providers.length === 0) { - return; - } - - const commandService = accessor.get(ICommandService); +- const listService = accessor.get(IListService); +- +- let provider = providers.length === 1 ? providers[0] : undefined; +- +- if (!provider) { +- const list = listService.lastFocusedList; +- const element = list?.getHTMLElement(); +- +- if (list instanceof WorkbenchList && element && isActiveElement(element)) { +- const [index] = list.getFocus(); +- const focusedElement = list.element(index); +- +- // Source Control Repositories +- if (isSCMRepository(focusedElement)) { +- provider = focusedElement.provider; +- } +- } +- } +- +- if (!provider?.rootUri) { +- return; +- } +- - await commandService.executeCommand('openInIntegratedTerminal', provider.rootUri); -}); - @@ -2779,13 +3443,33 @@ index a927038..84e3f51 100644 - await commandService.executeCommand('openInTerminal', provider.rootUri); -}); - +-CommandsRegistry.registerCommand('scm.setActiveProvider', async (accessor) => { +- const instantiationService = accessor.get(IInstantiationService); +- const scmViewService = accessor.get(ISCMViewService); +- +- const placeHolder = localize('scmActiveRepositoryPlaceHolder', "Select the active repository, type to filter all repositories"); +- const autoQuickItemDescription = localize('scmActiveRepositoryAutoDescription', "The active repository is updated based on focused repository/active editor"); +- const repositoryPicker = instantiationService.createInstance(RepositoryPicker, placeHolder, autoQuickItemDescription); +- +- const result = await repositoryPicker.pickRepository(); +- if (result?.repository) { +- const repository = result.repository !== 'auto' ? result.repository : undefined; +- scmViewService.pinActiveRepository(repository); +- } +-}); +- -MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { - group: '100_end', - command: { - id: 'scm.openInTerminal', - title: localize('open in external terminal', "Open in External Terminal") - }, -- when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) +- when: ContextKeyExpr.and( +- RemoteNameContext.isEqualTo(''), +- ContextKeyExpr.equals('scmProviderHasRootUri', true), +- ContextKeyExpr.or( +- ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'external'), +- ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) -}); - -MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { @@ -2794,45 +3478,638 @@ index a927038..84e3f51 100644 - id: 'scm.openInIntegratedTerminal', - title: localize('open in integrated terminal', "Open in Integrated Terminal") - }, -- when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) +- when: ContextKeyExpr.and( +- ContextKeyExpr.equals('scmProviderHasRootUri', true), +- ContextKeyExpr.or( +- ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), +- ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) -}); +- +-KeybindingsRegistry.registerCommandAndKeybindingRule({ +- id: 'workbench.scm.action.focusPreviousInput', +- weight: KeybindingWeight.WorkbenchContrib, +- when: ContextKeys.RepositoryVisibilityCount.notEqualsTo(0), +- handler: async accessor => { +- const viewsService = accessor.get(IViewsService); +- const scmView = await viewsService.openView(VIEW_PANE_ID); +- if (scmView) { +- scmView.focusPreviousInput(); +- } +- } +-}); +- +-KeybindingsRegistry.registerCommandAndKeybindingRule({ +- id: 'workbench.scm.action.focusNextInput', +- weight: KeybindingWeight.WorkbenchContrib, +- when: ContextKeys.RepositoryVisibilityCount.notEqualsTo(0), +- handler: async accessor => { +- const viewsService = accessor.get(IViewsService); +- const scmView = await viewsService.openView(VIEW_PANE_ID); +- if (scmView) { +- scmView.focusNextInput(); +- } +- } +-}); +- +-KeybindingsRegistry.registerCommandAndKeybindingRule({ +- id: 'workbench.scm.action.focusPreviousResourceGroup', +- weight: KeybindingWeight.WorkbenchContrib, +- handler: async accessor => { +- const viewsService = accessor.get(IViewsService); +- const scmView = await viewsService.openView(VIEW_PANE_ID); +- if (scmView) { +- scmView.focusPreviousResourceGroup(); +- } +- } +-}); +- +-KeybindingsRegistry.registerCommandAndKeybindingRule({ +- id: 'workbench.scm.action.focusNextResourceGroup', +- weight: KeybindingWeight.WorkbenchContrib, +- handler: async accessor => { +- const viewsService = accessor.get(IViewsService); +- const scmView = await viewsService.openView(VIEW_PANE_ID); +- if (scmView) { +- scmView.focusNextResourceGroup(); +- } +- } +-}); +- +-MenuRegistry.appendMenuItem(MenuId.EditorLineNumberContext, { +- title: localize('quickDiffDecoration', "Diff Decorations"), +- submenu: MenuId.SCMQuickDiffDecorations, +- when: ContextKeyExpr.or( +- ContextKeyExpr.equals('config.scm.diffDecorations', 'all'), +- ContextKeyExpr.equals('config.scm.diffDecorations', 'gutter')), +- group: '9_quickDiffDecorations' +-}); +- +-MenuRegistry.appendMenuItem(MenuId.SCMHistoryItemContext, { +- title: localize('scmHistoryItemChatContext', "Copilot"), +- submenu: MenuId.SCMHistoryItemChatContext, +- group: '8_chat' +-}); ++import { IQuickDiffModelService, QuickDiffModelService } from './quickDiffModel.js'; registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed); + registerSingleton(IQuickDiffService, QuickDiffService, InstantiationType.Delayed); + registerSingleton(IQuickDiffModelService, QuickDiffModelService, InstantiationType.Delayed); +- +-AccessibleViewRegistry.register(new SCMAccessibilityHelp()); +diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +index ba5dc5bf06d..5a62cadb89d 100644 +--- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts ++++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +@@ -5,7 +5,7 @@ + + import { localize, localize2 } from '../../../../nls.js'; + import { KeyMod, KeyChord, KeyCode } from '../../../../base/common/keyCodes.js'; +-import { MenuRegistry, MenuId, Action2, registerAction2, ISubmenuItem } from '../../../../platform/actions/common/actions.js'; ++import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; + import { equalsIgnoreCase } from '../../../../base/common/strings.js'; + import { Registry } from '../../../../platform/registry/common/platform.js'; + import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +@@ -808,41 +808,3 @@ registerAction2(class extends Action2 { + await marketplaceThemePicker.openQuickPick('', themeService.getColorTheme(), selectTheme).then(undefined, onUnexpectedError); + } + }); +- +-const ThemesSubMenu = new MenuId('ThemesSubMenu'); +-MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { +- title: localize('themes', "Themes"), +- submenu: ThemesSubMenu, +- group: '2_configuration', +- order: 7 +-} satisfies ISubmenuItem); +-MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { +- title: localize({ key: 'miSelectTheme', comment: ['&& denotes a mnemonic'] }, "&&Themes"), +- submenu: ThemesSubMenu, +- group: '2_configuration', +- order: 7 +-} satisfies ISubmenuItem); +- +-MenuRegistry.appendMenuItem(ThemesSubMenu, { +- command: { +- id: SelectColorThemeCommandId, +- title: localize('selectTheme.label', 'Color Theme') +- }, +- order: 1 +-}); +- +-MenuRegistry.appendMenuItem(ThemesSubMenu, { +- command: { +- id: SelectFileIconThemeCommandId, +- title: localize('themes.selectIconTheme.label', "File Icon Theme") +- }, +- order: 2 +-}); +- +-MenuRegistry.appendMenuItem(ThemesSubMenu, { +- command: { +- id: SelectProductIconThemeCommandId, +- title: localize('themes.selectProductIconTheme.label', "Product Icon Theme") +- }, +- order: 3 +-}); +diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +index 5af51edebe9..f31649c1d46 100644 +--- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts ++++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +@@ -3,24 +3,19 @@ + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +-import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; ++import { Disposable } from '../../../../base/common/lifecycle.js'; + import { isWeb } from '../../../../base/common/platform.js'; +-import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +-import { localize, localize2 } from '../../../../nls.js'; +-import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; +-import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +-import { IUserDataProfile, IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; ++import { localize } from '../../../../nls.js'; ++import { MenuId } from '../../../../platform/actions/common/actions.js'; ++import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; ++import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; + import { IWorkbenchContribution } from '../../../common/contributions.js'; + import { ILifecycleService, LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +-import { CURRENT_PROFILE_CONTEXT, HAS_PROFILES_CONTEXT, IS_CURRENT_PROFILE_TRANSIENT_CONTEXT, IUserDataProfileManagementService, IUserDataProfileService, PROFILES_CATEGORY, PROFILES_TITLE, isProfileURL } from '../../../services/userDataProfile/common/userDataProfile.js'; +-import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; +-import { INotificationService } from '../../../../platform/notification/common/notification.js'; ++import { CURRENT_PROFILE_CONTEXT, HAS_PROFILES_CONTEXT, IS_CURRENT_PROFILE_TRANSIENT_CONTEXT, IUserDataProfileService, isProfileURL } from '../../../services/userDataProfile/common/userDataProfile.js'; + import { URI } from '../../../../base/common/uri.js'; + import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; + import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; + import { IWorkspaceTagsService } from '../../tags/common/workspaceTags.js'; +-import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +-import { IOpenerService } from '../../../../platform/opener/common/opener.js'; + import { Registry } from '../../../../platform/registry/common/platform.js'; + import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; + import { EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; +@@ -28,13 +23,11 @@ import { UserDataProfilesEditor, UserDataProfilesEditorInput, UserDataProfilesEd + import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; + import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; + import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +-import { IHostService } from '../../../services/host/browser/host.js'; + import { IUserDataProfilesEditor } from '../common/userDataProfile.js'; + import { IURLService } from '../../../../platform/url/common/url.js'; + import { IBrowserWorkbenchEnvironmentService } from '../../../services/environment/browser/environmentService.js'; + + export const OpenProfileMenu = new MenuId('OpenProfile'); +-const ProfilesMenu = new MenuId('Profiles'); + + export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution { + +@@ -47,7 +40,6 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements + constructor( + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, +- @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IWorkspaceTagsService private readonly workspaceTagsService: IWorkspaceTagsService, +@@ -65,14 +57,14 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements + + this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); + this.isCurrentProfileTransientContext.set(!!this.userDataProfileService.currentProfile.isTransient); +- this._register(this.userDataProfileService.onDidChangeCurrentProfile(e => { ++ this._register(this.userDataProfileService.onDidChangeCurrentProfile(() => { + this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); + this.isCurrentProfileTransientContext.set(!!this.userDataProfileService.currentProfile.isTransient); + })); + + this.hasProfilesContext = HAS_PROFILES_CONTEXT.bindTo(contextKeyService); + this.hasProfilesContext.set(this.userDataProfilesService.profiles.length > 1); +- this._register(this.userDataProfilesService.onDidChangeProfiles(e => this.hasProfilesContext.set(this.userDataProfilesService.profiles.length > 1))); ++ this._register(this.userDataProfilesService.onDidChangeProfiles(() => this.hasProfilesContext.set(this.userDataProfilesService.profiles.length > 1))); + + this.registerEditor(); + this.registerActions(); +@@ -121,383 +113,6 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements + } + + private registerActions(): void { +- this.registerProfileSubMenu(); +- this._register(this.registerManageProfilesAction()); +- this._register(this.registerSwitchProfileAction()); +- +- this.registerOpenProfileSubMenu(); +- this.registerNewWindowWithProfileAction(); +- this.registerProfilesActions(); +- this._register(this.userDataProfilesService.onDidChangeProfiles(() => this.registerProfilesActions())); +- +- this._register(this.registerExportCurrentProfileAction()); +- +- this.registerCreateFromCurrentProfileAction(); +- this.registerNewProfileAction(); +- this.registerDeleteProfileAction(); +- +- this.registerHelpAction(); +- } +- +- private registerProfileSubMenu(): void { +- const getProfilesTitle = () => { +- return localize('profiles', "Profile ({0})", this.userDataProfileService.currentProfile.name); +- }; +- MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { +- get title() { +- return getProfilesTitle(); +- }, +- submenu: ProfilesMenu, +- group: '2_configuration', +- order: 1, +- when: HAS_PROFILES_CONTEXT +- }); +- MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { +- get title() { +- return getProfilesTitle(); +- }, +- submenu: ProfilesMenu, +- group: '2_configuration', +- order: 1, +- when: HAS_PROFILES_CONTEXT +- }); +- } +- +- private registerOpenProfileSubMenu(): void { +- MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { +- title: localize('New Profile Window', "New Window with Profile"), +- submenu: OpenProfileMenu, +- group: '1_new', +- order: 4, +- }); +- } +- +- private readonly profilesDisposable = this._register(new MutableDisposable()); +- private registerProfilesActions(): void { +- this.profilesDisposable.value = new DisposableStore(); +- for (const profile of this.userDataProfilesService.profiles) { +- if (!profile.isTransient) { +- this.profilesDisposable.value.add(this.registerProfileEntryAction(profile)); +- this.profilesDisposable.value.add(this.registerNewWindowAction(profile)); +- } +- } +- } +- +- private registerProfileEntryAction(profile: IUserDataProfile): IDisposable { +- const that = this; +- return registerAction2(class ProfileEntryAction extends Action2 { +- constructor() { +- super({ +- id: `workbench.profiles.actions.profileEntry.${profile.id}`, +- title: profile.name, +- metadata: { +- description: localize2('change profile', "Switch to {0} profile", profile.name), +- }, +- toggled: ContextKeyExpr.equals(CURRENT_PROFILE_CONTEXT.key, profile.id), +- menu: [ +- { +- id: ProfilesMenu, +- group: '0_profiles', +- } +- ] +- }); +- } +- async run(accessor: ServicesAccessor) { +- if (that.userDataProfileService.currentProfile.id !== profile.id) { +- return that.userDataProfileManagementService.switchProfile(profile); +- } +- } +- }); +- } +- +- private registerNewWindowWithProfileAction(): IDisposable { +- return registerAction2(class NewWindowWithProfileAction extends Action2 { +- constructor() { +- super({ +- id: `workbench.profiles.actions.newWindowWithProfile`, +- title: localize2('newWindowWithProfile', "New Window with Profile..."), +- category: PROFILES_CATEGORY, +- precondition: HAS_PROFILES_CONTEXT, +- f1: true, +- }); +- } +- async run(accessor: ServicesAccessor) { +- const quickInputService = accessor.get(IQuickInputService); +- const userDataProfilesService = accessor.get(IUserDataProfilesService); +- const hostService = accessor.get(IHostService); +- +- const pick = await quickInputService.pick( +- userDataProfilesService.profiles.map(profile => ({ +- label: profile.name, +- profile +- })), +- { +- title: localize('new window with profile', "New Window with Profile"), +- placeHolder: localize('pick profile', "Select Profile"), +- canPickMany: false +- }); +- if (pick) { +- return hostService.openWindow({ remoteAuthority: null, forceProfile: pick.profile.name }); +- } +- } +- }); +- } +- +- private registerNewWindowAction(profile: IUserDataProfile): IDisposable { +- const disposables = new DisposableStore(); +- +- const id = `workbench.action.openProfile.${profile.name.replace('/\s+/', '_')}`; +- +- disposables.add(registerAction2(class NewWindowAction extends Action2 { +- +- constructor() { +- super({ +- id, +- title: localize2('openShort', "{0}", profile.name), +- metadata: { +- description: localize2('open profile', "Open New Window with {0} Profile", profile.name), +- }, +- menu: { +- id: OpenProfileMenu, +- group: '0_profiles', +- when: HAS_PROFILES_CONTEXT +- } +- }); +- } +- +- override run(accessor: ServicesAccessor): Promise { +- const hostService = accessor.get(IHostService); +- return hostService.openWindow({ remoteAuthority: null, forceProfile: profile.name }); +- } +- })); +- +- disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { +- command: { +- id, +- category: PROFILES_CATEGORY, +- title: localize2('open', "Open {0} Profile", profile.name), +- precondition: HAS_PROFILES_CONTEXT +- }, +- })); +- +- return disposables; +- } +- +- private registerSwitchProfileAction(): IDisposable { +- const that = this; +- return registerAction2(class SwitchProfileAction extends Action2 { +- constructor() { +- super({ +- id: `workbench.profiles.actions.switchProfile`, +- title: localize2('switchProfile', 'Switch Profile...'), +- category: PROFILES_CATEGORY, +- f1: true, +- }); +- } +- async run(accessor: ServicesAccessor) { +- const quickInputService = accessor.get(IQuickInputService); +- +- const items: Array = []; +- for (const profile of that.userDataProfilesService.profiles) { +- items.push({ +- id: profile.id, +- label: profile.id === that.userDataProfileService.currentProfile.id ? `$(check) ${profile.name}` : profile.name, +- profile, +- }); +- } +- +- const result = await quickInputService.pick(items.sort((a, b) => a.profile.name.localeCompare(b.profile.name)), { +- placeHolder: localize('selectProfile', "Select Profile") +- }); +- if (result) { +- await that.userDataProfileManagementService.switchProfile(result.profile); +- } +- } +- }); +- } +- +- private registerManageProfilesAction(): IDisposable { +- const disposables = new DisposableStore(); +- disposables.add(registerAction2(class ManageProfilesAction extends Action2 { +- constructor() { +- super({ +- id: `workbench.profiles.actions.manageProfiles`, +- title: { +- ...localize2('manage profiles', "Profiles"), +- mnemonicTitle: localize({ key: 'miOpenProfiles', comment: ['&& denotes a mnemonic'] }, "&&Profiles"), +- }, +- menu: [ +- { +- id: MenuId.GlobalActivity, +- group: '2_configuration', +- order: 1, +- when: HAS_PROFILES_CONTEXT.negate() +- }, +- { +- id: MenuId.MenubarPreferencesMenu, +- group: '2_configuration', +- order: 1, +- when: HAS_PROFILES_CONTEXT.negate() +- }, +- { +- id: ProfilesMenu, +- group: '1_manage', +- order: 1, +- }, +- ] +- }); +- } +- run(accessor: ServicesAccessor) { +- const editorGroupsService = accessor.get(IEditorGroupsService); +- const instantiationService = accessor.get(IInstantiationService); +- return editorGroupsService.activeGroup.openEditor(new UserDataProfilesEditorInput(instantiationService)); +- } +- })); +- disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { +- command: { +- id: 'workbench.profiles.actions.manageProfiles', +- category: Categories.Preferences, +- title: localize2('open profiles', "Open Profiles (UI)"), +- }, +- })); +- +- return disposables; +- } +- +- private registerExportCurrentProfileAction(): IDisposable { +- const that = this; +- const disposables = new DisposableStore(); +- const id = 'workbench.profiles.actions.exportProfile'; +- disposables.add(registerAction2(class ExportProfileAction extends Action2 { +- constructor() { +- super({ +- id, +- title: localize2('export profile', "Export Profile..."), +- category: PROFILES_CATEGORY, +- f1: true, +- }); +- } +- +- async run() { +- const editor = await that.openProfilesEditor(); +- editor?.selectProfile(that.userDataProfileService.currentProfile); +- } +- })); +- disposables.add(MenuRegistry.appendMenuItem(MenuId.MenubarShare, { +- command: { +- id, +- title: localize2('export profile in share', "Export Profile ({0})...", that.userDataProfileService.currentProfile.name), +- }, +- })); +- return disposables; +- } +- +- +- private registerCreateFromCurrentProfileAction(): void { +- const that = this; +- this._register(registerAction2(class CreateFromCurrentProfileAction extends Action2 { +- constructor() { +- super({ +- id: 'workbench.profiles.actions.createFromCurrentProfile', +- title: localize2('save profile as', "Save Current Profile As..."), +- category: PROFILES_CATEGORY, +- f1: true, +- }); +- } +- +- async run() { +- const editor = await that.openProfilesEditor(); +- editor?.createNewProfile(that.userDataProfileService.currentProfile); +- } +- })); +- } +- +- private registerNewProfileAction(): void { +- const that = this; +- this._register(registerAction2(class CreateProfileAction extends Action2 { +- constructor() { +- super({ +- id: 'workbench.profiles.actions.createProfile', +- title: localize2('create profile', "New Profile..."), +- category: PROFILES_CATEGORY, +- f1: true, +- menu: [ +- { +- id: OpenProfileMenu, +- group: '1_manage_profiles', +- order: 1 +- } +- ] +- }); +- } +- +- async run(accessor: ServicesAccessor) { +- const editor = await that.openProfilesEditor(); +- return editor?.createNewProfile(); +- } +- })); +- } +- +- private registerDeleteProfileAction(): void { +- this._register(registerAction2(class DeleteProfileAction extends Action2 { +- constructor() { +- super({ +- id: 'workbench.profiles.actions.deleteProfile', +- title: localize2('delete profile', "Delete Profile..."), +- category: PROFILES_CATEGORY, +- f1: true, +- precondition: HAS_PROFILES_CONTEXT, +- }); +- } +- +- async run(accessor: ServicesAccessor) { +- const quickInputService = accessor.get(IQuickInputService); +- const userDataProfileService = accessor.get(IUserDataProfileService); +- const userDataProfilesService = accessor.get(IUserDataProfilesService); +- const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); +- const notificationService = accessor.get(INotificationService); +- +- const profiles = userDataProfilesService.profiles.filter(p => !p.isDefault && !p.isTransient); +- if (profiles.length) { +- const picks = await quickInputService.pick( +- profiles.map(profile => ({ +- label: profile.name, +- description: profile.id === userDataProfileService.currentProfile.id ? localize('current', "Current") : undefined, +- profile +- })), +- { +- title: localize('delete specific profile', "Delete Profile..."), +- placeHolder: localize('pick profile to delete', "Select Profiles to Delete"), +- canPickMany: true +- }); +- if (picks) { +- try { +- await Promise.all(picks.map(pick => userDataProfileManagementService.removeProfile(pick.profile))); +- } catch (error) { +- notificationService.error(error); +- } +- } +- } +- } +- })); +- } +- +- private registerHelpAction(): void { +- this._register(registerAction2(class HelpAction extends Action2 { +- constructor() { +- super({ +- id: 'workbench.profiles.actions.help', +- title: PROFILES_TITLE, +- category: Categories.Help, +- menu: [{ +- id: MenuId.CommandPalette, +- }], +- }); +- } +- run(accessor: ServicesAccessor): unknown { +- return accessor.get(IOpenerService).open(URI.parse('https://aka.ms/vscode-profiles-help')); +- } +- })); + } + + private async reportWorkspaceProfileInfo(): Promise { diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts -index 30c9f21..39c04b3 100644 +index 750087f2e72..ac4c7b4bf09 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts -@@ -161,9 +161,6 @@ registerSingleton(IOpenerService, OpenerService, InstantiationType.Delayed); +@@ -183,9 +183,6 @@ registerSingleton(IMcpManagementService, McpManagementService, InstantiationType //#region --- workbench contributions -// Telemetry --import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; +-import './contrib/telemetry/browser/telemetry.contribution.js'; - // Preferences - import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; - import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; -@@ -221,11 +218,6 @@ import 'vs/workbench/contrib/scm/browser/scm.contribution'; + import './contrib/preferences/browser/preferences.contribution.js'; + import './contrib/preferences/browser/keybindingsEditorContribution.js'; +@@ -247,11 +244,6 @@ import './contrib/scm/browser/scm.contribution.js'; // Debug - import 'vs/workbench/contrib/debug/browser/debug.contribution'; --import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; --import 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; --import 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; --import 'vs/workbench/contrib/debug/browser/repl'; --import 'vs/workbench/contrib/debug/browser/debugViewlet'; + import './contrib/debug/browser/debug.contribution.js'; +-import './contrib/debug/browser/debugEditorContribution.js'; +-import './contrib/debug/browser/breakpointEditorContribution.js'; +-import './contrib/debug/browser/callStackEditorContribution.js'; +-import './contrib/debug/browser/repl.js'; +-import './contrib/debug/browser/debugViewlet.js'; // Markers - import 'vs/workbench/contrib/markers/browser/markers.contribution'; -@@ -259,7 +251,6 @@ import 'vs/workbench/contrib/externalUriOpener/common/externalUriOpener.contribu + import './contrib/markers/browser/markers.contribution.js'; +@@ -285,7 +277,6 @@ import './contrib/externalUriOpener/common/externalUriOpener.contribution.js'; // Extensions Management - import 'vs/workbench/contrib/extensions/browser/extensions.contribution'; --import 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; + import './contrib/extensions/browser/extensions.contribution.js'; +-import './contrib/extensions/browser/extensionsViewlet.js'; // Output View - import 'vs/workbench/contrib/output/common/outputChannelModelService'; --- -2.39.3 (Apple Git-145) - + import './contrib/output/browser/output.contribution.js'; +@@ -417,8 +408,4 @@ import './contrib/inlineCompletions/browser/inlineCompletions.contribution.js'; + // Drop or paste into + import './contrib/dropOrPasteInto/browser/dropOrPasteInto.contribution.js'; + +-// Edit Telemetry +-import './contrib/editTelemetry/browser/editTelemetry.contribution.js'; +- +- + //#endregion diff --git a/packages/quick-edit/patches/0008-Enable-project-wide-intellisense-for-web.patch b/packages/quick-edit/patches/typechecking-without-shared-array-buffer.diff similarity index 89% rename from packages/quick-edit/patches/0008-Enable-project-wide-intellisense-for-web.patch rename to packages/quick-edit/patches/typechecking-without-shared-array-buffer.diff index 97bb7a87de55..23be75568607 100644 --- a/packages/quick-edit/patches/0008-Enable-project-wide-intellisense-for-web.patch +++ b/packages/quick-edit/patches/typechecking-without-shared-array-buffer.diff @@ -1,62 +1,55 @@ -From ad0eca736dfef06d2055336a2885f655432ef7df Mon Sep 17 00:00:00 2001 -From: Workers DevProd -Date: Sat, 17 Feb 2024 07:31:36 +0000 -Subject: [PATCH 8/9] Enable project-wide intellisense for web - ---- - .../typescript-language-features/package.json | 2 +- - .../src/languageProvider.ts | 4 - - .../src/tsServer/serverProcess.browser.ts | 66 ++- - .../src/typescriptServiceClient.ts | 4 +- - .../web/src/cfs-simple.ts | 426 ++++++++++++++++++ - .../web/src/fileWatcherManager.ts | 23 +- - .../web/src/pathMapper.ts | 3 +- - .../web/src/serverHost.ts | 124 ++--- - .../web/src/webServer.ts | 12 +- - .../web/src/workerSession.ts | 32 +- - 10 files changed, 571 insertions(+), 125 deletions(-) - create mode 100644 extensions/typescript-language-features/web/src/cfs-simple.ts - -diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json -index 880c8dc..61b2888 100644 ---- a/extensions/typescript-language-features/package.json -+++ b/extensions/typescript-language-features/package.json -@@ -983,7 +983,7 @@ - "%typescript.preferences.importModuleSpecifierEnding.index%", - "%typescript.preferences.importModuleSpecifierEnding.js%" - ], -- "default": "auto", -+ "default": "js", - "description": "%typescript.preferences.importModuleSpecifierEnding%", - "scope": "language-overridable" - }, +# VSCode for Web doesn't support proper typechecking across multiple files without `SharedArrayBuffer` support. +# Unfortunately we can't guarantee `SharedArrayBuffer` support in the context this editor is used in, and so +# this diff patches the Typescript extension to maintain an in-memory buffer of the filesystem, performing typechecking +# on that. In theory this updates on a slight lag from changes in the editor, but in practice this delay is too small to +# be noticeable. Notably, we include a simplified version of the "CFS" filesystem provider from `packages/quick-edit-extension` diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts -index 6157c3b..a1f02c1 100644 +index f2d52f2..b1a9e81 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts -@@ -17,7 +17,6 @@ import { ClientCapability } from './typescriptService'; +@@ -9,7 +9,6 @@ import { CommandManager } from './commands/commandManager'; + import { DocumentSelector } from './configuration/documentSelector'; + import * as fileSchemes from './configuration/fileSchemes'; + import { LanguageDescription } from './configuration/languageDescription'; +-import { Schemes } from './configuration/schemes'; + import { DiagnosticKind } from './languageFeatures/diagnostics'; + import FileConfigurationManager from './languageFeatures/fileConfigurationManager'; + import { TelemetryReporter } from './logging/telemetry'; +@@ -18,7 +17,6 @@ import { ClientCapability } from './typescriptService'; import TypeScriptServiceClient from './typescriptServiceClient'; import TypingsStatus from './ui/typingsStatus'; import { Disposable } from './utils/dispose'; --import { isWeb } from './utils/platform'; +-import { isWeb, isWebAndHasSharedArrayBuffers, supportsReadableByteStreams } from './utils/platform'; const validateSetting = 'validate.enable'; -@@ -141,9 +140,6 @@ export default class LanguageProvider extends Disposable { +@@ -146,21 +144,6 @@ export default class LanguageProvider extends Disposable { return; } -- if (diagnosticsKind === DiagnosticKind.Semantic && isWeb() && this.client.configuration.webProjectWideIntellisenseSuppressSemanticErrors) { +- if (diagnosticsKind === DiagnosticKind.Semantic && isWeb()) { +- if ( +- !isWebAndHasSharedArrayBuffers() +- || !supportsReadableByteStreams() // No ata. Will result in lots of false positives +- || this.client.configuration.webProjectWideIntellisenseSuppressSemanticErrors +- || !this.client.configuration.webProjectWideIntellisenseEnabled +- ) { +- return; +- } +- } +- +- // Disable semantic errors in notebooks until we have better notebook support +- if (diagnosticsKind === DiagnosticKind.Semantic && file.scheme === Schemes.notebookCell) { - return; - } const config = vscode.workspace.getConfiguration(this.id, file); const reportUnnecessary = config.get('showUnused', true); diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts -index bb57c26..9f8c71b 100644 +index 5adf186..25a6def 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts -@@ -114,11 +114,34 @@ class WorkerServerProcess implements TsServerProcess { +@@ -115,11 +115,34 @@ class WorkerServerProcess implements TsServerProcess { } case 'watchDirectory': case 'watchFile': { @@ -96,7 +89,7 @@ index bb57c26..9f8c71b 100644 break; } default: -@@ -143,9 +166,36 @@ class WorkerServerProcess implements TsServerProcess { +@@ -144,9 +167,36 @@ class WorkerServerProcess implements TsServerProcess { } }; @@ -137,19 +130,19 @@ index bb57c26..9f8c71b 100644 const connection = new ServiceConnection(syncChannel.port2); diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts -index a389142..e216be7 100644 +index 4201d6d..d9559da 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts -@@ -24,7 +24,7 @@ import { ServiceConfigurationProvider, SyntaxServerConfiguration, TsServerLogLev - import { Disposable } from './utils/dispose'; - import * as fileSchemes from './configuration/fileSchemes'; - import { Logger } from './logging/logger'; +@@ -32,7 +32,7 @@ import { ITypeScriptVersionProvider, TypeScriptVersion } from './tsServer/versio + import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; + import { Disposable, DisposableStore, disposeAll } from './utils/dispose'; + import { hash } from './utils/hash'; -import { isWeb, isWebAndHasSharedArrayBuffers } from './utils/platform'; +import { isWeb } from './utils/platform'; - import { PluginManager, TypeScriptServerPlugin } from './tsServer/plugins'; - import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './logging/telemetry'; - import Tracer from './logging/tracer'; -@@ -269,7 +269,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType + + + export interface TsDiagnostics { +@@ -284,7 +284,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; private isProjectWideIntellisenseOnWebEnabled(): boolean { @@ -591,7 +584,7 @@ index 0000000..043a369 + } +} diff --git a/extensions/typescript-language-features/web/src/fileWatcherManager.ts b/extensions/typescript-language-features/web/src/fileWatcherManager.ts -index 8c8d740..1cd6557 100644 +index 6ae4472..4358d4b 100644 --- a/extensions/typescript-language-features/web/src/fileWatcherManager.ts +++ b/extensions/typescript-language-features/web/src/fileWatcherManager.ts @@ -7,6 +7,7 @@ import type * as ts from 'typescript/lib/tsserverlibrary'; @@ -632,7 +625,7 @@ index 8c8d740..1cd6557 100644 watchFile(path: string, callback: ts.FileWatcherCallback, pollingInterval?: number, options?: ts.WatchOptions): ts.FileWatcher { diff --git a/extensions/typescript-language-features/web/src/pathMapper.ts b/extensions/typescript-language-features/web/src/pathMapper.ts -index e925489..bbcd6ef 100644 +index e925489..4bb32d3 100644 --- a/extensions/typescript-language-features/web/src/pathMapper.ts +++ b/extensions/typescript-language-features/web/src/pathMapper.ts @@ -76,7 +76,8 @@ export function fromResource(extensionUri: URI, uri: URI) { @@ -640,16 +633,16 @@ index e925489..bbcd6ef 100644 return uri.path; } - return `/${uri.scheme}/${uri.authority}${uri.path}`; -+ // Use `ts-nul-authority` when the authority is an empty string. All CFS file have an empty authority ++ // Use `ts-nul-authority` when the authority is an empty string. All CFS files have an empty authority + return `/${uri.scheme}/${uri.authority === '' ? 'ts-nul-authority' : uri.authority}${uri.path}`; } export function looksLikeLibDtsPath(filepath: string) { diff --git a/extensions/typescript-language-features/web/src/serverHost.ts b/extensions/typescript-language-features/web/src/serverHost.ts -index f2f9ca9..8069d8c 100644 +index d746501..c44f8e5 100644 --- a/extensions/typescript-language-features/web/src/serverHost.ts +++ b/extensions/typescript-language-features/web/src/serverHost.ts -@@ -3,29 +3,36 @@ +@@ -3,30 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ @@ -663,7 +656,8 @@ index f2f9ca9..8069d8c 100644 import { PathMapper, looksLikeNodeModules, mapUri } from './pathMapper'; -import { findArgument, hasArgument } from './util/args'; +import { findArgument } from './util/args'; -+ + import { URI } from 'vscode-uri'; + +// Keep an in-memory representation of the current Worker script. +// This is in-memory within this (Web) Worker to avoid a dependency on SharedArrayBuffer +// The contents are updated on a slight lag from VSCode filesystem events @@ -673,7 +667,7 @@ index f2f9ca9..8069d8c 100644 +// BEGIN misc internals + +// End misc internals - ++ type ServerHostWithImport = ts.server.ServerHost & { importPlugin(root: string, moduleName: string): Promise }; function createServerHost( @@ -694,7 +688,7 @@ index f2f9ca9..8069d8c 100644 // Internals const combinePaths: (path: string, ...paths: (string | undefined)[]) => string = (ts as any).combinePaths; -@@ -98,7 +105,7 @@ function createServerHost( +@@ -99,7 +106,7 @@ function createServerHost( newLine: '\n', useCaseSensitiveFileNames: true, write: s => { @@ -703,7 +697,7 @@ index f2f9ca9..8069d8c 100644 }, writeOutputIsTTY() { return true; -@@ -106,7 +113,9 @@ function createServerHost( +@@ -107,7 +114,9 @@ function createServerHost( readFile(path) { logger.logVerbose('fs.readFile', { path }); @@ -714,7 +708,7 @@ index f2f9ca9..8069d8c 100644 const webPath = getWebPath(path); if (webPath) { const request = new XMLHttpRequest(); -@@ -118,28 +127,12 @@ function createServerHost( +@@ -119,28 +128,12 @@ function createServerHost( } } @@ -746,7 +740,7 @@ index f2f9ca9..8069d8c 100644 }, getFileSize(path) { logger.logVerbose('fs.getFileSize', { path }); -@@ -153,12 +146,6 @@ function createServerHost( +@@ -154,12 +147,6 @@ function createServerHost( try { ret = fs.stat(uri).size; } catch (_error) { @@ -759,7 +753,7 @@ index f2f9ca9..8069d8c 100644 } return ret; }, -@@ -173,19 +160,11 @@ function createServerHost( +@@ -174,19 +161,11 @@ function createServerHost( data = byteOrderMarkIndicator + data; } @@ -783,7 +777,7 @@ index f2f9ca9..8069d8c 100644 } catch (error) { console.error('fs.writeFile', { path, error }); } -@@ -196,7 +175,9 @@ function createServerHost( +@@ -197,7 +176,9 @@ function createServerHost( fileExists(path: string): boolean { logger.logVerbose('fs.fileExists', { path }); @@ -794,7 +788,7 @@ index f2f9ca9..8069d8c 100644 const webPath = getWebPath(path); if (!webPath) { return false; -@@ -208,24 +189,12 @@ function createServerHost( +@@ -209,24 +190,12 @@ function createServerHost( return request.status === 200; } @@ -822,7 +816,7 @@ index f2f9ca9..8069d8c 100644 }, directoryExists(path: string): boolean { logger.logVerbose('fs.directoryExists', { path }); -@@ -245,12 +214,7 @@ function createServerHost( +@@ -246,12 +215,7 @@ function createServerHost( try { stat = fs.stat(uri); } catch (_error) { @@ -836,7 +830,7 @@ index f2f9ca9..8069d8c 100644 } if (stat) { if (path.startsWith('/https') && !path.endsWith('.d.ts')) { -@@ -303,12 +267,7 @@ function createServerHost( +@@ -304,12 +268,7 @@ function createServerHost( try { s = fs.stat(uri); } catch (_e) { @@ -850,7 +844,7 @@ index f2f9ca9..8069d8c 100644 } return s && new Date(s.mtime); }, -@@ -406,23 +365,12 @@ function createServerHost( +@@ -423,23 +382,12 @@ function createServerHost( export async function createSys( ts: typeof import('typescript/lib/tsserverlibrary'), args: readonly string[], @@ -917,7 +911,7 @@ index 3d2d5f9..72522b8 100644 const args = e.data.args; const extensionUri = URI.from(e.data.extensionUri); diff --git a/extensions/typescript-language-features/web/src/workerSession.ts b/extensions/typescript-language-features/web/src/workerSession.ts -index 6ae517c..47802cd 100644 +index 6ae517c..9091986 100644 --- a/extensions/typescript-language-features/web/src/workerSession.ts +++ b/extensions/typescript-language-features/web/src/workerSession.ts @@ -2,10 +2,8 @@ @@ -973,11 +967,3 @@ index 6ae517c..47802cd 100644 this.listener = (message: any) => { // TEMP fix since Cancellation.retrieveCheck is not correct -@@ -124,3 +121,4 @@ export function startWorkerSession( - - worker.listen(); - } -+ --- -2.39.3 (Apple Git-145) - diff --git a/packages/quick-edit/setup.sh b/packages/quick-edit/setup.sh index 9a7bab02220d..611df5389818 100755 --- a/packages/quick-edit/setup.sh +++ b/packages/quick-edit/setup.sh @@ -1,18 +1,13 @@ set -eu -# The preinstall script in vscode will fail if npm_execpath is not set by yarn -# This make sure the env is unset so yarn can set it accordingly -unset npm_execpath # The upstream VSCode version (tag) to build from -VERSION="1.85.2" -# We cannot run yarn without disabling the corepack check as the packageManager field is set to pnpm -SKIP_YARN_COREPACK_CHECK=0 +VERSION="1.102.1" # Setup for the CI environment -if [ -n "${CI:-}" ]; then - sudo apt-get install -y libkrb5-dev - yarn global add node-gyp -fi +# if [ -n "${CI:-}" ]; then +# sudo apt-get install -y libkrb5-dev +# yarn global add node-gyp +# fi rm -rf web rm -rf ../../vendor/vscode @@ -25,7 +20,7 @@ pnpm dlx vscode-dts dev $VERSION pnpm dlx vscode-dts $VERSION cd ../quick-edit -git clone --depth 1 --branch "$VERSION" https://github.com/microsoft/vscode ../../vendor/vscode +git clone --branch "$VERSION" --depth 1 https://github.com/microsoft/vscode ../../vendor/vscode cd ../../vendor/vscode git checkout -b base @@ -33,8 +28,8 @@ git checkout -b cloudflare git config user.email "workers-devprod@cloudflare.com" git config user.name "Workers DevProd" -git am ../../packages/quick-edit/patches/*.patch -yarn install +git apply ../../packages/quick-edit/patches/*.diff + +npm install cd ../../packages/quick-edit pnpm exec tsx bundle-dts.ts -ln -s $PWD/../../vendor/vscode $PWD/web/assets diff --git a/packages/quick-edit/src/index.ts b/packages/quick-edit/src/index.ts new file mode 100644 index 000000000000..a732d8426356 --- /dev/null +++ b/packages/quick-edit/src/index.ts @@ -0,0 +1,75 @@ +export default { + async fetch(request: Request, env: Env) { + const url = new URL(request.url); + + const configValues = { + WORKBENCH_WEB_CONFIGURATION: JSON.stringify({ + configurationDefaults: { + "workbench.colorTheme": + url.searchParams.get("theme") === "dark" + ? "Solarflare Dark" + : "Solarflare Light", + "workbench.startupEditor": "none", + "editor.minimap.autohide": true, + "files.exclude": { + "*.d.ts": true, + "jsconfig.json": true, + "package.json": true, + "wrangler.toml": true, + }, + "files.autoSave": "afterDelay", + "files.autoSaveDelay": 200, + "telemetry.telemetryLevel": "off", + "window.menuBarVisibility": "hidden", + }, + productConfiguration: { + nameShort: "Quick Edit", + nameLong: "Cloudflare Workers Quick Edit", + applicationName: "workers-quick-edit", + dataFolderName: ".quick-edit", + version: "1.76.0", + extensionEnabledApiProposals: { + "cloudflare.quick-edit-extension": [ + "fileSearchProvider", + "textSearchProvider", + "ipc", + ], + }, + }, + additionalBuiltinExtensions: [ + { + scheme: url.protocol === "https:" ? "https" : "http", + path: "/quick-edit-extension", + authority: url.host, + }, + { + scheme: url.protocol === "https:" ? "https" : "http", + path: "/solarflare-theme", + authority: url.host, + }, + ], + }).replace(/"/g, """), + WORKBENCH_AUTH_SESSION: "", + WORKBENCH_WEB_BASE_URL: "/assets", + }; + if (url.pathname !== "/") { + return new Response(null, { status: 404 }); + } + + const workbench = await env.ASSETS.fetch( + `http://fake.host${configValues.WORKBENCH_WEB_BASE_URL}/workbench.html` + ); + + // Replace configuration values + const replacedWorkbenchText = (await workbench.text()).replaceAll( + /\{\{([^}]+)\}\}/g, + (_, key) => configValues[key as keyof typeof configValues] ?? "undefined" + ); + + return new Response(replacedWorkbenchText, { + headers: { + "Content-Type": "text/html", + }, + }); + }, +}; diff --git a/packages/quick-edit/tsconfig.json b/packages/quick-edit/tsconfig.json index d410dc63507b..09f1cc46efa7 100644 --- a/packages/quick-edit/tsconfig.json +++ b/packages/quick-edit/tsconfig.json @@ -1,11 +1,18 @@ { - "extends": "@cloudflare/workers-tsconfig/tsconfig.json", "compilerOptions": { - "target": "esnext", - "module": "esnext", - "lib": ["esnext"], - "types": ["@cloudflare/workers-types"] + "target": "es2021", + "lib": ["es2021"], + "module": "NodeNext", + "moduleResolution": "nodenext", + "noEmit": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "strict": true, + "types": ["@cloudflare/workers-types/experimental", "node"], + "skipLibCheck": true }, - "include": ["functions/**/*.ts"], - "exclude": ["vscode", "dist"] + "exclude": ["web", "editor-files"] } diff --git a/packages/quick-edit/worker-configuration.d.ts b/packages/quick-edit/worker-configuration.d.ts new file mode 100644 index 000000000000..15a055021d0c --- /dev/null +++ b/packages/quick-edit/worker-configuration.d.ts @@ -0,0 +1,8 @@ +/* eslint-disable */ +// Generated by Wrangler by running `wrangler types --no-include-runtime` (hash: 187132f48ddf0f604882ba8213fe386f) +declare namespace Cloudflare { + interface Env { + ASSETS: Fetcher; + } +} +interface Env extends Cloudflare.Env {} diff --git a/packages/quick-edit/wrangler.jsonc b/packages/quick-edit/wrangler.jsonc new file mode 100644 index 000000000000..613dfc7dd0bb --- /dev/null +++ b/packages/quick-edit/wrangler.jsonc @@ -0,0 +1,10 @@ +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "main": "src/index.ts", + "name": "quick-edit", + "compatibility_date": "2025-07-01", + "assets": { + "directory": "web", + "binding": "ASSETS", + }, +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0b4984270d3..0772f6892588 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2006,9 +2006,18 @@ importers: '@cloudflare/workers-tsconfig': specifier: workspace:* version: link:../workers-tsconfig + '@cloudflare/workers-types': + specifier: ^4.20250712.0 + version: 4.20250803.0 + '@types/node': + specifier: ^20.19.9 + version: 20.19.9 concurrently: specifier: ^8.2.2 version: 8.2.2 + esbuild: + specifier: catalog:default + version: 0.25.4 tsx: specifier: ^3.12.8 version: 3.12.10