mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-28 23:58:23 +01:00
Merge branch 'main' into ben/voice
This commit is contained in:
@@ -56,7 +56,11 @@
|
||||
{
|
||||
"command": "ipynb.cleanInvalidImageAttachment",
|
||||
"title": "%cleanInvalidImageAttachment.title%"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "notebook.cellOutput.copyToClipboard",
|
||||
"title": "%copyOutputToClipboard.title%"
|
||||
}
|
||||
],
|
||||
"notebooks": [
|
||||
{
|
||||
@@ -99,7 +103,13 @@
|
||||
"command": "ipynb.cleanInvalidImageAttachment",
|
||||
"when": "false"
|
||||
}
|
||||
]
|
||||
],
|
||||
"webview/context": [
|
||||
{
|
||||
"command": "notebook.cellOutput.copyToClipboard",
|
||||
"when": "webviewId == 'notebook.output' && webviewSection == 'image'"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"newUntitledIpynb.shortTitle": "Jupyter Notebook",
|
||||
"openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor",
|
||||
"cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference",
|
||||
"copyOutputToClipboard.title": "Copy Output to Clipboard",
|
||||
"markdownAttachmentRenderer.displayName": {
|
||||
"message": "Markdown-It ipynb Cell Attachment renderer",
|
||||
"comment": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, OutputChannel, window, workspace, l10n } from 'vscode';
|
||||
import { ExtensionContext, OutputChannel, window, workspace, l10n, env } from 'vscode';
|
||||
import { startClient, LanguageClientConstructor, SchemaRequestService, languageServerDescription } from '../jsonClient';
|
||||
import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient, BaseLanguageClient } from 'vscode-languageclient/node';
|
||||
|
||||
@@ -129,7 +129,10 @@ async function getSchemaRequestService(context: ExtensionContext, log: Log): Pro
|
||||
const isXHRResponse = (error: any): error is XHRResponse => typeof error?.status === 'number';
|
||||
|
||||
const request = async (uri: string, etag?: string): Promise<string> => {
|
||||
const headers: Headers = { 'Accept-Encoding': 'gzip, deflate' };
|
||||
const headers: Headers = {
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'User-Agent': `${env.appName} (${env.appHost})`
|
||||
};
|
||||
if (etag) {
|
||||
headers['If-None-Match'] = etag;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"displayName": "JSON Language Features",
|
||||
"description": "Provides rich language support for JSON files.",
|
||||
"json.schemas.desc": "Associate schemas to JSON files in the current project.",
|
||||
"json.schemas.url.desc": "A URL to a schema or a relative path to a schema in the current directory",
|
||||
"json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
|
||||
"json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.",
|
||||
"json.schemas.url.desc": "A URL or absolute file path to a schema. Can be a relative path in workspace and workspace folder settings.",
|
||||
"json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas. `*` and '**' can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
|
||||
"json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' and '**' to match against when resolving JSON files to schemas. When beginning with '!', it defines an exclusion pattern.",
|
||||
"json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.",
|
||||
"json.format.enable.desc": "Enable/disable default JSON formatter",
|
||||
"json.format.keepLines.desc" : "Keep all existing new lines when formatting.",
|
||||
|
||||
@@ -37,6 +37,7 @@ function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable
|
||||
if (alt) {
|
||||
image.alt = alt;
|
||||
}
|
||||
image.setAttribute('data-vscode-context', JSON.stringify({ webviewSection: 'image', outputId: outputInfo.id, 'preventDefaultContextMenuItems': true }));
|
||||
const display = document.createElement('div');
|
||||
display.classList.add('display');
|
||||
display.appendChild(image);
|
||||
|
||||
+3
-3
@@ -95,14 +95,14 @@
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.42",
|
||||
"xterm": "5.3.0-beta.46",
|
||||
"xterm-addon-canvas": "0.5.0-beta.9",
|
||||
"xterm-addon-image": "0.6.0-beta.2",
|
||||
"xterm-addon-search": "0.13.0-beta.8",
|
||||
"xterm-addon-serialize": "0.11.0-beta.8",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.14",
|
||||
"xterm-headless": "5.3.0-beta.42",
|
||||
"xterm-addon-webgl": "0.16.0-beta.16",
|
||||
"xterm-headless": "5.3.0-beta.46",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
},
|
||||
|
||||
+3
-3
@@ -27,14 +27,14 @@
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.42",
|
||||
"xterm": "5.3.0-beta.46",
|
||||
"xterm-addon-canvas": "0.5.0-beta.9",
|
||||
"xterm-addon-image": "0.6.0-beta.2",
|
||||
"xterm-addon-search": "0.13.0-beta.8",
|
||||
"xterm-addon-serialize": "0.11.0-beta.8",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.14",
|
||||
"xterm-headless": "5.3.0-beta.42",
|
||||
"xterm-addon-webgl": "0.16.0-beta.16",
|
||||
"xterm-headless": "5.3.0-beta.46",
|
||||
"yauzl": "^2.9.2",
|
||||
"yazl": "^2.4.3"
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@
|
||||
"tas-client-umd": "0.1.8",
|
||||
"vscode-oniguruma": "1.7.0",
|
||||
"vscode-textmate": "9.0.0",
|
||||
"xterm": "5.3.0-beta.42",
|
||||
"xterm": "5.3.0-beta.46",
|
||||
"xterm-addon-canvas": "0.5.0-beta.9",
|
||||
"xterm-addon-image": "0.6.0-beta.2",
|
||||
"xterm-addon-search": "0.13.0-beta.8",
|
||||
"xterm-addon-unicode11": "0.5.0",
|
||||
"xterm-addon-webgl": "0.16.0-beta.14"
|
||||
"xterm-addon-webgl": "0.16.0-beta.16"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ xterm-addon-unicode11@0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
|
||||
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
|
||||
|
||||
xterm-addon-webgl@0.16.0-beta.14:
|
||||
version "0.16.0-beta.14"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02"
|
||||
integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w==
|
||||
xterm-addon-webgl@0.16.0-beta.16:
|
||||
version "0.16.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224"
|
||||
integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg==
|
||||
|
||||
xterm@5.3.0-beta.42:
|
||||
version "5.3.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f"
|
||||
integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw==
|
||||
xterm@5.3.0-beta.46:
|
||||
version "5.3.0-beta.46"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a"
|
||||
integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw==
|
||||
|
||||
+12
-12
@@ -902,20 +902,20 @@ xterm-addon-unicode11@0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
|
||||
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
|
||||
|
||||
xterm-addon-webgl@0.16.0-beta.14:
|
||||
version "0.16.0-beta.14"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02"
|
||||
integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w==
|
||||
xterm-addon-webgl@0.16.0-beta.16:
|
||||
version "0.16.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224"
|
||||
integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg==
|
||||
|
||||
xterm-headless@5.3.0-beta.42:
|
||||
version "5.3.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9"
|
||||
integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA==
|
||||
xterm-headless@5.3.0-beta.46:
|
||||
version "5.3.0-beta.46"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48"
|
||||
integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg==
|
||||
|
||||
xterm@5.3.0-beta.42:
|
||||
version "5.3.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f"
|
||||
integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw==
|
||||
xterm@5.3.0-beta.46:
|
||||
version "5.3.0-beta.46"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a"
|
||||
integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw==
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
|
||||
@@ -105,40 +105,36 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
|
||||
|
||||
this._register(model.modified.onDidChangeContent((e) => {
|
||||
const diff = this._diff.get();
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
transaction(tx => {
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
|
||||
updateUnchangedRegions(result, tx);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
|
||||
});
|
||||
if (diff) {
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
transaction(tx => {
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
|
||||
updateUnchangedRegions(result, tx);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debouncer.schedule();
|
||||
}));
|
||||
this._register(model.original.onDidChangeContent((e) => {
|
||||
const diff = this._diff.get();
|
||||
if (!diff) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
transaction(tx => {
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
|
||||
updateUnchangedRegions(result, tx);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
|
||||
});
|
||||
if (diff) {
|
||||
const textEdits = TextEditInfo.fromModelContentChanges(e.changes);
|
||||
const result = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified);
|
||||
if (result) {
|
||||
this._lastDiff = result;
|
||||
transaction(tx => {
|
||||
this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx);
|
||||
updateUnchangedRegions(result, tx);
|
||||
const currentSyncedMovedText = this.syncedMovedTexts.get();
|
||||
this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
debouncer.schedule();
|
||||
@@ -175,7 +171,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
|
||||
result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result;
|
||||
result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result;
|
||||
|
||||
|
||||
transaction(tx => {
|
||||
updateUnchangedRegions(result, tx);
|
||||
|
||||
|
||||
@@ -908,13 +908,13 @@ class UnfoldAllRegionsAction extends FoldingAction<void> {
|
||||
}
|
||||
}
|
||||
|
||||
class FoldAllRegionsExceptAction extends FoldingAction<void> {
|
||||
class FoldAllExceptAction extends FoldingAction<void> {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.foldAllExcept',
|
||||
label: nls.localize('foldAllExcept.label', "Fold All Regions Except Selected"),
|
||||
alias: 'Fold All Regions Except Selected',
|
||||
label: nls.localize('foldAllExcept.label', "Fold All Except Selected"),
|
||||
alias: 'Fold All Except Selected',
|
||||
precondition: CONTEXT_FOLDING_ENABLED,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
@@ -931,13 +931,13 @@ class FoldAllRegionsExceptAction extends FoldingAction<void> {
|
||||
|
||||
}
|
||||
|
||||
class UnfoldAllRegionsExceptAction extends FoldingAction<void> {
|
||||
class UnfoldAllExceptAction extends FoldingAction<void> {
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.unfoldAllExcept',
|
||||
label: nls.localize('unfoldAllExcept.label', "Unfold All Regions Except Selected"),
|
||||
alias: 'Unfold All Regions Except Selected',
|
||||
label: nls.localize('unfoldAllExcept.label', "Unfold All Except Selected"),
|
||||
alias: 'Unfold All Except Selected',
|
||||
precondition: CONTEXT_FOLDING_ENABLED,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.editorTextFocus,
|
||||
@@ -1194,8 +1194,8 @@ registerEditorAction(UnfoldAllAction);
|
||||
registerEditorAction(FoldAllBlockCommentsAction);
|
||||
registerEditorAction(FoldAllRegionsAction);
|
||||
registerEditorAction(UnfoldAllRegionsAction);
|
||||
registerEditorAction(FoldAllRegionsExceptAction);
|
||||
registerEditorAction(UnfoldAllRegionsExceptAction);
|
||||
registerEditorAction(FoldAllExceptAction);
|
||||
registerEditorAction(UnfoldAllExceptAction);
|
||||
registerEditorAction(ToggleFoldAction);
|
||||
registerEditorAction(GotoParentFoldAction);
|
||||
registerEditorAction(GotoPreviousFoldAction);
|
||||
|
||||
@@ -1264,8 +1264,7 @@ export class QuickWidget extends QuickInput implements IQuickWidget {
|
||||
|
||||
const visibilities: Visibilities = {
|
||||
title: !!this.title || !!this.step || !!this.buttons.length,
|
||||
description: !!this.description || !!this.step,
|
||||
progressBar: true
|
||||
description: !!this.description || !!this.step
|
||||
};
|
||||
|
||||
this.ui.setVisibilities(visibilities);
|
||||
|
||||
@@ -125,6 +125,19 @@ export interface IQuickAccessProviderHelp {
|
||||
* The command to bring up this quick access provider.
|
||||
*/
|
||||
readonly commandId?: string;
|
||||
|
||||
/**
|
||||
* The order of help entries in the Command Center.
|
||||
* Lower values will be placed above higher values.
|
||||
* No value will hide this help entry from the Command Center.
|
||||
*/
|
||||
readonly commandCenterOrder?: number;
|
||||
|
||||
/**
|
||||
* An optional label to use for the Command Center entry. If not set
|
||||
* the description will be used instead.
|
||||
*/
|
||||
readonly commandCenterLabel?: string;
|
||||
}
|
||||
|
||||
export interface IQuickAccessProviderDescriptor {
|
||||
|
||||
@@ -8,15 +8,24 @@ import { streamToBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IRequestOptions } from 'vs/base/parts/request/common/request';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import * as https from 'https';
|
||||
import { AbstractOneDataSystemAppender, IAppInsightsCore } from 'vs/platform/telemetry/common/1dsAppender';
|
||||
|
||||
type OnCompleteFunc = (status: number, headers: { [headerName: string]: string }, response?: string) => void;
|
||||
|
||||
interface IResponseData {
|
||||
headers: { [headerName: string]: string };
|
||||
statusCode: number;
|
||||
responseData: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes a request to submit telemetry to the server utilizing the request service
|
||||
* @param options The options which will be used to make the request
|
||||
* @param requestService The request service
|
||||
* @returns An object containing the headers, statusCode, and responseData
|
||||
*/
|
||||
async function makeTelemetryRequest(options: IRequestOptions, requestService: IRequestService) {
|
||||
async function makeTelemetryRequest(options: IRequestOptions, requestService: IRequestService): Promise<IResponseData> {
|
||||
const response = await requestService.request(options, CancellationToken.None);
|
||||
const responseData = (await streamToBuffer(response.stream)).toString();
|
||||
const statusCode = response.res.statusCode ?? 200;
|
||||
@@ -31,30 +40,57 @@ async function makeTelemetryRequest(options: IRequestOptions, requestService: IR
|
||||
/**
|
||||
* Complete a request to submit telemetry to the server utilizing the https module. Only used when the request service is not available
|
||||
* @param options The options which will be used to make the request
|
||||
* @param httpsModule The https node module
|
||||
* @returns An object containing the headers, statusCode, and responseData
|
||||
*/
|
||||
function makeLegacyTelemetryRequest(options: IRequestOptions, httpsModule: typeof import('https')) {
|
||||
async function makeLegacyTelemetryRequest(options: IRequestOptions): Promise<IResponseData> {
|
||||
const httpsOptions = {
|
||||
method: options.type,
|
||||
headers: options.headers
|
||||
};
|
||||
const req = httpsModule.request(options.url ?? '', httpsOptions, res => {
|
||||
res.on('data', function (responseData) {
|
||||
return {
|
||||
headers: res.headers as Record<string, any>,
|
||||
statusCode: res.statusCode ?? 200,
|
||||
responseData: responseData.toString()
|
||||
};
|
||||
const responsePromise = new Promise<IResponseData>((resolve, reject) => {
|
||||
const req = https.request(options.url ?? '', httpsOptions, res => {
|
||||
res.on('data', function (responseData) {
|
||||
resolve({
|
||||
headers: res.headers as Record<string, any>,
|
||||
statusCode: res.statusCode ?? 200,
|
||||
responseData: responseData.toString()
|
||||
});
|
||||
});
|
||||
// On response with error send status of 0 and a blank response to oncomplete so we can retry events
|
||||
res.on('error', function (err) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
// On response with error send status of 0 and a blank response to oncomplete so we can retry events
|
||||
res.on('error', function (err) {
|
||||
throw err;
|
||||
req.write(options.data, (err) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
req.write(options.data);
|
||||
req.end();
|
||||
return;
|
||||
return responsePromise;
|
||||
}
|
||||
|
||||
async function sendPostAsync(requestService: IRequestService | undefined, payload: IPayloadData, oncomplete: OnCompleteFunc) {
|
||||
const telemetryRequestData = typeof payload.data === 'string' ? payload.data : new TextDecoder().decode(payload.data);
|
||||
const requestOptions: IRequestOptions = {
|
||||
type: 'POST',
|
||||
headers: {
|
||||
...payload.headers,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(payload.data).toString()
|
||||
},
|
||||
url: payload.urlString,
|
||||
data: telemetryRequestData
|
||||
};
|
||||
|
||||
try {
|
||||
const responseData = requestService ? await makeTelemetryRequest(requestOptions, requestService) : await makeLegacyTelemetryRequest(requestOptions);
|
||||
oncomplete(responseData.statusCode, responseData.headers, responseData.responseData);
|
||||
} catch {
|
||||
// If it errors out, send status of 0 and a blank response to oncomplete so we can retry events
|
||||
oncomplete(0, {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,41 +103,11 @@ export class OneDataSystemAppender extends AbstractOneDataSystemAppender {
|
||||
defaultData: { [key: string]: any } | null,
|
||||
iKeyOrClientFactory: string | (() => IAppInsightsCore), // allow factory function for testing
|
||||
) {
|
||||
let httpsModule: typeof import('https') | undefined;
|
||||
if (!requestService) {
|
||||
httpsModule = require('https');
|
||||
}
|
||||
// Override the way events get sent since node doesn't have XHTMLRequest
|
||||
const customHttpXHROverride: IXHROverride = {
|
||||
sendPOST: (payload: IPayloadData, oncomplete) => {
|
||||
|
||||
const telemetryRequestData = typeof payload.data === 'string' ? payload.data : new TextDecoder().decode(payload.data);
|
||||
const requestOptions: IRequestOptions = {
|
||||
type: 'POST',
|
||||
headers: {
|
||||
...payload.headers,
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(payload.data).toString()
|
||||
},
|
||||
url: payload.urlString,
|
||||
data: telemetryRequestData
|
||||
};
|
||||
|
||||
try {
|
||||
if (requestService) {
|
||||
makeTelemetryRequest(requestOptions, requestService).then(({ statusCode, headers, responseData }) => {
|
||||
oncomplete(statusCode, headers, responseData);
|
||||
});
|
||||
} else {
|
||||
if (!httpsModule) {
|
||||
throw new Error('https module is undefined');
|
||||
}
|
||||
makeLegacyTelemetryRequest(requestOptions, httpsModule);
|
||||
}
|
||||
} catch {
|
||||
// If it errors out, send status of 0 and a blank response to oncomplete so we can retry events
|
||||
oncomplete(0, {});
|
||||
}
|
||||
// Fire off the async request without awaiting it
|
||||
sendPostAsync(requestService, payload, oncomplete);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1217,7 +1217,6 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase {
|
||||
|
||||
const normalizedResult = Array.isArray(result) ? result : result.items;
|
||||
const commands = this._isAdditionsProposedApiEnabled ? Array.isArray(result) ? [] : result.commands || [] : [];
|
||||
const suppressSuggestions = this._isAdditionsProposedApiEnabled && !Array.isArray(result) ? result.suppressSuggestions : undefined;
|
||||
const enableForwardStability = this._isAdditionsProposedApiEnabled && !Array.isArray(result) ? result.enableForwardStability : undefined;
|
||||
|
||||
let disposableStore: DisposableStore | undefined = undefined;
|
||||
@@ -1255,7 +1254,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase {
|
||||
}
|
||||
return this._commands.toInternal(c, disposableStore);
|
||||
}),
|
||||
suppressSuggestions,
|
||||
suppressSuggestions: false,
|
||||
enableForwardStability,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,18 +4,31 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration';
|
||||
|
||||
export const accessibilityHelpIsShown = new RawContextKey<boolean>('accessibilityHelpIsShown', false, true);
|
||||
export const accessibleViewIsShown = new RawContextKey<boolean>('accessibleViewIsShown', false, true);
|
||||
export const accessibleViewSupportsNavigation = new RawContextKey<boolean>('accessibleViewSupportsNavigation', false, true);
|
||||
export const accessibleViewVerbosityEnabled = new RawContextKey<boolean>('accessibleViewVerbosityEnabled', false, true);
|
||||
export const accessibleViewGoToSymbolSupported = new RawContextKey<boolean>('accessibleViewGoToSymbolSupported', false, true);
|
||||
export const accessibleViewCurrentProviderId = new RawContextKey<string>('accessibleViewCurrentProviderId', undefined, undefined);
|
||||
|
||||
export const enum AccessibilitySettingId {
|
||||
UnfocusedViewOpacity = 'accessibility.unfocusedViewOpacity'
|
||||
/**
|
||||
* Miscellaneous settings tagged with accessibility and implemented in the accessibility contrib but
|
||||
* were better to live under workbench for discoverability.
|
||||
*/
|
||||
export const enum AccessibilityWorkbenchSettingId {
|
||||
ViewDimUnfocusedEnabled = 'workbench.view.dimUnfocused.enabled',
|
||||
ViewDimUnfocusedOpacity = 'workbench.view.dimUnfocused.opacity'
|
||||
}
|
||||
|
||||
export const enum ViewDimUnfocusedOpacityProperties {
|
||||
Default = 0.75,
|
||||
Minimum = 0.2,
|
||||
Maximum = 1
|
||||
}
|
||||
|
||||
export const enum AccessibilityVerbositySettingId {
|
||||
@@ -82,19 +95,33 @@ const configuration: IConfigurationNode = {
|
||||
[AccessibilityVerbositySettingId.EditorUntitledHint]: {
|
||||
description: localize('verbosity.editor.untitledhint', 'Provide information about relevant actions in an untitled text editor.'),
|
||||
...baseProperty
|
||||
},
|
||||
[AccessibilitySettingId.UnfocusedViewOpacity]: {
|
||||
description: localize('unfocusedViewOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will dim inactive views to make the focused views more obvious.'),
|
||||
type: 'number',
|
||||
minimum: 0.2,
|
||||
maximum: 1,
|
||||
default: 1,
|
||||
tags: ['accessibility']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function registerAccessibilityConfiguration() {
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
configurationRegistry.registerConfiguration(configuration);
|
||||
const registry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
registry.registerConfiguration(configuration);
|
||||
|
||||
registry.registerConfiguration({
|
||||
...workbenchConfigurationNodeBase,
|
||||
properties: {
|
||||
[AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled]: {
|
||||
description: localize('dimUnfocusedEnabled', 'Whether to dim unfocused editors and terminals, making the focused view more obvious.'),
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
tags: ['accessibility'],
|
||||
scope: ConfigurationScope.MACHINE,
|
||||
},
|
||||
[AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity]: {
|
||||
description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled}#\``),
|
||||
type: 'number',
|
||||
minimum: ViewDimUnfocusedOpacityProperties.Minimum,
|
||||
maximum: ViewDimUnfocusedOpacityProperties.Maximum,
|
||||
default: ViewDimUnfocusedOpacityProperties.Default,
|
||||
tags: ['accessibility'],
|
||||
scope: ConfigurationScope.MACHINE,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class EditorAccessibilityHelpContribution extends Disposable {
|
||||
codeEditor = codeEditorService.getActiveCodeEditor()!;
|
||||
}
|
||||
accessibleViewService.show(instantiationService.createInstance(AccessibilityHelpProvider, codeEditor));
|
||||
}));
|
||||
}, EditorContextKeys.focus));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +282,7 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable {
|
||||
this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => {
|
||||
const accessibleViewService = accessor.get(IAccessibleViewService);
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const contextViewService = accessor.get(IContextViewService);
|
||||
const show = () => {
|
||||
const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor();
|
||||
if (!editor) {
|
||||
@@ -310,33 +311,19 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable {
|
||||
editor.focus();
|
||||
},
|
||||
next() {
|
||||
model.next().then(() => show());
|
||||
contextViewService.hideContextView();
|
||||
setTimeout(() => model.next().then(() => show()), 50);
|
||||
},
|
||||
previous() {
|
||||
model.previous().then(() => show());
|
||||
contextViewService.hideContextView();
|
||||
setTimeout(() => model.previous().then(() => show()), 50);
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
id: 'inlineCompletions.accept',
|
||||
label: localize('inlineCompletions.accept', "Accept Completion"),
|
||||
tooltip: localize('inlineCompletions.accept', "Accept Completion"),
|
||||
run: () => {
|
||||
model.accept(editor).then(() => {
|
||||
alert('Accepted');
|
||||
model.stop();
|
||||
editor.focus();
|
||||
});
|
||||
},
|
||||
class: ThemeIcon.asClassName(Codicon.check),
|
||||
enabled: true
|
||||
}
|
||||
],
|
||||
options: this._options
|
||||
});
|
||||
return true;
|
||||
};
|
||||
}; ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible);
|
||||
return show();
|
||||
}, ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.focus, EditorContextKeys.hasCodeActionsProvider)
|
||||
},
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands';
|
||||
import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
@@ -101,6 +101,7 @@ class AccessibleView extends Disposable {
|
||||
private _accessibleViewSupportsNavigation: IContextKey<boolean>;
|
||||
private _accessibleViewVerbosityEnabled: IContextKey<boolean>;
|
||||
private _accessibleViewGoToSymbolSupported: IContextKey<boolean>;
|
||||
private _accessibleViewCurrentProviderId: IContextKey<string>;
|
||||
|
||||
get editorWidget() { return this._editorWidget; }
|
||||
private _editorContainer: HTMLElement;
|
||||
@@ -126,6 +127,7 @@ class AccessibleView extends Disposable {
|
||||
this._accessibleViewSupportsNavigation = accessibleViewSupportsNavigation.bindTo(this._contextKeyService);
|
||||
this._accessibleViewVerbosityEnabled = accessibleViewVerbosityEnabled.bindTo(this._contextKeyService);
|
||||
this._accessibleViewGoToSymbolSupported = accessibleViewGoToSymbolSupported.bindTo(this._contextKeyService);
|
||||
this._accessibleViewCurrentProviderId = accessibleViewCurrentProviderId.bindTo(this._contextKeyService);
|
||||
|
||||
this._editorContainer = document.createElement('div');
|
||||
this._editorContainer.classList.add('accessible-view');
|
||||
@@ -182,6 +184,7 @@ class AccessibleView extends Disposable {
|
||||
onHide: () => {
|
||||
if (!showAccessibleViewHelp) {
|
||||
this._currentProvider = undefined;
|
||||
this._accessibleViewCurrentProviderId.reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -291,6 +294,7 @@ class AccessibleView extends Disposable {
|
||||
if (!showAccessibleViewHelp) {
|
||||
// don't overwrite the current provider
|
||||
this._currentProvider = provider;
|
||||
this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey.replaceAll('accessibility.verbosity.', ''));
|
||||
}
|
||||
this._updateContextKeys(provider, true);
|
||||
const value = this._configurationService.getValue(provider.verbositySettingKey);
|
||||
|
||||
@@ -11,8 +11,11 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands';
|
||||
import { accessibilityHelpIsShown, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
|
||||
const accessibleViewMenu = {
|
||||
id: MenuId.AccessibleView,
|
||||
@@ -33,7 +36,12 @@ class AccessibleViewNextAction extends Action2 {
|
||||
primary: KeyMod.Alt | KeyCode.BracketRight,
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
menu: [commandPalette, accessibleViewMenu],
|
||||
menu: [
|
||||
commandPalette,
|
||||
{
|
||||
...accessibleViewMenu,
|
||||
when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation),
|
||||
}],
|
||||
icon: Codicon.chevronRight,
|
||||
title: localize('editor.action.accessibleViewNext', "Show Next in Accessible View")
|
||||
});
|
||||
@@ -55,7 +63,13 @@ class AccessibleViewPreviousAction extends Action2 {
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
icon: Codicon.chevronLeft,
|
||||
menu: [commandPalette, accessibleViewMenu],
|
||||
menu: [
|
||||
commandPalette,
|
||||
{
|
||||
...accessibleViewMenu,
|
||||
when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation),
|
||||
}
|
||||
],
|
||||
title: localize('editor.action.accessibleViewPrevious', "Show Previous in Accessible View")
|
||||
});
|
||||
}
|
||||
@@ -73,10 +87,17 @@ class AccessibleViewGoToSymbolAction extends Action2 {
|
||||
precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewGoToSymbolSupported),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyO,
|
||||
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Period],
|
||||
weight: KeybindingWeight.WorkbenchContrib + 10
|
||||
},
|
||||
icon: Codicon.symbolField,
|
||||
menu: [commandPalette, accessibleViewMenu],
|
||||
menu: [
|
||||
commandPalette,
|
||||
{
|
||||
...accessibleViewMenu,
|
||||
when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation),
|
||||
}
|
||||
],
|
||||
title: localize('editor.action.accessibleViewGoToSymbol', "Go To Symbol in Accessible View")
|
||||
});
|
||||
}
|
||||
@@ -140,11 +161,14 @@ class AccessibleViewDisableHintAction extends Action2 {
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
icon: Codicon.treeFilterClear,
|
||||
menu: [commandPalette,
|
||||
menu: [
|
||||
commandPalette,
|
||||
{
|
||||
id: MenuId.AccessibleView,
|
||||
group: 'navigation'
|
||||
}],
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), accessibleViewVerbosityEnabled),
|
||||
}
|
||||
],
|
||||
title: localize('editor.action.accessibleViewDisableHint', "Disable Accessible View Hint")
|
||||
});
|
||||
}
|
||||
@@ -153,3 +177,44 @@ class AccessibleViewDisableHintAction extends Action2 {
|
||||
}
|
||||
}
|
||||
registerAction2(AccessibleViewDisableHintAction);
|
||||
|
||||
class AccessibleViewAcceptInlineCompletionAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletion,
|
||||
precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)),
|
||||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Slash,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.Slash },
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
icon: Codicon.check,
|
||||
menu: [
|
||||
commandPalette,
|
||||
{
|
||||
id: MenuId.AccessibleView,
|
||||
group: 'navigation',
|
||||
order: 0,
|
||||
when: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions))
|
||||
}],
|
||||
title: localize('editor.action.accessibleViewAcceptInlineCompletionAction', "Accept Inline Completion")
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor();
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const model = InlineCompletionsController.get(editor)?.model.get();
|
||||
const state = model?.state.get();
|
||||
if (!model || !state) {
|
||||
return;
|
||||
}
|
||||
await model.accept(editor);
|
||||
alert('Accepted');
|
||||
model.stop();
|
||||
editor.focus();
|
||||
}
|
||||
}
|
||||
registerAction2(AccessibleViewAcceptInlineCompletionAction);
|
||||
|
||||
+44
-19
@@ -8,38 +8,35 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { clamp } from 'vs/base/common/numbers';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { AccessibilitySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { AccessibilityWorkbenchSettingId, ViewDimUnfocusedOpacityProperties } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
|
||||
export class UnfocusedViewDimmingContribution extends Disposable implements IWorkbenchContribution {
|
||||
private _styleElement?: HTMLStyleElement;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
const elStyle = document.createElement('style');
|
||||
elStyle.className = 'accessibilityUnfocusedViewOpacity';
|
||||
document.head.appendChild(elStyle);
|
||||
this._register(toDisposable(() => elStyle.remove()));
|
||||
this._register(toDisposable(() => this._removeStyleElement()));
|
||||
|
||||
this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => {
|
||||
if (e && !e.affectsConfiguration(AccessibilitySettingId.UnfocusedViewOpacity)) {
|
||||
if (e && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled) && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let opacity: number;
|
||||
const opacityConfig = configurationService.getValue(AccessibilitySettingId.UnfocusedViewOpacity);
|
||||
if (typeof opacityConfig !== 'number') {
|
||||
opacity = 1;
|
||||
} else {
|
||||
opacity = clamp(opacityConfig, 0.2, 1);
|
||||
}
|
||||
|
||||
let cssTextContent = '';
|
||||
|
||||
// Only add the styles if the feature is used
|
||||
if (opacity !== 1) {
|
||||
const rules = new Set<string>();
|
||||
const enabled = ensureBoolean(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled), false);
|
||||
if (enabled) {
|
||||
const opacity = clamp(
|
||||
ensureNumber(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity), ViewDimUnfocusedOpacityProperties.Default),
|
||||
ViewDimUnfocusedOpacityProperties.Minimum,
|
||||
ViewDimUnfocusedOpacityProperties.Maximum
|
||||
);
|
||||
|
||||
if (opacity !== 1) {
|
||||
const rules = new Set<string>();
|
||||
const filterRule = `filter: opacity(${opacity});`;
|
||||
// Terminal tabs
|
||||
rules.add(`.monaco-workbench .pane-body.integrated-terminal:not(:focus-within) .tabs-container { ${filterRule} }`);
|
||||
@@ -49,11 +46,39 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor
|
||||
rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`);
|
||||
// Terminal editors
|
||||
rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`);
|
||||
cssTextContent = [...rules].join('\n');
|
||||
}
|
||||
cssTextContent = [...rules].join('\n');
|
||||
|
||||
}
|
||||
|
||||
elStyle.textContent = cssTextContent;
|
||||
if (cssTextContent.length === 0) {
|
||||
this._removeStyleElement();
|
||||
} else {
|
||||
this._getStyleElement().textContent = cssTextContent;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _getStyleElement(): HTMLStyleElement {
|
||||
if (!this._styleElement) {
|
||||
this._styleElement = document.createElement('style');
|
||||
this._styleElement.className = 'accessibilityUnfocusedViewOpacity';
|
||||
document.head.appendChild(this._styleElement);
|
||||
}
|
||||
return this._styleElement;
|
||||
}
|
||||
|
||||
private _removeStyleElement(): void {
|
||||
this._styleElement?.remove();
|
||||
this._styleElement = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ensureBoolean(value: unknown, defaultValue: boolean): boolean {
|
||||
return typeof value === 'boolean' ? value : defaultValue;
|
||||
}
|
||||
|
||||
function ensureNumber(value: unknown, defaultValue: number): number {
|
||||
return typeof value === 'number' ? value : defaultValue;
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ export const enum AccessibilityCommandId {
|
||||
DisableVerbosityHint = 'editor.action.accessibleViewDisableHint',
|
||||
GoToSymbol = 'editor.action.accessibleViewGoToSymbol',
|
||||
ShowNext = 'editor.action.accessibleViewNext',
|
||||
ShowPrevious = 'editor.action.accessibleViewPrevious'
|
||||
ShowPrevious = 'editor.action.accessibleViewPrevious',
|
||||
AccessibleViewAcceptInlineCompletion = 'editor.action.accessibleViewAcceptInlineCompletion'
|
||||
}
|
||||
|
||||
@@ -3,32 +3,69 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr, IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions';
|
||||
import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatViewOptions } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
|
||||
export const ASK_QUICK_QUESTION_ACTION_ID = 'chat.action.askQuickQuestion';
|
||||
export const ASK_QUICK_QUESTION_ACTION_ID = 'workbench.action.quickchat.toggle';
|
||||
export function registerQuickChatActions() {
|
||||
registerAction2(QuickChatGlobalAction);
|
||||
|
||||
registerAction2(class OpenInChatViewAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.quickchat.openInChatView',
|
||||
title: {
|
||||
value: localize('chat.openInChatView.label', "Open in Chat View"),
|
||||
original: 'Open in Chat View'
|
||||
},
|
||||
f1: false,
|
||||
category: CHAT_CATEGORY,
|
||||
icon: Codicon.commentDiscussion,
|
||||
menu: {
|
||||
id: MenuId.ChatInputSide,
|
||||
group: 'navigation',
|
||||
order: 10
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor) {
|
||||
const quickChatService = accessor.get(IQuickChatService);
|
||||
quickChatService.openInChatView();
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class CloseQuickChatAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.action.quickchat.close',
|
||||
title: {
|
||||
value: localize('chat.closeQuickChat.label', "Close Quick Chat"),
|
||||
original: 'Close Quick Chat'
|
||||
},
|
||||
f1: false,
|
||||
category: CHAT_CATEGORY,
|
||||
icon: Codicon.close,
|
||||
menu: {
|
||||
id: MenuId.ChatInputSide,
|
||||
group: 'navigation',
|
||||
order: 20
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
run(accessor: ServicesAccessor) {
|
||||
const quickChatService = accessor.get(IQuickChatService);
|
||||
quickChatService.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class QuickChatGlobalAction extends Action2 {
|
||||
@@ -46,24 +83,13 @@ class QuickChatGlobalAction extends Action2 {
|
||||
linux: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KeyI
|
||||
}
|
||||
},
|
||||
menu: {
|
||||
id: MenuId.LayoutControlMenu,
|
||||
group: '0_workbench_toggles',
|
||||
when: ContextKeyExpr.notEquals('config.chat.experimental.defaultMode', 'chatView'),
|
||||
order: 0
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor, query: string): Promise<void> {
|
||||
const chatService = accessor.get(IChatService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
// Grab the first provider and run its command
|
||||
const info = chatService.getProviderInfos()[0];
|
||||
if (info) {
|
||||
await commandService.executeCommand(`workbench.action.openQuickChat.${info.id}`, query);
|
||||
}
|
||||
override run(accessor: ServicesAccessor, query?: string): void {
|
||||
const quickChatService = accessor.get(IQuickChatService);
|
||||
quickChatService.toggle(undefined, query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +103,6 @@ class QuickChatGlobalAction extends Action2 {
|
||||
*/
|
||||
export function getQuickChatActionForProvider(id: string, label: string) {
|
||||
return class AskQuickChatAction extends Action2 {
|
||||
_currentTimer: any | undefined;
|
||||
_input: IQuickWidget | undefined;
|
||||
_currentChat: QuickChat | undefined;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: `workbench.action.openQuickChat.${id}`,
|
||||
@@ -90,198 +112,9 @@ export function getQuickChatActionForProvider(id: string, label: string) {
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor, query: string): void {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const chatService = accessor.get(IChatService);
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
// First things first, clear the existing timer that will dispose the session
|
||||
clearTimeout(this._currentTimer);
|
||||
this._currentTimer = undefined;
|
||||
|
||||
// If the input is already shown, hide it. This provides a toggle behavior of the quick pick
|
||||
if (this._input !== undefined) {
|
||||
this._input.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any providers are available. If not, show nothing
|
||||
// This shouldn't be needed because of the precondition, but just in case
|
||||
const providerInfo = chatService.getProviderInfos()[0];
|
||||
if (!providerInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disposableStore = new DisposableStore();
|
||||
|
||||
//#region Setup quick pick
|
||||
|
||||
this._input = quickInputService.createQuickWidget();
|
||||
disposableStore.add(this._input);
|
||||
|
||||
const containerSession = dom.$('.interactive-session');
|
||||
this._input.widget = containerSession;
|
||||
|
||||
this._currentChat ??= instantiationService.createInstance(QuickChat, {
|
||||
providerId: providerInfo.id,
|
||||
});
|
||||
// show needs to come before the current chat rendering
|
||||
this._input.show();
|
||||
this._currentChat.render(containerSession);
|
||||
|
||||
const clearButton = {
|
||||
iconClass: ThemeIcon.asClassName(Codicon.clearAll),
|
||||
tooltip: localize('clear', "Clear"),
|
||||
};
|
||||
this._input.buttons = [
|
||||
clearButton,
|
||||
{
|
||||
iconClass: ThemeIcon.asClassName(Codicon.commentDiscussion),
|
||||
tooltip: localize('openInChat', "Open In Chat View"),
|
||||
}
|
||||
];
|
||||
this._input.title = providerInfo.displayName;
|
||||
|
||||
disposableStore.add(this._input.onDidHide(() => {
|
||||
disposableStore.dispose();
|
||||
this._input = undefined;
|
||||
this._currentTimer = setTimeout(() => {
|
||||
this._currentChat?.dispose();
|
||||
this._currentChat = undefined;
|
||||
}, 1000 * 30); // 30 seconds
|
||||
}));
|
||||
|
||||
disposableStore.add(this._input.onDidTriggerButton((e) => {
|
||||
if (e === clearButton) {
|
||||
this._currentChat?.clear();
|
||||
} else {
|
||||
this._currentChat?.openChatView();
|
||||
}
|
||||
}));
|
||||
|
||||
//#endregion
|
||||
|
||||
this._currentChat.focus();
|
||||
|
||||
if (query) {
|
||||
this._currentChat.setValue(query);
|
||||
this._currentChat.acceptInput();
|
||||
}
|
||||
override run(accessor: ServicesAccessor, query?: string): void {
|
||||
const quickChatService = accessor.get(IQuickChatService);
|
||||
quickChatService.toggle(id, query);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class QuickChat extends Disposable {
|
||||
private widget!: ChatWidget;
|
||||
private model: ChatModel | undefined;
|
||||
private _currentQuery: string | undefined;
|
||||
|
||||
private _scopedContextKeyService!: IScopedContextKeyService;
|
||||
get scopedContextKeyService() {
|
||||
return this._scopedContextKeyService;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _options: IChatViewOptions,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IChatWidgetService private readonly _chatWidgetService: IChatWidgetService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.model?.dispose();
|
||||
this.model = undefined;
|
||||
this.updateModel();
|
||||
this.widget.inputEditor.setValue('');
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.widget) {
|
||||
this.widget.focusInput();
|
||||
}
|
||||
}
|
||||
|
||||
render(parent: HTMLElement): void {
|
||||
this._scopedContextKeyService?.dispose();
|
||||
this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent));
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]));
|
||||
this.widget?.dispose();
|
||||
this.widget = this._register(
|
||||
scopedInstantiationService.createInstance(
|
||||
ChatWidget,
|
||||
{ resource: true, renderInputOnTop: true, renderStyle: 'compact' },
|
||||
{
|
||||
listForeground: editorForeground,
|
||||
listBackground: editorBackground,
|
||||
inputEditorBackground: inputBackground,
|
||||
resultEditorBackground: editorBackground
|
||||
}));
|
||||
this.widget.render(parent);
|
||||
this.widget.setVisible(true);
|
||||
this.widget.setDynamicChatTreeItemLayout(2, 600);
|
||||
this.updateModel();
|
||||
if (this._currentQuery) {
|
||||
this.widget.inputEditor.setSelection({
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: this._currentQuery.length + 1
|
||||
});
|
||||
}
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.widget.inputEditor.onDidChangeModelContent((e) => {
|
||||
this._currentQuery = this.widget.inputEditor.getValue();
|
||||
}));
|
||||
this._register(this.widget.onDidClear(() => this.clear()));
|
||||
}
|
||||
|
||||
async acceptInput(): Promise<void> {
|
||||
return this.widget.acceptInput();
|
||||
}
|
||||
|
||||
async openChatView(): Promise<void> {
|
||||
const widget = await this._chatWidgetService.revealViewForProvider(this._options.providerId);
|
||||
if (!widget?.viewModel || !this.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const request of this.model.getRequests()) {
|
||||
if (request.response?.response.value || request.response?.errorDetails) {
|
||||
this.chatService.addCompleteRequest(widget.viewModel.sessionId,
|
||||
request.message as string,
|
||||
{
|
||||
message: request.response.response.asString(),
|
||||
errorDetails: request.response.errorDetails
|
||||
});
|
||||
} else if (request.message) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const value = this.widget.inputEditor.getValue();
|
||||
if (value) {
|
||||
widget.inputEditor.setValue(value);
|
||||
}
|
||||
widget.focusInput();
|
||||
}
|
||||
|
||||
setValue(value: string): void {
|
||||
this.widget.inputEditor.setValue(value);
|
||||
}
|
||||
|
||||
private updateModel(): void {
|
||||
this.model ??= this.chatService.startSession(this._options.providerId, CancellationToken.None);
|
||||
if (!this.model) {
|
||||
throw new Error('Could not start chat session');
|
||||
}
|
||||
|
||||
this.widget.setModel(this.model, { inputValue: this._currentQuery });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { registerChatExecuteActions } from 'vs/workbench/contrib/chat/browser/ac
|
||||
import { registerQuickChatActions } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions';
|
||||
import { registerChatTitleActions } from 'vs/workbench/contrib/chat/browser/actions/chatTitleActions';
|
||||
import { registerChatExportActions } from 'vs/workbench/contrib/chat/browser/actions/chatImportExport';
|
||||
import { IChatAccessibilityService, IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatAccessibilityService, IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { ChatContributionService } from 'vs/workbench/contrib/chat/browser/chatContributionServiceImpl';
|
||||
import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor';
|
||||
import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput';
|
||||
@@ -52,6 +52,7 @@ import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables';
|
||||
import { registerChatFileTreeActions } from 'vs/workbench/contrib/chat/browser/actions/chatFileTreeActions';
|
||||
import { QuickChatService } from 'vs/workbench/contrib/chat/browser/chatQuick';
|
||||
|
||||
// Register configuration
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
@@ -85,18 +86,6 @@ configurationRegistry.registerConfiguration({
|
||||
type: 'number',
|
||||
description: nls.localize('interactiveSession.editor.lineHeight', "Controls the line height in pixels in chat codeblocks. Use 0 to compute the line height from the font size."),
|
||||
default: 0
|
||||
},
|
||||
'chat.experimental.defaultMode': {
|
||||
type: 'string',
|
||||
tags: ['experimental'],
|
||||
enum: ['chatView', 'quickQuestion', 'both'],
|
||||
enumDescriptions: [
|
||||
nls.localize('interactiveSession.defaultMode.chatView', "Use the chat view as the default mode. Displays the chat icon in the Activity Bar."),
|
||||
nls.localize('interactiveSession.defaultMode.quickQuestion', "Use the quick question as the default mode. Displays the chat icon in the Title Bar."),
|
||||
nls.localize('interactiveSession.defaultMode.both', "Displays the chat icon in the Activity Bar and the Title Bar which open their respective chat modes.")
|
||||
],
|
||||
description: nls.localize('interactiveSession.defaultMode', "Controls the default mode of the chat experience."),
|
||||
default: 'chatView'
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -258,6 +247,7 @@ registerClearActions();
|
||||
registerSingleton(IChatService, ChatService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatContributionService, ChatContributionService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatWidgetService, ChatWidgetService, InstantiationType.Delayed);
|
||||
registerSingleton(IQuickChatService, QuickChatService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatAccessibilityService, ChatAccessibilityService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatWidgetHistoryService, ChatWidgetHistoryService, InstantiationType.Delayed);
|
||||
registerSingleton(IChatProviderService, ChatProviderService, InstantiationType.Delayed);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export const IChatWidgetService = createDecorator<IChatWidgetService>('chatWidgetService');
|
||||
export const IQuickChatService = createDecorator<IQuickChatService>('quickChatService');
|
||||
export const IChatAccessibilityService = createDecorator<IChatAccessibilityService>('chatAccessibilityService');
|
||||
|
||||
export interface IChatWidgetService {
|
||||
@@ -32,6 +33,14 @@ export interface IChatWidgetService {
|
||||
getWidgetBySessionId(sessionId: string): IChatWidget | undefined;
|
||||
}
|
||||
|
||||
export interface IQuickChatService {
|
||||
readonly _serviceBrand: undefined;
|
||||
enabled: boolean;
|
||||
toggle(providerId?: string, query?: string): void;
|
||||
focus(): void;
|
||||
close(): void;
|
||||
openInChatView(): void;
|
||||
}
|
||||
|
||||
export interface IChatAccessibilityService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
@@ -126,10 +126,7 @@ export class ChatContributionService implements IChatContributionService {
|
||||
canToggleVisibility: false,
|
||||
canMoveView: true,
|
||||
ctorDescriptor: new SyncDescriptor(ChatViewPane, [<IChatViewOptions>{ providerId: providerDescriptor.id }]),
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.deserialize(providerDescriptor.when),
|
||||
ContextKeyExpr.notEquals('config.chat.experimental.defaultMode', 'quickQuestion')
|
||||
)
|
||||
when: ContextKeyExpr.deserialize(providerDescriptor.when)
|
||||
}];
|
||||
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews(viewDescriptor, viewContainer);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups';
|
||||
import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions';
|
||||
import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys';
|
||||
import { IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel';
|
||||
import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer';
|
||||
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
|
||||
@@ -103,7 +103,6 @@ export interface IChatListItemRendererOptions {
|
||||
}
|
||||
|
||||
export class ChatListItemRenderer extends Disposable implements ITreeRenderer<ChatTreeItem, FuzzyScore, IChatListItemTemplate> {
|
||||
static readonly cursorCharacter = '\u258c';
|
||||
static readonly ID = 'item';
|
||||
|
||||
private readonly codeBlocksByResponseId = new Map<string, IChatCodeBlockInfo[]>();
|
||||
@@ -399,29 +398,92 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
this.basicRenderElement(element.response.value, element, index, templateData);
|
||||
isFullyRendered = true;
|
||||
} else {
|
||||
const renderValue = this.getWordsForProgressiveRender(element);
|
||||
isFullyRendered = !!element.renderData?.isFullyRendered;
|
||||
// Figure out what we need to render in addition to what has already been rendered
|
||||
const currentResponseData = element.response.value;
|
||||
element.renderData ??= { renderedParts: [] };
|
||||
const renderedParts = element.renderData.renderedParts;
|
||||
const wordCountResults: IWordCountResult[] = [];
|
||||
const partsToRender: IChatResponseRenderData['renderedParts'] = [];
|
||||
|
||||
currentResponseData.forEach((part, index) => {
|
||||
const renderedPart = renderedParts[index];
|
||||
// Is this part completely new?
|
||||
if (!renderedPart) {
|
||||
if (isInteractiveProgressTreeData(part)) {
|
||||
partsToRender[index] = part;
|
||||
} else {
|
||||
const wordCountResult = this.getDataForProgressiveRender(element, part);
|
||||
if (wordCountResult !== undefined) {
|
||||
partsToRender[index] = {
|
||||
renderedWordCount: wordCountResult.actualWordCount,
|
||||
lastRenderTime: Date.now(),
|
||||
isFullyRendered: wordCountResult.isFullString,
|
||||
};
|
||||
wordCountResults[index] = wordCountResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Did this part go from being a placeholder string to resolved tree data?
|
||||
else if (isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart)) {
|
||||
partsToRender[index] = part;
|
||||
}
|
||||
|
||||
// Did this part's content change?
|
||||
else if (!isInteractiveProgressTreeData(part) && !isInteractiveProgressTreeData(renderedPart) && !renderedPart.isFullyRendered) {
|
||||
const wordCountResult = this.getDataForProgressiveRender(element, part, renderedPart);
|
||||
if (wordCountResult !== undefined) {
|
||||
partsToRender[index] = {
|
||||
renderedWordCount: wordCountResult.actualWordCount,
|
||||
lastRenderTime: Date.now(),
|
||||
isFullyRendered: wordCountResult.isFullString,
|
||||
};
|
||||
wordCountResults[index] = wordCountResult;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
isFullyRendered = partsToRender.length === 0;
|
||||
|
||||
if (isFullyRendered && element.isComplete) {
|
||||
// Response is done and content is rendered, so do a normal render
|
||||
this.traceLayout('runProgressiveRender', `end progressive render, index=${index} and clearing renderData, response is complete, index=${index}`);
|
||||
element.renderData = undefined;
|
||||
disposables.clear();
|
||||
this.basicRenderElement(element.response.value, element, index, templateData);
|
||||
} else if (renderValue) {
|
||||
element.renderData = {
|
||||
renderedWordCount: renderValue.actualWordCount,
|
||||
lastRenderTime: Date.now(),
|
||||
isFullyRendered: renderValue.isFullString
|
||||
};
|
||||
} else if (!isFullyRendered) {
|
||||
let hasRenderedOneMarkdownBlock = false;
|
||||
partsToRender.forEach((partToRender, index) => {
|
||||
if (!partToRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
const plusCursor = (renderValue.value.match(/```\s*$/) ?
|
||||
renderValue.value + '\n\n' :
|
||||
renderValue.value) + ` ${ChatListItemRenderer.cursorCharacter}`;
|
||||
const result = this.renderMarkdown(new MarkdownString(plusCursor), element, disposables, templateData, true);
|
||||
// Doing the progressive render
|
||||
dom.clearNode(templateData.value);
|
||||
templateData.value.appendChild(result.element);
|
||||
disposables.add(result);
|
||||
let result;
|
||||
if (isInteractiveProgressTreeData(partToRender)) {
|
||||
result = this.renderTreeData(partToRender, element, disposables, templateData, index);
|
||||
}
|
||||
|
||||
// Avoid doing progressive rendering for multiple markdown parts simultaneously
|
||||
else if (!hasRenderedOneMarkdownBlock) {
|
||||
const value = wordCountResults[index].value;
|
||||
result = this.renderMarkdown(new MarkdownString(value), element, disposables, templateData, true);
|
||||
hasRenderedOneMarkdownBlock = true;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Doing the progressive render
|
||||
renderedParts[index] = partToRender;
|
||||
const existingElement = templateData.value.children[index];
|
||||
if (existingElement) {
|
||||
templateData.value.replaceChild(result.element, existingElement);
|
||||
} else {
|
||||
templateData.value.appendChild(result.element);
|
||||
}
|
||||
disposables.add(result);
|
||||
});
|
||||
} else {
|
||||
// Nothing new to render, not done, keep waiting
|
||||
return false;
|
||||
@@ -435,7 +497,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
this._onDidChangeItemHeight.fire({ element, height: templateData.rowContainer.offsetHeight });
|
||||
}
|
||||
|
||||
return !!isFullyRendered;
|
||||
return isFullyRendered;
|
||||
}
|
||||
|
||||
private renderTreeData(data: IChatResponseProgressFileTreeData, element: ChatTreeItem, disposables: DisposableStore, templateData: IChatListItemTemplate, treeDataIndex: number): { element: HTMLElement; dispose: () => void } {
|
||||
@@ -558,8 +620,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
return ref;
|
||||
}
|
||||
|
||||
private getWordsForProgressiveRender(element: IChatResponseViewModel): IWordCountResult | undefined {
|
||||
const renderData = element.renderData ?? { renderedWordCount: 0, lastRenderTime: 0 };
|
||||
private getDataForProgressiveRender(element: IChatResponseViewModel, data: IMarkdownString, renderData: IChatResponseMarkdownRenderData = { renderedWordCount: 0, lastRenderTime: 0, isFullyRendered: false }): IWordCountResult | undefined {
|
||||
const rate = this.getProgressiveRenderRate(element);
|
||||
const numWordsToRender = renderData.lastRenderTime === 0 ?
|
||||
1 :
|
||||
@@ -571,7 +632,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getNWords(element.response.asString(), numWordsToRender);
|
||||
return getNWords(data.value, numWordsToRender);
|
||||
}
|
||||
|
||||
disposeElement(node: ITreeNode<ChatTreeItem, FuzzyScore>, index: number, templateData: IChatListItemTemplate): void {
|
||||
@@ -886,28 +947,16 @@ class CodeBlockPart extends Disposable implements IChatResultCodeBlockPart {
|
||||
}
|
||||
|
||||
private setText(newText: string): void {
|
||||
let currentText = this.textModel.getLinesContent().join('\n');
|
||||
const currentText = this.textModel.getLinesContent().join('\n');
|
||||
if (newText === currentText) {
|
||||
return;
|
||||
}
|
||||
|
||||
let removedChars = 0;
|
||||
if (currentText.endsWith(` ${ChatListItemRenderer.cursorCharacter}`)) {
|
||||
removedChars = 2;
|
||||
} else if (currentText.endsWith(ChatListItemRenderer.cursorCharacter)) {
|
||||
removedChars = 1;
|
||||
}
|
||||
|
||||
if (removedChars > 0) {
|
||||
currentText = currentText.slice(0, currentText.length - removedChars);
|
||||
}
|
||||
|
||||
if (newText.startsWith(currentText)) {
|
||||
const text = newText.slice(currentText.length);
|
||||
const lastLine = this.textModel.getLineCount();
|
||||
const lastCol = this.textModel.getLineMaxColumn(lastLine);
|
||||
const insertAtCol = lastCol - removedChars;
|
||||
this.textModel.applyEdits([{ range: new Range(lastLine, insertAtCol, lastLine, lastCol), text }]);
|
||||
this.textModel.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]);
|
||||
} else {
|
||||
// console.log(`Failed to optimize setText`);
|
||||
this.textModel.setValue(newText);
|
||||
@@ -1133,3 +1182,7 @@ class ChatListTreeDataSource implements IAsyncDataSource<IChatResponseProgressFi
|
||||
return element.children ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
function isInteractiveProgressTreeData(item: IChatResponseProgressFileTreeData | IChatResponseMarkdownRenderData | IMarkdownString): item is IChatResponseProgressFileTreeData {
|
||||
return 'label' in item;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
import { IChatViewOptions } from 'vs/workbench/contrib/chat/browser/chatViewPane';
|
||||
import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget';
|
||||
import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChatService } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
|
||||
export class QuickChatService implements IQuickChatService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
_input: IQuickWidget | undefined;
|
||||
_currentChat: QuickChat | undefined;
|
||||
|
||||
constructor(
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) { }
|
||||
|
||||
get enabled(): boolean {
|
||||
return this.chatService.getProviderInfos().length > 0;
|
||||
}
|
||||
|
||||
get focused(): boolean {
|
||||
const widget = this._input?.widget as HTMLElement;
|
||||
if (!widget) {
|
||||
return false;
|
||||
}
|
||||
return dom.isAncestor(document.activeElement, widget);
|
||||
}
|
||||
|
||||
toggle(providerId?: string, query?: string | undefined): void {
|
||||
// If the input is already shown, hide it. This provides a toggle behavior of the quick pick
|
||||
if (this.focused) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any providers are available. If not, show nothing
|
||||
// This shouldn't be needed because of the precondition, but just in case
|
||||
const providerInfo = providerId
|
||||
? this.chatService.getProviderInfos().find(info => info.id === providerId)
|
||||
: this.chatService.getProviderInfos()[0];
|
||||
if (!providerInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disposableStore = new DisposableStore();
|
||||
|
||||
this._input = this.quickInputService.createQuickWidget();
|
||||
this._input.contextKey = 'chatInputVisible';
|
||||
this._input.ignoreFocusOut = true;
|
||||
disposableStore.add(this._input);
|
||||
|
||||
const containerSession = dom.$('.interactive-session');
|
||||
this._input.widget = containerSession;
|
||||
|
||||
this._currentChat ??= this.instantiationService.createInstance(QuickChat, {
|
||||
providerId: providerInfo.id,
|
||||
});
|
||||
|
||||
// show needs to come before the current chat rendering
|
||||
this._input.show();
|
||||
this._currentChat.render(containerSession);
|
||||
|
||||
disposableStore.add(this._input.onDidHide(() => {
|
||||
disposableStore.dispose();
|
||||
this._input = undefined;
|
||||
}));
|
||||
|
||||
this._currentChat.focus();
|
||||
|
||||
if (query) {
|
||||
this._currentChat.setValue(query);
|
||||
this._currentChat.acceptInput();
|
||||
}
|
||||
}
|
||||
focus(): void {
|
||||
this._currentChat?.focus();
|
||||
}
|
||||
close(): void {
|
||||
this._input?.dispose();
|
||||
}
|
||||
async openInChatView(): Promise<void> {
|
||||
await this._currentChat?.openChatView();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
class QuickChat extends Disposable {
|
||||
private widget!: ChatWidget;
|
||||
private model: ChatModel | undefined;
|
||||
private _currentQuery: string | undefined;
|
||||
|
||||
private _scopedContextKeyService!: IScopedContextKeyService;
|
||||
get scopedContextKeyService() {
|
||||
return this._scopedContextKeyService;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _options: IChatViewOptions,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IChatService private readonly chatService: IChatService,
|
||||
@IChatWidgetService private readonly _chatWidgetService: IChatWidgetService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.model?.dispose();
|
||||
this.model = undefined;
|
||||
this.updateModel();
|
||||
this.widget.inputEditor.setValue('');
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
if (this.widget) {
|
||||
this.widget.focusInput();
|
||||
}
|
||||
}
|
||||
|
||||
render(parent: HTMLElement): void {
|
||||
this._scopedContextKeyService?.dispose();
|
||||
this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent));
|
||||
const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]));
|
||||
this.widget?.dispose();
|
||||
this.widget = this._register(
|
||||
scopedInstantiationService.createInstance(
|
||||
ChatWidget,
|
||||
{ resource: true, renderInputOnTop: true, renderStyle: 'compact' },
|
||||
{
|
||||
listForeground: editorForeground,
|
||||
listBackground: editorBackground,
|
||||
inputEditorBackground: inputBackground,
|
||||
resultEditorBackground: editorBackground
|
||||
}));
|
||||
this.widget.render(parent);
|
||||
this.widget.setVisible(true);
|
||||
this.widget.setDynamicChatTreeItemLayout(2, 600);
|
||||
this.updateModel();
|
||||
if (this._currentQuery) {
|
||||
this.widget.inputEditor.setSelection({
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: this._currentQuery.length + 1
|
||||
});
|
||||
}
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.widget.inputEditor.onDidChangeModelContent((e) => {
|
||||
this._currentQuery = this.widget.inputEditor.getValue();
|
||||
}));
|
||||
this._register(this.widget.onDidClear(() => this.clear()));
|
||||
}
|
||||
|
||||
async acceptInput(): Promise<void> {
|
||||
return this.widget.acceptInput();
|
||||
}
|
||||
|
||||
async openChatView(): Promise<void> {
|
||||
const widget = await this._chatWidgetService.revealViewForProvider(this._options.providerId);
|
||||
if (!widget?.viewModel || !this.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const request of this.model.getRequests()) {
|
||||
if (request.response?.response.value || request.response?.errorDetails) {
|
||||
this.chatService.addCompleteRequest(widget.viewModel.sessionId,
|
||||
request.message as string,
|
||||
{
|
||||
message: request.response.response.asString(),
|
||||
errorDetails: request.response.errorDetails
|
||||
});
|
||||
} else if (request.message) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const value = this.widget.inputEditor.getValue();
|
||||
if (value) {
|
||||
widget.inputEditor.setValue(value);
|
||||
}
|
||||
widget.focusInput();
|
||||
}
|
||||
|
||||
setValue(value: string): void {
|
||||
this.widget.inputEditor.setValue(value);
|
||||
}
|
||||
|
||||
private updateModel(): void {
|
||||
this.model ??= this.chatService.startSession(this._options.providerId, CancellationToken.None);
|
||||
if (!this.model) {
|
||||
throw new Error('Could not start chat session');
|
||||
}
|
||||
|
||||
this.widget.setModel(this.model, { inputValue: this._currentQuery });
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IChatRequestModel, IChatResponseModel, IChatModel, IChatWelcomeMessageContent, IResponse, Response } from 'vs/workbench/contrib/chat/common/chatModel';
|
||||
import { IChatResponseErrorDetails, IChatReplyFollowup, IChatResponseCommandFollowup, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { IChatResponseErrorDetails, IChatReplyFollowup, IChatResponseCommandFollowup, InteractiveSessionVoteDirection, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService';
|
||||
import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter';
|
||||
|
||||
export function isRequestVM(item: unknown): item is IChatRequestViewModel {
|
||||
@@ -56,12 +56,16 @@ export interface IChatRequestViewModel {
|
||||
currentRenderedHeight: number | undefined;
|
||||
}
|
||||
|
||||
export interface IChatResponseRenderData {
|
||||
export interface IChatResponseMarkdownRenderData {
|
||||
renderedWordCount: number;
|
||||
lastRenderTime: number;
|
||||
isFullyRendered: boolean;
|
||||
}
|
||||
|
||||
export interface IChatResponseRenderData {
|
||||
renderedParts: (IChatResponseProgressFileTreeData | IChatResponseMarkdownRenderData)[];
|
||||
}
|
||||
|
||||
export interface IChatLiveUpdateData {
|
||||
wordCountAfterLastUpdate: number;
|
||||
loadingStartTime: number;
|
||||
@@ -327,8 +331,9 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
||||
const wordCount = countWords(_model.response.asString());
|
||||
const timeDiff = now - this._contentUpdateTimings!.loadingStartTime;
|
||||
const impliedWordLoadRate = wordCount / (timeDiff / 1000);
|
||||
const renderedWordCount = this.renderData?.renderedParts.reduce((acc, part) => acc += ('label' in part ? 0 : part.renderedWordCount), 0);
|
||||
if (!this.isComplete) {
|
||||
this.trace('onDidChange', `Update- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${this.renderData?.renderedWordCount} words are rendered.`);
|
||||
this.trace('onDidChange', `Update- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`);
|
||||
this._contentUpdateTimings = {
|
||||
loadingStartTime: this._contentUpdateTimings!.loadingStartTime,
|
||||
lastUpdateTime: now,
|
||||
@@ -336,7 +341,7 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
||||
impliedWordLoadRate
|
||||
};
|
||||
} else {
|
||||
this.trace(`onDidChange`, `Done- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${this.renderData?.renderedWordCount} words are rendered.`);
|
||||
this.trace(`onDidChange`, `Done- got ${wordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${renderedWordCount} words are rendered.`);
|
||||
}
|
||||
} else {
|
||||
this.logService.warn('ChatResponseViewModel#onDidChange: got model update but contentUpdateTimings is not initialized');
|
||||
@@ -344,10 +349,6 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi
|
||||
|
||||
// new data -> new id, new content to render
|
||||
this._modelChangeCount++;
|
||||
if (this.renderData) {
|
||||
this.renderData.isFullyRendered = false;
|
||||
this.renderData.lastRenderTime = Date.now();
|
||||
}
|
||||
|
||||
this._onDidChange.fire();
|
||||
}));
|
||||
|
||||
@@ -273,7 +273,15 @@ Registry.as<IQuickAccessRegistry>(QuickaccessExtensions.Quickaccess).registerQui
|
||||
contextKey: 'inFileSymbolsPicker',
|
||||
placeholder: localize('gotoSymbolQuickAccessPlaceholder', "Type the name of a symbol to go to."),
|
||||
helpEntries: [
|
||||
{ description: localize('gotoSymbolQuickAccess', "Go to Symbol in Editor"), prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX, commandId: GotoSymbolAction.ID },
|
||||
{ description: localize('gotoSymbolByCategoryQuickAccess', "Go to Symbol in Editor by Category"), prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY }
|
||||
{
|
||||
description: localize('gotoSymbolQuickAccess', "Go to Symbol in Editor"),
|
||||
prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX,
|
||||
commandId: GotoSymbolAction.ID,
|
||||
commandCenterOrder: 40
|
||||
},
|
||||
{
|
||||
description: localize('gotoSymbolByCategoryQuickAccess', "Go to Symbol in Editor by Category"),
|
||||
prefix: AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -77,7 +77,11 @@ Registry.as<IQuickAccessRegistry>(QuickAccessExtensions.Quickaccess).registerQui
|
||||
prefix: DEBUG_QUICK_ACCESS_PREFIX,
|
||||
contextKey: 'inLaunchConfigurationsPicker',
|
||||
placeholder: nls.localize('startDebugPlaceholder', "Type the name of a launch configuration to run."),
|
||||
helpEntries: [{ description: nls.localize('startDebuggingHelp', "Start Debugging"), commandId: SELECT_AND_START_ID }]
|
||||
helpEntries: [{
|
||||
description: nls.localize('startDebuggingHelp', "Start Debugging"),
|
||||
commandId: SELECT_AND_START_ID,
|
||||
commandCenterOrder: 50
|
||||
}]
|
||||
});
|
||||
|
||||
// Register quick access for debug console
|
||||
|
||||
@@ -206,14 +206,17 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
||||
} else if (shouldAutoResumeOnReload) {
|
||||
// The application has previously launched via a protocol URL Continue On flow
|
||||
const hasApplicationLaunchedFromContinueOnFlow = this.storageService.getBoolean(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION, false);
|
||||
this.logService.info(`Prompting to enable cloud changes, has application previously launched from Continue On flow: ${hasApplicationLaunchedFromContinueOnFlow}`);
|
||||
|
||||
const handlePendingEditSessions = () => {
|
||||
// display a badge in the accounts menu but do not prompt the user to sign in again
|
||||
this.logService.info('Showing badge to enable cloud changes in accounts menu...');
|
||||
this.updateAccountsMenuBadge();
|
||||
this.pendingEditSessionsContext.set(true);
|
||||
// attempt a resume if we are in a pending state and the user just signed in
|
||||
const disposable = this.editSessionsStorageService.onDidSignIn(async () => {
|
||||
disposable.dispose();
|
||||
this.logService.info('Showing badge to enable cloud changes in accounts menu succeeded, resuming cloud changes...');
|
||||
await this.progressService.withProgress(resumeProgressOptions, async (progress) => await this.resumeEditSession(undefined, true, undefined, undefined, progress));
|
||||
this.storageService.remove(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION);
|
||||
this.environmentService.continueOn = undefined;
|
||||
@@ -227,8 +230,10 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
||||
) {
|
||||
// store the fact that we prompted the user
|
||||
this.storageService.store(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
|
||||
this.logService.info('Prompting to enable cloud changes...');
|
||||
await this.editSessionsStorageService.initialize('read');
|
||||
if (this.editSessionsStorageService.isSignedIn) {
|
||||
this.logService.info('Prompting to enable cloud changes succeeded, resuming cloud changes...');
|
||||
await this.progressService.withProgress(resumeProgressOptions, async (progress) => await this.resumeEditSession(undefined, true, undefined, undefined, progress));
|
||||
} else {
|
||||
handlePendingEditSessions();
|
||||
@@ -239,6 +244,8 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
||||
) {
|
||||
handlePendingEditSessions();
|
||||
}
|
||||
} else {
|
||||
this.logService.debug('Auto resuming cloud changes disabled.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,42 +5,59 @@
|
||||
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { localize } from 'vs/nls';
|
||||
import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { INotebookOutputActionContext, NotebookAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
import { NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
|
||||
export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copyToClipboard';
|
||||
|
||||
registerAction2(class CopyCellOutputAction extends NotebookAction {
|
||||
registerAction2(class CopyCellOutputAction extends Action2 {
|
||||
constructor() {
|
||||
super(
|
||||
{
|
||||
id: COPY_OUTPUT_COMMAND_ID,
|
||||
title: localize('notebookActions.copyOutput', "Copy Output to Clipboard"),
|
||||
menu: {
|
||||
id: MenuId.NotebookOutputToolbar,
|
||||
when: NOTEBOOK_CELL_HAS_OUTPUTS
|
||||
},
|
||||
icon: icons.copyIcon,
|
||||
});
|
||||
super({
|
||||
id: COPY_OUTPUT_COMMAND_ID,
|
||||
title: localize('notebookActions.copyOutput', "Copy Output to Clipboard"),
|
||||
menu: {
|
||||
id: MenuId.NotebookOutputToolbar,
|
||||
when: NOTEBOOK_CELL_HAS_OUTPUTS
|
||||
},
|
||||
category: NOTEBOOK_ACTIONS_CATEGORY,
|
||||
icon: icons.copyIcon,
|
||||
});
|
||||
}
|
||||
|
||||
async runWithContext(accessor: ServicesAccessor, context: INotebookOutputActionContext): Promise<void> {
|
||||
const outputViewModel = context.outputViewModel;
|
||||
async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel }): Promise<void> {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorService.activeEditorPane);
|
||||
|
||||
if (!notebookEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let outputViewModel: ICellOutputViewModel | undefined;
|
||||
if ('outputId' in outputContext && typeof outputContext.outputId === 'string') {
|
||||
outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor);
|
||||
} else {
|
||||
outputViewModel = outputContext.outputViewModel;
|
||||
}
|
||||
|
||||
if (!outputViewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mimeType = outputViewModel.pickedMimeType?.mimeType;
|
||||
|
||||
if (mimeType?.startsWith('image/')) {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const editor = editorService.activeEditorPane?.getControl() as INotebookEditor;
|
||||
await editor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', { skipReveal: true, outputId: outputViewModel.model.outputId });
|
||||
editor.copyOutputImage(outputViewModel);
|
||||
const focusOptions = { skipReveal: true, outputId: outputViewModel.model.outputId };
|
||||
await notebookEditor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', focusOptions);
|
||||
notebookEditor.copyOutputImage(outputViewModel);
|
||||
} else {
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
const logService = accessor.get(ILogService);
|
||||
@@ -48,4 +65,20 @@ registerAction2(class CopyCellOutputAction extends NotebookAction {
|
||||
copyCellOutput(mimeType, outputViewModel, clipboardService, logService);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEditor): ICellOutputViewModel | undefined {
|
||||
const notebookViewModel = notebookEditor.getViewModel();
|
||||
if (notebookViewModel) {
|
||||
const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[];
|
||||
for (const cell of codeCells) {
|
||||
const output = cell.outputsViewModels.find(output => output.model.outputId === outputId);
|
||||
if (output) {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1074,10 +1074,12 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Themable {
|
||||
allowScripts: true,
|
||||
localResourceRoots: this.localResourceRootsCache,
|
||||
},
|
||||
extension: undefined
|
||||
extension: undefined,
|
||||
providedViewType: 'notebook.output'
|
||||
});
|
||||
|
||||
webview.setHtml(content);
|
||||
webview.setContextKeyService(this.contextKeyService);
|
||||
return webview;
|
||||
}
|
||||
|
||||
|
||||
@@ -1362,33 +1362,38 @@ async function webviewPreloads(ctx: PreloadContext) {
|
||||
});
|
||||
};
|
||||
|
||||
const copyImage = async (image: HTMLImageElement, retries = 5) => {
|
||||
const copyOutputImage = async (outputId: string, retries = 5) => {
|
||||
if (!document.hasFocus() && retries > 0) {
|
||||
// copyImage can be called from outside of the webview, which means this function may be running whilst the webview is gaining focus.
|
||||
// Since navigator.clipboard.write requires the document to be focused, we need to wait for focus.
|
||||
// We cannot use a listener, as there is a high chance the focus is gained during the setup of the listener resulting in us missing it.
|
||||
setTimeout(() => { copyImage(image, retries - 1); }, 20);
|
||||
setTimeout(() => { copyOutputImage(outputId, retries - 1); }, 20);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await navigator.clipboard.write([new ClipboardItem({
|
||||
'image/png': new Promise((resolve) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
if (canvas !== null) {
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
const context = canvas.getContext('2d');
|
||||
context?.drawImage(image, 0, 0);
|
||||
}
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
const image = document.getElementById(outputId)?.querySelector('img');
|
||||
if (image) {
|
||||
await navigator.clipboard.write([new ClipboardItem({
|
||||
'image/png': new Promise((resolve) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
if (canvas !== null) {
|
||||
canvas.width = image.naturalWidth;
|
||||
canvas.height = image.naturalHeight;
|
||||
const context = canvas.getContext('2d');
|
||||
context?.drawImage(image, 0, 0);
|
||||
}
|
||||
canvas.remove();
|
||||
}, 'image/png');
|
||||
})
|
||||
})]);
|
||||
canvas.toBlob((blob) => {
|
||||
if (blob) {
|
||||
resolve(blob);
|
||||
}
|
||||
canvas.remove();
|
||||
}, 'image/png');
|
||||
})
|
||||
})]);
|
||||
} else {
|
||||
console.error('Could not find image element to copy for output with id', outputId);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Could not copy image:', e);
|
||||
}
|
||||
@@ -1495,12 +1500,8 @@ async function webviewPreloads(ctx: PreloadContext) {
|
||||
break;
|
||||
}
|
||||
case 'copyImage': {
|
||||
const image = document.getElementById(event.data.outputId)?.querySelector('img');
|
||||
if (image) {
|
||||
await copyImage(image);
|
||||
} else {
|
||||
console.warn('Could not find image element to copy for output with id', event.data.outputId);
|
||||
}
|
||||
|
||||
await copyOutputImage(event.data.outputId);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2561,6 +2562,7 @@ async function webviewPreloads(ctx: PreloadContext) {
|
||||
) {
|
||||
this.element = document.createElement('div');
|
||||
this.element.classList.add('output_container');
|
||||
this.element.setAttribute('data-vscode-context', JSON.stringify({ 'preventDefaultContextMenuItems': true }));
|
||||
this.element.style.position = 'absolute';
|
||||
this.element.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/co
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands';
|
||||
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
|
||||
|
||||
export const enum SettingsFocusContext {
|
||||
@@ -237,6 +238,7 @@ export class SettingsEditor2 extends EditorPane {
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
|
||||
@IEditorProgressService private readonly editorProgressService: IEditorProgressService,
|
||||
) {
|
||||
super(SettingsEditor2.ID, telemetryService, themeService, storageService);
|
||||
this.delayedFilterLogging = new Delayer<void>(1000);
|
||||
@@ -566,6 +568,9 @@ export class SettingsEditor2 extends EditorPane {
|
||||
this.searchWidget.updateAriaLabel(label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the header of the Settings editor, which includes the content above the splitview.
|
||||
*/
|
||||
private createHeader(parent: HTMLElement): void {
|
||||
this.headerContainer = DOM.append(parent, $('.settings-header'));
|
||||
|
||||
@@ -1622,19 +1627,20 @@ export class SettingsEditor2 extends EditorPane {
|
||||
|
||||
// Trigger the local search. If it didn't find an exact match, trigger the remote search.
|
||||
const searchInProgress = this.searchInProgress = new CancellationTokenSource();
|
||||
return this.localSearchDelayer.trigger(() => {
|
||||
return this.localSearchDelayer.trigger(async () => {
|
||||
if (searchInProgress && !searchInProgress.token.isCancellationRequested) {
|
||||
return this.localFilterPreferences(query).then(result => {
|
||||
if (result && !result.exactMatch) {
|
||||
this.remoteSearchThrottle.trigger(() => {
|
||||
return searchInProgress && !searchInProgress.token.isCancellationRequested ?
|
||||
this.remoteSearchPreferences(query, this.searchInProgress!.token) :
|
||||
Promise.resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
const progressRunner = this.editorProgressService.show(true);
|
||||
const result = await this.localFilterPreferences(query);
|
||||
if (result && !result.exactMatch) {
|
||||
this.remoteSearchThrottle.trigger(async () => {
|
||||
if (searchInProgress && !searchInProgress.token.isCancellationRequested) {
|
||||
await this.remoteSearchPreferences(query, this.searchInProgress!.token);
|
||||
}
|
||||
progressRunner.done();
|
||||
});
|
||||
} else {
|
||||
progressRunner.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1654,36 +1660,32 @@ export class SettingsEditor2 extends EditorPane {
|
||||
]).then(() => { });
|
||||
}
|
||||
|
||||
private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise<ISearchResult | null> {
|
||||
return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => {
|
||||
if (token && token.isCancellationRequested) {
|
||||
// Handle cancellation like this because cancellation is lost inside the search provider due to async/await
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.searchResultModel) {
|
||||
this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState, this.workspaceTrustManagementService.isWorkspaceTrusted());
|
||||
// Must be called before this.renderTree()
|
||||
// to make sure the search results count is set.
|
||||
this.searchResultModel.setResult(type, result);
|
||||
this.tocTreeModel.currentSearchModel = this.searchResultModel;
|
||||
this.onSearchModeToggled();
|
||||
} else {
|
||||
this.searchResultModel.setResult(type, result);
|
||||
this.tocTreeModel.update();
|
||||
}
|
||||
|
||||
if (type === SearchResultIdx.Local) {
|
||||
this.tocTree.setFocus([]);
|
||||
this.viewState.filterToCategory = undefined;
|
||||
this.tocTree.expandAll();
|
||||
}
|
||||
|
||||
this.settingsTree.scrollTop = 0;
|
||||
this.refreshTOCTree();
|
||||
this.renderTree(undefined, true);
|
||||
return result;
|
||||
});
|
||||
private async filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider?: ISearchProvider, token?: CancellationToken): Promise<ISearchResult | null> {
|
||||
const result = await this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token);
|
||||
if (token?.isCancellationRequested) {
|
||||
// Handle cancellation like this because cancellation is lost inside the search provider due to async/await
|
||||
return null;
|
||||
}
|
||||
if (!this.searchResultModel) {
|
||||
this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState, this.workspaceTrustManagementService.isWorkspaceTrusted());
|
||||
// Must be called before this.renderTree()
|
||||
// to make sure the search results count is set.
|
||||
this.searchResultModel.setResult(type, result);
|
||||
this.tocTreeModel.currentSearchModel = this.searchResultModel;
|
||||
this.onSearchModeToggled();
|
||||
} else {
|
||||
this.searchResultModel.setResult(type, result);
|
||||
this.tocTreeModel.update();
|
||||
}
|
||||
if (type === SearchResultIdx.Local) {
|
||||
this.tocTree.setFocus([]);
|
||||
this.viewState.filterToCategory = undefined;
|
||||
this.tocTree.expandAll();
|
||||
}
|
||||
this.settingsTree.scrollTop = 0;
|
||||
this.refreshTOCTree();
|
||||
this.renderTree(undefined, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
private renderResultCountMessages() {
|
||||
|
||||
@@ -208,18 +208,13 @@ export abstract class AbstractListSettingWidget<TDataItem extends object> extend
|
||||
}
|
||||
|
||||
const header = this.renderHeader();
|
||||
const ITEM_HEIGHT = 24;
|
||||
let listHeight = ITEM_HEIGHT * this.model.items.length;
|
||||
|
||||
if (header) {
|
||||
listHeight += ITEM_HEIGHT;
|
||||
this.listElement.appendChild(header);
|
||||
}
|
||||
|
||||
this.rowElements = this.model.items.map((item, i) => this.renderDataOrEditItem(item, i, focused));
|
||||
this.rowElements.forEach(rowElement => this.listElement.appendChild(rowElement));
|
||||
|
||||
this.listElement.style.height = listHeight + 'px';
|
||||
}
|
||||
|
||||
protected createBasicSelectBox(value: IObjectEnumData): SelectBox {
|
||||
|
||||
@@ -24,7 +24,11 @@ quickAccessRegistry.registerQuickAccessProvider({
|
||||
ctor: HelpQuickAccessProvider,
|
||||
prefix: HelpQuickAccessProvider.PREFIX,
|
||||
placeholder: localize('helpQuickAccessPlaceholder', "Type '{0}' to get help on the actions you can take from here.", HelpQuickAccessProvider.PREFIX),
|
||||
helpEntries: [{ description: localize('helpQuickAccess', "Show all Quick Access Providers") }]
|
||||
helpEntries: [{
|
||||
description: localize('helpQuickAccess', "Show all Quick Access Providers"),
|
||||
commandCenterOrder: 70,
|
||||
commandCenterLabel: localize('more', 'More')
|
||||
}]
|
||||
});
|
||||
|
||||
quickAccessRegistry.registerQuickAccessProvider({
|
||||
@@ -40,7 +44,7 @@ quickAccessRegistry.registerQuickAccessProvider({
|
||||
prefix: CommandsQuickAccessProvider.PREFIX,
|
||||
contextKey: 'inCommandsPicker',
|
||||
placeholder: localize('commandsQuickAccessPlaceholder', "Type the name of a command to run."),
|
||||
helpEntries: [{ description: localize('commandsQuickAccess', "Show and Run Commands"), commandId: ShowAllCommandsAction.ID }]
|
||||
helpEntries: [{ description: localize('commandsQuickAccess', "Show and Run Commands"), commandId: ShowAllCommandsAction.ID, commandCenterOrder: 20 }]
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -218,8 +218,8 @@ class PortColumn implements ITableColumn<ITunnelItem, ActionBarCell> {
|
||||
}
|
||||
|
||||
class LocalAddressColumn implements ITableColumn<ITunnelItem, ActionBarCell> {
|
||||
readonly label: string = nls.localize('tunnel.addressColumn.label', "Local Address");
|
||||
readonly tooltip: string = nls.localize('tunnel.addressColumn.tooltip', "The address that the forwarded port is available at locally.");
|
||||
readonly label: string = nls.localize('tunnel.addressColumn.label', "Forwarded Address");
|
||||
readonly tooltip: string = nls.localize('tunnel.addressColumn.tooltip', "The address that the forwarded port is available at.");
|
||||
readonly weight: number = 1;
|
||||
readonly templateId: string = 'actionbar';
|
||||
project(row: ITunnelItem): ActionBarCell {
|
||||
|
||||
@@ -40,7 +40,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { IFilesConfigurationService, AutoSaveMode } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess';
|
||||
import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue, Extensions, IQuickAccessRegistry } from 'vs/platform/quickinput/common/quickAccess';
|
||||
import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess';
|
||||
import { GotoSymbolQuickAccessProvider } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
@@ -52,11 +52,11 @@ import { Codicon } from 'vs/base/common/codicons';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { stripIcons } from 'vs/base/common/iconLabels';
|
||||
import { HelpQuickAccessProvider } from 'vs/platform/quickinput/browser/helpQuickAccess';
|
||||
import { CommandsQuickAccessProvider } from 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess';
|
||||
import { DEBUG_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/debug/browser/debugCommands';
|
||||
import { TasksQuickAccessProvider } from 'vs/workbench/contrib/tasks/browser/tasksQuickAccess';
|
||||
import { Lazy } from 'vs/base/common/lazy';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ASK_QUICK_QUESTION_ACTION_ID } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions';
|
||||
import { IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat';
|
||||
|
||||
interface IAnythingQuickPickItem extends IPickerQuickAccessItem, IQuickPickItemWithResource { }
|
||||
|
||||
@@ -187,6 +187,8 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
|
||||
@ITextModelService private readonly textModelService: ITextModelService,
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IQuickChatService private readonly quickChatService: IQuickChatService,
|
||||
) {
|
||||
super(AnythingQuickAccessProvider.PREFIX, {
|
||||
canAcceptInBackground: true,
|
||||
@@ -756,68 +758,54 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider<IAnyt
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Help (if enabled)
|
||||
//#region Command Center (if enabled)
|
||||
|
||||
private helpQuickAccess = this.instantiationService.createInstance(HelpQuickAccessProvider);
|
||||
private readonly lazyRegistry = new Lazy(() => Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess));
|
||||
|
||||
private getHelpPicks(query: IPreparedQuery, token: CancellationToken, runOptions?: AnythingQuickAccessProviderRunOptions): IAnythingQuickPickItem[] {
|
||||
if (query.normalized) {
|
||||
return []; // If there's a filter, we don't show the help
|
||||
}
|
||||
|
||||
type IHelpAnythingQuickPickItem = IAnythingQuickPickItem & { prefix: string };
|
||||
const providers: Array<IHelpAnythingQuickPickItem> = this.helpQuickAccess.getQuickAccessProviders();
|
||||
const mapOfProviders = new Map<string, IHelpAnythingQuickPickItem>();
|
||||
for (const provider of providers) {
|
||||
mapOfProviders.set(provider.prefix, provider);
|
||||
type IHelpAnythingQuickPickItem = IAnythingQuickPickItem & { commandCenterOrder: number };
|
||||
const providers: IHelpAnythingQuickPickItem[] = this.lazyRegistry.value.getQuickAccessProviders()
|
||||
.filter(p => p.helpEntries.some(h => h.commandCenterOrder !== undefined))
|
||||
.flatMap(provider => provider.helpEntries
|
||||
.filter(h => h.commandCenterOrder !== undefined)
|
||||
.map(helpEntry => {
|
||||
const providerSpecificOptions: AnythingQuickAccessProviderRunOptions | undefined = {
|
||||
...runOptions,
|
||||
includeHelp: provider.prefix === AnythingQuickAccessProvider.PREFIX ? false : runOptions?.includeHelp
|
||||
};
|
||||
|
||||
const label = helpEntry.commandCenterLabel ?? helpEntry.description!;
|
||||
return {
|
||||
label,
|
||||
description: helpEntry.prefix ?? provider.prefix,
|
||||
commandCenterOrder: helpEntry.commandCenterOrder!,
|
||||
keybinding: helpEntry.commandId ? this.keybindingService.lookupKeybinding(helpEntry.commandId) : undefined,
|
||||
ariaLabel: localize('helpPickAriaLabel', "{0}, {1}", label, helpEntry.description),
|
||||
accept: () => {
|
||||
this.quickInputService.quickAccess.show(provider.prefix, {
|
||||
preserveValue: true,
|
||||
providerOptions: providerSpecificOptions
|
||||
});
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
// TODO: There has to be a better place for this, but it's the first time we are adding a non-quick access provider
|
||||
// to the command center, so for now, let's do this.
|
||||
if (this.quickChatService.enabled) {
|
||||
providers.push({
|
||||
label: localize('chat', "Open Quick Chat"),
|
||||
commandCenterOrder: 30,
|
||||
keybinding: this.keybindingService.lookupKeybinding(ASK_QUICK_QUESTION_ACTION_ID),
|
||||
accept: () => this.quickChatService.toggle()
|
||||
});
|
||||
}
|
||||
|
||||
const importantProviders: Array<IHelpAnythingQuickPickItem> = [];
|
||||
const AddProvider = (prefix: string, modifications: Partial<IAnythingQuickPickItem> = {}) => {
|
||||
if (mapOfProviders.has(prefix)) {
|
||||
const provider = mapOfProviders.get(prefix)!;
|
||||
|
||||
// We swap the label and description in this to emphasize the ability
|
||||
// not the prefix.
|
||||
provider.label = provider.description!;
|
||||
provider.description = provider.prefix;
|
||||
|
||||
// If the user chooses 'Go to File' the help should go away as if they were
|
||||
// entering a new mode
|
||||
const providerSpecificOptions: AnythingQuickAccessProviderRunOptions | undefined = {
|
||||
...runOptions,
|
||||
includeHelp: provider.prefix === AnythingQuickAccessProvider.PREFIX ? false : runOptions?.includeHelp
|
||||
};
|
||||
|
||||
importantProviders.push({
|
||||
...mapOfProviders.get(prefix)!,
|
||||
...modifications,
|
||||
accept: () => {
|
||||
this.quickInputService.quickAccess.show(provider.prefix, {
|
||||
preserveValue: true,
|
||||
providerOptions: providerSpecificOptions
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// TODO@TylerLeonhardt ideally this hardcoded list and hardcoded dependency moves
|
||||
// into a provider model where when I register a quick access provider I can enlist
|
||||
// for showing up in command center
|
||||
|
||||
// Acts as the ordering too
|
||||
AddProvider(AnythingQuickAccessProvider.PREFIX);
|
||||
AddProvider(CommandsQuickAccessProvider.PREFIX);
|
||||
AddProvider(GotoSymbolQuickAccessProvider.PREFIX);
|
||||
AddProvider(DEBUG_QUICK_ACCESS_PREFIX);
|
||||
AddProvider(TasksQuickAccessProvider.PREFIX);
|
||||
AddProvider(HelpQuickAccessProvider.PREFIX, {
|
||||
// More concise
|
||||
label: localize('more', 'More')
|
||||
});
|
||||
|
||||
return importantProviders;
|
||||
return providers.sort((a, b) => a.commandCenterOrder - b.commandCenterOrder);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -107,7 +107,11 @@ quickAccessRegistry.registerQuickAccessProvider({
|
||||
prefix: AnythingQuickAccessProvider.PREFIX,
|
||||
placeholder: nls.localize('anythingQuickAccessPlaceholder', "Search files by name (append {0} to go to line or {1} to go to symbol)", AbstractGotoLineQuickAccessProvider.PREFIX, GotoSymbolQuickAccessProvider.PREFIX),
|
||||
contextKey: defaultQuickAccessContextKeyValue,
|
||||
helpEntries: [{ description: nls.localize('anythingQuickAccess', "Go to File"), commandId: 'workbench.action.quickOpen' }]
|
||||
helpEntries: [{
|
||||
description: nls.localize('anythingQuickAccess', "Go to File"),
|
||||
commandId: 'workbench.action.quickOpen',
|
||||
commandCenterOrder: 10
|
||||
}]
|
||||
});
|
||||
|
||||
quickAccessRegistry.registerQuickAccessProvider({
|
||||
|
||||
@@ -408,7 +408,7 @@ quickAccessRegistry.registerQuickAccessProvider({
|
||||
prefix: TasksQuickAccessProvider.PREFIX,
|
||||
contextKey: tasksPickerContextKey,
|
||||
placeholder: nls.localize('tasksQuickAccessPlaceholder', "Type the name of a task to run."),
|
||||
helpEntries: [{ description: nls.localize('tasksQuickAccessHelp', "Run Task") }]
|
||||
helpEntries: [{ description: nls.localize('tasksQuickAccessHelp', "Run Task"), commandCenterOrder: 60 }]
|
||||
});
|
||||
|
||||
// tasks.json validation
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Delayer } from 'vs/base/common/async';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { MicrotaskDelay } from 'vs/base/common/symbols';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable';
|
||||
import { ITerminalBackend } from 'vs/platform/terminal/common/terminal';
|
||||
import { IDetachedTerminalInstance, IDetachedXTermOptions, IDetachedXtermTerminal, ITerminalContribution, IXtermAttachToElementOptions } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalExtensionsRegistry } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { ITerminalProcessInfo, ProcessState } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export class DeatachedTerminal extends Disposable implements IDetachedTerminalInstance {
|
||||
private readonly _widgets = this._register(new TerminalWidgetManager());
|
||||
public readonly capabilities = new TerminalCapabilityStore();
|
||||
private readonly _contributions: Map<string, ITerminalContribution> = new Map();
|
||||
|
||||
public get xterm(): IDetachedXtermTerminal {
|
||||
return this._xterm;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _xterm: XtermTerminal,
|
||||
options: IDetachedXTermOptions,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
this._register(_xterm);
|
||||
|
||||
// Initialize contributions
|
||||
const contributionDescs = TerminalExtensionsRegistry.getTerminalContributions();
|
||||
for (const desc of contributionDescs) {
|
||||
if (this._contributions.has(desc.id)) {
|
||||
onUnexpectedError(new Error(`Cannot have two terminal contributions with the same id ${desc.id}`));
|
||||
continue;
|
||||
}
|
||||
if (desc.canRunInDetachedTerminals === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let contribution: ITerminalContribution;
|
||||
try {
|
||||
contribution = instantiationService.createInstance(desc.ctor, this, options.processInfo, this._widgets);
|
||||
this._contributions.set(desc.id, contribution);
|
||||
this._register(contribution);
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// xterm is already by the time DetachedTerminal is created, so trigger everything
|
||||
// on the next microtask, allowing the caller to do any extra initialization
|
||||
this._register(new Delayer(MicrotaskDelay)).trigger(() => {
|
||||
for (const contr of this._contributions.values()) {
|
||||
contr.xtermReady?.(this._xterm);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
attachToElement(container: HTMLElement, options?: Partial<IXtermAttachToElementOptions> | undefined): void {
|
||||
const screenElement = this._xterm.attachToElement(container, options);
|
||||
this._widgets.attachToElement(screenElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@link ITerminalProcessInfo} for a detached terminal where most
|
||||
* properties are stubbed. Properties are mutable and can be updated by
|
||||
* the instantiator.
|
||||
*/
|
||||
export class DetachedProcessInfo implements ITerminalProcessInfo {
|
||||
processState = ProcessState.Running;
|
||||
ptyProcessReady = Promise.resolve();
|
||||
shellProcessId: number | undefined;
|
||||
remoteAuthority: string | undefined;
|
||||
os: OperatingSystem | undefined;
|
||||
userHome: string | undefined;
|
||||
initialCwd = '';
|
||||
environmentVariableInfo: IEnvironmentVariableInfo | undefined;
|
||||
persistentProcessId: number | undefined;
|
||||
shouldPersist = false;
|
||||
hasWrittenData = false;
|
||||
hasChildProcesses = false;
|
||||
backend: ITerminalBackend | undefined;
|
||||
capabilities = new TerminalCapabilityStore();
|
||||
shellIntegrationNonce = '';
|
||||
extEnvironmentVariableCollection: IMergedEnvironmentVariableCollection | undefined;
|
||||
|
||||
constructor(initialValues: Partial<ITerminalProcessInfo>) {
|
||||
Object.assign(this, initialValues);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import { IEditableData } from 'vs/workbench/common/views';
|
||||
import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList';
|
||||
import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon';
|
||||
import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn';
|
||||
import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget';
|
||||
import type { IMarker, Terminal as RawXtermTerminal } from 'xterm';
|
||||
@@ -146,8 +146,32 @@ export interface IDetachedXTermOptions {
|
||||
colorProvider: IXtermColorProvider;
|
||||
capabilities?: ITerminalCapabilityStore;
|
||||
readonly?: boolean;
|
||||
processInfo: ITerminalProcessInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link ITerminalInstance}-like object that emulates a subset of
|
||||
* capabilities. This instance is returned from {@link ITerminalService.createDetachedTerminal}
|
||||
* to represent terminals that appear in other parts of the VS Code UI outside
|
||||
* of the "Terminal" view or editors.
|
||||
*/
|
||||
export interface IDetachedTerminalInstance extends IDisposable {
|
||||
readonly xterm: IDetachedXtermTerminal;
|
||||
readonly capabilities: ITerminalCapabilityStore;
|
||||
|
||||
/**
|
||||
* Attached the terminal to the given element. This should be preferred over
|
||||
* calling {@link IXtermTerminal.attachToElement} so that extra DOM elements
|
||||
* for contributions are initialized.
|
||||
*
|
||||
* @param container Container the terminal will be rendered in
|
||||
* @param options Additional options for mounting the terminal in an element
|
||||
*/
|
||||
attachToElement(container: HTMLElement, options?: Partial<IXtermAttachToElementOptions>): void;
|
||||
}
|
||||
|
||||
export const isDetachedTerminalInstance = (t: ITerminalInstance | IDetachedTerminalInstance): t is IDetachedTerminalInstance => typeof (t as ITerminalInstance).instanceId === 'number';
|
||||
|
||||
export interface ITerminalService extends ITerminalInstanceHost {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
@@ -192,7 +216,7 @@ export interface ITerminalService extends ITerminalInstanceHost {
|
||||
* tracked as a terminal instance.
|
||||
* @params options The options to create the terminal with
|
||||
*/
|
||||
createDetachedXterm(options: IDetachedXTermOptions): Promise<IDetachedXtermTerminal>;
|
||||
createDetachedTerminal(options: IDetachedXTermOptions): Promise<IDetachedTerminalInstance>;
|
||||
|
||||
/**
|
||||
* Creates a raw terminal instance, this should not be used outside of the terminal part.
|
||||
@@ -1107,7 +1131,6 @@ export interface IXtermTerminal extends IDisposable {
|
||||
}
|
||||
|
||||
export interface IDetachedXtermTerminal extends IXtermTerminal {
|
||||
|
||||
/**
|
||||
* Writes data to the terminal.
|
||||
* @param data data to write
|
||||
|
||||
@@ -5,19 +5,24 @@
|
||||
|
||||
import { BrandedService, IConstructorSignature } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITerminalContribution, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
/** Constructor compatible with full terminal instances, is assignable to {@link DetachedCompatibleTerminalContributionCtor} */
|
||||
export type TerminalContributionCtor = IConstructorSignature<ITerminalContribution, [ITerminalInstance, ITerminalProcessManager, TerminalWidgetManager]>;
|
||||
/** Constructor compatible with detached terminals */
|
||||
export type DetachedCompatibleTerminalContributionCtor = IConstructorSignature<ITerminalContribution, [IDetachedTerminalInstance, ITerminalProcessInfo, TerminalWidgetManager]>;
|
||||
|
||||
export interface ITerminalContributionDescription {
|
||||
readonly id: string;
|
||||
readonly ctor: TerminalContributionCtor;
|
||||
}
|
||||
export type ITerminalContributionDescription = { readonly id: string } & (
|
||||
| { readonly canRunInDetachedTerminals: false; readonly ctor: TerminalContributionCtor }
|
||||
| { readonly canRunInDetachedTerminals: true; readonly ctor: DetachedCompatibleTerminalContributionCtor }
|
||||
);
|
||||
|
||||
export function registerTerminalContribution<Services extends BrandedService[]>(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }): void {
|
||||
TerminalContributionRegistry.INSTANCE.registerTerminalContribution(id, ctor);
|
||||
export function registerTerminalContribution<Services extends BrandedService[]>(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals?: false): void;
|
||||
export function registerTerminalContribution<Services extends BrandedService[]>(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessInfo, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals: true): void;
|
||||
export function registerTerminalContribution<Services extends BrandedService[]>(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals = false): void {
|
||||
TerminalContributionRegistry.INSTANCE.registerTerminalContribution({ id, ctor, canRunInDetachedTerminals } as ITerminalContributionDescription);
|
||||
}
|
||||
|
||||
export namespace TerminalExtensionsRegistry {
|
||||
@@ -35,8 +40,8 @@ class TerminalContributionRegistry {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public registerTerminalContribution<Services extends BrandedService[]>(id: string, ctor: { new(instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, ...services: Services): ITerminalContribution }): void {
|
||||
this._terminalContributions.push({ id, ctor: ctor as TerminalContributionCtor });
|
||||
public registerTerminalContribution(description: ITerminalContributionDescription): void {
|
||||
this._terminalContributions.push(description);
|
||||
}
|
||||
|
||||
public getTerminalContributions(): ITerminalContributionDescription[] {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys';
|
||||
import { IEditableData, IViewsService } from 'vs/workbench/common/views';
|
||||
import { ICreateTerminalOptions, IDetachedXTermOptions, IDetachedXtermTerminal, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, IXtermTerminal, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { ICreateTerminalOptions, IDetachedTerminalInstance, IDetachedXTermOptions, IRequestAddInstanceToGroupEvent, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceHost, ITerminalInstanceService, ITerminalLocationOptions, ITerminalService, ITerminalServiceNativeDelegate, IXtermTerminal, TerminalConnectionState, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { getCwdForSplit } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
|
||||
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
|
||||
@@ -52,6 +52,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
import { DeatachedTerminal } from 'vs/workbench/contrib/terminal/browser/detachedTerminal';
|
||||
|
||||
export class TerminalService extends Disposable implements ITerminalService {
|
||||
declare _serviceBrand: undefined;
|
||||
@@ -1018,9 +1019,9 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
return this._createTerminal(shellLaunchConfig, location, options);
|
||||
}
|
||||
|
||||
async createDetachedXterm(options: IDetachedXTermOptions): Promise<IDetachedXtermTerminal> {
|
||||
async createDetachedTerminal(options: IDetachedXTermOptions): Promise<IDetachedTerminalInstance> {
|
||||
const ctor = await TerminalInstance.getXtermConstructor(this._keybindingService, this._contextKeyService);
|
||||
const instance = this._instantiationService.createInstance(
|
||||
const xterm = this._instantiationService.createInstance(
|
||||
XtermTerminal,
|
||||
ctor,
|
||||
this._configHelper,
|
||||
@@ -1034,13 +1035,16 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
);
|
||||
|
||||
if (options.readonly) {
|
||||
instance.raw.attachCustomKeyEventHandler(() => false);
|
||||
xterm.raw.attachCustomKeyEventHandler(() => false);
|
||||
}
|
||||
|
||||
this._detachedXterms.add(instance);
|
||||
instance.onDidDispose(() => this._detachedXterms.delete(instance));
|
||||
this._detachedXterms.add(xterm);
|
||||
const l = xterm.onDidDispose(() => {
|
||||
this._detachedXterms.delete(xterm);
|
||||
l.dispose();
|
||||
});
|
||||
|
||||
return instance;
|
||||
return new DeatachedTerminal(xterm, options, this._instantiationService);
|
||||
}
|
||||
|
||||
private async _resolveCwd(shellLaunchConfig: IShellLaunchConfig, splitActiveTerminal: boolean, options?: ICreateTerminalOptions): Promise<void> {
|
||||
|
||||
@@ -253,7 +253,8 @@ export interface IDefaultShellAndArgsRequest {
|
||||
callback: (shell: string, args: string[] | string | undefined) => void;
|
||||
}
|
||||
|
||||
export interface ITerminalProcessManager extends IDisposable {
|
||||
/** Read-only process information that can apply to detached terminals. */
|
||||
export interface ITerminalProcessInfo {
|
||||
readonly processState: ProcessState;
|
||||
readonly ptyProcessReady: Promise<void>;
|
||||
readonly shellProcessId: number | undefined;
|
||||
@@ -270,7 +271,11 @@ export interface ITerminalProcessManager extends IDisposable {
|
||||
readonly capabilities: ITerminalCapabilityStore;
|
||||
readonly shellIntegrationNonce: string;
|
||||
readonly extEnvironmentVariableCollection: IMergedEnvironmentVariableCollection | undefined;
|
||||
}
|
||||
|
||||
export const isTerminalProcessManager = (t: ITerminalProcessInfo | ITerminalProcessManager): t is ITerminalProcessManager => typeof (t as ITerminalProcessManager).write === 'function';
|
||||
|
||||
export interface ITerminalProcessManager extends IDisposable, ITerminalProcessInfo {
|
||||
readonly onPtyDisconnect: Event<void>;
|
||||
readonly onPtyReconnect: Event<void>;
|
||||
|
||||
|
||||
+18
-12
@@ -10,11 +10,11 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IXtermTerminal, isDetachedTerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
|
||||
import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions';
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId, isTerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey';
|
||||
import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings';
|
||||
import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links';
|
||||
@@ -40,8 +40,8 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr
|
||||
private _linkResolver: TerminalLinkResolver;
|
||||
|
||||
constructor(
|
||||
private readonly _instance: ITerminalInstance,
|
||||
private readonly _processManager: ITerminalProcessManager,
|
||||
private readonly _instance: ITerminalInstance | IDetachedTerminalInstance,
|
||||
private readonly _processManager: ITerminalProcessManager | ITerminalProcessInfo,
|
||||
private readonly _widgetManager: TerminalWidgetManager,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ITerminalLinkProviderService private readonly _terminalLinkProviderService: ITerminalLinkProviderService
|
||||
@@ -52,18 +52,24 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr
|
||||
|
||||
xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void {
|
||||
const linkManager = this._instantiationService.createInstance(TerminalLinkManager, xterm.raw, this._processManager, this._instance.capabilities, this._linkResolver);
|
||||
this._processManager.onProcessReady(() => {
|
||||
if (isTerminalProcessManager(this._processManager)) {
|
||||
this._processManager.onProcessReady(() => {
|
||||
linkManager.setWidgetManager(this._widgetManager);
|
||||
});
|
||||
} else {
|
||||
linkManager.setWidgetManager(this._widgetManager);
|
||||
});
|
||||
}
|
||||
this._linkManager = this.add(linkManager);
|
||||
|
||||
// Attach the link provider(s) to the instance and listen for changes
|
||||
for (const linkProvider of this._terminalLinkProviderService.linkProviders) {
|
||||
this._linkManager.registerExternalLinkProvider(linkProvider.provideLinks.bind(linkProvider, this._instance));
|
||||
if (!isDetachedTerminalInstance(this._instance)) {
|
||||
for (const linkProvider of this._terminalLinkProviderService.linkProviders) {
|
||||
this._linkManager.registerExternalLinkProvider(linkProvider.provideLinks.bind(linkProvider, this._instance));
|
||||
}
|
||||
this.add(this._terminalLinkProviderService.onDidAddLinkProvider(e => {
|
||||
linkManager.registerExternalLinkProvider(e.provideLinks.bind(e, this._instance as ITerminalInstance));
|
||||
}));
|
||||
}
|
||||
this.add(this._terminalLinkProviderService.onDidAddLinkProvider(e => {
|
||||
linkManager.registerExternalLinkProvider(e.provideLinks.bind(e, this._instance));
|
||||
}));
|
||||
// TODO: Currently only a single link provider is supported; the one registered by the ext host
|
||||
this.add(this._terminalLinkProviderService.onDidRemoveLinkProvider(e => {
|
||||
linkManager.dispose();
|
||||
@@ -97,7 +103,7 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr
|
||||
}
|
||||
}
|
||||
|
||||
registerTerminalContribution(TerminalLinkContribution.ID, TerminalLinkContribution);
|
||||
registerTerminalContribution(TerminalLinkContribution.ID, TerminalLinkContribution, true);
|
||||
|
||||
const category = terminalStrings.actionCategory;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ILinkHoverTargetOptions, TerminalHover } from 'vs/workbench/contrib/ter
|
||||
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager';
|
||||
import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
|
||||
import { ITerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ITerminalConfiguration, ITerminalProcessInfo, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IHoverAction } from 'vs/workbench/services/hover/browser/hover';
|
||||
import type { ILink, ILinkProvider, IViewportRange, Terminal } from 'xterm';
|
||||
import { convertBufferRangeToViewport } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers';
|
||||
@@ -47,7 +47,7 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
|
||||
constructor(
|
||||
private readonly _xterm: Terminal,
|
||||
private readonly _processManager: ITerminalProcessManager,
|
||||
private readonly _processInfo: ITerminalProcessInfo,
|
||||
capabilities: ITerminalCapabilityStore,
|
||||
private readonly _linkResolver: ITerminalLinkResolver,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@@ -65,15 +65,15 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
enableFileLinks = false;
|
||||
break;
|
||||
case 'notRemote':
|
||||
enableFileLinks = !this._processManager.remoteAuthority;
|
||||
enableFileLinks = !this._processInfo.remoteAuthority;
|
||||
break;
|
||||
}
|
||||
|
||||
// Setup link detectors in their order of priority
|
||||
this._setupLinkDetector(TerminalUriLinkDetector.id, this._instantiationService.createInstance(TerminalUriLinkDetector, this._xterm, this._processManager, this._linkResolver));
|
||||
this._setupLinkDetector(TerminalUriLinkDetector.id, this._instantiationService.createInstance(TerminalUriLinkDetector, this._xterm, this._processInfo, this._linkResolver));
|
||||
if (enableFileLinks) {
|
||||
this._setupLinkDetector(TerminalMultiLineLinkDetector.id, this._instantiationService.createInstance(TerminalMultiLineLinkDetector, this._xterm, this._processManager, this._linkResolver));
|
||||
this._setupLinkDetector(TerminalLocalLinkDetector.id, this._instantiationService.createInstance(TerminalLocalLinkDetector, this._xterm, capabilities, this._processManager, this._linkResolver));
|
||||
this._setupLinkDetector(TerminalMultiLineLinkDetector.id, this._instantiationService.createInstance(TerminalMultiLineLinkDetector, this._xterm, this._processInfo, this._linkResolver));
|
||||
this._setupLinkDetector(TerminalLocalLinkDetector.id, this._instantiationService.createInstance(TerminalLocalLinkDetector, this._xterm, capabilities, this._processInfo, this._linkResolver));
|
||||
}
|
||||
this._setupLinkDetector(TerminalWordLinkDetector.id, this.add(this._instantiationService.createInstance(TerminalWordLinkDetector, this._xterm)));
|
||||
|
||||
@@ -83,8 +83,8 @@ export class TerminalLinkManager extends DisposableStore {
|
||||
this._openers.set(TerminalBuiltinLinkType.LocalFile, localFileOpener);
|
||||
this._openers.set(TerminalBuiltinLinkType.LocalFolderInWorkspace, localFolderInWorkspaceOpener);
|
||||
this._openers.set(TerminalBuiltinLinkType.LocalFolderOutsideWorkspace, this._instantiationService.createInstance(TerminalLocalFolderOutsideWorkspaceLinkOpener));
|
||||
this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processManager.initialCwd, localFileOpener, localFolderInWorkspaceOpener, () => this._processManager.os || OS));
|
||||
this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processManager.remoteAuthority));
|
||||
this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processInfo.initialCwd, localFileOpener, localFolderInWorkspaceOpener, () => this._processInfo.os || OS));
|
||||
this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processInfo.remoteAuthority));
|
||||
|
||||
this._registerStandardLinkProviders();
|
||||
|
||||
|
||||
@@ -74,7 +74,8 @@ import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/vie
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IDetachedXtermTerminal, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal';
|
||||
import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal';
|
||||
import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
|
||||
import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display';
|
||||
@@ -1352,7 +1353,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
private readonly xtermLayoutDelayer = this._register(new Delayer(50));
|
||||
|
||||
/** Active terminal instance. */
|
||||
private readonly terminal = this._register(new MutableDisposable<IDetachedXtermTerminal>());
|
||||
private readonly terminal = this._register(new MutableDisposable<IDetachedTerminalInstance>());
|
||||
/** Listener for streaming result data */
|
||||
private readonly outputDataListener = this._register(new MutableDisposable());
|
||||
|
||||
@@ -1369,11 +1370,11 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
private async makeTerminal() {
|
||||
const prev = this.terminal.value;
|
||||
if (prev) {
|
||||
prev.clearBuffer();
|
||||
prev.clearSearchDecorations();
|
||||
prev.xterm.clearBuffer();
|
||||
prev.xterm.clearSearchDecorations();
|
||||
// clearBuffer tries to retain the prompt line, but this doesn't exist for tests.
|
||||
// So clear the screen (J) and move to home (H) to ensure previous data is cleaned up.
|
||||
prev.write(`\x1b[2J\x1b[0;0H`);
|
||||
prev.xterm.write(`\x1b[2J\x1b[0;0H`);
|
||||
return prev;
|
||||
}
|
||||
|
||||
@@ -1387,11 +1388,12 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
updateCwd: () => { },
|
||||
});
|
||||
|
||||
return this.terminal.value = await this.terminalService.createDetachedXterm({
|
||||
return this.terminal.value = await this.terminalService.createDetachedTerminal({
|
||||
rows: 10,
|
||||
cols: 80,
|
||||
readonly: true,
|
||||
capabilities,
|
||||
processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }),
|
||||
colorProvider: {
|
||||
getBackgroundColor: theme => {
|
||||
const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR);
|
||||
@@ -1440,17 +1442,17 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
}
|
||||
}
|
||||
},
|
||||
doListenForMoreData: (output, result, terminal) => result.onChange(e => {
|
||||
doListenForMoreData: (output, result, { xterm }) => result.onChange(e => {
|
||||
if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) {
|
||||
for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) {
|
||||
terminal.write(chunk.buffer);
|
||||
xterm.write(chunk.buffer);
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) {
|
||||
terminal?.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true);
|
||||
terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1464,7 +1466,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri);
|
||||
return task.output.buffers;
|
||||
},
|
||||
doListenForMoreData: (task, _result, terminal) => task.output.onDidWriteData(e => terminal.write(e.buffer)),
|
||||
doListenForMoreData: (task, _result, { xterm }) => task.output.onDidWriteData(e => xterm.write(e.buffer)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1472,7 +1474,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
subject: InspectSubject;
|
||||
getTarget: (result: ITestResult) => T | undefined;
|
||||
doInitialWrite: (target: T, result: LiveTestResult) => Iterable<VSBuffer>;
|
||||
doListenForMoreData: (target: T, result: LiveTestResult, terminal: IDetachedXtermTerminal) => IDisposable | undefined;
|
||||
doListenForMoreData: (target: T, result: LiveTestResult, terminal: IDetachedTerminalInstance) => IDisposable | undefined;
|
||||
}) {
|
||||
const result = opts.subject.result;
|
||||
const target = opts.getTarget(result);
|
||||
@@ -1488,7 +1490,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
for (const chunk of opts.doInitialWrite(target, result)) {
|
||||
didWriteData ||= chunk.byteLength > 0;
|
||||
pendingWrites.value++;
|
||||
terminal.write(chunk.buffer, () => pendingWrites.value--);
|
||||
terminal.xterm.write(chunk.buffer, () => pendingWrites.value--);
|
||||
}
|
||||
} else {
|
||||
this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.'));
|
||||
@@ -1525,12 +1527,12 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private writeNotice(terminal: IDetachedXtermTerminal, str: string) {
|
||||
terminal.write(`\x1b[2m${str}\x1b[0m`);
|
||||
private writeNotice(terminal: IDetachedTerminalInstance, str: string) {
|
||||
terminal.xterm.write(`\x1b[2m${str}\x1b[0m`);
|
||||
}
|
||||
|
||||
private attachTerminalToDom(terminal: IDetachedXtermTerminal) {
|
||||
terminal.write('\x1b[?25l'); // hide cursor
|
||||
private attachTerminalToDom(terminal: IDetachedTerminalInstance) {
|
||||
terminal.xterm.write('\x1b[?25l'); // hide cursor
|
||||
requestAnimationFrame(() => this.layoutTerminal(terminal));
|
||||
terminal.attachToElement(this.container, { enableGpu: false });
|
||||
}
|
||||
@@ -1549,7 +1551,7 @@ class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer {
|
||||
}
|
||||
|
||||
private layoutTerminal(
|
||||
xterm: IDetachedXtermTerminal,
|
||||
{ xterm }: IDetachedTerminalInstance,
|
||||
width = this.dimensions?.width ?? this.container.clientWidth,
|
||||
height = this.dimensions?.height ?? this.container.clientHeight
|
||||
) {
|
||||
|
||||
@@ -30,15 +30,18 @@ export class BrowserCredentialsService extends Disposable implements ICredential
|
||||
) {
|
||||
super();
|
||||
|
||||
if (environmentService.remoteAuthority && !environmentService.options?.credentialsProvider) {
|
||||
if (
|
||||
environmentService.remoteAuthority
|
||||
&& !environmentService.options?.credentialsProvider
|
||||
&& !environmentService.options?.secretStorageProvider
|
||||
) {
|
||||
// If we have a remote authority but the embedder didn't provide a credentialsProvider,
|
||||
// we can use the CredentialsService on the remote side
|
||||
const remoteCredentialsService = ProxyChannel.toService<ICredentialsService>(remoteAgentService.getConnection()!.getChannel('credentials'));
|
||||
this.credentialsProvider = remoteCredentialsService;
|
||||
this._secretStoragePrefix = remoteCredentialsService.getSecretStoragePrefix();
|
||||
} else {
|
||||
// fall back to InMemoryCredentialsProvider if none was given to us. This should really only be used
|
||||
// when running tests.
|
||||
// fall back to InMemoryCredentialsProvider if none was given to us.
|
||||
this.credentialsProvider = environmentService.options?.credentialsProvider ?? new InMemoryCredentialsProvider();
|
||||
this._secretStoragePrefix = Promise.resolve(this.productService.urlProtocol);
|
||||
}
|
||||
|
||||
@@ -62,11 +62,6 @@ declare module 'vscode' {
|
||||
*/
|
||||
commands?: Command[];
|
||||
|
||||
/**
|
||||
* When set, overrides the user setting of `editor.inlineSuggest.suppressSuggestions`.
|
||||
*/
|
||||
suppressSuggestions?: boolean;
|
||||
|
||||
/**
|
||||
* When set and the user types a suggestion without derivating from it, the inline suggestion is not updated.
|
||||
* Defaults to false (might change).
|
||||
|
||||
@@ -10784,20 +10784,20 @@ xterm-addon-unicode11@0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.5.0.tgz#41c0d96acc1e3bb6c6596eee64e163b6bca74be7"
|
||||
integrity sha512-Jm4/g4QiTxiKiTbYICQgC791ubhIZyoIwxAIgOW8z8HWFNY+lwk+dwaKEaEeGBfM48Vk8fklsUW9u/PlenYEBg==
|
||||
|
||||
xterm-addon-webgl@0.16.0-beta.14:
|
||||
version "0.16.0-beta.14"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.14.tgz#de5bf6c97e31f62d13907a875ffc6be154bc4a02"
|
||||
integrity sha512-cj+rQWqOeZYJ6JMjMuTTKu3OnP+1D+sqSZPuwZAd6s4NkY4dFXFQlKxJ2yvDHOU/jrgpIYR1c3Cnu0AOWZpS3w==
|
||||
xterm-addon-webgl@0.16.0-beta.16:
|
||||
version "0.16.0-beta.16"
|
||||
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.16.0-beta.16.tgz#2c31308f8c7f636576720adca529297f8fff3224"
|
||||
integrity sha512-k0ZSwpBVtxXaqUc29CmlfFgIK7LEYAmwcDC3QVp2ESZo2JHYuQrWFpImjbzbYu0ON6qqcfbX8SnbzRid/XfGUg==
|
||||
|
||||
xterm-headless@5.3.0-beta.42:
|
||||
version "5.3.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.42.tgz#714d167dc0af010067420956ffca80149dddeca9"
|
||||
integrity sha512-3xhyy9DaPpadd1iiuKjOn4Zvntnx8WSBMEXPJbrORmy3ZbIwmLf/HYvg7iZfVreSDH+UFlm06uq3ictXpv7YvA==
|
||||
xterm-headless@5.3.0-beta.46:
|
||||
version "5.3.0-beta.46"
|
||||
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.3.0-beta.46.tgz#3f941f673e3c61aad2705e4b34b42fbe467aed48"
|
||||
integrity sha512-e/VbZKrfyD1TTxOY5/jah3dUih6q8nTO5v5GQV7YOASp+yyI0XHKiDjLrCyFPQOLTKxCcRutMHvr+CoCcPpcHg==
|
||||
|
||||
xterm@5.3.0-beta.42:
|
||||
version "5.3.0-beta.42"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.42.tgz#f0806ebe9b033530c54ac5b03955827fb729c38f"
|
||||
integrity sha512-3OLXTfwtSy9UKo8Uzm3/kd7qBg0JD/qFpotg8iKJ2aB9zx0fkJnysl4aIuNleUJzgvuxzBUHkcRZUmEw1XCKdw==
|
||||
xterm@5.3.0-beta.46:
|
||||
version "5.3.0-beta.46"
|
||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.3.0-beta.46.tgz#9bd2b2a588c88ae64f1989ca3bf5bd9116554b5a"
|
||||
integrity sha512-keB3C6sXm56ug1+hylAQiNyZP4YtPgd2W+Gu/ZKwJMk5haD25DEnenBYL++Cl3+sNGYxOGS42QlmYj6Mrq1LPw==
|
||||
|
||||
y18n@^3.2.1:
|
||||
version "3.2.2"
|
||||
|
||||
Reference in New Issue
Block a user