diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 41c33f3f265..9a3748ed6fc 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -135,13 +135,22 @@ steps: - script: | set -e - AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ node build/azure-pipelines/upload-sourcemaps displayName: Upload sourcemaps to Azure + - script: | + set -e + AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + node build/azure-pipelines/upload-sourcemaps + displayName: Upload sourcemaps to Azure (Deprecated) + - script: ./build/azure-pipelines/common/extract-telemetry.sh displayName: Generate lists of telemetry events diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index bf43d9212cf..5e423d077c7 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -129,6 +129,15 @@ steps: node build/azure-pipelines/upload-cdn displayName: Upload to CDN + - script: | + set -e + AZURE_STORAGE_ACCOUNT="vscodeweb" \ + AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ + AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ + AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.main.js.map + displayName: Upload sourcemaps (Web) + # upload only the workbench.web.main.js source maps because # we just compiled these bits in the previous step and the # general task to upload source maps has already been run @@ -139,7 +148,7 @@ steps: AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.main.js.map - displayName: Upload sourcemaps (Web) + displayName: Upload sourcemaps (Deprecated) - script: | set -e diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 55904959778..b85425bccfc 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -71,7 +71,7 @@ const compilations = [ '.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json', ]; -const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`; +const getBaseUrl = out => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`; const tasks = compilations.map(function (tsconfigFile) { const absolutePath = path.join(root, tsconfigFile); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index c2b81d0cf7c..4ab7dcbc4b6 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -439,7 +439,7 @@ function tweakProductForServerWeb(product) { const minifyTask = task.define(`minify-vscode-${type}`, task.series( optimizeTask, util.rimraf(`out-vscode-${type}-min`), - optimize.minifyTask(`out-vscode-${type}`, `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`) + optimize.minifyTask(`out-vscode-${type}`, `https://main.vscode-cdn.net/sourcemaps/${commit}/core`) )); gulp.task(minifyTask); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3b1aeafd080..c6202c70883 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -133,7 +133,7 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( )); gulp.task(optimizeVSCodeTask); -const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; const minifyVSCodeTask = task.define('minify-vscode', task.series( optimizeVSCodeTask, util.rimraf('out-vscode-min'), diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 85129a523da..50c7e6fb631 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -175,7 +175,7 @@ const optimizeVSCodeWebTask = task.define('optimize-vscode-web', task.series( const minifyVSCodeWebTask = task.define('minify-vscode-web', task.series( optimizeVSCodeWebTask, util.rimraf('out-vscode-web-min'), - optimize.minifyTask('out-vscode-web', `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`) + optimize.minifyTask('out-vscode-web', `https://main.vscode-cdn.net/sourcemaps/${commit}/core`) )); gulp.task(minifyVSCodeWebTask); diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 6a6c0a7b4cd..58d4d3e9a7f 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -34,7 +34,7 @@ const getVersion_1 = require("./getVersion"); const fetch_1 = require("./fetch"); const root = path.dirname(path.dirname(__dirname)); const commit = (0, getVersion_1.getVersion)(root); -const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; function minifyExtensionResources(input) { const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); return input diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 6edfdcb63fb..0582e0cb11e 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -28,7 +28,7 @@ import { fetchUrls, fetchGithub } from './fetch'; const root = path.dirname(path.dirname(__dirname)); const commit = getVersion(root); -const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; function minifyExtensionResources(input: Stream): Stream { const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); diff --git a/extensions/git/package.json b/extensions/git/package.json index dfbb29289db..23b3220a00c 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3401,7 +3401,7 @@ "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", - "jschardet": "3.0.0", + "jschardet": "3.1.2", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 266157e9e5c..06023a61227 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -182,10 +182,10 @@ isexe@^3.1.1: resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== -jschardet@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" - integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +jschardet@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" + integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== peek-readable@^4.1.0: version "4.1.0" diff --git a/package.json b/package.json index 63471db823f..8d306cca712 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "graceful-fs": "4.2.11", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", - "jschardet": "3.0.0", + "jschardet": "3.1.2", "kerberos": "^2.0.1", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", diff --git a/remote/package.json b/remote/package.json index ad0f9efc07d..cded7ebea1a 100644 --- a/remote/package.json +++ b/remote/package.json @@ -25,7 +25,7 @@ "graceful-fs": "4.2.11", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", - "jschardet": "3.0.0", + "jschardet": "3.1.2", "kerberos": "^2.0.1", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", diff --git a/remote/web/package.json b/remote/web/package.json index 9dce97a17af..d920465ca2e 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -13,7 +13,7 @@ "@xterm/addon-unicode11": "0.9.0-beta.18", "@xterm/addon-webgl": "0.19.0-beta.18", "@xterm/xterm": "5.6.0-beta.18", - "jschardet": "3.0.0", + "jschardet": "3.1.2", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 8e908085a48..e77a58360ab 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -78,10 +78,10 @@ resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.18.tgz#c346d5773b1e253b2cef986b5c7f9bfe372e5c54" integrity sha512-PKpqGVlJ5e65RFxG9CgCR1Gdpz1eSttdRZZd5eEFJIyz3VQv1DUlZgfwlFhMMGRi42zyw40HuArkrw+fRRDU7Q== -jschardet@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" - integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +jschardet@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" + integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== tas-client-umd@0.2.0: version "0.2.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 4018559683c..dcf8b277eca 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -347,10 +347,10 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -jschardet@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" - integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +jschardet@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" + integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== jsonfile@^6.0.1: version "6.1.0" diff --git a/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts b/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts index ba8ea6446a6..a132d7d6eb4 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/electronTypes.ts @@ -172,3 +172,19 @@ export interface AuthInfo { port: number; realm: string; } + +export interface WebUtils { + + // Docs: https://electronjs.org/docs/api/web-utils + + /** + * The file system path that this `File` object points to. In the case where the + * object passed in is not a `File` object an exception is thrown. In the case + * where the File object passed in was constructed in JS and is not backed by a + * file on disk an empty string is returned. + * + * This method superceded the previous augmentation to the `File` object with the + * `path` property. An example is included below. + */ + getPathForFile(file: File): string; +} diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index 44a54904e26..10737a23101 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -5,7 +5,7 @@ import { INodeProcess, IProcessEnvironment } from 'vs/base/common/platform'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; -import { IpcRenderer, ProcessMemoryInfo, WebFrame } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes'; +import { IpcRenderer, ProcessMemoryInfo, WebFrame, WebUtils } from 'vs/base/parts/sandbox/electron-sandbox/electronTypes'; /** * In Electron renderers we cannot expose all of the `process` global of node.js @@ -121,6 +121,7 @@ export const ipcMessagePort: IpcMessagePort = vscodeGlobal.ipcMessagePort; export const webFrame: WebFrame = vscodeGlobal.webFrame; export const process: ISandboxNodeProcess = vscodeGlobal.process; export const context: ISandboxContext = vscodeGlobal.context; +export const webUtils: WebUtils = vscodeGlobal.webUtils; /** * A set of globals that are available in all windows that either diff --git a/src/vs/base/parts/sandbox/electron-sandbox/preload.js b/src/vs/base/parts/sandbox/electron-sandbox/preload.js index 90ac940861f..e2161616487 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/preload.js +++ b/src/vs/base/parts/sandbox/electron-sandbox/preload.js @@ -7,7 +7,7 @@ (function () { 'use strict'; - const { ipcRenderer, webFrame, contextBridge } = require('electron'); + const { ipcRenderer, webFrame, contextBridge, webUtils } = require('electron'); //#region Utilities @@ -237,6 +237,19 @@ } }, + /** + * Support for subset of Electron's `webUtils` type. + */ + webUtils: { + + /** + * @param {File} file + */ + getPathForFile(file) { + return webUtils.getPathForFile(file); + } + }, + /** * Support for a subset of access to node.js global `process`. * diff --git a/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts index 491f2209c6d..837d80457d6 100644 --- a/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts +++ b/src/vs/base/parts/sandbox/test/electron-sandbox/globals.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { context, ipcRenderer, process, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { context, ipcRenderer, process, webFrame, webUtils } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Sandbox', () => { @@ -13,6 +13,7 @@ suite('Sandbox', () => { assert.ok(typeof ipcRenderer.send === 'function'); assert.ok(typeof webFrame.setZoomLevel === 'function'); assert.ok(typeof process.platform === 'string'); + assert.ok(typeof webUtils.getPathForFile === 'function'); const config = await context.resolveConfiguration(); assert.ok(config); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index be4de9dc7c6..41354c47118 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3405,7 +3405,7 @@ class EditorQuickSuggestions extends BaseEditorOption { + this._disposables.add(this._tree.onDidOpen(e => { if (e.sideBySide) { onEvent(e.element, 'side'); } else if (e.editorOptions.pinned) { @@ -398,7 +398,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { } else { onEvent(e.element, 'show'); } - }); + })); dom.hide(this._treeContainer); } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index bdc0542b18e..6f5e09d5a48 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -51,7 +51,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib private readonly _sessionStore: DisposableStore = new DisposableStore(); private _widgetState: StickyScrollWidgetState; - private _foldingModel: FoldingModel | null = null; + private _foldingModel: FoldingModel | undefined; private _maxStickyLines: number = Number.MAX_SAFE_INTEGER; private _stickyRangeProjectedOnEditor: IRange | undefined; @@ -67,7 +67,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _positionRevealed = false; private _onMouseDown = false; private _endLineNumbers: number[] = []; - private _showEndForLine: number | null = null; + private _showEndForLine: number | undefined; constructor( private readonly _editor: ICodeEditor, @@ -84,7 +84,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._register(this._stickyScrollWidget); this._register(this._stickyLineCandidateProvider); - this._widgetState = new StickyScrollWidgetState([], [], 0); + this._widgetState = StickyScrollWidgetState.Empty; this._onDidResize(); this._readConfiguration(); const stickyScrollDomNode = this._stickyScrollWidget.getDomNode(); @@ -291,14 +291,14 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._renderStickyScroll(); return; } - if (this._showEndForLine !== null) { - this._showEndForLine = null; + if (this._showEndForLine !== undefined) { + this._showEndForLine = undefined; this._renderStickyScroll(); } })); this._register(dom.addDisposableListener(stickyScrollWidgetDomNode, dom.EventType.MOUSE_LEAVE, (e) => { - if (this._showEndForLine !== null) { - this._showEndForLine = null; + if (this._showEndForLine !== undefined) { + this._showEndForLine = undefined; this._renderStickyScroll(); } })); @@ -415,14 +415,14 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._editor.addOverlayWidget(this._stickyScrollWidget); this._sessionStore.add(this._editor.onDidScrollChange((e) => { if (e.scrollTopChanged) { - this._showEndForLine = null; + this._showEndForLine = undefined; this._renderStickyScroll(); } })); this._sessionStore.add(this._editor.onDidLayoutChange(() => this._onDidResize())); this._sessionStore.add(this._editor.onDidChangeModelTokens((e) => this._onTokensChange(e))); this._sessionStore.add(this._stickyLineCandidateProvider.onDidChangeStickyScroll(() => { - this._showEndForLine = null; + this._showEndForLine = undefined; this._renderStickyScroll(); })); this._enabled = true; @@ -431,7 +431,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); if (lineNumberOption.renderType === RenderLineNumbersType.Relative) { this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => { - this._showEndForLine = null; + this._showEndForLine = undefined; this._renderStickyScroll(0); })); } @@ -479,32 +479,28 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._maxStickyLines = Math.round(theoreticalLines * .25); } - private async _renderStickyScroll(rebuildFromLine?: number) { + private async _renderStickyScroll(rebuildFromLine?: number): Promise { const model = this._editor.getModel(); if (!model || model.isTooLargeForTokenization()) { - this._foldingModel = null; - this._stickyScrollWidget.setState(undefined, null); + this._resetState(); return; } - const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId(); - if (stickyLineVersion === undefined || stickyLineVersion === model.getVersionId()) { - this._foldingModel = await FoldingController.get(this._editor)?.getFoldingModel() ?? null; - this._widgetState = this.findScrollWidgetState(); - this._stickyScrollVisibleContextKey.set(!(this._widgetState.startLineNumbers.length === 0)); - + const stickyWidgetVersion = this._stickyLineCandidateProvider.getVersionId(); + const shouldUpdateState = stickyWidgetVersion === undefined || stickyWidgetVersion === model.getVersionId(); + if (shouldUpdateState) { if (!this._focused) { - this._stickyScrollWidget.setState(this._widgetState, this._foldingModel, rebuildFromLine); + await this._updateState(rebuildFromLine); } else { // Suppose that previously the sticky scroll widget had height 0, then if there are visible lines, set the last line as focused if (this._focusedStickyElementIndex === -1) { - this._stickyScrollWidget.setState(this._widgetState, this._foldingModel, rebuildFromLine); + await this._updateState(rebuildFromLine); this._focusedStickyElementIndex = this._stickyScrollWidget.lineNumberCount - 1; if (this._focusedStickyElementIndex !== -1) { this._stickyScrollWidget.focusLineWithIndex(this._focusedStickyElementIndex); } } else { const focusedStickyElementLineNumber = this._stickyScrollWidget.lineNumbers[this._focusedStickyElementIndex]; - this._stickyScrollWidget.setState(this._widgetState, this._foldingModel, rebuildFromLine); + await this._updateState(rebuildFromLine); // Suppose that after setting the state, there are no sticky lines, set the focused index to -1 if (this._stickyScrollWidget.lineNumberCount === 0) { this._focusedStickyElementIndex = -1; @@ -522,6 +518,20 @@ export class StickyScrollController extends Disposable implements IEditorContrib } } } + private async _updateState(rebuildFromLine?: number): Promise { + this._foldingModel = await FoldingController.get(this._editor)?.getFoldingModel() ?? undefined; + this._widgetState = this.findScrollWidgetState(); + const stickyWidgetHasLines = this._widgetState.startLineNumbers.length > 0; + this._stickyScrollVisibleContextKey.set(stickyWidgetHasLines); + this._stickyScrollWidget.setState(this._widgetState, this._foldingModel, rebuildFromLine); + } + + private async _resetState(): Promise { + this._foldingModel = undefined; + this._widgetState = StickyScrollWidgetState.Empty; + this._stickyScrollVisibleContextKey.set(false); + this._stickyScrollWidget.setState(undefined, undefined); + } findScrollWidgetState(): StickyScrollWidgetState { const lineHeight: number = this._editor.getOption(EditorOption.lineHeight); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index d0e8da4b17a..27d3d8e1a58 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -35,6 +35,10 @@ export class StickyScrollWidgetState { && equals(this.startLineNumbers, other.startLineNumbers) && equals(this.endLineNumbers, other.endLineNumbers); } + + static get Empty() { + return new StickyScrollWidgetState([], [], 0); + } } const _ttPolicy = createTrustedTypesPolicy('stickyScrollViewLayer', { createHTML: value => value }); @@ -126,7 +130,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return this._lineNumbers; } - setState(_state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, _rebuildFromLine?: number): void { + setState(_state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, _rebuildFromLine?: number): void { if (_rebuildFromLine === undefined && ((!this._previousState && !_state) || (this._previousState && this._previousState.equals(_state))) ) { @@ -205,7 +209,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } } - private async _renderRootNode(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, rebuildFromLine: number): Promise { + private async _renderRootNode(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, rebuildFromLine: number): Promise { this._clearStickyLinesFromLine(rebuildFromLine); if (!state) { return; @@ -258,7 +262,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { })); } - private _renderChildNode(index: number, line: number, foldingModel: FoldingModel | null, layoutInfo: EditorLayoutInfo): RenderedStickyLine | undefined { + private _renderChildNode(index: number, line: number, foldingModel: FoldingModel | undefined, layoutInfo: EditorLayoutInfo): RenderedStickyLine | undefined { const viewModel = this._editor._getViewModel(); if (!viewModel) { return; @@ -358,7 +362,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return stickyLine; } - private _renderFoldingIconForLine(foldingModel: FoldingModel | null, line: number): StickyFoldingIcon | undefined { + private _renderFoldingIconForLine(foldingModel: FoldingModel | undefined, line: number): StickyFoldingIcon | undefined { const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); if (!foldingModel || showFoldingControls === 'never') { return; diff --git a/src/vs/platform/progress/common/progress.ts b/src/vs/platform/progress/common/progress.ts index e1eb0a2f5f2..2a1c7349aae 100644 --- a/src/vs/platform/progress/common/progress.ts +++ b/src/vs/platform/progress/common/progress.ts @@ -65,7 +65,7 @@ export interface IProgressNotificationOptions extends IProgressOptions { readonly secondaryActions?: readonly IAction[]; readonly delay?: number; readonly priority?: NotificationPriority; - readonly type?: 'syncing' | 'loading'; + readonly type?: 'loading' | 'syncing'; } export interface IProgressDialogOptions extends IProgressOptions { @@ -77,7 +77,7 @@ export interface IProgressDialogOptions extends IProgressOptions { export interface IProgressWindowOptions extends IProgressOptions { readonly location: ProgressLocation.Window; readonly command?: string; - readonly type?: 'syncing' | 'loading'; + readonly type?: 'loading' | 'syncing'; } export interface IProgressCompositeOptions extends IProgressOptions { diff --git a/src/vs/platform/tunnel/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index 86b4da4b409..b1433f2e745 100644 --- a/src/vs/platform/tunnel/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -215,7 +215,7 @@ export class DisposableTunnel { } } -export abstract class AbstractTunnelService implements ITunnelService { +export abstract class AbstractTunnelService extends Disposable implements ITunnelService { declare readonly _serviceBrand: undefined; private _onTunnelOpened: Emitter = new Emitter(); @@ -234,7 +234,7 @@ export abstract class AbstractTunnelService implements ITunnelService { public constructor( @ILogService protected readonly logService: ILogService, @IConfigurationService protected readonly configurationService: IConfigurationService - ) { } + ) { super(); } get hasTunnelProvider(): boolean { return !!this._tunnelProvider; @@ -308,7 +308,8 @@ export abstract class AbstractTunnelService implements ITunnelService { return tunnels; } - async dispose(): Promise { + override async dispose(): Promise { + super.dispose(); for (const portMap of this._tunnels.values()) { for (const { value } of portMap.values()) { await value.then(tunnel => typeof tunnel !== 'string' ? tunnel?.dispose() : undefined); diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 348cd234e76..a54fc865a98 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -25,6 +25,7 @@ import { IModelService } from 'vs/editor/common/services/model'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { Schemas } from 'vs/base/common/network'; import { ITextModel } from 'vs/editor/common/model'; +import { observableValue } from 'vs/base/common/observable'; function getIconFromIconDto(iconDto?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon): URI | { light: URI; dark: URI } | ThemeIcon | undefined { if (iconDto === undefined) { @@ -224,7 +225,6 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { get inputBoxTextModel(): ITextModel { return this._inputBoxTextModel; } get contextValue(): string { return this._providerId; } - get commitTemplate(): string { return this.features.commitTemplate || ''; } get historyProvider(): ISCMHistoryProvider | undefined { return this._historyProvider; } get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; } get actionButton(): ISCMActionButtonDescriptor | undefined { return this.features.actionButton ?? undefined; } @@ -234,8 +234,8 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { private readonly _name: string | undefined; get name(): string { return this._name ?? this._label; } - private readonly _onDidChangeCommitTemplate = new Emitter(); - readonly onDidChangeCommitTemplate: Event = this._onDidChangeCommitTemplate.event; + private readonly _commitTemplate = observableValue(this, ''); + get commitTemplate() { return this._commitTemplate; } private readonly _onDidChangeStatusBarCommands = new Emitter(); get onDidChangeStatusBarCommands(): Event { return this._onDidChangeStatusBarCommands.event; } @@ -277,7 +277,7 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { this._onDidChange.fire(); if (typeof features.commitTemplate !== 'undefined') { - this._onDidChangeCommitTemplate.fire(this.commitTemplate); + this._commitTemplate.set(features.commitTemplate, undefined); } if (typeof features.statusBarCommands !== 'undefined') { diff --git a/src/vs/workbench/api/common/extHostSearch.ts b/src/vs/workbench/api/common/extHostSearch.ts index c2e0b93f7b9..f7da534aab8 100644 --- a/src/vs/workbench/api/common/extHostSearch.ts +++ b/src/vs/workbench/api/common/extHostSearch.ts @@ -25,7 +25,7 @@ export interface IExtHostSearch extends ExtHostSearchShape { export const IExtHostSearch = createDecorator('IExtHostSearch'); -export class ExtHostSearch implements ExtHostSearchShape { +export class ExtHostSearch implements IExtHostSearch { protected readonly _proxy: MainThreadSearchShape = this.extHostRpc.getProxy(MainContext.MainThreadSearch); protected _handlePool: number = 0; @@ -124,7 +124,7 @@ export class ExtHostSearch implements ExtHostSearchShape { $provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: vscode.CancellationToken): Promise { const provider = this._textSearchProvider.get(handle); if (!provider || !provider.provideTextSearchResults) { - throw new Error(`2 Unknown provider ${handle}`); + throw new Error(`Unknown Text Search Provider ${handle}`); } const query = reviveQuery(rawQuery); @@ -135,7 +135,7 @@ export class ExtHostSearch implements ExtHostSearchShape { $provideAITextSearchResults(handle: number, session: number, rawQuery: IRawAITextQuery, token: vscode.CancellationToken): Promise { const provider = this._aiTextSearchProvider.get(handle); if (!provider || !provider.provideAITextSearchResults) { - throw new Error(`1 Unknown provider ${handle}`); + throw new Error(`Unknown AI Text Search Provider ${handle}`); } const query = reviveQuery(rawQuery); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 4e2809e149c..4cc439c1fb7 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -14,7 +14,7 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation, shouldShowCustomTitleBar } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -208,11 +208,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private getContainerDimension(container: HTMLElement): IDimension { if (container === this.mainContainer) { - // main window - return this.mainContainerDimension; + return this.mainContainerDimension; // main window } else { - // auxiliary window - return getClientArea(container); + return getClientArea(container); // auxiliary window } } @@ -1533,28 +1531,27 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi })); } - this._register(this.storageService.onWillSaveState(willSaveState => { - if (willSaveState.reason === WillSaveStateReason.SHUTDOWN) { - // Side Bar Size - const sideBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN) - ? this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) - : this.workbenchGrid.getViewSize(this.sideBarPartView).width; - this.stateModel.setInitializationValue(LayoutStateKeys.SIDEBAR_SIZE, sideBarSize as number); + this._register(this.storageService.onWillSaveState(e => { - // Panel Size - const panelSize = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) - ? this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) - : (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.BOTTOM ? this.workbenchGrid.getViewSize(this.panelPartView).height : this.workbenchGrid.getViewSize(this.panelPartView).width); - this.stateModel.setInitializationValue(LayoutStateKeys.PANEL_SIZE, panelSize as number); + // Side Bar Size + const sideBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN) + ? this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) + : this.workbenchGrid.getViewSize(this.sideBarPartView).width; + this.stateModel.setInitializationValue(LayoutStateKeys.SIDEBAR_SIZE, sideBarSize as number); - // Auxiliary Bar Size - const auxiliaryBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN) - ? this.workbenchGrid.getViewCachedVisibleSize(this.auxiliaryBarPartView) - : this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width; - this.stateModel.setInitializationValue(LayoutStateKeys.AUXILIARYBAR_SIZE, auxiliaryBarSize as number); + // Panel Size + const panelSize = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) + ? this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) + : (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.BOTTOM ? this.workbenchGrid.getViewSize(this.panelPartView).height : this.workbenchGrid.getViewSize(this.panelPartView).width); + this.stateModel.setInitializationValue(LayoutStateKeys.PANEL_SIZE, panelSize as number); - this.stateModel.save(true, true); - } + // Auxiliary Bar Size + const auxiliaryBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN) + ? this.workbenchGrid.getViewCachedVisibleSize(this.auxiliaryBarPartView) + : this.workbenchGrid.getViewSize(this.auxiliaryBarPartView).width; + this.stateModel.setInitializationValue(LayoutStateKeys.AUXILIARYBAR_SIZE, auxiliaryBarSize as number); + + this.stateModel.save(true, true); })); } @@ -1935,12 +1932,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi height: panelPosition === Position.BOTTOM ? this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT) : size.height }); } + this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_WAS_LAST_MAXIMIZED, !isMaximized); } - /** - * Returns whether or not the panel opens maximized - */ private panelOpensMaximized(): boolean { // The workbench grid currently prevents us from supporting panel maximization with non-center panel alignment @@ -2367,7 +2362,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi visible: !this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) }; - const middleSection: ISerializedNode[] = this.arrangeMiddleSectionNodes({ activityBar: activityBarNode, auxiliaryBar: auxiliaryBarNode, @@ -2717,6 +2711,7 @@ class LayoutStateModel extends Disposable { if (oldValue !== undefined) { return !oldValue; } + return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) !== ActivityBarPosition.DEFAULT; } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index c2ceeb2d7d7..83016b30a46 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1079,11 +1079,20 @@ export class ChangeLanguageAction extends Action2 { weight: KeybindingWeight.WorkbenchContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyM) }, - precondition: ContextKeyExpr.not('notebookEditorFocused') + precondition: ContextKeyExpr.not('notebookEditorFocused'), + metadata: { + description: localize('changeLanguageMode.description', "Change the language mode of the active text editor."), + args: [ + { + name: localize('changeLanguageMode.arg.name', "The name of the language mode to change to."), + constraint: (value: any) => typeof value === 'string', + } + ] + } }); } - override async run(accessor: ServicesAccessor): Promise { + override async run(accessor: ServicesAccessor, languageMode?: string): Promise { const quickInputService = accessor.get(IQuickInputService); const editorService = accessor.get(IEditorService); const languageService = accessor.get(ILanguageService); @@ -1162,7 +1171,7 @@ export class ChangeLanguageAction extends Action2 { }; picks.unshift(autoDetectLanguage); - const pick = await quickInputService.pick(picks, { placeHolder: localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }); + const pick = typeof languageMode === 'string' ? { label: languageMode } : await quickInputService.pick(picks, { placeHolder: localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }); if (!pick) { return; } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index 07dd5640c0c..dd412fa30f9 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts @@ -283,7 +283,7 @@ class StatusBarCodiconLabel extends SimpleIconLabel { private progressCodicon = renderIcon(syncing); private currentText = ''; - private currentShowProgress: boolean | 'syncing' | 'loading' = false; + private currentShowProgress: boolean | 'loading' | 'syncing' = false; constructor( private readonly container: HTMLElement @@ -291,10 +291,10 @@ class StatusBarCodiconLabel extends SimpleIconLabel { super(container); } - set showProgress(showProgress: boolean | 'syncing' | 'loading') { + set showProgress(showProgress: boolean | 'loading' | 'syncing') { if (this.currentShowProgress !== showProgress) { this.currentShowProgress = showProgress; - this.progressCodicon = renderIcon(showProgress === 'loading' ? spinningLoading : syncing); + this.progressCodicon = renderIcon(showProgress === 'syncing' ? syncing : spinningLoading); this.text = this.currentText; } } diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 38459b48cdc..47fc7c4e9a4 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -1100,6 +1100,10 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { if (!(this.options.mergeViewWithContainerWhenSingleView && this.paneItems.length === 1)) { return false; } + if (this.paneItems[0].pane.titleDescription) { + // Don't merge a view with a titleDescription. See #166000 + return false; + } if (!this.areExtensionsReady) { if (this.visibleViewsCountFromCache === undefined) { return this.paneItems[0].pane.isExpanded(); diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 7d45fc86091..44064deca56 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -43,6 +43,7 @@ import { ChatCodeBlockContextProviderService } from 'vs/workbench/contrib/chat/b import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib'; import 'vs/workbench/contrib/chat/browser/contrib/chatContextAttachments'; import 'vs/workbench/contrib/chat/browser/contrib/chatInputCompletions'; +import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover'; import { ChatAgentLocation, ChatAgentNameService, ChatAgentService, IChatAgentNameService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; diff --git a/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts b/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts index 9163fd24fdb..9e48ba6bc18 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts @@ -9,6 +9,7 @@ import { IUpdatableHoverOptions } from 'vs/base/browser/ui/hover/hover'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { FileAccess } from 'vs/base/common/network'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -29,6 +30,9 @@ export class ChatAgentHover extends Disposable { private readonly publisherName: HTMLElement; private readonly description: HTMLElement; + private readonly _onDidChangeContents = this._register(new Emitter()); + public readonly onDidChangeContents: Event = this._onDidChangeContents.event; + constructor( @IChatAgentService private readonly chatAgentService: IChatAgentService, @IExtensionsWorkbenchService private readonly extensionService: IExtensionsWorkbenchService, @@ -110,6 +114,7 @@ export class ChatAgentHover extends Disposable { const extension = extensions[0]; if (extension?.publisherDomain?.verified) { this.domNode.classList.toggle('verifiedPublisher', true); + this._onDidChangeContents.fire(); } }); } diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 30e33ebabd2..90acf8cf0b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -530,6 +530,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const inputEditorHeight = Math.min(data.inputPartEditorHeight, height - data.followupsHeight - data.inputPartVerticalPadding); + this.followupsContainer.style.width = `${width}px`; + this._inputPartHeight = data.followupsHeight + inputEditorHeight + data.inputPartVerticalPadding + data.inputEditorBorder + data.implicitContextHeight; const initialEditorScrollWidth = this._inputEditor.getScrollWidth(); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index bafc22d77ee..e58d0fd8294 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -197,10 +197,7 @@ class InputEditorDecorations extends Disposable { const textDecorations: IDecorationOptions[] | undefined = []; if (agentPart) { - const isDupe = !!this.chatAgentService.getAgents().find(other => other.name === agentPart.agent.name && other.id !== agentPart.agent.id); - const publisher = isDupe ? `(${agentPart.agent.publisherDisplayName}) ` : ''; - const agentHover = `${publisher}${agentPart.agent.description}`; - textDecorations.push({ range: agentPart.editorRange, hoverMessage: new MarkdownString(agentHover) }); + textDecorations.push({ range: agentPart.editorRange }); if (agentSubcommandPart) { textDecorations.push({ range: agentSubcommandPart.editorRange, hoverMessage: new MarkdownString(agentSubcommandPart.command.description) }); } diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts new file mode 100644 index 00000000000..2ad7122eb92 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDecoration } from 'vs/editor/common/model'; +import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { ChatAgentHover, getChatAgentHoverOptions } from 'vs/workbench/contrib/chat/browser/chatAgentHover'; +import { ChatEditorHoverWrapper } from 'vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper'; +import { IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { extractAgentAndCommand } from 'vs/workbench/contrib/chat/common/chatParserTypes'; + +export class ChatAgentHoverParticipant implements IEditorHoverParticipant { + + public readonly hoverOrdinal: number = 1; + + constructor( + private readonly editor: ICodeEditor, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, + @ICommandService private readonly commandService: ICommandService, + ) { } + + public computeSync(anchor: HoverAnchor, _lineDecorations: IModelDecoration[]): ChatAgentHoverPart[] { + if (!this.editor.hasModel()) { + return []; + } + + const widget = this.chatWidgetService.getWidgetByInputUri(this.editor.getModel().uri); + if (!widget) { + return []; + } + + const { agentPart } = extractAgentAndCommand(widget.parsedInput); + if (!agentPart) { + return []; + } + + if (Range.containsPosition(agentPart.editorRange, anchor.range.getStartPosition())) { + return [new ChatAgentHoverPart(this, Range.lift(agentPart.editorRange), agentPart.agent)]; + } + + return []; + } + + public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: ChatAgentHoverPart[]): IDisposable { + if (!hoverParts.length) { + return Disposable.None; + } + + const store = new DisposableStore(); + const hover = store.add(this.instantiationService.createInstance(ChatAgentHover)); + store.add(hover.onDidChangeContents(() => context.onContentsChanged())); + const agent = hoverParts[0].agent; + hover.setAgent(agent.id); + + const actions = getChatAgentHoverOptions(() => agent, this.commandService).actions; + const wrapper = this.instantiationService.createInstance(ChatEditorHoverWrapper, hover.domNode, actions); + context.fragment.appendChild(wrapper.domNode); + + return store; + } +} + +export class ChatAgentHoverPart implements IHoverPart { + + constructor( + public readonly owner: IEditorHoverParticipant, + public readonly range: Range, + public readonly agent: IChatAgentData + ) { } + + public isValidForHoverAnchor(anchor: HoverAnchor): boolean { + return ( + anchor.type === HoverAnchorType.Range + && this.range.startColumn <= anchor.range.startColumn + && this.range.endColumn >= anchor.range.endColumn + ); + } +} + +HoverParticipantRegistry.register(ChatAgentHoverParticipant); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper.ts b/src/vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper.ts new file mode 100644 index 00000000000..5cbc7d932cc --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/editorHoverWrapper'; +import * as dom from 'vs/base/browser/dom'; +import { IHoverAction } from 'vs/base/browser/ui/hover/hover'; +import { HoverAction } from 'vs/base/browser/ui/hover/hoverWidget'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +const $ = dom.$; +const h = dom.h; + +/** + * This borrows some of HoverWidget so that a chat editor hover can be rendered in the same way as a workbench hover. + * Maybe it can be reusable in a generic way. + */ +export class ChatEditorHoverWrapper { + public readonly domNode: HTMLElement; + + constructor( + hoverContentElement: HTMLElement, + actions: IHoverAction[] | undefined, + @IKeybindingService private readonly keybindingService: IKeybindingService, + ) { + const hoverElement = h( + '.chat-editor-hover-wrapper@root', + [h('.chat-editor-hover-wrapper-content@content')]); + this.domNode = hoverElement.root; + hoverElement.content.appendChild(hoverContentElement); + + if (actions && actions.length > 0) { + const statusBarElement = $('.hover-row.status-bar'); + const actionsElement = $('.actions'); + actions.forEach(action => { + const keybinding = this.keybindingService.lookupKeybinding(action.commandId); + const keybindingLabel = keybinding ? keybinding.getLabel() : null; + HoverAction.render(actionsElement, { + label: action.label, + commandId: action.commandId, + run: e => { + action.run(e); + }, + iconClass: action.iconClass + }, keybindingLabel); + }); + statusBarElement.appendChild(actionsElement); + this.domNode.appendChild(statusBarElement); + } + } +} diff --git a/src/vs/workbench/contrib/chat/browser/contrib/media/editorHoverWrapper.css b/src/vs/workbench/contrib/chat/browser/contrib/media/editorHoverWrapper.css new file mode 100644 index 00000000000..d95fd395255 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/contrib/media/editorHoverWrapper.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.chat-editor-hover-wrapper-content { + padding: 2px 8px; +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 5310cc02f75..f827efc194f 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -555,6 +555,12 @@ display: block; color: var(--vscode-textLink-foreground); font-size: 12px; + + /* clamp to max 3 lines */ + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; } .interactive-session .interactive-input-part .interactive-input-followups .interactive-session-followups code { diff --git a/src/vs/workbench/contrib/chat/browser/media/chatAgentHover.css b/src/vs/workbench/contrib/chat/browser/media/chatAgentHover.css index 1599c4ffeac..29e38f48cad 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatAgentHover.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatAgentHover.css @@ -22,8 +22,8 @@ outline: 1px solid var(--vscode-chat-requestBorder); } -.monaco-hover .markdown-hover .hover-contents .chat-agent-hover-icon .codicon { - font-size: 23px; +.chat-agent-hover .chat-agent-hover-icon .codicon { + font-size: 23px !important; /* Override workbench hover styles */ display: flex; justify-content: center; align-items: center; @@ -34,7 +34,7 @@ gap: 4px; } -.monaco-hover .chat-agent-hover .chat-agent-hover-publisher .codicon.codicon-extensions-verified-publisher { +.chat-agent-hover .chat-agent-hover-publisher .codicon.codicon-extensions-verified-publisher { color: var(--vscode-extensionIcon-verifiedForeground); } @@ -60,6 +60,10 @@ font-weight: 600; } +.chat-agent-hover-header .chat-agent-hover-details { + font-size: 12px; +} + .chat-agent-hover-extension { display: flex; gap: 6px; diff --git a/src/vs/workbench/contrib/chat/common/chatWordCounter.ts b/src/vs/workbench/contrib/chat/common/chatWordCounter.ts index 94870296160..c3b7ff8822c 100644 --- a/src/vs/workbench/contrib/chat/common/chatWordCounter.ts +++ b/src/vs/workbench/contrib/chat/common/chatWordCounter.ts @@ -11,7 +11,7 @@ export interface IWordCountResult { export function getNWords(str: string, numWordsToCount: number): IWordCountResult { // Match words and markdown style links - const allWordMatches = Array.from(str.matchAll(/\[([^\]]+)\]\(([^)]+)\)|[^\s\|\-]+/g)); + const allWordMatches = Array.from(str.matchAll(/\[([^\]]+)\]\(([^)]+)\)|\p{sc=Han}|[^\s\|\-|\p{sc=Han}]+/gu)); const targetWords = allWordMatches.slice(0, numWordsToCount); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index a79ec4c2b3c..2c909a2ed5e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -139,9 +139,9 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget super(editor, { keepEditorSelection: true, isAccessible: true }); this._contextKeyService = contextKeyService.createScoped(this.domNode); - this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection( + this._scopedInstantiationService = this._globalToDispose.add(instantiationService.createChild(new ServiceCollection( [IContextKeyService, this._contextKeyService] - )); + ))); const controller = this.commentService.getCommentController(this._uniqueOwner); if (controller) { diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index d679c223423..c55036a111d 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -728,7 +728,7 @@ export class CommentController implements IEditorContribution { return; } - const after = this.editor.getSelection().getEndPosition(); + const after = reverse ? this.editor.getSelection().getStartPosition() : this.editor.getSelection().getEndPosition(); const sortedWidgets = this._commentWidgets.sort((a, b) => { if (reverse) { const temp = a; diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index 0ee64b833ea..f643c68547f 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -244,11 +244,6 @@ margin-top: 0; } -.review-widget .body .comment-body code { - border-radius: 3px; - padding: 0 0.4em; -} - .review-widget .body .comment-body span { white-space: pre; } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 36da9c868e0..ab797aa530c 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -480,7 +480,7 @@ configurationRegistry.registerConfiguration({ }, 'debug.toolBarLocation': { enum: ['floating', 'docked', 'commandCenter', 'hidden'], - markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, `commandCenter` (requires `{0}`), or `hidden`.", '#window.commandCenter#'), + markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, `commandCenter` (requires {0}), or `hidden`.", '`#window.commandCenter#`'), default: 'floating', markdownEnumDescriptions: [ nls.localize('debugToolBar.floating', "Show debug toolbar in all views."), @@ -621,7 +621,7 @@ configurationRegistry.registerConfiguration({ }, 'debug.hideLauncherWhileDebugging': { type: 'boolean', - markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'debug.hideLauncherWhileDebugging' }, "Hide 'Start Debugging' control in title bar of 'Run and Debug' view while debugging is active. Only relevant when `{0}` is not `docked`.", '#debug.toolBarLocation#'), + markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'debug.hideLauncherWhileDebugging' }, "Hide 'Start Debugging' control in title bar of 'Run and Debug' view while debugging is active. Only relevant when {0} is not `docked`.", '`#debug.toolBarLocation#`'), default: false } } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 3e98384f357..58a3df13a9f 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -1110,18 +1110,32 @@ export const pasteFileHandler = async (accessor: ServicesAccessor, fileList?: Fi const configurationService = accessor.get(IConfigurationService); const uriIdentityService = accessor.get(IUriIdentityService); const dialogService = accessor.get(IDialogService); + const hostService = accessor.get(IHostService); const context = explorerService.getContext(false); const hasNativeFilesToPaste = fileList && fileList.length > 0; const confirmPasteNative = hasNativeFilesToPaste && configurationService.getValue('explorer.confirmPasteNative'); - const toPaste = await getFilesToPaste(fileList, clipboardService); + const toPaste = await getFilesToPaste(fileList, clipboardService, hostService); if (confirmPasteNative && toPaste.files.length >= 1) { const message = toPaste.files.length > 1 ? nls.localize('confirmMultiPasteNative', "Are you sure you want to paste the following {0} items?", toPaste.files.length) : nls.localize('confirmPasteNative', "Are you sure you want to paste '{0}'?", basename(toPaste.type === 'paths' ? toPaste.files[0].fsPath : toPaste.files[0].name)); - const detail = toPaste.files.length > 1 ? getFileNamesMessage(toPaste.files.map(item => toPaste.type === 'paths' ? item.path : (item as File).name)) : undefined; + const detail = toPaste.files.length > 1 ? getFileNamesMessage(toPaste.files.map(item => { + if (URI.isUri(item)) { + return item.fsPath; + } + + if (toPaste.type === 'paths') { + const path = hostService.getPathForFile(item); + if (path) { + return path; + } + } + + return item.name; + })) : undefined; const confirmation = await dialogService.confirm({ message, detail, @@ -1270,16 +1284,16 @@ type FilesToPaste = | { type: 'paths'; files: URI[] } | { type: 'data'; files: File[] }; -async function getFilesToPaste(fileList: FileList | undefined, clipboardService: IClipboardService): Promise { +async function getFilesToPaste(fileList: FileList | undefined, clipboardService: IClipboardService, hostService: IHostService): Promise { if (fileList && fileList.length > 0) { // with a `fileList` we support natively pasting file from disk from clipboard - const resources = [...fileList].filter(file => !!file.path && isAbsolute(file.path)).map(file => URI.file(file.path)); + const resources = [...fileList].map(file => hostService.getPathForFile(file)).filter(filePath => !!filePath && isAbsolute(filePath)).map((filePath) => URI.file(filePath!)); if (resources.length) { return { type: 'paths', files: resources, }; } // Support pasting files that we can't read from disk - return { type: 'data', files: [...fileList].filter(file => !file.path) }; + return { type: 'data', files: [...fileList].filter(file => !hostService.getPathForFile(file)) }; } else { // otherwise we fallback to reading resources from our clipboard service return { type: 'paths', files: resources.distinctParents(await clipboardService.readResources(), resource => resource) }; diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 4af58b0bd45..35a5a532e0f 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -270,13 +270,13 @@ configurationRegistry.registerConfiguration({ 'files.autoSaveWorkspaceFilesOnly': { 'type': 'boolean', 'default': false, - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveWorkspaceFilesOnly' }, "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that are inside the opened workspace. Only applies when `#files.autoSave#` is enabled."), + 'markdownDescription': nls.localize('autoSaveWorkspaceFilesOnly', "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that are inside the opened workspace. Only applies when {0} is enabled.", '`#files.autoSave#`'), scope: ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'files.autoSaveWhenNoErrors': { 'type': 'boolean', 'default': false, - 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveWhenNoErrors' }, "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that have no errors reported in them at the time the auto save is triggered. Only applies when `#files.autoSave#` is enabled."), + 'markdownDescription': nls.localize('autoSaveWhenNoErrors', "When enabled, will limit [auto save](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save) of editors to files that have no errors reported in them at the time the auto save is triggered. Only applies when {0} is enabled.", '`#files.autoSave#`'), scope: ConfigurationScope.LANGUAGE_OVERRIDABLE }, 'files.watcherExclude': { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 0fd22533e89..a5de264bfcf 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -256,7 +256,7 @@ export class OpenEditorsView extends ViewPane { this.readonlyEditorFocusedContext = OpenEditorsReadonlyEditorContext.bindTo(this.contextKeyService); this._register(this.list.onContextMenu(e => this.onListContextMenu(e))); - this.list.onDidChangeFocus(e => { + this._register(this.list.onDidChangeFocus(e => { this.resourceContext.reset(); this.groupFocusedContext.reset(); this.dirtyEditorFocusedContext.reset(); @@ -270,7 +270,7 @@ export class OpenEditorsView extends ViewPane { } else if (!!element) { this.groupFocusedContext.set(true); } - }); + })); // Open when selecting via keyboard this._register(this.list.onMouseMiddleClick(e => { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts index aec9f5cdcad..c305b01aab4 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts @@ -136,7 +136,7 @@ export class InlineChatContentWidget implements IContentWidget { const maxHeight = this._widget.input.inputEditor.getOption(EditorOption.lineHeight) * 5; const inputEditorHeight = this._widget.contentHeight; - this._widget.layout(Math.min(maxHeight, inputEditorHeight), 360); + this._widget.layout(Math.min(maxHeight, inputEditorHeight), 390); // const actualHeight = this._widget.inputPartHeight; // return new dom.Dimension(width, actualHeight); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css index bc9e18e6ea6..829b3d78fc3 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css @@ -11,9 +11,6 @@ box-shadow: 0 4px 8px var(--vscode-inlineChat-shadow); } -.monaco-workbench .inline-chat-content-widget .hidden { - display: none; -} .monaco-workbench .inline-chat-content-widget.interactive-session .interactive-session { max-width: unset; @@ -39,3 +36,7 @@ font-size: 12px; line-height: 18px; } + +.monaco-workbench .inline-chat-content-widget .hidden { + display: none; +} diff --git a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts index e7355a0dd3d..c97455cb4ed 100644 --- a/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts +++ b/src/vs/workbench/contrib/markers/browser/markersFileDecorations.ts @@ -111,7 +111,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'type': 'object', 'properties': { 'problems.decorations.enabled': { - 'markdownDescription': localize('markers.showOnFile', "Show Errors & Warnings on files and folder. Overwritten by `#problems.visibility#` when it is off."), + 'markdownDescription': localize('markers.showOnFile', "Show Errors & Warnings on files and folder. Overwritten by {0} when it is off.", '`#problems.visibility#`'), 'type': 'boolean', 'default': true } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 5ad7c674268..197230a61cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -10,8 +10,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; +import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; import { CENTER_ACTIVE_CELL } from 'vs/workbench/contrib/notebook/browser/contrib/navigation/arrow'; import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { SELECT_NOTEBOOK_INDENTATION_ID } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; @@ -20,8 +19,9 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; class ImplictKernelSelector implements IDisposable { @@ -179,8 +179,6 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(KernelStatus, LifecyclePhase.Restored); - export class ActiveCellStatus extends Disposable implements IWorkbenchContribution { private readonly _itemDisposables = this._register(new DisposableStore()); @@ -255,9 +253,7 @@ export class ActiveCellStatus extends Disposable implements IWorkbenchContributi } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ActiveCellStatus, LifecyclePhase.Restored); - -export class NotebookIndentationStatus extends Disposable implements IWorkbenchContribution { +export class NotebookIndentationStatus extends Disposable { private readonly _itemDisposables = this._register(new DisposableStore()); private readonly _accessor = this._register(new MutableDisposable()); @@ -339,4 +335,33 @@ export class NotebookIndentationStatus extends Disposable implements IWorkbenchC } } -registerWorkbenchContribution2(NotebookIndentationStatus.ID, NotebookIndentationStatus, WorkbenchPhase.AfterRestored); // TODO@Yoyokrazy -- unsure on the phase +export class NotebookEditorStatusContribution extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'notebook.contrib.editorStatus'; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IEditorService editorService: IEditorService + ) { + super(); + + // Main Editor Status + const mainInstantiationService = instantiationService.createChild(new ServiceCollection( + [IEditorService, editorService.createScoped('main', this._store)] + )); + this._register(mainInstantiationService.createInstance(KernelStatus)); + this._register(mainInstantiationService.createInstance(ActiveCellStatus)); + this._register(mainInstantiationService.createInstance(NotebookIndentationStatus)); + + // Auxiliary Editor Status + this._register(editorGroupService.onDidCreateAuxiliaryEditorPart(({ part, instantiationService, disposables }) => { + disposables.add(instantiationService.createInstance(KernelStatus)); + disposables.add(instantiationService.createInstance(ActiveCellStatus)); + disposables.add(instantiationService.createInstance(NotebookIndentationStatus)); + })); + } +} + + +registerWorkbenchContribution2(NotebookEditorStatusContribution.ID, NotebookEditorStatusContribution, WorkbenchPhase.AfterRestored); diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css b/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css index 4577677c02a..68974e01dc1 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookCellChat.css @@ -188,14 +188,6 @@ user-select: text; } -.monaco-workbench .notebookOverlay .cell-chat-part .inline-chat .markdownMessage .message[state="cropped"] { - -webkit-line-clamp: var(--vscode-inline-chat-cropped, 3); -} - -.monaco-workbench .notebookOverlay .cell-chat-part .inline-chat .markdownMessage .message[state="expanded"] { - -webkit-line-clamp: var(--vscode-inline-chat-expanded, 10); -} - .monaco-workbench .notebookOverlay .cell-chat-part .inline-chat .status .label A { color: var(--vscode-textLink-foreground); cursor: pointer; @@ -352,4 +344,3 @@ .monaco-workbench .notebookOverlay .cell-chat-part .glyph-margin-widgets .cgmr.codicon-inline-chat-transparent:hover { opacity: 1; } - diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 9cc82bc223a..ee8efdec5a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -436,7 +436,7 @@ export class BackLayerWebView extends Themable { } table, thead, tr, th, td, tbody { - border: none !important; + border: none; border-color: transparent; border-spacing: 0; border-collapse: collapse; diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index 0a21d1d05bd..794c8bdfdd3 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -65,17 +65,17 @@ Registry.as(ConfigurationExtensions.Configuration).regis 'default': 'alwaysExpand' }, [OutlineConfigKeys.problemsEnabled]: { - 'markdownDescription': localize('outline.showProblem', "Show errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off."), + 'markdownDescription': localize('outline.showProblem', "Show errors and warnings on Outline elements. Overwritten by {0} when it is off.", '`#problems.visibility#`'), 'type': 'boolean', 'default': true }, [OutlineConfigKeys.problemsColors]: { - 'markdownDescription': localize('outline.problem.colors', "Use colors for errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off."), + 'markdownDescription': localize('outline.problem.colors', "Use colors for errors and warnings on Outline elements. Overwritten by {0} when it is off.", '`#problems.visibility#`'), 'type': 'boolean', 'default': true }, [OutlineConfigKeys.problemsBadges]: { - 'markdownDescription': localize('outline.problems.badges', "Use badges for errors and warnings on Outline elements. Overwritten by `#problems.visibility#` when it is off."), + 'markdownDescription': localize('outline.problems.badges', "Use badges for errors and warnings on Outline elements. Overwritten by {0} when it is off.", '`#problems.visibility#`'), 'type': 'boolean', 'default': true }, diff --git a/src/vs/workbench/contrib/performance/electron-sandbox/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-sandbox/startupTimings.ts index 200f6380244..028a744f38a 100644 --- a/src/vs/workbench/contrib/performance/electron-sandbox/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-sandbox/startupTimings.ts @@ -179,11 +179,9 @@ export class NativeStartupTimings extends StartupTimings implements IWorkbenchCo // Major/Minor GC Events case 'MinorGC': minorGCs++; + break; case 'MajorGC': majorGCs++; - if (event.args && typeof event.args.usedHeapSizeAfter === 'number' && typeof event.args.usedHeapSizeBefore === 'number') { - garbage += (event.args.usedHeapSizeBefore - event.args.usedHeapSizeAfter); - } break; // GC Events that block the main thread @@ -193,6 +191,12 @@ export class NativeStartupTimings extends StartupTimings implements IWorkbenchCo duration += event.dur; break; } + + if (event.name === 'MajorGC' || event.name === 'MinorGC') { + if (typeof event.args?.usedHeapSizeAfter === 'number' && typeof event.args.usedHeapSizeBefore === 'number') { + garbage += (event.args.usedHeapSizeBefore - event.args.usedHeapSizeAfter); + } + } } return { minorGCs, majorGCs, used, garbage, duration: Math.round(duration / 1000) }; diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 4e826cecb77..aa8ce0ff514 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -1562,24 +1562,25 @@ namespace SetTunnelProtocolAction { export const LABEL_HTTP = nls.localize('remote.tunnel.protocolHttp', "HTTP"); export const LABEL_HTTPS = nls.localize('remote.tunnel.protocolHttps', "HTTPS"); - async function handler(arg: any, protocol: TunnelProtocol, remoteExplorerService: IRemoteExplorerService) { + async function handler(arg: any, protocol: TunnelProtocol, remoteExplorerService: IRemoteExplorerService, environmentService: IWorkbenchEnvironmentService) { if (isITunnelItem(arg)) { const attributes: Partial = { protocol }; - return remoteExplorerService.tunnelModel.configPortsAttributes.addAttributes(arg.remotePort, attributes, ConfigurationTarget.USER_REMOTE); + const target = environmentService.remoteAuthority ? ConfigurationTarget.USER_REMOTE : ConfigurationTarget.USER_LOCAL; + return remoteExplorerService.tunnelModel.configPortsAttributes.addAttributes(arg.remotePort, attributes, target); } } export function handlerHttp(): ICommandHandler { return async (accessor, arg) => { - return handler(arg, TunnelProtocol.Http, accessor.get(IRemoteExplorerService)); + return handler(arg, TunnelProtocol.Http, accessor.get(IRemoteExplorerService), accessor.get(IWorkbenchEnvironmentService)); }; } export function handlerHttps(): ICommandHandler { return async (accessor, arg) => { - return handler(arg, TunnelProtocol.Https, accessor.get(IRemoteExplorerService)); + return handler(arg, TunnelProtocol.Https, accessor.get(IRemoteExplorerService), accessor.get(IWorkbenchEnvironmentService)); }; } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 97455f3bc25..ca07b9168af 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -109,6 +109,7 @@ import { IHoverService } from 'vs/platform/hover/browser/hover'; import { OpenScmGroupAction } from 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver'; import { HoverController } from 'vs/editor/contrib/hover/browser/hoverController'; import { ITextModel } from 'vs/editor/common/model'; +import { autorun } from 'vs/base/common/observable'; // type SCMResourceTreeNode = IResourceNode; // type SCMHistoryItemChangeResourceTreeNode = IResourceNode; @@ -2352,24 +2353,21 @@ class SCMInputWidget { // Update input template let commitTemplate = ''; - const updateTemplate = () => { - if (typeof input.repository.provider.commitTemplate === 'undefined' || !input.visible) { + this.repositoryDisposables.add(autorun(reader => { + if (!input.visible) { return; } const oldCommitTemplate = commitTemplate; - commitTemplate = input.repository.provider.commitTemplate; + commitTemplate = input.repository.provider.commitTemplate.read(reader); const value = textModel.getValue(); - if (value && value !== oldCommitTemplate) { return; } textModel.setValue(commitTemplate); - }; - this.repositoryDisposables.add(input.repository.provider.onDidChangeCommitTemplate(updateTemplate, this)); - updateTemplate(); + })); // Update input enablement const updateEnablement = (enabled: boolean) => { diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 3fe568b77d5..c0a56b7ee90 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -15,6 +15,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResourceTree } from 'vs/base/common/resourceTree'; import { ISCMHistoryProvider, ISCMHistoryProviderMenus } from 'vs/workbench/contrib/scm/common/history'; import { ITextModel } from 'vs/editor/common/model'; +import { IObservable } from 'vs/base/common/observable'; export const VIEWLET_ID = 'workbench.view.scm'; export const VIEW_PANE_ID = 'workbench.scm'; @@ -73,9 +74,8 @@ export interface ISCMProvider extends IDisposable { readonly rootUri?: URI; readonly inputBoxTextModel: ITextModel; readonly count?: number; - readonly commitTemplate: string; + readonly commitTemplate: IObservable; readonly historyProvider?: ISCMHistoryProvider; - readonly onDidChangeCommitTemplate: Event; readonly onDidChangeHistoryProvider: Event; readonly onDidChangeStatusBarCommands?: Event; readonly acceptInputCommand?: Command; diff --git a/src/vs/workbench/contrib/scm/common/scmService.ts b/src/vs/workbench/contrib/scm/common/scmService.ts index 762dc9ed1b6..5ea28588e21 100644 --- a/src/vs/workbench/contrib/scm/common/scmService.ts +++ b/src/vs/workbench/contrib/scm/common/scmService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator, ISCMInputChangeEvent, SCMInputChangeReason, InputValidationType, IInputValidation } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; @@ -16,7 +16,7 @@ import { URI } from 'vs/base/common/uri'; import { Iterable } from 'vs/base/common/iterator'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -class SCMInput implements ISCMInput { +class SCMInput extends Disposable implements ISCMInput { private _value = ''; @@ -104,9 +104,11 @@ class SCMInput implements ISCMInput { readonly repository: ISCMRepository, private readonly history: SCMInputHistory ) { + super(); + if (this.repository.provider.rootUri) { this.historyNavigator = history.getHistory(this.repository.provider.label, this.repository.provider.rootUri); - this.history.onWillSaveHistory(event => { + this._register(this.history.onWillSaveHistory(event => { if (this.historyNavigator.isAtEnd()) { this.saveValue(); } @@ -116,7 +118,7 @@ class SCMInput implements ISCMInput { } this.didChangeHistory = false; - }); + })); } else { // in memory only this.historyNavigator = new HistoryNavigator2([''], 100); } diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 26e2d6cc45f..e80602a3f82 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -183,13 +183,13 @@ configurationRegistry.registerConfiguration({ }, 'search.useGlobalIgnoreFiles': { type: 'boolean', - markdownDescription: nls.localize('useGlobalIgnoreFiles', "Controls whether to use your global gitignore file (for example, from `$HOME/.config/git/ignore`) when searching for files. Requires `#search.useIgnoreFiles#` to be enabled."), + markdownDescription: nls.localize('useGlobalIgnoreFiles', "Controls whether to use your global gitignore file (for example, from `$HOME/.config/git/ignore`) when searching for files. Requires {0} to be enabled.", '`#search.useIgnoreFiles#`'), default: false, scope: ConfigurationScope.RESOURCE }, 'search.useParentIgnoreFiles': { type: 'boolean', - markdownDescription: nls.localize('useParentIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files in parent directories when searching for files. Requires `#search.useIgnoreFiles#` to be enabled."), + markdownDescription: nls.localize('useParentIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files in parent directories when searching for files. Requires {0} to be enabled.", '`#search.useIgnoreFiles#`'), default: false, scope: ConfigurationScope.RESOURCE }, diff --git a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts index e848ced9bb3..a16f5a19f44 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts @@ -49,6 +49,7 @@ export interface IFindInFilesArgs { matchWholeWord?: boolean; useExcludeSettingsAndIgnoreFiles?: boolean; onlyOpenEditors?: boolean; + showIncludesExcludes?: boolean; } //#endregion @@ -208,6 +209,7 @@ registerAction2(class FindInFilesAction extends Action2 { matchWholeWord: { 'type': 'boolean' }, useExcludeSettingsAndIgnoreFiles: { 'type': 'boolean' }, onlyOpenEditors: { 'type': 'boolean' }, + showIncludesExcludes: { 'type': 'boolean' } } } }, @@ -407,6 +409,9 @@ export async function findInFilesCommand(accessor: ServicesAccessor, _args: IFin updatedText = openedView.updateTextFromFindWidgetOrSelection({ allowUnselectedWord: typeof args.replace !== 'string' }); } openedView.setSearchParameters(args); + if (typeof args.showIncludesExcludes === 'boolean') { + openedView.toggleQueryDetails(false, args.showIncludesExcludes); + } openedView.searchAndReplaceWidget.focus(undefined, updatedText, updatedText); } diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index d7c088c442e..5e66ae72d6c 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -136,7 +136,7 @@ export class FolderMatchRenderer extends Disposable implements ICompressibleTree SearchContext.FileFocusKey.bindTo(contextKeyServiceMain).set(false); SearchContext.FolderFocusKey.bindTo(contextKeyServiceMain).set(true); - const instantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain])); + const instantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain]))); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { shouldForwardArgs: true diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 69e8a910540..6359674b81f 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -237,8 +237,8 @@ export class SearchView extends ViewPane { this.inputPatternExclusionsFocused = Constants.SearchContext.PatternExcludesFocusedKey.bindTo(this.contextKeyService); this.isEditableItem = Constants.SearchContext.IsEditableItemKey.bindTo(this.contextKeyService); - this.instantiationService = this.instantiationService.createChild( - new ServiceCollection([IContextKeyService, this.contextKeyService])); + this.instantiationService = this._register(this.instantiationService.createChild( + new ServiceCollection([IContextKeyService, this.contextKeyService]))); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('search.sortOrder')) { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index cf86112b1c2..d5b80134341 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -523,7 +523,7 @@ export class SearchWidget extends Widget { } })); - this.replaceInput.onKeyDown((keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent)); + this._register(this.replaceInput.onKeyDown((keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent))); this.replaceInput.setValue(options.replaceValue || ''); this._register(this.replaceInput.inputBox.onDidChange(() => this._onReplaceValueChanged.fire())); this._register(this.replaceInput.inputBox.onDidHeightChange(() => this._onDidHeightChange.fire())); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index c0f0f27dbfa..e1929d3a930 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -141,7 +141,7 @@ export class SearchEditor extends AbstractTextCodeEditor this.createQueryEditor( this.queryEditorContainer, - this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService])), + this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, scopedContextKeyService]))), SearchContext.InputBoxFocusedKey.bindTo(scopedContextKeyService) ); } diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 08e8292b60c..6de31d4010c 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -109,7 +109,7 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench } if (promise && (event.kind === TaskEventKind.Active) && (this._activeTasksCount === 1)) { - this._progressService.withProgress({ location: ProgressLocation.Window, command: 'workbench.action.tasks.showTasks', type: 'loading' }, progress => { + this._progressService.withProgress({ location: ProgressLocation.Window, command: 'workbench.action.tasks.showTasks' }, progress => { progress.report({ message: nls.localize('building', 'Building...') }); return promise!; }).then(() => { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index e6ca453010d..f7bc50614cf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -88,6 +88,7 @@ import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/commo import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard'; import { TerminalIconPicker } from 'vs/workbench/contrib/terminal/browser/terminalIconPicker'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; // HACK: This file should not depend on terminalContrib // eslint-disable-next-line local/code-import-patterns @@ -2288,6 +2289,7 @@ class TerminalInstanceDragAndDropController extends Disposable implements dom.ID private readonly _container: HTMLElement, @IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService, @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IHostService private readonly _hostService: IHostService, ) { super(); this._register(toDisposable(() => this._clearDropOverlay())); @@ -2372,9 +2374,9 @@ class TerminalInstanceDragAndDropController extends Disposable implements dom.ID path = URI.file(JSON.parse(rawCodeFiles)[0]); } - if (!path && e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { + if (!path && e.dataTransfer.files.length > 0 && this._hostService.getPathForFile(e.dataTransfer.files[0])) { // Check if the file was dragged from the filesystem - path = URI.file(e.dataTransfer.files[0].path); + path = URI.file(this._hostService.getPathForFile(e.dataTransfer.files[0])!); } if (!path) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts b/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts index a17b660a13c..b795f91f201 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalRunRecentQuickPick.ts @@ -269,6 +269,9 @@ export async function showRunRecentQuickPick( return; } const [item] = quickPick.activeItems; + if (!item) { + return; + } if ('command' in item && item.command && item.command.marker) { if (!terminalScrollStateSaved) { xterm.markTracker.saveScrollState(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index b32bd3f28d6..b81f20a1e7d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -51,6 +51,7 @@ import { Schemas } from 'vs/base/common/network'; import { getColorForSeverity } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { TerminalContextActionRunner } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import type { IHoverAction } from 'vs/base/browser/ui/hover/hover'; +import { IHostService } from 'vs/workbench/services/host/browser/host'; const $ = DOM.$; @@ -577,6 +578,7 @@ class TerminalTabsDragAndDrop extends Disposable implements IListDragAndDrop 0 && e.dataTransfer.files[0].path /* Electron only */) { + if (!resource && e.dataTransfer.files.length > 0 && this._hostService.getPathForFile(e.dataTransfer.files[0])) { // Check if the file was dragged from the filesystem - resource = URI.file(e.dataTransfer.files[0].path); + resource = URI.file(this._hostService.getPathForFile(e.dataTransfer.files[0])!); } if (!resource) { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts index a8f77d089e0..bf22031f1b1 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts @@ -29,9 +29,14 @@ import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal import 'vs/css!./media/terminalInitialHint'; import { TerminalInitialHintSettingId } from 'vs/workbench/contrib/terminalContrib/chat/common/terminalInitialHintConfiguration'; import { ChatAgentLocation, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; const $ = dom.$; +const enum Constants { + InitialHintHideStorageKey = 'terminal.initialHint.hide' +} + export class InitialHintAddon extends Disposable implements ITerminalAddon { private readonly _onDidRequestCreateHint = this._register(new Emitter()); get onDidRequestCreateHint(): Event { return this._onDidRequestCreateHint.event; } @@ -90,11 +95,22 @@ export class TerminalInitialHintContribution extends Disposable implements ITerm @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, @ITerminalEditorService private readonly _terminalEditorService: ITerminalEditorService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, + @IStorageService private readonly _storageService: IStorageService, ) { super(); + + // Reset hint state when config changes + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(TerminalInitialHintSettingId.Enabled)) { + this._storageService.remove(Constants.InitialHintHideStorageKey, StorageScope.APPLICATION); + } + })); } xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { + if (this._storageService.getBoolean(Constants.InitialHintHideStorageKey, StorageScope.APPLICATION, false)) { + return; + } if (this._terminalGroupService.instances.length + this._terminalEditorService.instances.length !== 1) { // only show for the first terminal return; @@ -193,12 +209,14 @@ class TerminalInitialHintWidget extends Disposable { constructor( private readonly _instance: ITerminalInstance, + @IChatAgentService private readonly _chatAgentService: IChatAgentService, @ICommandService private readonly commandService: ICommandService, @IConfigurationService private readonly configurationService: IConfigurationService, @IKeybindingService private readonly keybindingService: IKeybindingService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IProductService private readonly productService: IProductService, - @ITerminalService private readonly terminalService: ITerminalService + @ITerminalService private readonly terminalService: ITerminalService, + @IStorageService private readonly _storageService: IStorageService ) { super(); this.toDispose.add(_instance.onDidFocus(() => { @@ -219,11 +237,16 @@ class TerminalInitialHintWidget extends Disposable { } private _getHintInlineChat(agents: IChatAgent[]) { - const providerName = (agents.length === 1 ? agents[0].fullName : undefined) ?? this.productService.nameShort; + let providerName = (agents.length === 1 ? agents[0].fullName : undefined) ?? this.productService.nameShort; + const defaultAgent = this._chatAgentService.getDefaultAgent(ChatAgentLocation.Panel); + if (defaultAgent?.extensionId.value === agents[0].extensionId.value) { + providerName = defaultAgent.fullName ?? providerName; + } let ariaLabel = `Ask ${providerName} something or start typing to dismiss.`; const handleClick = () => { + this._storageService.store(Constants.InitialHintHideStorageKey, true, StorageScope.APPLICATION, StorageTarget.USER); this.telemetryService.publicLog2('workbenchActionExecuted', { id: 'terminalInlineChat.hintAction', from: 'hint' diff --git a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts index 83836adc556..2321f8edb96 100644 --- a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts @@ -114,7 +114,7 @@ registerTerminalAction({ text, name: text, ariaLabel: text, - showProgress: 'loading' + showProgress: true }; const statusbarHandle = statusbarService.addEntry(statusbarEntry, 'recordSession', StatusbarAlignment.LEFT); store.add(statusbarHandle); diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index ec9e50d67f0..91ca1a2bd18 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -159,7 +159,7 @@ export const testingConfiguration: IConfigurationNode = { description: localize('testing.openTesting', "Controls when the testing view should open.") }, [TestingConfigKeys.AlwaysRevealTestOnStateChange]: { - markdownDescription: localize('testing.alwaysRevealTestOnStateChange', "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed."), + markdownDescription: localize('testing.alwaysRevealTestOnStateChange', "Always reveal the executed test when {0} is on. If this setting is turned off, only failed tests will be revealed.", '`#testing.followRunningTest#`'), type: 'boolean', default: false, }, diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index e1519b9e31d..0fbe0079ed1 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -241,11 +241,11 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'markdownEnumDescriptions': [ localize(`window.customTitleBarVisibility.auto`, "Automatically changes custom title bar visibility."), localize(`window.customTitleBarVisibility.windowed`, "Hide custom titlebar in full screen. When not in full screen, automatically change custom title bar visibility."), - localize(`window.customTitleBarVisibility.never`, "Hide custom titlebar when `#window.titleBarStyle#` is set to `native`."), + localize(`window.customTitleBarVisibility.never`, "Hide custom titlebar when {0} is set to `native`.", '`#window.titleBarStyle#`'), ], 'default': isLinux ? 'never' : 'auto', 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in none full screen mode with `never` when `#window.titleBarStyle#` is set to `native`."), + 'markdownDescription': localize('window.customTitleBarVisibility', "Adjust when the custom title bar should be shown. The custom title bar can be hidden when in full screen mode with `windowed`. The custom title bar can only be hidden in none full screen mode with `never` when {0} is set to `native`.", '`#window.titleBarStyle#`'), }, 'window.dialogStyle': { 'type': 'string', diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 738e41bd672..11f6227989f 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -576,6 +576,14 @@ export class BrowserHostService extends Disposable implements IHostService { } //#endregion + + //#region File + + getPathForFile(): undefined { + return undefined; // unsupported in browser environments + } + + //#endregion } registerSingleton(IHostService, BrowserHostService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index f1586f4b9c4..724bc5a52bf 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -19,7 +19,6 @@ export interface IHostService { readonly _serviceBrand: undefined; - //#region Focus /** @@ -56,7 +55,6 @@ export interface IHostService { //#endregion - //#region Window /** @@ -123,4 +121,10 @@ export interface IHostService { withExpectedShutdown(expectedShutdownTask: () => Promise): Promise; //#endregion + + //#region File + + getPathForFile(file: File): string | undefined; + + //#endregion } diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index d3fbff27c39..3163d8822b8 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -17,6 +17,7 @@ import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; import { disposableWindowInterval, getActiveDocument, getWindowId, getWindowsCount, hasWindow, onDidRegisterWindow } from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; import { isAuxiliaryWindow } from 'vs/base/browser/window'; +import { webUtils } from 'vs/base/parts/sandbox/electron-sandbox/globals'; class WorkbenchNativeHostService extends NativeHostService { @@ -66,7 +67,6 @@ class WorkbenchHostService extends Disposable implements IHostService { //#endregion - //#region Window @memoize @@ -160,7 +160,6 @@ class WorkbenchHostService extends Disposable implements IHostService { //#endregion - //#region Lifecycle focus(targetWindow: Window, options?: { force: boolean }): Promise { @@ -187,6 +186,15 @@ class WorkbenchHostService extends Disposable implements IHostService { } //#endregion + + //#region File + + getPathForFile(file: File): string { + return webUtils.getPathForFile(file); + } + + //#endregion + } registerSingleton(IHostService, WorkbenchHostService, InstantiationType.Delayed); diff --git a/src/vs/workbench/services/statusbar/browser/statusbar.ts b/src/vs/workbench/services/statusbar/browser/statusbar.ts index 1d00e77bca7..73fac7bb924 100644 --- a/src/vs/workbench/services/statusbar/browser/statusbar.ts +++ b/src/vs/workbench/services/statusbar/browser/statusbar.ts @@ -174,9 +174,9 @@ export interface IStatusbarEntry { /** * Will enable a spinning icon in front of the text to indicate progress. When `true` is - * specified, `syncing` will be used. + * specified, `loading` will be used. */ - readonly showProgress?: boolean | 'syncing' | 'loading'; + readonly showProgress?: boolean | 'loading' | 'syncing'; /** * The kind of status bar entry. This applies different colors to the entry. diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index b127ae7b7ca..3150fdd7b1d 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -33,6 +33,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IProgress, IProgressService, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress'; interface IBackupMetaData extends IWorkingCopyBackupMeta { mtime: number; @@ -123,7 +124,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil @ILanguageDetectionService languageDetectionService: ILanguageDetectionService, @IAccessibilityService accessibilityService: IAccessibilityService, @IPathService private readonly pathService: IPathService, - @IExtensionService private readonly extensionService: IExtensionService + @IExtensionService private readonly extensionService: IExtensionService, + @IProgressService private readonly progressService: IProgressService ) { super(modelService, languageService, languageDetectionService, accessibilityService); @@ -756,7 +758,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil options.reason = SaveReason.EXPLICIT; } - let versionId = this.versionId; + const versionId = this.versionId; this.trace(`doSave(${versionId}) - enter with versionId ${versionId}`); // Return early if saved from within save participant to break recursion @@ -818,6 +820,21 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const saveCancellation = new CancellationTokenSource(); + return this.progressService.withProgress({ + title: localize('saveParticipants', "Saving '{0}'", this.name), + location: ProgressLocation.Window, + cancellable: true, + delay: this.isDirty() ? 3000 : 5000 + }, progress => { + return this.doSaveSequential(versionId, options, progress, saveCancellation); + }, () => { + saveCancellation.cancel(); + }).finally(() => { + saveCancellation.dispose(); + }); + } + + private doSaveSequential(versionId: number, options: ITextFileSaveAsOptions, progress: IProgress, saveCancellation: CancellationTokenSource): Promise { return this.saveSequentializer.run(versionId, (async () => { // A save participant can still change the model now and since we are so close to saving @@ -852,7 +869,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (!saveCancellation.token.isCancellationRequested) { this.ignoreSaveFromSaveParticipants = true; try { - await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, saveCancellation.token); + await this.textFileService.files.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, progress, saveCancellation.token); } finally { this.ignoreSaveFromSaveParticipants = false; } @@ -898,6 +915,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk. We mark the save operation as currently running with // the latest versionId because it might have changed from a save // participant triggering + progress.report({ message: localize('saveTextFile', "Writing into file...") }); this.trace(`doSave(${versionId}) - before write()`); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); const resolvedTextFileEditorModel = this; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 24f0d458459..773ee118d54 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -24,6 +24,7 @@ import { extname, joinPath } from 'vs/base/common/resources'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { PLAINTEXT_EXTENSION, PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { @@ -540,8 +541,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this.saveParticipants.addSaveParticipant(participant); } - runSaveParticipants(model: ITextFileEditorModel, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { - return this.saveParticipants.participate(model, context, token); + runSaveParticipants(model: ITextFileEditorModel, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { + return this.saveParticipants.participate(model, context, progress, token); } //#endregion diff --git a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts index 97d74549b9a..e9430fd80ad 100644 --- a/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts +++ b/src/vs/workbench/services/textfile/common/textFileSaveParticipant.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { raceCancellation } from 'vs/base/common/async'; -import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITextFileSaveParticipant, ITextFileEditorModel, ITextFileSaveParticipantContext } from 'vs/workbench/services/textfile/common/textfiles'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { insert } from 'vs/base/common/arrays'; @@ -17,7 +16,6 @@ export class TextFileSaveParticipant extends Disposable { private readonly saveParticipants: ITextFileSaveParticipant[] = []; constructor( - @IProgressService private readonly progressService: IProgressService, @ILogService private readonly logService: ILogService ) { super(); @@ -29,40 +27,26 @@ export class TextFileSaveParticipant extends Disposable { return toDisposable(() => remove()); } - participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, token: CancellationToken): Promise { - const cts = new CancellationTokenSource(token); + async participate(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { - return this.progressService.withProgress({ - title: localize('saveParticipants', "Saving '{0}'", model.name), - location: ProgressLocation.Notification, - cancellable: true, - delay: model.isDirty() ? 3000 : 5000 - }, async progress => { + // undoStop before participation + model.textEditorModel?.pushStackElement(); - // undoStop before participation - model.textEditorModel?.pushStackElement(); - - for (const saveParticipant of this.saveParticipants) { - if (cts.token.isCancellationRequested || !model.textEditorModel /* disposed */) { - break; - } - - try { - const promise = saveParticipant.participate(model, context, progress, cts.token); - await raceCancellation(promise, cts.token); - } catch (err) { - this.logService.error(err); - } + for (const saveParticipant of this.saveParticipants) { + if (token.isCancellationRequested || !model.textEditorModel /* disposed */) { + break; } - // undoStop after participation - model.textEditorModel?.pushStackElement(); - }, () => { - // user cancel - cts.cancel(); - }).finally(() => { - cts.dispose(); - }); + try { + const promise = saveParticipant.participate(model, context, progress, token); + await raceCancellation(promise, token); + } catch (err) { + this.logService.error(err); + } + } + + // undoStop after participation + model.textEditorModel?.pushStackElement(); } override dispose(): void { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index d126c88df6e..73abb110fb1 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -385,7 +385,7 @@ export interface ITextFileEditorModelManager { /** * Runs the registered save participants on the provided model. */ - runSaveParticipants(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, token: CancellationToken): Promise; + runSaveParticipants(model: ITextFileEditorModel, context: ITextFileSaveParticipantContext, progress: IProgress, token: CancellationToken): Promise; /** * Waits for the model to be ready to be disposed. There may be conditions diff --git a/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts index 17948851797..794fb8960ce 100644 --- a/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts +++ b/src/vs/workbench/services/tunnel/electron-sandbox/tunnelService.ts @@ -68,11 +68,11 @@ export class TunnelService extends AbstractTunnelService { super(logService, configurationService); // Destroy any shared process tunnels that might still be active - lifecycleService.onDidShutdown(() => { + this._register(lifecycleService.onDidShutdown(() => { this._activeSharedProcessTunnels.forEach((id) => { this._sharedProcessTunnelService.destroyTunnel(id); }); - }); + })); } public isPortPrivileged(port: number): boolean { diff --git a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts index 1a950b3e8dc..35eb7cf3c8e 100644 --- a/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/fileWorkingCopyManager.ts @@ -38,6 +38,7 @@ import { Schemas } from 'vs/base/common/network'; import { IDecorationData, IDecorationsProvider, IDecorationsService } from 'vs/workbench/services/decorations/common/decorations'; import { Codicon } from 'vs/base/common/codicons'; import { listErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IProgressService } from 'vs/platform/progress/common/progress'; export interface IFileWorkingCopyManager extends IBaseFileWorkingCopyManager> { @@ -162,7 +163,8 @@ export class FileWorkingCopyManager extend @INotificationService private readonly notificationService: INotificationService, @IWorkingCopyEditorService private readonly workingCopyEditorService: IWorkingCopyEditorService, @IEditorService private readonly editorService: IEditorService, - @IElevatedFileService private readonly elevatedFileService: IElevatedFileService + @IElevatedFileService private readonly elevatedFileService: IElevatedFileService, + @IProgressService private readonly progressService: IProgressService ) { super(resource, fileService); @@ -865,7 +867,7 @@ export class StoredFileWorkingCopy extend options.reason = SaveReason.EXPLICIT; } - let versionId = this.versionId; + const versionId = this.versionId; this.trace(`doSave(${versionId}) - enter with versionId ${versionId}`); // Return early if saved from within save participant to break recursion @@ -929,6 +931,21 @@ export class StoredFileWorkingCopy extend const saveCancellation = new CancellationTokenSource(); + return this.progressService.withProgress({ + title: localize('saveParticipants', "Saving '{0}'", this.name), + location: ProgressLocation.Window, + cancellable: true, + delay: this.isDirty() ? 3000 : 5000 + }, progress => { + return this.doSaveSequential(versionId, options, progress, saveCancellation); + }, () => { + saveCancellation.cancel(); + }).finally(() => { + saveCancellation.dispose(); + }); + } + + private doSaveSequential(versionId: number, options: IStoredFileWorkingCopySaveAsOptions, progress: IProgress, saveCancellation: CancellationTokenSource): Promise { return this.saveSequentializer.run(versionId, (async () => { // A save participant can still change the working copy now @@ -964,7 +981,7 @@ export class StoredFileWorkingCopy extend if (!saveCancellation.token.isCancellationRequested) { this.ignoreSaveFromSaveParticipants = true; try { - await this.workingCopyFileService.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, saveCancellation.token); + await this.workingCopyFileService.runSaveParticipants(this, { reason: options.reason ?? SaveReason.EXPLICIT, savedFrom: options.from }, progress, saveCancellation.token); } finally { this.ignoreSaveFromSaveParticipants = false; } @@ -1004,6 +1021,7 @@ export class StoredFileWorkingCopy extend // Save to Disk. We mark the save operation as currently running with // the latest versionId because it might have changed from a save // participant triggering + progress.report({ message: localize('saveTextFile', "Writing into file...") }); this.trace(`doSave(${versionId}) - before write()`); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); const resolvedFileWorkingCopy = this; diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts index f445e7d36dd..bbd5aa58f01 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts @@ -30,6 +30,7 @@ import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/wo import { isWeb } from 'vs/base/common/platform'; import { onUnexpectedError } from 'vs/base/common/errors'; import { SnapshotContext } from 'vs/workbench/services/workingCopy/common/fileWorkingCopy'; +import { IProgressService } from 'vs/platform/progress/common/progress'; /** * The only one that should be dealing with `IStoredFileWorkingCopy` and handle all @@ -186,7 +187,8 @@ export class StoredFileWorkingCopyManager @INotificationService private readonly notificationService: INotificationService, @IWorkingCopyEditorService private readonly workingCopyEditorService: IWorkingCopyEditorService, @IEditorService private readonly editorService: IEditorService, - @IElevatedFileService private readonly elevatedFileService: IElevatedFileService + @IElevatedFileService private readonly elevatedFileService: IElevatedFileService, + @IProgressService private readonly progressService: IProgressService ) { super(fileService, logService, workingCopyBackupService); @@ -532,7 +534,7 @@ export class StoredFileWorkingCopyManager async options => { await this.resolve(resource, { ...options, reload: { async: false } }); }, this.fileService, this.logService, this.workingCopyFileService, this.filesConfigurationService, this.workingCopyBackupService, this.workingCopyService, this.notificationService, this.workingCopyEditorService, - this.editorService, this.elevatedFileService + this.editorService, this.elevatedFileService, this.progressService ); workingCopyResolve = workingCopy.resolve(resolveOptions); diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts index 2473efad9f0..240beb17907 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopySaveParticipant.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { raceCancellation } from 'vs/base/common/async'; -import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { insert } from 'vs/base/common/arrays'; import { IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; @@ -20,7 +19,6 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { get length(): number { return this.saveParticipants.length; } constructor( - @IProgressService private readonly progressService: IProgressService, @ILogService private readonly logService: ILogService ) { super(); @@ -32,41 +30,26 @@ export class StoredFileWorkingCopySaveParticipant extends Disposable { return toDisposable(() => remove()); } - participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { - const cts = new CancellationTokenSource(token); + async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { - return this.progressService.withProgress({ - title: localize('saveParticipants', "Saving '{0}'", workingCopy.name), - location: ProgressLocation.Notification, - cancellable: true, - delay: workingCopy.isDirty() ? 3000 : 5000 - }, async progress => { + // undoStop before participation + workingCopy.model?.pushStackElement(); - // undoStop before participation - workingCopy.model?.pushStackElement(); - - for (const saveParticipant of this.saveParticipants) { - if (cts.token.isCancellationRequested || workingCopy.isDisposed()) { - break; - } - - try { - const promise = saveParticipant.participate(workingCopy, context, progress, cts.token); - await raceCancellation(promise, cts.token); - } catch (err) { - this.logService.warn(err); - } + for (const saveParticipant of this.saveParticipants) { + if (token.isCancellationRequested || workingCopy.isDisposed()) { + break; } - // undoStop after participation - workingCopy.model?.pushStackElement(); + try { + const promise = saveParticipant.participate(workingCopy, context, progress, token); + await raceCancellation(promise, token); + } catch (err) { + this.logService.warn(err); + } + } - // Cleanup - cts.dispose(); - }, () => { - // user cancel - cts.dispose(true); - }); + // undoStop after participation + workingCopy.model?.pushStackElement(); } override dispose(): void { diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts index 1896c957597..704be2bbecd 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyFileService.ts @@ -206,7 +206,7 @@ export interface IWorkingCopyFileService { /** * Runs all available save participants for stored file working copies. */ - runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise; + runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise; //#endregion @@ -507,8 +507,8 @@ export class WorkingCopyFileService extends Disposable implements IWorkingCopyFi return this.saveParticipants.addSaveParticipant(participant); } - runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { - return this.saveParticipants.participate(workingCopy, context, token); + runSaveParticipants(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { + return this.saveParticipants.participate(workingCopy, context, progress, token); } //#endregion diff --git a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts index 16185733f61..2988bbb28dc 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts @@ -40,7 +40,7 @@ suite('FileWorkingCopyManager', () => { accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.fileDialogService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, - accessor.environmentService, accessor.dialogService, accessor.decorationsService + accessor.environmentService, accessor.dialogService, accessor.decorationsService, accessor.progressService, )); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts index d4d63ff6958..ab5922a8e16 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts @@ -151,7 +151,7 @@ suite('StoredFileWorkingCopy (with custom save)', function () { accessor = instantiationService.createInstance(TestServiceAccessor); const resource = URI.file('test/resource'); - workingCopy = disposables.add(new StoredFileWorkingCopy('testStoredFileWorkingCopyType', resource, basename(resource), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService)); + workingCopy = disposables.add(new StoredFileWorkingCopy('testStoredFileWorkingCopyType', resource, basename(resource), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.progressService)); }); teardown(() => { @@ -249,7 +249,7 @@ suite('StoredFileWorkingCopy', function () { let workingCopy: StoredFileWorkingCopy; function createWorkingCopy(uri: URI = resource) { - const workingCopy: StoredFileWorkingCopy = new StoredFileWorkingCopy('testStoredFileWorkingCopyType', uri, basename(uri), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService); + const workingCopy: StoredFileWorkingCopy = new StoredFileWorkingCopy('testStoredFileWorkingCopyType', uri, basename(uri), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.progressService); return workingCopy; } diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts index 54366a2df21..fc20107d810 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts @@ -37,7 +37,8 @@ suite('StoredFileWorkingCopyManager', () => { accessor.fileService, accessor.lifecycleService, accessor.labelService, accessor.logService, accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, - accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService + accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, + accessor.progressService )); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts index cfac100bd5b..e1237220554 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts @@ -39,7 +39,7 @@ suite('UntitledFileWorkingCopyManager', () => { accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.fileDialogService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, - accessor.environmentService, accessor.dialogService, accessor.decorationsService + accessor.environmentService, accessor.dialogService, accessor.decorationsService, accessor.progressService )); }); @@ -318,7 +318,7 @@ suite('UntitledFileWorkingCopyManager', () => { accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.fileDialogService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, - accessor.environmentService, accessor.dialogService, accessor.decorationsService + accessor.environmentService, accessor.dialogService, accessor.decorationsService, accessor.progressService )); const untitled1OriginalType = disposables.add(await manager.untitled.resolve()); @@ -340,7 +340,7 @@ suite('UntitledFileWorkingCopyManager', () => { accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.fileDialogService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, - accessor.environmentService, accessor.dialogService, accessor.decorationsService + accessor.environmentService, accessor.dialogService, accessor.decorationsService, accessor.progressService )); const result = disposables.add(await manager.untitled.resolve()); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index e7c8dbf262a..bad005e50c6 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -396,7 +396,8 @@ export class TestServiceAccessor { @IInstantiationService public instantiationService: IInstantiationService, @IElevatedFileService public elevatedFileService: IElevatedFileService, @IWorkspaceTrustRequestService public workspaceTrustRequestService: TestWorkspaceTrustRequestService, - @IDecorationsService public decorationsService: IDecorationsService + @IDecorationsService public decorationsService: IDecorationsService, + @IProgressService public progressService: IProgressService, ) { } } @@ -1545,6 +1546,10 @@ export class TestHostService implements IHostService { readonly colorScheme = ColorScheme.DARK; onDidChangeColorScheme = Event.None; + + getPathForFile(file: File): string | undefined { + return undefined; + } } export class TestFilesConfigurationService extends FilesConfigurationService { diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 1a938d7dbd6..2caf3646a28 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -30,6 +30,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IAutoSaveConfiguration, IAutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; import { IMarker, IMarkerData, IMarkerService, IResourceMarker, MarkerStatistics } from 'vs/platform/markers/common/markers'; +import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; export class TestLoggerService extends AbstractLoggerService { constructor(logsHome?: URI) { @@ -253,7 +254,7 @@ export class TestWorkingCopyFileService implements IWorkingCopyFileService { readonly hasSaveParticipants = false; addSaveParticipant(participant: IStoredFileWorkingCopySaveParticipant): IDisposable { return Disposable.None; } - async runSaveParticipants(workingCopy: IWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, token: CancellationToken): Promise { } + async runSaveParticipants(workingCopy: IWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { } async delete(operations: IDeleteOperation[], token: CancellationToken, undoInfo?: IFileOperationUndoRedoInfo): Promise { } diff --git a/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts b/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts index 93f9761211b..a75d7eb6f7e 100644 --- a/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.aiTextSearchProvider.d.ts @@ -5,99 +5,6 @@ declare module 'vscode' { - // https://github.com/microsoft/vscode/issues/205317 - - /** - * The parameters of a query for text search. - */ - export interface TextSearchQuery { - /** - * The text pattern to search for. - */ - pattern: string; - - /** - * Whether or not `pattern` should match multiple lines of text. - */ - isMultiline?: boolean; - - /** - * Whether or not `pattern` should be interpreted as a regular expression. - */ - isRegExp?: boolean; - - /** - * Whether or not the search should be case-sensitive. - */ - isCaseSensitive?: boolean; - - /** - * Whether or not to search for whole word matches only. - */ - isWordMatch?: boolean; - } - - /** - * Options common to file and text search - */ - export interface SearchOptions { - /** - * The root folder to search within. - */ - folder: Uri; - - /** - * Files that match an `includes` glob pattern should be included in the search. - */ - includes: GlobString[]; - - /** - * Files that match an `excludes` glob pattern should be excluded from the search. - */ - excludes: GlobString[]; - - /** - * Whether external files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useIgnoreFiles"`. - */ - useIgnoreFiles: boolean; - - /** - * Whether symlinks should be followed while searching. - * See the vscode setting `"search.followSymlinks"`. - */ - followSymlinks: boolean; - - /** - * Whether global files that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useGlobalIgnoreFiles"`. - */ - useGlobalIgnoreFiles: boolean; - - /** - * Whether files in parent directories that exclude files, like .gitignore, should be respected. - * See the vscode setting `"search.useParentIgnoreFiles"`. - */ - useParentIgnoreFiles: boolean; - } - - /** - * Options to specify the size of the result text preview. - * These options don't affect the size of the match itself, just the amount of preview text. - */ - export interface TextSearchPreviewOptions { - /** - * The maximum number of lines in the preview. - * Only search providers that support multiline search will ever return more than one line in the match. - */ - matchLines: number; - - /** - * The maximum number of characters included per line. - */ - charsPerLine: number; - } - /** * Options that apply to AI text search. */ @@ -129,109 +36,6 @@ declare module 'vscode' { } - - /** - * A message regarding a completed search. - */ - export interface TextSearchCompleteMessage { - /** - * Markdown text of the message. - */ - text: string; - /** - * Whether the source of the message is trusted, command links are disabled for untrusted message sources. - * Messaged are untrusted by default. - */ - trusted?: boolean; - /** - * The message type, this affects how the message will be rendered. - */ - type: TextSearchCompleteMessageType; - } - - /** - * Information collected when text search is complete. - */ - export interface TextSearchComplete { - /** - * Whether the search hit the limit on the maximum number of search results. - * `maxResults` on {@linkcode AITextSearchOptions} specifies the max number of results. - * - If exactly that number of matches exist, this should be false. - * - If `maxResults` matches are returned and more exist, this should be true. - * - If search hits an internal limit which is less than `maxResults`, this should be true. - */ - limitHit?: boolean; - - /** - * Additional information regarding the state of the completed search. - * - * Messages with "Information" style support links in markdown syntax: - * - Click to [run a command](command:workbench.action.OpenQuickPick) - * - Click to [open a website](https://aka.ms) - * - * Commands may optionally return { triggerSearch: true } to signal to the editor that the original search should run be again. - */ - message?: TextSearchCompleteMessage | TextSearchCompleteMessage[]; - } - - /** - * A preview of the text result. - */ - export interface TextSearchMatchPreview { - /** - * The matching lines of text, or a portion of the matching line that contains the match. - */ - text: string; - - /** - * The Range within `text` corresponding to the text of the match. - * The number of matches must match the TextSearchMatch's range property. - */ - matches: Range | Range[]; - } - - /** - * A match from a text search - */ - export interface TextSearchMatch { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * The range of the match within the document, or multiple ranges for multiple matches. - */ - ranges: Range | Range[]; - - /** - * A preview of the text match. - */ - preview: TextSearchMatchPreview; - } - - /** - * A line of context surrounding a TextSearchMatch. - */ - export interface TextSearchContext { - /** - * The uri for the matching document. - */ - uri: Uri; - - /** - * One line of text. - * previewOptions.charsPerLine applies to this - */ - text: string; - - /** - * The line number of this line of context. - */ - lineNumber: number; - } - - /** * An AITextSearchProvider provides additional AI text search results in the workspace. */ diff --git a/yarn.lock b/yarn.lock index b651237b72f..ee60608ade2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6229,10 +6229,10 @@ js-yaml@^3.13.0: argparse "^1.0.7" esprima "^4.0.0" -jschardet@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" - integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +jschardet@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" + integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== jsdoc-type-pratt-parser@~4.0.0: version "4.0.0"