diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 98d61becadd..cc4464178f4 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -318,7 +318,7 @@ steps: targetPath: .build/logs displayName: "Publish Log Files" continueOnError: true - condition: failed() + condition: succeededOrFailed() - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index 8f3b4aad146..98eb2c6a9d4 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -387,7 +387,7 @@ steps: targetPath: .build/logs displayName: "Publish Log Files" continueOnError: true - condition: and(failed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 7501869b57d..7fd6ad92186 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -339,7 +339,7 @@ steps: targetPath: .build\logs displayName: "Publish Log Files" continueOnError: true - condition: and(failed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index e3968df3a0d..1f134c56866 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -3129,9 +3129,7 @@ export class CommandCenter { if (result) { const resultFn = choices.get(result); - if (resultFn) { - resultFn(); - } + resultFn?.(); } }); }; diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 5e7ddef8599..26e1773f277 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -556,9 +556,7 @@ export class Git { private async _exec(args: string[], options: SpawnOptions = {}): Promise> { const child = this.spawn(args, options); - if (options.onSpawn) { - options.onSpawn(child); - } + options.onSpawn?.(child); if (options.input) { child.stdin!.end(options.input, 'utf8'); diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 3fb9603a230..2da36da1054 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -251,9 +251,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { const settings = change.settings; - if (runtime.configureHttpRequests) { - runtime.configureHttpRequests(settings?.http?.proxy, !!settings.http?.proxyStrictSSL); - } + runtime.configureHttpRequests?.(settings?.http?.proxy, !!settings.http?.proxyStrictSSL); jsonConfigurationSettings = settings.json?.schemas; validateEnabled = !!settings.json?.validate?.enable; updateConfiguration(); diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index d1457dd3e2a..337872e596f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -185,12 +185,12 @@ function stripAngleBrackets(link: string) { /** * Matches `[text](link)` */ -const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; /** * Matches `[text]()` */ -const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; /** diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 37d9330d357..ba71ca4d559 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -101,6 +101,21 @@ suite('markdown.DocumentLinkProvider', () => { } }); + test('Should ignore texts in brackets inside link title (#150921)', async () => { + { + const links = await getLinksForFile('[some [inner bracket pairs] in title]()'); + assertLinksEqual(links, [ + new vscode.Range(0, 39, 0, 43), + ]); + } + { + const links = await getLinksForFile('[some [inner bracket pairs] in title](link)'); + assertLinksEqual(links, [ + new vscode.Range(0, 38, 0, 42) + ]); + } + }); + test('Should handle two links without space', async () => { const links = await getLinksForFile('a ([test](test)[test2](test2)) c'); assertLinksEqual(links, [ diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 676914a2dfb..4c196dc1170 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -287,8 +287,9 @@ async function provideNpmScriptsForFolder(context: ExtensionContext, packageJson result.push({ task, location: new Location(packageJsonUri, nameRange) }); } - // always add npm install (without a problem matcher) - result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) }); + if (!workspace.getConfiguration('npm', folder).get('scriptExplorerExclude', []).find(e => e.includes('install'))) { + result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) }); + } return result; } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index c4d4733578a..270d9ea9aa2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -927,6 +927,80 @@ const apiTestContentProvider: vscode.NotebookContentProvider = { assert.strictEqual(executionWasCancelled, true); }); + + test('appendOutput to different cell', async function () { + const notebook = await openRandomNotebookDocument(); + const editor = await vscode.window.showNotebookDocument(notebook); + const cell0 = editor.notebook.cellAt(0); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + const cell1 = editor.notebook.cellAt(1); + + const nextCellKernel = new class extends Kernel { + constructor() { + super('nextCellKernel', 'Append to cell kernel'); + } + + override async _runCell(cell: vscode.NotebookCell) { + const task = this.controller.createNotebookCellExecution(cell); + task.start(); + await task.appendOutput([new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.text('my output') + ])], cell1); + await task.appendOutput([new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.text('my output 2') + ])], cell1); + task.end(true); + } + }; + testDisposables.push(nextCellKernel.controller); + + await withEvent(vscode.workspace.onDidChangeNotebookDocument, async (event) => { + await assertKernel(nextCellKernel, notebook); + await vscode.commands.executeCommand('notebook.cell.execute'); + await event; + assert.strictEqual(cell0.outputs.length, 0, 'should not change cell 0'); + assert.strictEqual(cell1.outputs.length, 2, 'should update cell 1'); + assert.strictEqual(cell1.outputs[0].items.length, 1); + assert.deepStrictEqual(new TextDecoder().decode(cell1.outputs[0].items[0].data), 'my output'); + }); + }); + + test('replaceOutput to different cell', async function () { + const notebook = await openRandomNotebookDocument(); + const editor = await vscode.window.showNotebookDocument(notebook); + const cell0 = editor.notebook.cellAt(0); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + const cell1 = editor.notebook.cellAt(1); + + const nextCellKernel = new class extends Kernel { + constructor() { + super('nextCellKernel', 'Replace to cell kernel'); + } + + override async _runCell(cell: vscode.NotebookCell) { + const task = this.controller.createNotebookCellExecution(cell); + task.start(); + await task.replaceOutput([new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.text('my output') + ])], cell1); + await task.replaceOutput([new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.text('my output 2') + ])], cell1); + task.end(true); + } + }; + testDisposables.push(nextCellKernel.controller); + + await withEvent(vscode.workspace.onDidChangeNotebookDocument, async (event) => { + await assertKernel(nextCellKernel, notebook); + await vscode.commands.executeCommand('notebook.cell.execute'); + await event; + assert.strictEqual(cell0.outputs.length, 0, 'should not change cell 0'); + assert.strictEqual(cell1.outputs.length, 1, 'should update cell 1'); + assert.strictEqual(cell1.outputs[0].items.length, 1); + assert.deepStrictEqual(new TextDecoder().decode(cell1.outputs[0].items[0].data), 'my output 2'); + }); + }); }); (vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('statusbar', () => { diff --git a/package.json b/package.json index cc87e68f1eb..584111cd29e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.68.0", + "version": "1.69.0", "distro": "2966cd72fc1a3a5fb89bf2d85a1a66e56206961a", "author": { "name": "Microsoft Corporation" diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 6db45c2bfc0..d8a7ac484c4 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1733,9 +1733,7 @@ export class DragAndDropObserver extends Disposable { this._register(addDisposableListener(this.element, EventType.DRAG_OVER, (e: DragEvent) => { e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome) - if (this.callbacks.onDragOver) { - this.callbacks.onDragOver(e, e.timeStamp - this.dragStartTime); - } + this.callbacks.onDragOver?.(e, e.timeStamp - this.dragStartTime); })); this._register(addDisposableListener(this.element, EventType.DRAG_LEAVE, (e: DragEvent) => { diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 9897367c182..c75402d6aea 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -220,9 +220,7 @@ export class ContextView extends Disposable { this.doLayout(); // Focus - if (this.delegate.focus) { - this.delegate.focus(); - } + this.delegate.focus?.(); } getViewElement(): HTMLElement { diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index dca11631c87..e121bcc10c8 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -857,9 +857,7 @@ class LeafNode implements ISplitView, IDisposable { set boundarySashes(boundarySashes: IRelativeBoundarySashes) { this._boundarySashes = boundarySashes; - if (this.view.setBoundarySashes) { - this.view.setBoundarySashes(toAbsoluteBoundarySashes(boundarySashes, this.orientation)); - } + this.view.setBoundarySashes?.(toAbsoluteBoundarySashes(boundarySashes, this.orientation)); } layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { @@ -897,9 +895,7 @@ class LeafNode implements ISplitView, IDisposable { } setVisible(visible: boolean): void { - if (this.view.setVisible) { - this.view.setVisible(visible); - } + this.view.setVisible?.(visible); } dispose(): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index fa70d9b7714..f10aa46ecef 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -1032,9 +1032,7 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragData = new ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); - if (this.dnd.onDragStart) { - this.dnd.onDragStart(this.currentDragData, event); - } + this.dnd.onDragStart?.(this.currentDragData, event); } private onDragOver(event: IListDragEvent): boolean { @@ -1169,9 +1167,7 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragData = undefined; StaticDND.CurrentDragAndDropData = undefined; - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(event); - } + this.dnd.onDragEnd?.(event); } private clearDragOverFeedback(): void { @@ -1379,16 +1375,12 @@ export class ListView implements ISpliceable, IDisposable { if (renderer) { renderer.renderElement(item.element, index, row.templateData, undefined); - if (renderer.disposeElement) { - renderer.disposeElement(item.element, index, row.templateData, undefined); - } + renderer.disposeElement?.(item.element, index, row.templateData, undefined); } item.size = row.domNode.offsetHeight; - if (this.virtualDelegate.setDynamicHeight) { - this.virtualDelegate.setDynamicHeight(item.element, item.size); - } + this.virtualDelegate.setDynamicHeight?.(item.element, item.size); item.lastDynamicHeightWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode); @@ -1429,9 +1421,7 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); if (renderer) { - if (renderer.disposeElement) { - renderer.disposeElement(item.element, -1, item.row.templateData, undefined); - } + renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined); renderer.disposeTemplate(item.row.templateData); } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 0bfef7f8ae7..4b6f146a51e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1109,9 +1109,7 @@ class PipelineRenderer implements IListRenderer { let i = 0; for (const renderer of this.renderers) { - if (renderer.disposeElement) { - renderer.disposeElement(element, index, templateData[i], height); - } + renderer.disposeElement?.(element, index, templateData[i], height); i += 1; } @@ -1182,9 +1180,7 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - if (this.dnd.onDragStart) { - this.dnd.onDragStart(data, originalEvent); - } + this.dnd.onDragStart?.(data, originalEvent); } onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { @@ -1196,9 +1192,7 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } onDragEnd(originalEvent: DragEvent): void { - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(originalEvent); - } + this.dnd.onDragEnd?.(originalEvent); } drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { @@ -1328,9 +1322,7 @@ export class List implements ISpliceable, IThemable, IDisposable { if (this.accessibilityProvider) { baseRenderers.push(new AccessibiltyRenderer(this.accessibilityProvider)); - if (this.accessibilityProvider.onDidChangeActiveDescendant) { - this.accessibilityProvider.onDidChangeActiveDescendant(this.onDidChangeActiveDescendant, this, this.disposables); - } + this.accessibilityProvider.onDidChangeActiveDescendant?.(this.onDidChangeActiveDescendant, this, this.disposables); } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 6242fc8278f..57fbf0b1544 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -231,9 +231,7 @@ abstract class ViewItem { this.container.classList.toggle('visible', visible); - if (this.view.setVisible) { - this.view.setVisible(visible); - } + this.view.setVisible?.(visible); } get minimumSize(): number { return this.visible ? this.view.minimumSize : 0; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 823fce9806f..7daab6f12d8 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -70,9 +70,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - if (this.dnd.onDragStart) { - this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent); - } + this.dnd.onDragStart?.(asTreeDragAndDropData(data), originalEvent); } onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { @@ -137,9 +135,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } onDragEnd(originalEvent: DragEvent): void { - if (this.dnd.onDragEnd) { - this.dnd.onDragEnd(originalEvent); - } + this.dnd.onDragEnd?.(originalEvent); } } @@ -220,9 +216,7 @@ export class ComposedTreeDelegate implements IListV } setDynamicHeight(element: N, height: number): void { - if (this.delegate.setDynamicHeight) { - this.delegate.setDynamicHeight(element.element, height); - } + this.delegate.setDynamicHeight?.(element.element, height); } } @@ -350,9 +344,7 @@ class TreeRenderer implements IListRenderer Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); - if (renderer.onDidChangeTwistieState) { - renderer.onDidChangeTwistieState(this.onDidChangeTwistieState, this, this.disposables); - } + renderer.onDidChangeTwistieState?.(this.onDidChangeTwistieState, this, this.disposables); } updateOptions(options: ITreeRendererOptions = {}): void { @@ -414,9 +406,7 @@ class TreeRenderer implements IListRenderer disposeElement(node: ITreeNode, index: number, templateData: ITreeListTemplateData, height: number | undefined): void { templateData.indentGuidesDisposable.dispose(); - if (this.renderer.disposeElement) { - this.renderer.disposeElement(node, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(node, index, templateData.templateData, height); if (typeof height === 'number') { this.renderedNodes.delete(node); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 5f1b89e1e03..fbd7ad55081 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -119,9 +119,7 @@ class AsyncDataTreeRenderer implements IT } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); } disposeTemplate(templateData: IDataTreeListTemplateData): void { @@ -196,9 +194,7 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { @@ -210,9 +206,7 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop i } disposeElement(node: ITreeNode, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); - } + this.renderer.disposeElement?.(this.nodeMapper.map(node) as ITreeNode, index, templateData.templateData, height); } disposeCompressedElements(node: ITreeNode>, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { - if (this.renderer.disposeCompressedElements) { - this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode, TFilterData>, index, templateData.templateData, height); - } + this.renderer.disposeCompressedElements?.(this.compressibleNodeMapperProvider().map(node) as ITreeNode, TFilterData>, index, templateData.templateData, height); } disposeTemplate(templateData: IDataTreeListTemplateData): void { diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index afb6fa662fe..4dfe854ec04 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -140,9 +140,7 @@ export class DataTree extends AbstractTree) => { diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 44c0d42819b..a7f024af3df 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -554,9 +554,7 @@ export class IndexTreeModel, TFilterData = voi node.renderNodeCount = renderNodeCount; } - if (onDidCreateNode) { - onDidCreateNode(node); - } + onDidCreateNode?.(node); return node; } diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index 847cb86d2fe..bd4aa34b2c7 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -140,13 +140,9 @@ class CompressibleRenderer, TFilterData, TTemplateDat disposeElement(node: ITreeNode, index: number, templateData: CompressibleTemplateData, height: number | undefined): void { if (templateData.compressedTreeNode) { - if (this.renderer.disposeCompressedElements) { - this.renderer.disposeCompressedElements(templateData.compressedTreeNode, index, templateData.data, height); - } + this.renderer.disposeCompressedElements?.(templateData.compressedTreeNode, index, templateData.data, height); } else { - if (this.renderer.disposeElement) { - this.renderer.disposeElement(node, index, templateData.data, height); - } + this.renderer.disposeElement?.(node, index, templateData.data, height); } } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 031b10f3e9f..e45c78ae5e3 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -354,9 +354,7 @@ export class Delayer implements IDisposable { this.cancelTimeout(); if (this.completionPromise) { - if (this.doReject) { - this.doReject(new CancellationError()); - } + this.doReject?.(new CancellationError()); this.completionPromise = null; } } @@ -885,9 +883,7 @@ export class RunOnceScheduler { } protected doRun(): void { - if (this.runner) { - this.runner(); - } + this.runner?.(); } } @@ -960,9 +956,7 @@ export class ProcessTimeRunOnceScheduler { // time elapsed clearInterval(this.intervalToken); this.intervalToken = -1; - if (this.runner) { - this.runner(); - } + this.runner?.(); } } @@ -985,9 +979,7 @@ export class RunOnceWorker extends RunOnceScheduler { const units = this.units; this.units = []; - if (this.runner) { - this.runner(units); - } + this.runner?.(units); } override dispose(): void { diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index d33ad816a9b..ba608b26ceb 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -95,6 +95,7 @@ export interface SerializedError { readonly name: string; readonly message: string; readonly stack: string; + readonly noTelemetry: boolean; } export function transformErrorForSerialization(error: Error): SerializedError; @@ -107,7 +108,8 @@ export function transformErrorForSerialization(error: any): any { $isError: true, name, message, - stack + stack, + noTelemetry: error instanceof ErrorNoTelemetry }; } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index b727ee7e8bc..6528709a915 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -690,9 +690,7 @@ export class Emitter { } const result = listener.subscription.set(() => { - if (removeMonitor) { - removeMonitor(); - } + removeMonitor?.(); if (!this._disposed) { removeListener(); if (this._options && this._options.onLastListenerRemove) { diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 85665a0668e..5e9df9db7dd 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -283,9 +283,7 @@ export class SimpleWorkerClient extends Disp (err: any) => { // in Firefox, web workers fail lazily :( // we will reject the proxy - if (lazyProxyReject) { - lazyProxyReject(err); - } + lazyProxyReject?.(err); } )); diff --git a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts index 7c058d45990..74ca18173c0 100644 --- a/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-sandbox/contextmenu.ts @@ -15,9 +15,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide const onClickChannel = `vscode:onContextMenu${contextMenuId}`; const onClickChannelHandler = (event: unknown, itemId: number, context: IContextMenuEvent) => { const item = processedItems[itemId]; - if (item.click) { - item.click(context); - } + item.click?.(context); }; ipcRenderer.once(onClickChannel, onClickChannelHandler); @@ -28,9 +26,7 @@ export function popup(items: IContextMenuItem[], options?: IPopupOptions, onHide ipcRenderer.removeListener(onClickChannel, onClickChannelHandler); - if (onHide) { - onHide(); - } + onHide?.(); }); ipcRenderer.send(CONTEXT_MENU_CHANNEL, contextMenuId, items.map(item => createItem(item, processedItems)), onClickChannel, options); diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 2231be036c5..42033ee91aa 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -707,9 +707,7 @@ export class ChannelClient implements IChannelClient, IDisposable { const handler = this.handlers.get(response.id); - if (handler) { - handler(response); - } + handler?.(response); } @memoize diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index bc8c91530aa..fa6d8073ab7 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -26,9 +26,7 @@ export class Server extends IPCServer { super({ send: r => { try { - if (process.send) { - process.send((r.buffer).toString('base64')); - } + process.send?.((r.buffer).toString('base64')); } catch (e) { /* not much to do */ } }, onMessage: Event.fromNodeEventEmitter(process, 'message', msg => VSBuffer.wrap(Buffer.from(msg, 'base64'))) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index e7099f2655b..1a496080028 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1406,9 +1406,7 @@ export class QuickInputController extends Disposable { return new Promise((doResolve, reject) => { let resolve = (result: R) => { resolve = doResolve; - if (options.onKeyMods) { - options.onKeyMods(input.keyMods); - } + options.onKeyMods?.(input.keyMods); doResolve(result); }; if (token.isCancellationRequested) { diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index e8bfec83a24..789481987b9 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -440,14 +440,10 @@ class SQLiteStorageDatabaseLogger { } trace(msg: string): void { - if (this.logTrace) { - this.logTrace(msg); - } + this.logTrace?.(msg); } error(error: string | Error): void { - if (this.logError) { - this.logError(error); - } + this.logError?.(error); } } diff --git a/src/vs/base/test/common/troubleshooting.ts b/src/vs/base/test/common/troubleshooting.ts index 84051e8a18a..7cfee55bd0b 100644 --- a/src/vs/base/test/common/troubleshooting.ts +++ b/src/vs/base/test/common/troubleshooting.ts @@ -47,13 +47,9 @@ export function endTrackingDisposables(): void { } export function beginLoggingFS(withStacks: boolean = false): void { - if ((self).beginLoggingFS) { - (self).beginLoggingFS(withStacks); - } + (self).beginLoggingFS?.(withStacks); } export function endLoggingFS(): void { - if ((self).endLoggingFS) { - (self).endLoggingFS(); - } + (self).endLoggingFS?.(); } diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 64df9a0859f..5e7104b9969 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -33,69 +33,47 @@ export class ViewUserInputEvents { } public emitKeyDown(e: IKeyboardEvent): void { - if (this.onKeyDown) { - this.onKeyDown(e); - } + this.onKeyDown?.(e); } public emitKeyUp(e: IKeyboardEvent): void { - if (this.onKeyUp) { - this.onKeyUp(e); - } + this.onKeyUp?.(e); } public emitContextMenu(e: IEditorMouseEvent): void { - if (this.onContextMenu) { - this.onContextMenu(this._convertViewToModelMouseEvent(e)); - } + this.onContextMenu?.(this._convertViewToModelMouseEvent(e)); } public emitMouseMove(e: IEditorMouseEvent): void { - if (this.onMouseMove) { - this.onMouseMove(this._convertViewToModelMouseEvent(e)); - } + this.onMouseMove?.(this._convertViewToModelMouseEvent(e)); } public emitMouseLeave(e: IPartialEditorMouseEvent): void { - if (this.onMouseLeave) { - this.onMouseLeave(this._convertViewToModelMouseEvent(e)); - } + this.onMouseLeave?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDown(e: IEditorMouseEvent): void { - if (this.onMouseDown) { - this.onMouseDown(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDown?.(this._convertViewToModelMouseEvent(e)); } public emitMouseUp(e: IEditorMouseEvent): void { - if (this.onMouseUp) { - this.onMouseUp(this._convertViewToModelMouseEvent(e)); - } + this.onMouseUp?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDrag(e: IEditorMouseEvent): void { - if (this.onMouseDrag) { - this.onMouseDrag(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDrag?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDrop(e: IPartialEditorMouseEvent): void { - if (this.onMouseDrop) { - this.onMouseDrop(this._convertViewToModelMouseEvent(e)); - } + this.onMouseDrop?.(this._convertViewToModelMouseEvent(e)); } public emitMouseDropCanceled(): void { - if (this.onMouseDropCanceled) { - this.onMouseDropCanceled(); - } + this.onMouseDropCanceled?.(); } public emitMouseWheel(e: IMouseWheelEvent): void { - if (this.onMouseWheel) { - this.onMouseWheel(e); - } + this.onMouseWheel?.(e); } private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 5e2dba3fc0b..1a4601d7434 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -2136,9 +2136,7 @@ class CodeEditorWidgetFocusTracker extends Disposable { } public refreshState(): void { - if (this._domFocusTracker.refreshState) { - this._domFocusTracker.refreshState(); - } + this._domFocusTracker.refreshState?.(); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeAction.ts b/src/vs/editor/contrib/codeAction/browser/codeAction.ts index 75f7f392e2e..72d71d496f0 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeAction.ts @@ -22,6 +22,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat export const codeActionCommandId = 'editor.action.codeAction'; export const refactorCommandId = 'editor.action.refactor'; +export const refactorPreviewCommandId = 'editor.action.refactor.preview'; export const sourceActionCommandId = 'editor.action.sourceAction'; export const organizeImportsCommandId = 'editor.action.organizeImports'; export const fixAllCommandId = 'editor.action.fixAll'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index 6e4c7a0b42e..6043a70dedf 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -18,7 +18,7 @@ import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CodeActionTriggerType } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; +import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, refactorPreviewCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { CodeActionUi } from 'vs/editor/contrib/codeAction/browser/codeActionUi'; import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; import * as nls from 'vs/nls'; @@ -27,8 +27,8 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IMarkerService } from 'vs/platform/markers/common/markers'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; @@ -39,6 +39,26 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { new RegExp('(\\s|^)' + escapeRegExpCharacters(kind.value) + '\\b')); } +function RefactorTrigger(editor: ICodeEditor, userArgs: any, preview: boolean) { + const args = CodeActionCommandArgs.fromUser(userArgs, { + kind: CodeActionKind.Refactor, + apply: CodeActionAutoApply.Never + }); + return triggerCodeActionsForEditorSelection(editor, + typeof userArgs?.kind === 'string' + ? args.preferred + ? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind) + : nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind) + : args.preferred + ? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available") + : nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), + { + include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None, + onlyIncludePreferredActions: args.preferred + }, + args.apply, preview); +} + const argsSchema: IJSONSchema = { type: 'object', defaultSnippets: [{ body: { kind: '' } }], @@ -92,11 +112,12 @@ export class QuickFixController extends Disposable implements IEditorContributio this._model = this._register(new CodeActionModel(this._editor, languageFeaturesService.codeActionProvider, markerService, contextKeyService, progressService)); this._register(this._model.onDidChangeState(newState => this.update(newState))); + this._ui = new Lazy(() => this._register(new CodeActionUi(editor, QuickFixAction.Id, AutoFixAction.Id, { - applyCodeAction: async (action, retrigger) => { + applyCodeAction: async (action, retrigger, preview) => { try { - await this._applyCodeAction(action); + await this._applyCodeAction(action, preview); } finally { if (retrigger) { this._trigger({ type: CodeActionTriggerType.Auto, filter: {} }); @@ -118,7 +139,8 @@ export class QuickFixController extends Disposable implements IEditorContributio public manualTriggerAtCurrentPosition( notAvailableMessage: string, filter?: CodeActionFilter, - autoApply?: CodeActionAutoApply + autoApply?: CodeActionAutoApply, + preview?: boolean ): void { if (!this._editor.hasModel()) { return; @@ -126,22 +148,22 @@ export class QuickFixController extends Disposable implements IEditorContributio MessageController.get(this._editor)?.closeMessage(); const triggerPosition = this._editor.getPosition(); - this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); + this._trigger({ type: CodeActionTriggerType.Invoke, filter, autoApply, context: { notAvailableMessage, position: triggerPosition }, preview }); } private _trigger(trigger: CodeActionTrigger) { return this._model.trigger(trigger); } - private _applyCodeAction(action: CodeActionItem): Promise { - return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor); + private _applyCodeAction(action: CodeActionItem, preview: boolean): Promise { + return this._instantiationService.invokeFunction(applyCodeAction, action, { preview, editor: this._editor }); } } export async function applyCodeAction( accessor: ServicesAccessor, item: CodeActionItem, - editor?: ICodeEditor, + options?: { preview?: boolean; editor?: ICodeEditor } ): Promise { const bulkEditService = accessor.get(IBulkEditService); const commandService = accessor.get(ICommandService); @@ -171,11 +193,12 @@ export async function applyCodeAction( if (item.action.edit) { await bulkEditService.apply(ResourceEdit.convert(item.action.edit), { - editor, + editor: options?.editor, label: item.action.title, quotableLabel: item.action.title, code: 'undoredo.codeAction', - respectAutoSaveConfig: true + respectAutoSaveConfig: true, + showPreview: options?.preview, }); } @@ -206,12 +229,13 @@ function triggerCodeActionsForEditorSelection( editor: ICodeEditor, notAvailableMessage: string, filter: CodeActionFilter | undefined, - autoApply: CodeActionAutoApply | undefined + autoApply: CodeActionAutoApply | undefined, + preview: boolean = false ): void { if (editor.hasModel()) { const controller = QuickFixController.get(editor); if (controller) { - controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply); + controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); } } } @@ -306,23 +330,27 @@ export class RefactorAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { - const args = CodeActionCommandArgs.fromUser(userArgs, { - kind: CodeActionKind.Refactor, - apply: CodeActionAutoApply.Never + return RefactorTrigger(editor, userArgs, false); + } +} + +export class RefactorPreview extends EditorAction { + + constructor() { + super({ + id: refactorPreviewCommandId, + label: nls.localize('refactor.preview.label', "Refactor with Preview..."), + alias: 'Refactor Preview...', + precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), + description: { + description: 'Refactor Preview...', + args: [{ name: 'args', schema: argsSchema }] + } }); - return triggerCodeActionsForEditorSelection(editor, - typeof userArgs?.kind === 'string' - ? args.preferred - ? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind) - : nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind) - : args.preferred - ? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available") - : nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), - { - include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None, - onlyIncludePreferredActions: args.preferred, - }, - args.apply); + } + + public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { + return RefactorTrigger(editor, userArgs, true); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionContributions.ts b/src/vs/editor/contrib/codeAction/browser/codeActionContributions.ts index ddbe57e0e31..359bbf7941f 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionContributions.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionContributions.ts @@ -4,12 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands'; +import { AutoFixAction, CodeActionCommand, FixAllAction, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, RefactorPreview, SourceAction } from 'vs/editor/contrib/codeAction/browser/codeActionCommands'; registerEditorContribution(QuickFixController.ID, QuickFixController); registerEditorAction(QuickFixAction); registerEditorAction(RefactorAction); +registerEditorAction(RefactorPreview); registerEditorAction(SourceAction); registerEditorAction(OrganizeImportsAction); registerEditorAction(AutoFixAction); diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index 9978c8a442c..56da9f50fb3 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -23,7 +23,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; interface CodeActionWidgetDelegate { - onSelectCodeAction: (action: CodeActionItem) => Promise; + onSelectCodeAction: (action: CodeActionItem, trigger: CodeActionTrigger) => Promise; } interface ResolveCodeActionKeybinding { @@ -115,7 +115,7 @@ export class CodeActionMenu extends Disposable { actionsToShow: readonly CodeActionItem[], documentation: readonly Command[] ): IAction[] { - const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item)); + const toCodeActionAction = (item: CodeActionItem): CodeActionAction => new CodeActionAction(item.action, () => this._delegate.onSelectCodeAction(item, trigger)); const result: IAction[] = actionsToShow .map(toCodeActionAction); diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts index e191d6ba9f5..09c82ccd2ea 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts @@ -31,7 +31,7 @@ export class CodeActionUi extends Disposable { quickFixActionId: string, preferredFixActionId: string, private readonly delegate: { - applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean) => Promise; + applyCodeAction: (action: CodeActionItem, regtriggerAfterApply: boolean, preview: boolean) => Promise; }, @IInstantiationService instantiationService: IInstantiationService, ) { @@ -39,8 +39,8 @@ export class CodeActionUi extends Disposable { this._codeActionWidget = new Lazy(() => { return this._register(instantiationService.createInstance(CodeActionMenu, this._editor, { - onSelectCodeAction: async (action) => { - this.delegate.applyCodeAction(action, /* retrigger */ true); + onSelectCodeAction: async (action, trigger) => { + this.delegate.applyCodeAction(action, /* retrigger */ true, Boolean(trigger.preview)); } })); }); @@ -85,7 +85,7 @@ export class CodeActionUi extends Disposable { if (validActionToApply) { try { this._lightBulbWidget.getValue().hide(); - await this.delegate.applyCodeAction(validActionToApply, false); + await this.delegate.applyCodeAction(validActionToApply, false, false); } finally { actions.dispose(); } diff --git a/src/vs/editor/contrib/codeAction/browser/types.ts b/src/vs/editor/contrib/codeAction/browser/types.ts index ae406530c68..cfc6d344980 100644 --- a/src/vs/editor/contrib/codeAction/browser/types.ts +++ b/src/vs/editor/contrib/codeAction/browser/types.ts @@ -122,6 +122,7 @@ export interface CodeActionTrigger { readonly notAvailableMessage: string; readonly position: Position; }; + readonly preview?: boolean; } export class CodeActionCommandArgs { diff --git a/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts index 2ad4c28a6eb..8710f815764 100644 --- a/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/copyPaste/browser/copyPasteController.ts @@ -53,7 +53,7 @@ export class CopyPasteController extends Disposable implements IEditorContributi private readonly _editor: ICodeEditor; - private _currentClipboardItem: undefined | { + private _currentClipboardItem?: { readonly handle: string; readonly dataTransferPromise: CancelablePromise; }; @@ -70,131 +70,134 @@ export class CopyPasteController extends Disposable implements IEditorContributi this._editor = editor; const container = editor.getContainerDomNode(); - - this._register(addDisposableListener(container, 'copy', (e: ClipboardEvent) => { - if (!e.clipboardData) { - return; - } - - const model = editor.getModel(); - const selections = this._editor.getSelections(); - if (!model || !selections?.length) { - return; - } - - if (!this.arePasteActionsEnabled(model)) { - return; - } - - const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste); - if (!providers.length) { - return; - } - - const dataTransfer = toVSDataTransfer(e.clipboardData); - - // Save off a handle pointing to data that VS Code maintains. - const handle = generateUuid(); - e.clipboardData.setData(vscodeClipboardMime, handle); - - const promise = createCancelablePromise(async token => { - const results = await Promise.all(providers.map(provider => { - return provider.prepareDocumentPaste!(model, selections, dataTransfer, token); - })); - - for (const result of results) { - result?.forEach((value, key) => { - dataTransfer.replace(key, value); - }); - } - - return dataTransfer; - }); - - this._currentClipboardItem?.dataTransferPromise.cancel(); - this._currentClipboardItem = { handle: handle, dataTransferPromise: promise }; - })); - - this._register(addDisposableListener(container, 'paste', async (e: ClipboardEvent) => { - const selections = this._editor.getSelections(); - if (!e.clipboardData || !selections?.length || !editor.hasModel()) { - return; - } - - const model = editor.getModel(); - if (!this.arePasteActionsEnabled(model)) { - return; - } - - const handle = e.clipboardData?.getData(vscodeClipboardMime); - if (typeof handle !== 'string') { - return; - } - - const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model); - if (!providers.length) { - return; - } - - e.preventDefault(); - e.stopImmediatePropagation(); - - const originalDocVersion = model.getVersionId(); - const tokenSource = new EditorStateCancellationTokenSource(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection); - - try { - const dataTransfer = toVSDataTransfer(e.clipboardData); - - if (handle && this._currentClipboardItem?.handle === handle) { - const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise; - toMergeDataTransfer.forEach((value, key) => { - dataTransfer.replace(key, value); - }); - } - - if (!dataTransfer.has(Mimes.uriList)) { - const resources = await this._clipboardService.readResources(); - if (resources.length) { - const value = resources.join('\n'); - dataTransfer.append(Mimes.uriList, createStringDataTransferItem(value)); - } - } - - dataTransfer.delete(vscodeClipboardMime); - - for (const provider of [...providers, defaultPasteEditProvider]) { - if (!provider.pasteMimeTypes.some(type => { - if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) { - return [...dataTransfer.values()].some(item => item.asFile()); - } - return dataTransfer.has(type); - })) { - continue; - } - - const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token); - if (originalDocVersion !== model.getVersionId()) { - return; - } - - if (edit) { - performSnippetEdit(editor, typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet, selections); - - if (edit.additionalEdit) { - await this._bulkEditService.apply(ResourceEdit.convert(edit.additionalEdit), { editor }); - } - return; - } - } - } finally { - tokenSource.dispose(); - } - }, true)); + this._register(addDisposableListener(container, 'copy', e => this.handleCopy(e))); + this._register(addDisposableListener(container, 'cut', e => this.handleCopy(e))); + this._register(addDisposableListener(container, 'paste', e => this.handlePaste(e), true)); } - public arePasteActionsEnabled(model: ITextModel): boolean { + private arePasteActionsEnabled(model: ITextModel): boolean { return this._configurationService.getValue('editor.experimental.pasteActions.enabled', { resource: model.uri }); } + + private handleCopy(e: ClipboardEvent) { + if (!e.clipboardData) { + return; + } + + const model = this._editor.getModel(); + const selections = this._editor.getSelections(); + if (!model || !selections?.length) { + return; + } + + if (!this.arePasteActionsEnabled(model)) { + return; + } + + const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model).filter(x => !!x.prepareDocumentPaste); + if (!providers.length) { + return; + } + + const dataTransfer = toVSDataTransfer(e.clipboardData); + + // Save off a handle pointing to data that VS Code maintains. + const handle = generateUuid(); + e.clipboardData.setData(vscodeClipboardMime, handle); + + const promise = createCancelablePromise(async token => { + const results = await Promise.all(providers.map(provider => { + return provider.prepareDocumentPaste!(model, selections, dataTransfer, token); + })); + + for (const result of results) { + result?.forEach((value, key) => { + dataTransfer.replace(key, value); + }); + } + + return dataTransfer; + }); + + this._currentClipboardItem?.dataTransferPromise.cancel(); + this._currentClipboardItem = { handle: handle, dataTransferPromise: promise }; + } + + private async handlePaste(e: ClipboardEvent) { + const selections = this._editor.getSelections(); + if (!e.clipboardData || !selections?.length || !this._editor.hasModel()) { + return; + } + + const model = this._editor.getModel(); + if (!this.arePasteActionsEnabled(model)) { + return; + } + + const handle = e.clipboardData?.getData(vscodeClipboardMime); + if (typeof handle !== 'string') { + return; + } + + const providers = this._languageFeaturesService.documentPasteEditProvider.ordered(model); + if (!providers.length) { + return; + } + + e.preventDefault(); + e.stopImmediatePropagation(); + + const originalDocVersion = model.getVersionId(); + const tokenSource = new EditorStateCancellationTokenSource(this._editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Selection); + + try { + const dataTransfer = toVSDataTransfer(e.clipboardData); + + if (handle && this._currentClipboardItem?.handle === handle) { + const toMergeDataTransfer = await this._currentClipboardItem.dataTransferPromise; + toMergeDataTransfer.forEach((value, key) => { + dataTransfer.replace(key, value); + }); + } + + if (!dataTransfer.has(Mimes.uriList)) { + const resources = await this._clipboardService.readResources(); + if (resources.length) { + const value = resources.join('\n'); + dataTransfer.append(Mimes.uriList, createStringDataTransferItem(value)); + } + } + + dataTransfer.delete(vscodeClipboardMime); + + for (const provider of [...providers, defaultPasteEditProvider]) { + if (!provider.pasteMimeTypes.some(type => { + if (type.toLowerCase() === DataTransfers.FILES.toLowerCase()) { + return [...dataTransfer.values()].some(item => item.asFile()); + } + return dataTransfer.has(type); + })) { + continue; + } + + const edit = await provider.provideDocumentPasteEdits(model, selections, dataTransfer, tokenSource.token); + if (originalDocVersion !== model.getVersionId()) { + return; + } + + if (edit) { + performSnippetEdit(this._editor, typeof edit.insertText === 'string' ? SnippetParser.escape(edit.insertText) : edit.insertText.snippet, selections); + + if (edit.additionalEdit) { + await this._bulkEditService.apply(ResourceEdit.convert(edit.additionalEdit), { editor: this._editor }); + } + return; + } + } + } finally { + tokenSource.dispose(); + } + } } diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index bc61d390b86..d13915a20ba 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -1348,9 +1348,7 @@ export class SimpleButton extends Widget { e.preventDefault(); return; } - if (this._opts.onKeyDown) { - this._opts.onKeyDown(e); - } + this._opts.onKeyDown?.(e); }); } diff --git a/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts b/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts index 596b68876e2..b79c3651a86 100644 --- a/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts @@ -74,9 +74,7 @@ export class RangesCollector { } return new FoldingRegions(startIndexes, endIndexes); } else { - if (this._notifyTooManyRegions) { - this._notifyTooManyRegions(this._foldingRangesLimit); - } + this._notifyTooManyRegions?.(this._foldingRangesLimit); let entries = 0; let maxIndent = this._indentOccurrences.length; for (let i = 0; i < this._indentOccurrences.length; i++) { diff --git a/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts index 186db7e6bab..1e8a0ac06b9 100644 --- a/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts @@ -121,9 +121,7 @@ export class RangesCollector { } return new FoldingRegions(startIndexes, endIndexes, this._types); } else { - if (this._notifyTooManyRegions) { - this._notifyTooManyRegions(this._foldingRangesLimit); - } + this._notifyTooManyRegions?.(this._foldingRangesLimit); let entries = 0; let maxLevel = this._nestingLevelCounts.length; for (let i = 0; i < this._nestingLevelCounts.length; i++) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 6ce5ee603ad..13a8e3b840c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -243,9 +243,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { lastCompletionItem = currentCompletion.sourceInlineCompletion; const provider = currentCompletion.sourceProvider; - if (provider.handleItemDidShow) { - provider.handleItemDidShow(currentCompletion.sourceInlineCompletions, lastCompletionItem); - } + provider.handleItemDidShow?.(currentCompletion.sourceInlineCompletions, lastCompletionItem); } })); diff --git a/src/vs/editor/contrib/rename/browser/renameInputField.ts b/src/vs/editor/contrib/rename/browser/renameInputField.ts index af9ade477ac..64b88fb6f82 100644 --- a/src/vs/editor/contrib/rename/browser/renameInputField.ts +++ b/src/vs/editor/contrib/rename/browser/renameInputField.ts @@ -146,15 +146,11 @@ export class RenameInputField implements IContentWidget { private _currentCancelInput?: (focusEditor: boolean) => void; acceptInput(wantsPreview: boolean): void { - if (this._currentAcceptInput) { - this._currentAcceptInput(wantsPreview); - } + this._currentAcceptInput?.(wantsPreview); } cancelInput(focusEditor: boolean): void { - if (this._currentCancelInput) { - this._currentCancelInput(focusEditor); - } + this._currentCancelInput?.(focusEditor); } getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean, token: CancellationToken): Promise { diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/bannerController.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/bannerController.ts index 868a8024e59..d2542d01582 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/bannerController.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/bannerController.ts @@ -39,9 +39,7 @@ export class BannerController extends Disposable { ...item, onClose: () => { this.hide(); - if (item.onClose) { - item.onClose(); - } + item.onClose?.(); } }); this._editor.setBanner(this.banner.element, BANNER_ELEMENT_HEIGHT); diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index b5bb0ff4462..8458bdcb22f 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -129,9 +129,7 @@ export class ContextMenuHandler { }, onHide: (didCancel?: boolean) => { - if (delegate.onHide) { - delegate.onHide(!!didCancel); - } + delegate.onHide?.(!!didCancel); if (this.block) { this.block.remove(); diff --git a/src/vs/platform/contextview/browser/contextMenuService.ts b/src/vs/platform/contextview/browser/contextMenuService.ts index 437b3ffd8ce..2d4ee16cb62 100644 --- a/src/vs/platform/contextview/browser/contextMenuService.ts +++ b/src/vs/platform/contextview/browser/contextMenuService.ts @@ -47,9 +47,7 @@ export class ContextMenuService extends Disposable implements IContextMenuServic this.contextMenuHandler.showContextMenu({ ...delegate, onHide: (didCancel) => { - if (delegate.onHide) { - delegate.onHide(didCancel); - } + delegate.onHide?.(didCancel); this._onDidHideContextMenu.fire(); } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 7431f898b41..50b6febdf85 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -816,9 +816,7 @@ export class Menubar { const originalClick = options.click; options.click = (item, window, event) => { this.reportMenuActionTelemetry(commandId); - if (originalClick) { - originalClick(item, window, event); - } + originalClick?.(item, window, event); }; return options; diff --git a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts index 36bca9adf94..f048d7153a9 100644 --- a/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-sandbox/remoteAuthorityResolverService.ts @@ -71,9 +71,7 @@ export class RemoteAuthorityResolverService extends Disposable implements IRemot const key = uri.toString(); if (!this._canonicalURIRequests.has(key)) { const request = new PendingPromise(uri); - if (this._canonicalURIProvider) { - this._canonicalURIProvider(request.input).then((uri) => request.resolve(uri), (err) => request.reject(err)); - } + this._canonicalURIProvider?.(request.input).then((uri) => request.resolve(uri), (err) => request.reject(err)); this._canonicalURIRequests.set(key, request); } return this._canonicalURIRequests.get(key)!.promise; diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 2c44f2358a6..6e54ef9a381 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -183,9 +183,10 @@ async function transformToTerminalProfiles( validatedProfile.color = profile.color; resultProfiles.push(validatedProfile); } else { - logService?.trace('profile not validated', profileName, originalPaths); + logService?.debug('Terminal profile not validated', profileName, originalPaths); } } + logService?.debug('Validated terminal profiles', resultProfiles); return resultProfiles; } diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index b1500fcbe64..078801e90c0 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -74,17 +74,18 @@ export class MainThreadCommands implements MainThreadCommandsShape { } } - async $executeCommand(id: string, args: any[] | SerializableObjectWithBuffers, retry: boolean): Promise { + async $activateByCommandEvent(id: string): Promise { + const activationEvent = `onCommand:${id}`; + await this._extensionService.activateByEvent(activationEvent); + } + + async $executeCommand(id: string, args: any[] | SerializableObjectWithBuffers): Promise { if (args instanceof SerializableObjectWithBuffers) { args = args.value; } for (let i = 0; i < args.length; i++) { args[i] = revive(args[i]); } - if (retry && args.length > 0 && !CommandsRegistry.getCommand(id)) { - await this._extensionService.activateByEvent(`onCommand:${id}`); - throw new Error('$executeCommand:retry'); - } return this._commandService.executeCommand(id, ...args); } diff --git a/src/vs/workbench/api/browser/mainThreadErrors.ts b/src/vs/workbench/api/browser/mainThreadErrors.ts index 36fdac455c6..2d05a6a0e44 100644 --- a/src/vs/workbench/api/browser/mainThreadErrors.ts +++ b/src/vs/workbench/api/browser/mainThreadErrors.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SerializedError, onUnexpectedError } from 'vs/base/common/errors'; +import { SerializedError, onUnexpectedError, ErrorNoTelemetry } from 'vs/base/common/errors'; import { extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { MainContext, MainThreadErrorsShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -17,7 +17,7 @@ export class MainThreadErrors implements MainThreadErrorsShape { $onUnexpectedError(err: any | SerializedError): void { if (err && err.$isError) { const { name, message, stack } = err; - err = new Error(); + err = err.noTelemetry ? new ErrorNoTelemetry() : new Error(); err.message = message; err.name = name; err.stack = stack; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDto.ts b/src/vs/workbench/api/browser/mainThreadNotebookDto.ts index 8f5d53fd444..9c7f5d67f0d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDto.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDto.ts @@ -96,6 +96,7 @@ export namespace NotebookDto { if (data.editType === CellExecutionUpdateType.Output) { return { editType: data.editType, + cellHandle: data.cellHandle, append: data.append, outputs: data.outputs.map(fromNotebookOutputDto) }; diff --git a/src/vs/workbench/api/browser/mainThreadSearch.ts b/src/vs/workbench/api/browser/mainThreadSearch.ts index 45a9fb1d1a3..1ff5cdfb75c 100644 --- a/src/vs/workbench/api/browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/browser/mainThreadSearch.ts @@ -94,9 +94,7 @@ class SearchOperation { this.matches.set(match.resource.toString(), match); } - if (this.progress) { - this.progress(match); - } + this.progress?.(match); } } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 60478d4a194..d156a5d2a03 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -92,7 +92,8 @@ export interface MainThreadClipboardShape extends IDisposable { export interface MainThreadCommandsShape extends IDisposable { $registerCommand(id: string): void; $unregisterCommand(id: string): void; - $executeCommand(id: string, args: any[] | SerializableObjectWithBuffers, retry: boolean): Promise; + $activateByCommandEvent(id: string): Promise; + $executeCommand(id: string, args: any[] | SerializableObjectWithBuffers): Promise; $getCommands(): Promise; } @@ -999,6 +1000,7 @@ export interface INotebookProxyKernelDto { export interface ICellExecuteOutputEditDto { editType: CellExecutionUpdateType.Output; + cellHandle: number; append?: boolean; outputs: NotebookOutputDto[]; } diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 7e6668d8a2a..f795c3d6080 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -163,10 +163,15 @@ export class ExtHostCommands implements ExtHostCommandsShape { executeCommand(id: string, ...args: any[]): Promise { this._logService.trace('ExtHostCommands#executeCommand', id); - return this._doExecuteCommand(id, args, true); + return this._doExecuteCommand(id, args); } - private async _doExecuteCommand(id: string, args: any[], retry: boolean): Promise { + private async _doExecuteCommand(id: string, args: any[]): Promise { + + // make sure to emit an onCommand-activation event in ALL cases + // (1) locally known command -> activation notificed bystander + // (2) unknown command -> can activate future local extension + await this.#proxy.$activateByCommandEvent(id); if (this._commands.has(id)) { // we stay inside the extension host and support @@ -201,17 +206,10 @@ export class ExtHostCommands implements ExtHostCommandsShape { }); try { - const result = await this.#proxy.$executeCommand(id, hasBuffers ? new SerializableObjectWithBuffers(toArgs) : toArgs, retry); + const result = await this.#proxy.$executeCommand(id, hasBuffers ? new SerializableObjectWithBuffers(toArgs) : toArgs); return revive(result); } catch (e) { - // Rerun the command when it wasn't known, had arguments, and when retry - // is enabled. We do this because the command might be registered inside - // the extension host now and can therfore accept the arguments as-is. - if (e instanceof Error && e.message === '$executeCommand:retry') { - return this._doExecuteCommand(id, args, false); - } else { - throw e; - } + throw e; } } } diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index f99ead59ac7..356c29a7a76 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -441,6 +441,17 @@ class NotebookCellExecutionTask extends Disposable { } } + private cellIndexToHandle(cellOrCellIndex: vscode.NotebookCell | undefined): number { + let cell: ExtHostCell | undefined = this._cell; + if (cellOrCellIndex) { + cell = this._cell.notebook.getCellFromApiCell(cellOrCellIndex); + } + if (!cell) { + throw new Error('INVALID cell'); + } + return cell.handle; + } + private validateAndConvertOutputs(items: vscode.NotebookCellOutput[]): NotebookOutputDto[] { return items.map(output => { const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.items, true); @@ -456,10 +467,12 @@ class NotebookCellExecutionTask extends Disposable { } private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | undefined, append: boolean): Promise { + const handle = this.cellIndexToHandle(cell); const outputDtos = this.validateAndConvertOutputs(asArray(outputs)); return this.updateSoon( { editType: CellExecutionUpdateType.Output, + cellHandle: handle, append, outputs: outputDtos }); diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 1a2dda0396f..67c56f94474 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -136,9 +136,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx } $onItemSelected(handle: number): void { - if (this._onDidSelectItem) { - this._onDidSelectItem(handle); - } + this._onDidSelectItem?.(handle); } // ---- input diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index e3bdbf13e03..69bd5b1e18a 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2473,9 +2473,9 @@ export class DataTransfer { this.#items.set(mimeType, [value]); } - forEach(callbackfn: (value: DataTransferItem, key: string) => void): void { + forEach(callbackfn: (value: DataTransferItem, key: string) => void, thisArg?: unknown): void { for (const [mime, items] of this.#items) { - items.forEach(item => callbackfn(item, mime)); + items.forEach(item => callbackfn(item, mime), thisArg); } } } diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index dda77ef065e..9d805271e48 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -546,9 +546,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } $handleTextSearchResult(result: IRawFileMatch2, requestId: number): void { - if (this._activeSearchCallbacks[requestId]) { - this._activeSearchCallbacks[requestId](result); - } + this._activeSearchCallbacks[requestId]?.(result); } saveAll(includeUntitled?: boolean): Promise { diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 9b7fa578f59..0d111d2f20a 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -197,9 +197,7 @@ function _createExtHostProtocol(): Promise { // Now that we have managed to install a message listener, ask the other side to send us the socket const req: IExtHostReadyMessage = { type: 'VSCODE_EXTHOST_IPC_READY' }; - if (process.send) { - process.send(req); - } + process.send?.(req); }); } else { diff --git a/src/vs/workbench/api/test/browser/extHostCommands.test.ts b/src/vs/workbench/api/test/browser/extHostCommands.test.ts index da9b47ccda9..7426d5d530e 100644 --- a/src/vs/workbench/api/test/browser/extHostCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostCommands.test.ts @@ -60,7 +60,7 @@ suite('ExtHostCommands', function () { assert.strictEqual(unregisterCounter, 1); }); - test('execute with retry', async function () { + test('execute triggers activate', async function () { let count = 0; @@ -68,16 +68,13 @@ suite('ExtHostCommands', function () { override $registerCommand(id: string): void { // } - override async $executeCommand(id: string, args: any[], retry: boolean): Promise { - count++; - assert.strictEqual(retry, count === 1); - if (count === 1) { - assert.strictEqual(retry, true); - throw new Error('$executeCommand:retry'); - } else { - assert.strictEqual(retry, false); - return 17; - } + + override async $activateByCommandEvent(id: string): Promise { + count += 1; + } + + override async $executeCommand(id: string, args: any[]): Promise { + return undefined; } }; @@ -86,8 +83,7 @@ suite('ExtHostCommands', function () { new NullLogService() ); - const result = await commands.executeCommand('fooo', [this, true]); - assert.strictEqual(result, 17); - assert.strictEqual(count, 2); + await commands.executeCommand('fooo', [this, true]); + assert.strictEqual(count, 1); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts b/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts index fe6133d11ad..e91553dc9b5 100644 --- a/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; -import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { mock } from 'vs/base/test/common/mock'; @@ -42,46 +42,4 @@ suite('MainThreadCommands', function () { assert.strictEqual(CommandsRegistry.getCommand('foo'), undefined); assert.strictEqual(CommandsRegistry.getCommand('bar'), undefined); }); - - test('activate and throw when needed', async function () { - - const activations: string[] = []; - const runs: string[] = []; - - const commands = new MainThreadCommands( - SingleProxyRPCProtocol(null), - new class extends mock() { - override executeCommand(id: string): Promise { - runs.push(id); - return Promise.resolve(undefined); - } - }, - new class extends mock() { - override activateByEvent(id: string) { - activations.push(id); - return Promise.resolve(); - } - } - ); - - // case 1: arguments and retry - try { - activations.length = 0; - await commands.$executeCommand('bazz', [1, 2, { n: 3 }], true); - assert.ok(false); - } catch (e) { - assert.deepStrictEqual(activations, ['onCommand:bazz']); - assert.strictEqual((e).message, '$executeCommand:retry'); - } - - // case 2: no arguments and retry - runs.length = 0; - await commands.$executeCommand('bazz', [], true); - assert.deepStrictEqual(runs, ['bazz']); - - // case 3: arguments and no retry - runs.length = 0; - await commands.$executeCommand('bazz', [1, 2, true], false); - assert.deepStrictEqual(runs, ['bazz']); - }); }); diff --git a/src/vs/workbench/api/test/common/testRPCProtocol.ts b/src/vs/workbench/api/test/common/testRPCProtocol.ts index 6af1082f456..4ba0e9f51e2 100644 --- a/src/vs/workbench/api/test/common/testRPCProtocol.ts +++ b/src/vs/workbench/api/test/common/testRPCProtocol.ts @@ -57,9 +57,7 @@ export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService { private set _callCount(value: number) { this._callCountValue = value; if (this._callCountValue === 0) { - if (this._completeIdle) { - this._completeIdle(); - } + this._completeIdle?.(); this._idle = undefined; } } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 8cda2f318c7..3a186e54ab4 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -609,9 +609,7 @@ export class CompositeDragAndDropObserver extends Disposable { return; } - if (callbacks.onDragLeave) { - callbacks.onDragLeave({ eventData: e, dragAndDropData: data! }); - } + callbacks.onDragLeave?.({ eventData: e, dragAndDropData: data! }); }, onDrop: e => { if (callbacks.onDrop) { diff --git a/src/vs/workbench/contrib/audioCues/browser/observable.ts b/src/vs/workbench/contrib/audioCues/browser/observable.ts index 80562a6266e..41e8161969e 100644 --- a/src/vs/workbench/contrib/audioCues/browser/observable.ts +++ b/src/vs/workbench/contrib/audioCues/browser/observable.ts @@ -6,7 +6,9 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -export interface IObservable { +export interface IObservable { + _change: TChange; + /** * Reads the current value. * @@ -39,7 +41,7 @@ export interface IReader { * * Is called by `Observable.read`. */ - handleBeforeReadObservable(observable: IObservable): void; + handleBeforeReadObservable(observable: IObservable): void; } export interface IObserver { @@ -61,7 +63,7 @@ export interface IObserver { * Implementations must not call into other observables! * The change should be processed when {@link IObserver.endUpdate} is called. */ - handleChange(observable: IObservable): void; + handleChange(observable: IObservable, change: TChange): void; /** * Indicates that an update operation has completed. @@ -69,8 +71,8 @@ export interface IObserver { endUpdate(observable: IObservable): void; } -export interface ISettable { - set(value: T, transaction: ITransaction | undefined): void; +export interface ISettable { + set(value: T, transaction: ITransaction | undefined, change: TChange): void; } export interface ITransaction { @@ -80,12 +82,14 @@ export interface ITransaction { */ updateObserver( observer: IObserver, - observable: IObservable + observable: IObservable ): void; } // === Base === -export abstract class ConvenientObservable implements IObservable { +export abstract class ConvenientObservable implements IObservable { + get _change(): TChange { return null!; } + public abstract get(): T; public abstract subscribe(observer: IObserver): void; public abstract unsubscribe(observer: IObserver): void; @@ -100,7 +104,7 @@ export abstract class ConvenientObservable implements IObservable { } } -export abstract class BaseObservable extends ConvenientObservable { +export abstract class BaseObservable extends ConvenientObservable { protected readonly observers = new Set(); public subscribe(observer: IObserver): void { @@ -151,9 +155,9 @@ class TransactionImpl implements ITransaction { } } -export class ObservableValue - extends BaseObservable - implements ISettable +export class ObservableValue + extends BaseObservable + implements ISettable { private value: T; @@ -166,14 +170,14 @@ export class ObservableValue return this.value; } - public set(value: T, tx: ITransaction | undefined): void { + public set(value: T, tx: ITransaction | undefined, change: TChange): void { if (this.value === value) { return; } if (!tx) { transaction((tx) => { - this.set(value, tx); + this.set(value, tx, change); }); return; } @@ -182,7 +186,7 @@ export class ObservableValue for (const observer of this.observers) { tx.updateObserver(observer, this); - observer.handleChange(this); + observer.handleChange(this, change); } } } @@ -191,7 +195,7 @@ export function constObservable(value: T): IObservable { return new ConstObservable(value); } -class ConstObservable extends ConvenientObservable { +class ConstObservable extends ConvenientObservable { constructor(private readonly value: T) { super(); } @@ -208,11 +212,28 @@ class ConstObservable extends ConvenientObservable { } // == autorun == -export function autorun( - fn: (reader: IReader) => void, - name: string +export function autorun(fn: (reader: IReader) => void, name: string): IDisposable { + return new AutorunObserver(fn, name, undefined); +} + +interface IChangeContext { + readonly changedObservable: IObservable; + readonly change: unknown; + + didChange(observable: IObservable): this is { change: TChange }; +} + +export function autorunHandleChanges( + name: string, + options: { + /** + * Returns if this change should cause a re-run of the autorun. + */ + handleChange: (context: IChangeContext) => boolean; + }, + fn: (reader: IReader) => void ): IDisposable { - return new AutorunObserver(fn, name); + return new AutorunObserver(fn, name, options.handleChange); } export function autorunWithStore( @@ -252,7 +273,8 @@ export class AutorunObserver implements IObserver, IReader, IDisposable { constructor( private readonly runFn: (reader: IReader) => void, - public readonly name: string + public readonly name: string, + private readonly _handleChange: ((context: IChangeContext) => boolean) | undefined ) { this.runIfNeeded(); } @@ -264,8 +286,13 @@ export class AutorunObserver implements IObserver, IReader, IDisposable { } } - public handleChange() { - this.needsToRun = true; + public handleChange(observable: IObservable, change: TChange): void { + const shouldReact = this._handleChange ? this._handleChange({ + changedObservable: observable, + change, + didChange: o => o === observable as any, + }) : true; + this.needsToRun = this.needsToRun || shouldReact; if (this.updateCount === 0) { this.runIfNeeded(); @@ -337,7 +364,7 @@ export function autorunDelta( export function derivedObservable(name: string, computeFn: (reader: IReader) => T): IObservable { return new LazyDerived(computeFn, name); } -export class LazyDerived extends ConvenientObservable { +export class LazyDerived extends ConvenientObservable { private readonly observer: LazyDerivedObserver; constructor(computeFn: (reader: IReader) => T, name: string) { @@ -366,7 +393,7 @@ export class LazyDerived extends ConvenientObservable { * @internal */ class LazyDerivedObserver - extends BaseObservable + extends BaseObservable implements IReader, IObserver { private hadValue = false; private hasValue = false; @@ -486,9 +513,8 @@ class LazyDerivedObserver this.hasValue = true; if (this.hadValue && oldValue !== this.value) { - // for (const r of this.observers) { - r.handleChange(this); + r.handleChange(this, undefined); } } } @@ -508,6 +534,20 @@ export function observableFromPromise(promise: Promise): IObservable<{ val return observable; } +export function waitForState(observable: IObservable, predicate: (state: T) => state is TState): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => boolean): Promise; +export function waitForState(observable: IObservable, predicate: (state: T) => boolean): Promise { + return new Promise(resolve => { + const d = autorun(reader => { + const currentState = observable.read(reader); + if (predicate(currentState)) { + d.dispose(); + resolve(currentState); + } + }, 'waitForState'); + }); +} + export function observableFromEvent( event: Event, getValue: (args: TArgs | undefined) => T @@ -540,7 +580,7 @@ class FromEventObservable extends BaseObservable { transaction(tx => { for (const o of this.observers) { tx.updateObserver(o, this); - o.handleChange(this); + o.handleChange(this, undefined); } }); } @@ -622,3 +662,12 @@ export function keepAlive(observable: IObservable): IDisposable { observable.read(reader); }, 'keep-alive'); } + +export function derivedObservableWithCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable { + let lastValue: T | undefined = undefined; + const observable = derivedObservable(name, reader => { + lastValue = computeFn(reader, lastValue); + return lastValue; + }); + return observable; +} diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 8335d7b3c23..7b404dde3cb 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -272,9 +272,7 @@ export class BulkEditPane extends ViewPane { } private _done(accept: boolean): void { - if (this._currentResolve) { - this._currentResolve(accept ? this._currentInput?.getWorkspaceEdit() : undefined); - } + this._currentResolve?.(accept ? this._currentInput?.getWorkspaceEdit() : undefined); this._currentInput = undefined; this._setState(State.Message); this._sessionDisposables.clear(); diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 258ce3ac264..87b195c52f8 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -250,9 +250,7 @@ export class CommentReply extends Disposable { })); this._commentFormActions = new CommentFormActions(container, async (action: IAction) => { - if (this._actionRunDelegate) { - this._actionRunDelegate(); - } + this._actionRunDelegate?.(); action.run({ thread: this._commentThread, diff --git a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts index 348aa5815b7..299a6ad3eb6 100644 --- a/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts +++ b/src/vs/workbench/contrib/debug/common/abstractDebugAdapter.ts @@ -155,14 +155,10 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { switch (message.type) { case 'event': - if (this.eventCallback) { - this.eventCallback(message); - } + this.eventCallback?.(message); break; case 'request': - if (this.requestCallback) { - this.requestCallback(message); - } + this.requestCallback?.(message); break; case 'response': { const response = message; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 64dbbb7c66b..355bfcfeb41 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1519,9 +1519,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.progressService.withProgress({ location: ProgressLocation.Extensions }, () => new Promise(resolve => this._activityCallBack = resolve)); } } else { - if (this._activityCallBack) { - this._activityCallBack(); - } + this._activityCallBack?.(); this._activityCallBack = null; } } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 28c231a1e7b..17da41a9707 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extension.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -215,6 +215,10 @@ min-width: 0; } +.monaco-list-row.disabled .extension-list-item .details .description { + color: var(--vscode-disabledForeground); +} + .extension-list-item .monaco-action-bar .action-label.icon { padding: 1px 2px; } diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index 79ed25b1299..614f5133bfd 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -45,12 +45,14 @@ interface IMarkerCodeColumnTemplateData { } interface IMarkerFileColumnTemplateData { + readonly columnElement: HTMLElement; readonly fileLabel: HighlightedLabel; readonly positionLabel: HighlightedLabel; } interface IMarkerHighlightedLabelColumnTemplateData { + readonly columnElement: HTMLElement; readonly highlightedLabel: HighlightedLabel; } @@ -86,6 +88,7 @@ class MarkerSeverityColumnRenderer implements ITableRenderer; - private readonly input1View = this.instantiation.createInstance(InputCodeEditorView, 1, { readonly: true }); - private readonly input2View = this.instantiation.createInstance(InputCodeEditorView, 2, { readonly: true }); + private readonly input1View = this.instantiation.createInstance(InputCodeEditorView, 1, { readonly: !this.inputsWritable }); + private readonly input2View = this.instantiation.createInstance(InputCodeEditorView, 2, { readonly: !this.inputsWritable }); private readonly inputResultView = this.instantiation.createInstance(ResultCodeEditorView, { readonly: false }); private readonly _ctxIsMergeEditor: IContextKey; @@ -71,6 +72,10 @@ export class MergeEditor extends EditorPane { private _model: MergeEditorModel | undefined; public get model(): MergeEditorModel | undefined { return this._model; } + private get inputsWritable(): boolean { + return !!this._configurationService.getValue('mergeEditor.writableInputs'); + } + constructor( @IInstantiationService private readonly instantiation: IInstantiationService, @ILabelService private readonly _labelService: ILabelService, @@ -80,6 +85,7 @@ export class MergeEditor extends EditorPane { @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(MergeEditor.ID, telemetryService, themeService, storageService); @@ -94,7 +100,7 @@ export class MergeEditor extends EditorPane { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs, model.result, resultDiffs); + const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs.read(reader), model.result, resultDiffs); return modifiedBaseRanges; }); const input2ResultMapping = derivedObservable('input2ResultMapping', reader => { @@ -103,7 +109,7 @@ export class MergeEditor extends EditorPane { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs, model.result, resultDiffs); + const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs.read(reader), model.result, resultDiffs); return modifiedBaseRanges; }); @@ -228,42 +234,44 @@ export class MergeEditor extends EditorPane { // TODO: Update editor options! - const input1ViewZoneIds: string[] = []; - const input2ViewZoneIds: string[] = []; - for (const m of model.modifiedBaseRanges) { - const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1); + this._sessionDisposables.add(autorunWithStore((reader, store) => { + const input1ViewZoneIds: string[] = []; + const input2ViewZoneIds: string[] = []; + for (const m of model.modifiedBaseRanges.read(reader)) { + const max = Math.max(m.input1Range.lineCount, m.input2Range.lineCount, 1); - this.input1View.editor.changeViewZones(a => { - input1ViewZoneIds.push(a.addZone({ - afterLineNumber: m.input1Range.endLineNumberExclusive - 1, - heightInLines: max - m.input1Range.lineCount, - domNode: $('div.diagonal-fill'), - })); - }); - - this.input2View.editor.changeViewZones(a => { - input2ViewZoneIds.push(a.addZone({ - afterLineNumber: m.input2Range.endLineNumberExclusive - 1, - heightInLines: max - m.input2Range.lineCount, - domNode: $('div.diagonal-fill'), - })); - }); - } - - this._sessionDisposables.add({ - dispose: () => { this.input1View.editor.changeViewZones(a => { - for (const zone of input1ViewZoneIds) { - a.removeZone(zone); - } + input1ViewZoneIds.push(a.addZone({ + afterLineNumber: m.input1Range.endLineNumberExclusive - 1, + heightInLines: max - m.input1Range.lineCount, + domNode: $('div.diagonal-fill'), + })); }); + this.input2View.editor.changeViewZones(a => { - for (const zone of input2ViewZoneIds) { - a.removeZone(zone); - } + input2ViewZoneIds.push(a.addZone({ + afterLineNumber: m.input2Range.endLineNumberExclusive - 1, + heightInLines: max - m.input2Range.lineCount, + domNode: $('div.diagonal-fill'), + })); }); } - }); + + store.add({ + dispose: () => { + this.input1View.editor.changeViewZones(a => { + for (const zone of input1ViewZoneIds) { + a.removeZone(zone); + } + }); + this.input2View.editor.changeViewZones(a => { + for (const zone of input2ViewZoneIds) { + a.removeZone(zone); + } + }); + } + }); + }, 'update alignment view zones')); } protected override setEditorVisible(visible: boolean): void { @@ -448,7 +456,7 @@ class InputCodeEditorView extends CodeEditorView { return []; } const result = new Array(); - for (const m of model.modifiedBaseRanges) { + for (const m of model.modifiedBaseRanges.read(reader)) { const range = m.getInputRange(this.inputNumber); if (!range.isEmpty) { result.push({ @@ -478,13 +486,14 @@ class InputCodeEditorView extends CodeEditorView { getIntersectingGutterItems: (range, reader) => { const model = this.model.read(reader); if (!model) { return []; } - return model.modifiedBaseRanges + return model.modifiedBaseRanges.read(reader) .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) .map((baseRange, idx) => ({ id: idx.toString(), additionalHeightInPx: 0, offsetInPx: 0, range: baseRange.getInputRange(this.inputNumber), + enabled: model.isUpToDate, toggleState: derivedObservable('toggle', (reader) => model .getState(baseRange) @@ -510,14 +519,19 @@ class InputCodeEditorView extends CodeEditorView { } interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { + enabled: IObservable; toggleState: IObservable; - setState(value: boolean, tx: ITransaction | undefined): void; + setState(value: boolean, tx: ITransaction): void; } class MergeConflictGutterItemView extends Disposable implements IGutterItemView { - constructor(private item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { + private readonly item = new ObservableValue(undefined, 'item'); + + constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { super(); + this.item.set(item, undefined); + target.classList.add('merge-accept-gutter-marker'); // TODO: localized title @@ -526,7 +540,8 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< this._register( autorun((reader) => { - const value = this.item.toggleState.read(reader); + const item = this.item.read(reader)!; + const value = item.toggleState.read(reader); checkBox.setIcon( value === true ? Codicon.check @@ -535,11 +550,19 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< : Codicon.circleFilled ); checkBox.checked = value === true; + + if (!item.enabled.read(reader)) { + checkBox.disable(); + } else { + checkBox.enable(); + } }, 'Update Toggle State') ); this._register(checkBox.onChange(() => { - this.item.setState(checkBox.checked, undefined); + transaction(tx => { + this.item.get()!.setState(checkBox.checked, tx); + }); })); target.appendChild(n('div.background', [noBreakWhitespace]).root); @@ -555,7 +578,7 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< } update(baseRange: ModifiedBaseRangeGutterItemInfo): void { - this.item = baseRange; + this.item.set(baseRange, undefined); } } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index f6b69488351..efa8b0360e0 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; -import { MergeEditorModel, MergeEditorModelFactory } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -39,10 +39,9 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { private _model?: MergeEditorModel; private _outTextModel?: ITextFileEditorModel; - private readonly mergeEditorModelFactory = this._instaService.createInstance(MergeEditorModelFactory); constructor( - private readonly _anchestor: URI, + private readonly _base: URI, private readonly _input1: MergeEditorInputData, private readonly _input2: MergeEditorInputData, private readonly _result: URI, @@ -101,13 +100,14 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { if (!this._model) { - const anchestor = await this._textModelService.createModelReference(this._anchestor); + const base = await this._textModelService.createModelReference(this._base); const input1 = await this._textModelService.createModelReference(this._input1.uri); const input2 = await this._textModelService.createModelReference(this._input2.uri); const result = await this._textModelService.createModelReference(this._result); - this._model = await this.mergeEditorModelFactory.create( - anchestor.object.textEditorModel, + this._model = this._instaService.createInstance( + MergeEditorModel, + base.object.textEditorModel, input1.object.textEditorModel, this._input1.detail, this._input1.description, @@ -117,13 +117,13 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { result.object.textEditorModel ); + await this._model.onInitialized; + this._store.add(this._model); - this._store.add(anchestor); + this._store.add(base); this._store.add(input1); this._store.add(input2); this._store.add(result); - - // result.object. } return this._model; } @@ -132,7 +132,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { if (!(otherInput instanceof MergeEditorInput)) { return false; } - return isEqual(this._anchestor, otherInput._anchestor) + return isEqual(this._base, otherInput._base) && isEqual(this._input1.uri, otherInput._input1.uri) && isEqual(this._input2.uri, otherInput._input2.uri) && isEqual(this._result, otherInput._result); @@ -140,7 +140,7 @@ export class MergeEditorInput extends AbstractTextResourceEditorInput { toJSON(): MergeEditorInputJSON { return { - anchestor: this._anchestor, + anchestor: this._base, inputOne: this._input1, inputTwo: this._input2, result: this._result, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 9e32d53e6cc..0d1907a4eb9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -3,96 +3,72 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; -import { compareBy, CompareResult, equals, numberComparator } from 'vs/base/common/arrays'; +import { compareBy, CompareResult, equals } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { ModifiedBaseRange, LineEdit, LineDiff, ModifiedBaseRangeState, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { leftJoin, ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { LineDiff, LineEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { EditorWorkerServiceDiffComputer, TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; +import { leftJoin } from 'vs/workbench/contrib/mergeEditor/browser/utils'; -export class MergeEditorModelFactory { - constructor( - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService - ) { - } - - public async create( - base: ITextModel, - input1: ITextModel, - input1Detail: string | undefined, - input1Description: string | undefined, - input2: ITextModel, - input2Detail: string | undefined, - input2Description: string | undefined, - result: ITextModel, - ): Promise { - - const baseToInput1DiffPromise = this._editorWorkerService.computeDiff( - base.uri, - input1.uri, - false, - 1000 - ); - const baseToInput2DiffPromise = this._editorWorkerService.computeDiff( - base.uri, - input2.uri, - false, - 1000 - ); - const baseToResultDiffPromise = this._editorWorkerService.computeDiff( - base.uri, - result.uri, - false, - 1000 - ); - - const [baseToInput1Diff, baseToInput2Diff, baseToResultDiff] = await Promise.all([ - baseToInput1DiffPromise, - baseToInput2DiffPromise, - baseToResultDiffPromise - ]); - - const changesInput1 = - baseToInput1Diff?.changes.map((c) => - LineDiff.fromLineChange(c, base, input1) - ) || []; - const changesInput2 = - baseToInput2Diff?.changes.map((c) => - LineDiff.fromLineChange(c, base, input2) - ) || []; - const changesResult = - baseToResultDiff?.changes.map((c) => - LineDiff.fromLineChange(c, base, result) - ) || []; - - return new MergeEditorModel( - InternalSymbol, - base, - input1, - input1Detail, - input1Description, - input2, - input2Detail, - input2Description, - result, - changesInput1, - changesInput2, - changesResult, - this._editorWorkerService, - ); - } +export const enum MergeEditorModelState { + initializing = 1, + upToDate = 2, + updating = 3, } -const InternalSymbol: unique symbol = null!; - export class MergeEditorModel extends EditorModel { - private resultEdits: ResultEdits; + private readonly diffComputer = new EditorWorkerServiceDiffComputer(this.editorWorkerService); + private readonly input1TextModelDiffs = new TextModelDiffs(this.base, this.input1, this.diffComputer); + private readonly input2TextModelDiffs = new TextModelDiffs(this.base, this.input2, this.diffComputer); + private readonly resultTextModelDiffs = new TextModelDiffs(this.base, this.result, this.diffComputer); + + public readonly state = derivedObservable('state', reader => { + const states = [ + this.input1TextModelDiffs, + this.input2TextModelDiffs, + this.resultTextModelDiffs, + ].map((s) => s.state.read(reader)); + + if (states.some((s) => s === TextModelDiffState.initializing)) { + return MergeEditorModelState.initializing; + } + if (states.some((s) => s === TextModelDiffState.updating)) { + return MergeEditorModelState.updating; + } + return MergeEditorModelState.upToDate; + }); + + public readonly isUpToDate = derivedObservable('isUpdating', reader => this.state.read(reader) === MergeEditorModelState.upToDate); + + public readonly onInitialized = waitForState(this.state, state => state === MergeEditorModelState.upToDate); + + public readonly modifiedBaseRanges = derivedObservableWithCache('modifiedBaseRanges', (reader, lastValue) => { + if (this.state.read(reader) !== MergeEditorModelState.upToDate) { + return lastValue || []; + } + + const input1Diffs = this.input1TextModelDiffs.diffs.read(reader); + const input2Diffs = this.input2TextModelDiffs.diffs.read(reader); + + return ModifiedBaseRange.fromDiffs(this.base, this.input1, input1Diffs, this.input2, input2Diffs); + }); + + public readonly input1LinesDiffs = this.input1TextModelDiffs.diffs; + public readonly input2LinesDiffs = this.input2TextModelDiffs.diffs; + public readonly resultDiffs = this.resultTextModelDiffs.diffs; + + private readonly modifiedBaseRangeStateStores = + derivedObservable('modifiedBaseRangeStateStores', reader => { + const map = new Map( + this.modifiedBaseRanges.read(reader).map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')])) + ); + return map; + }); constructor( - _symbol: typeof InternalSymbol, readonly base: ITextModel, readonly input1: ITextModel, readonly input1Detail: string | undefined, @@ -101,27 +77,43 @@ export class MergeEditorModel extends EditorModel { readonly input2Detail: string | undefined, readonly input2Description: string | undefined, readonly result: ITextModel, - public readonly input1LinesDiffs: readonly LineDiff[], - public readonly input2LinesDiffs: readonly LineDiff[], - resultDiffs: LineDiff[], - private readonly editorWorkerService: IEditorWorkerService + @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService ) { super(); - this.resultEdits = new ResultEdits(resultDiffs, this.base, this.result, this.editorWorkerService); - this.resultEdits.onDidChange(() => { - this.recomputeState(); - }); - this.recomputeState(); + this._register(keepAlive(this.modifiedBaseRangeStateStores)); - this.resetUnknown(); + this._register( + autorunHandleChanges( + 'Recompute State', + { + handleChange: (ctx) => + ctx.didChange(this.resultTextModelDiffs.diffs) + // Ignore non-text changes as we update the state directly + ? ctx.change === TextModelDiffChangeReason.textChange + : true, + }, + (reader) => { + if (!this.isUpToDate.read(reader)) { + return; + } + const resultDiffs = this.resultTextModelDiffs.diffs.read(reader); + const stores = this.modifiedBaseRangeStateStores.read(reader); + this.recomputeState(resultDiffs, stores); + } + ) + ); + + this.onInitialized.then(() => { + this.resetUnknown(); + }); } - private recomputeState(): void { + private recomputeState(resultDiffs: LineDiff[], stores: Map>): void { transaction(tx => { const baseRangeWithStoreAndTouchingDiffs = leftJoin( - this.modifiedBaseRangeStateStores, - this.resultEdits.diffs.get(), + stores, + resultDiffs, (baseRange, diff) => baseRange[0].baseRange.touches(diff.originalRange) ? CompareResult.neitherLessOrGreaterThan @@ -132,14 +124,14 @@ export class MergeEditorModel extends EditorModel { ); for (const row of baseRangeWithStoreAndTouchingDiffs) { - row.left[1].set(this.computeState(row.left[0], row.rights), tx); + row.left[1].set(computeState(row.left[0], row.rights), tx); } }); } public resetUnknown(): void { transaction(tx => { - for (const range of this.modifiedBaseRanges) { + for (const range of this.modifiedBaseRanges.get()) { if (this.getState(range).get().conflicting) { this.setState(range, ModifiedBaseRangeState.default, tx); } @@ -149,7 +141,7 @@ export class MergeEditorModel extends EditorModel { public mergeNonConflictingDiffs(): void { transaction((tx) => { - for (const m of this.modifiedBaseRanges) { + for (const m of this.modifiedBaseRanges.get()) { if (m.isConflicting) { continue; } @@ -164,54 +156,8 @@ export class MergeEditorModel extends EditorModel { }); } - public get resultDiffs(): IObservable { - return this.resultEdits.diffs; - } - - public readonly modifiedBaseRanges = ModifiedBaseRange.fromDiffs( - this.base, - this.input1, - this.input1LinesDiffs, - this.input2, - this.input2LinesDiffs - ); - - private readonly modifiedBaseRangeStateStores: ReadonlyMap> = new Map( - this.modifiedBaseRanges.map(s => ([s, new ObservableValue(ModifiedBaseRangeState.default, 'State')])) - ); - - private computeState(baseRange: ModifiedBaseRange, conflictingDiffs?: LineDiff[]): ModifiedBaseRangeState { - if (!conflictingDiffs) { - conflictingDiffs = this.resultEdits.findTouchingDiffs( - baseRange.baseRange - ); - } - - if (conflictingDiffs.length === 0) { - return ModifiedBaseRangeState.default; - } - const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); - - function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean { - return equals( - conflictingEdits, - diffs.map((d) => d.getLineEdit()), - (a, b) => a.equals(b) - ); - } - - if (editsAgreeWithDiffs(baseRange.input1Diffs)) { - return ModifiedBaseRangeState.default.withInput1(true); - } - if (editsAgreeWithDiffs(baseRange.input2Diffs)) { - return ModifiedBaseRangeState.default.withInput2(true); - } - - return ModifiedBaseRangeState.conflicting; - } - public getState(baseRange: ModifiedBaseRange): IObservable { - const existingState = this.modifiedBaseRangeStateStores.get(baseRange); + const existingState = this.modifiedBaseRangeStateStores.get().get(baseRange); if (!existingState) { throw new BugIndicatingError('object must be from this instance'); } @@ -221,240 +167,122 @@ export class MergeEditorModel extends EditorModel { public setState( baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState, - transaction: ITransaction | undefined + transaction: ITransaction ): void { - const existingState = this.modifiedBaseRangeStateStores.get(baseRange); + if (!this.isUpToDate.get()) { + throw new BugIndicatingError('Cannot set state while updating'); + } + + const existingState = this.modifiedBaseRangeStateStores.get().get(baseRange); if (!existingState) { throw new BugIndicatingError('object must be from this instance'); } - - const conflictingDiffs = this.resultEdits.findTouchingDiffs( + const conflictingDiffs = this.resultTextModelDiffs.findTouchingDiffs( baseRange.baseRange ); if (conflictingDiffs) { - this.resultEdits.removeDiffs(conflictingDiffs, transaction); + this.resultTextModelDiffs.removeDiffs(conflictingDiffs, transaction); } - function getEdit(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } { - interface LineDiffWithInputNumber { - diff: LineDiff; - inputNumber: 1 | 2; - } - - const diffs = new Array(); - if (state.input1) { - if (baseRange.input1CombinedDiff) { - diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 }); - } - } - if (state.input2) { - if (baseRange.input2CombinedDiff) { - diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 }); - } - } - if (state.input2First) { - diffs.reverse(); - } - const firstDiff: LineDiffWithInputNumber | undefined = diffs[0]; - const secondDiff: LineDiffWithInputNumber | undefined = diffs[1]; - diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart)); - - if (!firstDiff) { - return { edit: undefined, effectiveState: state }; - } - - if (!secondDiff) { - return { edit: firstDiff.diff.getLineEdit(), effectiveState: state }; - } - - // Two inserts - if ( - firstDiff.diff.originalRange.lineCount === 0 && - firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange) - ) { - return { - edit: new LineEdit( - firstDiff.diff.originalRange, - firstDiff.diff - .getLineEdit() - .newLines.concat(secondDiff.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; - } - - // Technically non-conflicting diffs - if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) { - return { - edit: new LineEdit( - LineRange.join(diffs.map(d => d.diff.originalRange))!, - diffs.flatMap(d => d.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; - } - - return { edit: firstDiff.diff.getLineEdit(), effectiveState: state }; - } - const { edit, effectiveState } = getEdit(baseRange, state); + const { edit, effectiveState } = getEditForBase(baseRange, state); existingState.set(effectiveState, transaction); if (edit) { - this.resultEdits.applyEditRelativeToOriginal(edit, transaction); + this.resultTextModelDiffs.applyEditRelativeToOriginal(edit, transaction); } } - - public getResultRange(baseRange: LineRange): LineRange { - return this.resultEdits.getResultRange(baseRange); - } } -class ResultEdits { - private readonly barrier = new ReentrancyBarrier(); - private readonly onDidChangeEmitter = new Emitter(); - public readonly onDidChange = this.onDidChangeEmitter.event; +function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } { + interface LineDiffWithInputNumber { + diff: LineDiff; + inputNumber: 1 | 2; + } - constructor( - diffs: LineDiff[], - private readonly baseTextModel: ITextModel, - private readonly resultTextModel: ITextModel, - private readonly _editorWorkerService: IEditorWorkerService + const diffs = new Array(); + if (state.input1) { + if (baseRange.input1CombinedDiff) { + diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 }); + } + } + if (state.input2) { + if (baseRange.input2CombinedDiff) { + diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 }); + } + } + if (state.input2First) { + diffs.reverse(); + } + const firstDiff: LineDiffWithInputNumber | undefined = diffs[0]; + const secondDiff: LineDiffWithInputNumber | undefined = diffs[1]; + diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart)); + + if (!firstDiff) { + return { edit: undefined, effectiveState: ModifiedBaseRangeState.default }; + } + + if (!secondDiff) { + return { edit: firstDiff.diff.getLineEdit(), effectiveState: ModifiedBaseRangeState.default.withInputValue(firstDiff.inputNumber, true) }; + } + + // Two inserts + if ( + firstDiff.diff.originalRange.lineCount === 0 && + firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange) ) { - diffs.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); - this._diffs.set(diffs, undefined); - - resultTextModel.onDidChangeContent(e => { - this.barrier.runExclusively(() => { - this._editorWorkerService.computeDiff( - baseTextModel.uri, - resultTextModel.uri, - false, - 1000 - ).then(e => { - const diffs = - e?.changes.map((c) => - LineDiff.fromLineChange(c, baseTextModel, resultTextModel) - ) || []; - this._diffs.set(diffs, undefined); - - this.onDidChangeEmitter.fire(undefined); - }); - }); - }); + return { + edit: new LineEdit( + firstDiff.diff.originalRange, + firstDiff.diff + .getLineEdit() + .newLines.concat(secondDiff.diff.getLineEdit().newLines) + ), + effectiveState: state, + }; } - private readonly _diffs = new ObservableValue([], 'diffs'); - - public readonly diffs: IObservable = this._diffs; - - public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void { - diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); - diffToRemoves.reverse(); - - let diffs = this._diffs.get(); - - for (const diffToRemove of diffToRemoves) { - // TODO improve performance - const len = diffs.length; - diffs = diffs.filter((d) => d !== diffToRemove); - if (len === diffs.length) { - throw new BugIndicatingError(); - } - - this.barrier.runExclusivelyOrThrow(() => { - diffToRemove.getReverseLineEdit().apply(this.resultTextModel); - }); - - diffs = diffs.map((d) => - d.modifiedRange.isAfter(diffToRemove.modifiedRange) - ? new LineDiff( - d.originalTextModel, - d.originalRange, - d.modifiedTextModel, - d.modifiedRange.delta( - diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount - ) - ) - : d - ); - } - - this._diffs.set(diffs, transaction); + // Technically non-conflicting diffs + if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) { + return { + edit: new LineEdit( + LineRange.join(diffs.map(d => d.diff.originalRange))!, + diffs.flatMap(d => d.diff.getLineEdit().newLines) + ), + effectiveState: state, + }; } - /** - * Edit must be conflict free. - */ - public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void { - let firstAfter = false; - let delta = 0; - const newDiffs = new Array(); - for (const diff of this._diffs.get()) { - if (diff.originalRange.touches(edit.range)) { - throw new BugIndicatingError('Edit must be conflict free.'); - } else if (diff.originalRange.isAfter(edit.range)) { - if (!firstAfter) { - firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.resultTextModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); - } - - newDiffs.push(new LineDiff( - diff.originalTextModel, - diff.originalRange, - diff.modifiedTextModel, - diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount) - )); - } else { - newDiffs.push(diff); - } - - if (!firstAfter) { - delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount; - } - } - - if (!firstAfter) { - firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.resultTextModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); - } - - this.barrier.runExclusivelyOrThrow(() => { - new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.resultTextModel); - }); - this._diffs.set(newDiffs, transaction); - } - - public findTouchingDiffs(baseRange: LineRange): LineDiff[] { - return this.diffs.get().filter(d => d.originalRange.touches(baseRange)); - } - - public getResultRange(baseRange: LineRange): LineRange { - let startOffset = 0; - let lengthOffset = 0; - for (const diff of this.diffs.get()) { - if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) { - startOffset += diff.resultingDeltaFromOriginalToModified; - } else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) { - lengthOffset += diff.resultingDeltaFromOriginalToModified; - } else { - break; - } - } - - return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset); - } + return { + edit: secondDiff.diff.getLineEdit(), + effectiveState: ModifiedBaseRangeState.default.withInputValue( + secondDiff.inputNumber, + true + ), + }; +} + +function computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineDiff[]): ModifiedBaseRangeState { + if (conflictingDiffs.length === 0) { + return ModifiedBaseRangeState.default; + } + const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); + + function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean { + return equals( + conflictingEdits, + diffs.map((d) => d.getLineEdit()), + (a, b) => a.equals(b) + ); + } + + if (editsAgreeWithDiffs(baseRange.input1Diffs)) { + return ModifiedBaseRangeState.default.withInput1(true); + } + if (editsAgreeWithDiffs(baseRange.input2Diffs)) { + return ModifiedBaseRangeState.default.withInput2(true); + } + + return ModifiedBaseRangeState.conflicting; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts new file mode 100644 index 00000000000..e7c6c9967e5 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { compareBy, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { LineDiff, LineEdit, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; + +export class TextModelDiffs extends Disposable { + private updateCount = 0; + private readonly _state = new ObservableValue(TextModelDiffState.initializing, 'LiveDiffState'); + private readonly _diffs = new ObservableValue([], 'LiveDiffs'); + + private readonly barrier = new ReentrancyBarrier(); + + constructor( + private readonly baseTextModel: ITextModel, + private readonly textModel: ITextModel, + private readonly diffComputer: IDiffComputer, + ) { + super(); + + this.update(true); + this._register(baseTextModel.onDidChangeContent(this.barrier.makeExclusive(() => this.update()))); + this._register(textModel.onDidChangeContent(this.barrier.makeExclusive(() => this.update()))); + } + + public get state(): IObservable { + return this._state; + } + + public get diffs(): IObservable { + return this._diffs; + } + + private async update(initializing: boolean = false): Promise { + this.updateCount++; + const currentUpdateCount = this.updateCount; + + if (this._state.get() === TextModelDiffState.initializing) { + initializing = true; + } + + transaction(tx => { + this._state.set( + initializing ? TextModelDiffState.initializing : TextModelDiffState.updating, + tx, + TextModelDiffChangeReason.other + ); + }); + + const result = await this.diffComputer.computeDiff(this.baseTextModel, this.textModel); + + if (currentUpdateCount !== this.updateCount) { + // There is a newer update call + return; + } + + transaction(tx => { + if (result) { + this._state.set(TextModelDiffState.upToDate, tx, TextModelDiffChangeReason.textChange); + this._diffs.set(result, tx, TextModelDiffChangeReason.textChange); + } else { + this._state.set(TextModelDiffState.error, tx, TextModelDiffChangeReason.textChange); + } + }); + } + + private ensureUpToDate(): void { + if (this.state.get() !== TextModelDiffState.upToDate) { + throw new BugIndicatingError('Cannot remove diffs when the model is not up to date'); + } + } + + public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void { + this.ensureUpToDate(); + + diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + diffToRemoves.reverse(); + + let diffs = this._diffs.get(); + + for (const diffToRemove of diffToRemoves) { + // TODO improve performance + const len = diffs.length; + diffs = diffs.filter((d) => d !== diffToRemove); + if (len === diffs.length) { + throw new BugIndicatingError(); + } + + this.barrier.runExclusivelyOrThrow(() => { + diffToRemove.getReverseLineEdit().apply(this.textModel); + }); + + diffs = diffs.map((d) => + d.modifiedRange.isAfter(diffToRemove.modifiedRange) + ? new LineDiff( + d.originalTextModel, + d.originalRange, + d.modifiedTextModel, + d.modifiedRange.delta( + diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount + ) + ) + : d + ); + } + + this._diffs.set(diffs, transaction, TextModelDiffChangeReason.other); + } + + /** + * Edit must be conflict free. + */ + public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void { + this.ensureUpToDate(); + + let firstAfter = false; + let delta = 0; + const newDiffs = new Array(); + for (const diff of this.diffs.get()) { + if (diff.originalRange.touches(edit.range)) { + throw new BugIndicatingError('Edit must be conflict free.'); + } else if (diff.originalRange.isAfter(edit.range)) { + if (!firstAfter) { + firstAfter = true; + + newDiffs.push(new LineDiff( + this.baseTextModel, + edit.range, + this.textModel, + new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) + )); + } + + newDiffs.push(new LineDiff( + diff.originalTextModel, + diff.originalRange, + diff.modifiedTextModel, + diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount) + )); + } else { + newDiffs.push(diff); + } + + if (!firstAfter) { + delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount; + } + } + + if (!firstAfter) { + firstAfter = true; + + newDiffs.push(new LineDiff( + this.baseTextModel, + edit.range, + this.textModel, + new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) + )); + } + + this.barrier.runExclusivelyOrThrow(() => { + new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel); + }); + this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other); + } + + public findTouchingDiffs(baseRange: LineRange): LineDiff[] { + return this.diffs.get().filter(d => d.originalRange.touches(baseRange)); + } + + /* + public getResultRange(baseRange: LineRange): LineRange { + let startOffset = 0; + let lengthOffset = 0; + for (const diff of this.diffs.get()) { + if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) { + startOffset += diff.resultingDeltaFromOriginalToModified; + } else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) { + lengthOffset += diff.resultingDeltaFromOriginalToModified; + } else { + break; + } + } + + return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset); + } + */ +} + +export const enum TextModelDiffChangeReason { + other = 0, + textChange = 1, +} + +export const enum TextModelDiffState { + initializing = 1, + upToDate = 2, + updating = 3, + error = 4, +} + +export interface ITextModelDiffsState { + state: TextModelDiffState; + diffs: LineDiff[]; +} + +export interface IDiffComputer { + computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; +} + +export class EditorWorkerServiceDiffComputer implements IDiffComputer { + constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { } + + async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise { + //await wait(1000); + const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000); + if (!diffs || diffs.quitEarly) { + return null; + } + return diffs.changes.map((c) => LineDiff.fromLineChange(c, textModel1, textModel2)); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 58227467a66..3e54598b92b 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -544,6 +544,8 @@ registerAction2(class RevealRunningCellAction extends NotebookAction { super({ id: REVEAL_RUNNING_CELL, title: localize('revealRunningCell', "Go To Running Cell"), + tooltip: localize('revealRunningCell', "Go To Running Cell"), + shortTitle: localize('revealRunningCellShort', "Go To"), precondition: NOTEBOOK_HAS_RUNNING_CELL, menu: [ { @@ -587,7 +589,7 @@ registerAction2(class RevealRunningCellAction extends NotebookAction { if (executingCells[0]) { const cell = context.notebookEditor.getCellByHandle(executingCells[0].cellHandle); if (cell) { - context.notebookEditor.revealInCenter(cell); + context.notebookEditor.focusNotebookCell(cell, 'container'); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index d1e8bfa5ddb..68e974f3e5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1459,11 +1459,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } hasPendingChangeContentHeight = true; - DOM.scheduleAtNextAnimationFrame(() => { + this._localStore.add(DOM.scheduleAtNextAnimationFrame(() => { hasPendingChangeContentHeight = false; this._updateScrollHeight(); this._onDidChangeContentHeight.fire(this._list.getScrollHeight()); - }, 100); + }, 100)); })); this._localStore.add(this._list.onDidRemoveOutputs(outputs => { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts index 3b487ec7684..e32dbce7ba8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl.ts @@ -217,7 +217,7 @@ function updateToEdit(update: ICellExecuteUpdate, cellHandle: number): ICellEdit if (update.editType === CellExecutionUpdateType.Output) { return { editType: CellEditType.Output, - handle: cellHandle, + handle: update.cellHandle, append: update.append, outputs: update.outputs, }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 45fc3cbb235..c84443b70ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -164,9 +164,7 @@ export class CellTitleToolbarPart extends CellPart { if (deferredUpdate && !visible) { this._register(disposableTimeout(() => { - if (deferredUpdate) { - deferredUpdate(); - } + deferredUpdate?.(); })); deferredUpdate = undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts index 243b4b96edc..1d1d38c2689 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorToolbar.ts @@ -405,9 +405,7 @@ export class NotebookEditorToolbar extends Disposable { if (deferredUpdate && !visible) { setTimeout(() => { - if (deferredUpdate) { - deferredUpdate(); - } + deferredUpdate?.(); }, 0); deferredUpdate = undefined; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts b/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts index f95ed7b6e30..1a3b6ac9a94 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookExecutionService.ts @@ -15,6 +15,7 @@ export enum CellExecutionUpdateType { export interface ICellExecuteOutputEdit { editType: CellExecutionUpdateType.Output; + cellHandle: number; append?: boolean; outputs: IOutputDto[]; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index b65d169f75c..c3893a2a036 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1166,9 +1166,7 @@ export class SettingArrayRenderer extends AbstractSettingRenderer implements ITr common.toDispose.add( listWidget.onDidChangeList(e => { const newList = this.computeNewList(template, e); - if (template.onChange) { - template.onChange(newList); - } + template.onChange?.(newList); }) ); @@ -1349,9 +1347,7 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp template.objectDropdownWidget!.setValue(newItems); } - if (template.onChange) { - template.onChange(newValue); - } + template.onChange?.(newValue); } } @@ -1539,9 +1535,7 @@ abstract class AbstractSettingTextRenderer extends AbstractSettingRenderer imple })); common.toDispose.add( inputBox.onDidChange(e => { - if (template.onChange) { - template.onChange(e); - } + template.onChange?.(e); })); common.toDispose.add(inputBox); inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS); @@ -1652,9 +1646,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre common.toDispose.add( selectBox.onDidSelect(e => { - if (template.onChange) { - template.onChange(e.index); - } + template.onChange?.(e.index); })); const enumDescriptionElement = common.containerElement.insertBefore($('.setting-item-enumDescription'), common.descriptionElement.nextSibling); @@ -1758,9 +1750,7 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT })); common.toDispose.add( inputBox.onDidChange(e => { - if (template.onChange) { - template.onChange(e); - } + template.onChange?.(e); })); common.toDispose.add(inputBox); inputBox.inputElement.classList.add(AbstractSettingRenderer.CONTROL_CLASS); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 8c56ab754f6..2fd8f4ab2c8 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -1077,9 +1077,7 @@ export class SearchModel extends Disposable { progressEmitter.fire(); this.onSearchProgress(p); - if (onProgress) { - onProgress(p); - } + onProgress?.(p); }); const dispose = () => tokenSource.dispose(); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 25330219880..c7e14b8054e 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -196,7 +196,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer public static OutputChannelId: string = 'tasks'; public static OutputChannelLabel: string = nls.localize('tasks', "Tasks"); - private static nextHandle: number = 0; + private static _nextHandle: number = 0; private _schemaVersion: JsonSchemaVersion | undefined; private _executionEngine: ExecutionEngine | undefined; @@ -225,58 +225,58 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer public onDidChangeTaskSystemInfo: Event = this._onDidChangeTaskSystemInfo.event; constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @IMarkerService protected readonly markerService: IMarkerService, - @IOutputService protected readonly outputService: IOutputService, - @IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService, - @IViewsService private readonly viewsService: IViewsService, - @ICommandService private readonly commandService: ICommandService, - @IEditorService private readonly editorService: IEditorService, - @IFileService protected readonly fileService: IFileService, - @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService, - @ITelemetryService protected readonly telemetryService: ITelemetryService, - @ITextFileService private readonly textFileService: ITextFileService, - @IModelService protected readonly modelService: IModelService, - @IExtensionService private readonly extensionService: IExtensionService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IConfigurationResolverService protected readonly configurationResolverService: IConfigurationResolverService, - @ITerminalService private readonly terminalService: ITerminalService, - @ITerminalGroupService private readonly terminalGroupService: ITerminalGroupService, - @IStorageService private readonly storageService: IStorageService, - @IProgressService private readonly progressService: IProgressService, - @IOpenerService private readonly openerService: IOpenerService, - @IDialogService protected readonly dialogService: IDialogService, - @INotificationService private readonly notificationService: INotificationService, - @IContextKeyService protected readonly contextKeyService: IContextKeyService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @ITerminalProfileResolverService private readonly terminalProfileResolverService: ITerminalProfileResolverService, - @IPathService private readonly pathService: IPathService, - @ITextModelService private readonly textModelResolverService: ITextModelService, - @IPreferencesService private readonly preferencesService: IPreferencesService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService, - @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService, - @ILogService private readonly logService: ILogService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IMarkerService protected readonly _markerService: IMarkerService, + @IOutputService protected readonly _outputService: IOutputService, + @IPaneCompositePartService private readonly _paneCompositeService: IPaneCompositePartService, + @IViewsService private readonly _viewsService: IViewsService, + @ICommandService private readonly _commandService: ICommandService, + @IEditorService private readonly _editorService: IEditorService, + @IFileService protected readonly _fileService: IFileService, + @IWorkspaceContextService protected readonly _contextService: IWorkspaceContextService, + @ITelemetryService protected readonly _telemetryService: ITelemetryService, + @ITextFileService private readonly _textFileService: ITextFileService, + @IModelService protected readonly _modelService: IModelService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IConfigurationResolverService protected readonly _configurationResolverService: IConfigurationResolverService, + @ITerminalService private readonly _terminalService: ITerminalService, + @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, + @IStorageService private readonly _storageService: IStorageService, + @IProgressService private readonly _progressService: IProgressService, + @IOpenerService private readonly _openerService: IOpenerService, + @IDialogService protected readonly _dialogService: IDialogService, + @INotificationService private readonly _notificationService: INotificationService, + @IContextKeyService protected readonly _contextKeyService: IContextKeyService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService, + @IPathService private readonly _pathService: IPathService, + @ITextModelService private readonly _textModelResolverService: ITextModelService, + @IPreferencesService private readonly _preferencesService: IPreferencesService, + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, + @IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService, + @IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService, + @ILogService private readonly _logService: ILogService ) { super(); this._workspaceTasksPromise = undefined; this._taskSystem = undefined; this._taskSystemListener = undefined; - this._outputChannel = this.outputService.getChannel(AbstractTaskService.OutputChannelId)!; + this._outputChannel = this._outputService.getChannel(AbstractTaskService.OutputChannelId)!; this._providers = new Map(); this._providerTypes = new Map(); this._taskSystemInfos = new Map(); - this._register(this.contextService.onDidChangeWorkspaceFolders(() => { - const folderSetup = this.computeWorkspaceFolderSetup(); + this._register(this._contextService.onDidChangeWorkspaceFolders(() => { + const folderSetup = this._computeWorkspaceFolderSetup(); if (this.executionEngine !== folderSetup[2]) { - this.disposeTaskSystemListeners(); + this._disposeTaskSystemListeners(); this._taskSystem = undefined; } - this.updateSetup(folderSetup); - return this.updateWorkspaceTasks(TaskRunSource.FolderOpen); + this._updateSetup(folderSetup); + return this._updateWorkspaceTasks(TaskRunSource.FolderOpen); })); - this._register(this.configurationService.onDidChangeConfiguration(() => { + this._register(this._configurationService.onDidChangeConfiguration(() => { if (!this._taskSystem && !this._workspaceTasksPromise) { return; } @@ -284,16 +284,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._outputChannel.clear(); } - this.setTaskLRUCacheLimit(); - return this.updateWorkspaceTasks(TaskRunSource.ConfigurationChange); + this._setTaskLRUCacheLimit(); + return this._updateWorkspaceTasks(TaskRunSource.ConfigurationChange); })); - this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); + this._taskRunningState = TASK_RUNNING_STATE.bindTo(_contextKeyService); this._onDidStateChange = this._register(new Emitter()); - this.registerCommands(); - this.configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise => { - let tasks = await this.getTasksForGroup(TaskGroup.Build); + this._registerCommands(); + this._configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise => { + let tasks = await this._getTasksForGroup(TaskGroup.Build); if (tasks.length > 0) { - const { none, defaults } = this.splitPerGroupType(tasks); + const { none, defaults } = this._splitPerGroupType(tasks); if (defaults.length === 1) { return defaults[0]._label; } else if (defaults.length + none.length > 0) { @@ -303,7 +303,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer let entry: ITaskQuickPickEntry | null | undefined; if (tasks && tasks.length > 0) { - entry = await this.showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task (there is no default build task defined)')); + entry = await this._showQuickPick(tasks, nls.localize('TaskService.pickBuildTaskForLabel', 'Select the build task (there is no default build task defined)')); } const task: Task | undefined | null = entry ? entry.task : undefined; @@ -316,21 +316,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._waitForSupportedExecutions = new Promise(resolve => { once(this._onDidRegisterSupportedExecutions.event)(() => resolve()); }); - this.upgrade(); + this._upgrade(); } public registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean) { if (custom !== undefined) { - const customContext = CustomExecutionSupportedContext.bindTo(this.contextKeyService); + const customContext = CustomExecutionSupportedContext.bindTo(this._contextKeyService); customContext.set(custom); } - const isVirtual = !!VirtualWorkspaceContext.getValue(this.contextKeyService); + const isVirtual = !!VirtualWorkspaceContext.getValue(this._contextKeyService); if (shell !== undefined) { - const shellContext = ShellExecutionSupportedContext.bindTo(this.contextKeyService); + const shellContext = ShellExecutionSupportedContext.bindTo(this._contextKeyService); shellContext.set(shell && !isVirtual); } if (process !== undefined) { - const processContext = ProcessExecutionSupportedContext.bindTo(this.contextKeyService); + const processContext = ProcessExecutionSupportedContext.bindTo(this._contextKeyService); processContext.set(process && !isVirtual); } this._onDidRegisterSupportedExecutions.fire(); @@ -344,12 +344,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this.inTerminal(); } - private registerCommands(): void { + private _registerCommands(): void { CommandsRegistry.registerCommand({ id: 'workbench.action.tasks.runTask', handler: async (accessor, arg) => { - if (await this.trust()) { - this.runTaskCommand(arg); + if (await this._trust()) { + this._runTaskCommand(arg); } }, description: { @@ -364,120 +364,120 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', async (accessor, arg) => { - if (await this.trust()) { - this.reRunTaskCommand(); + if (await this._trust()) { + this._reRunTaskCommand(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', async (accessor, arg) => { - if (await this.trust()) { - this.runRestartTaskCommand(arg); + if (await this._trust()) { + this._runRestartTaskCommand(arg); } }); CommandsRegistry.registerCommand('workbench.action.tasks.terminate', async (accessor, arg) => { - if (await this.trust()) { - this.runTerminateCommand(arg); + if (await this._trust()) { + this._runTerminateCommand(arg); } }); CommandsRegistry.registerCommand('workbench.action.tasks.showLog', () => { - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return; } - this.showOutput(); + this._showOutput(); }); CommandsRegistry.registerCommand('workbench.action.tasks.build', async () => { - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return; } - if (await this.trust()) { - this.runBuildCommand(); + if (await this._trust()) { + this._runBuildCommand(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.test', async () => { - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return; } - if (await this.trust()) { - this.runTestCommand(); + if (await this._trust()) { + this._runTestCommand(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.configureTaskRunner', async () => { - if (await this.trust()) { - this.runConfigureTasks(); + if (await this._trust()) { + this._runConfigureTasks(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultBuildTask', async () => { - if (await this.trust()) { - this.runConfigureDefaultBuildTask(); + if (await this._trust()) { + this._runConfigureDefaultBuildTask(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.configureDefaultTestTask', async () => { - if (await this.trust()) { - this.runConfigureDefaultTestTask(); + if (await this._trust()) { + this._runConfigureDefaultTestTask(); } }); CommandsRegistry.registerCommand('workbench.action.tasks.showTasks', async () => { - if (await this.trust()) { + if (await this._trust()) { return this.runShowTasks(); } }); - CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this.commandService.executeCommand(Markers.TOGGLE_MARKERS_VIEW_ACTION_ID)); + CommandsRegistry.registerCommand('workbench.action.tasks.toggleProblems', () => this._commandService.executeCommand(Markers.TOGGLE_MARKERS_VIEW_ACTION_ID)); CommandsRegistry.registerCommand('workbench.action.tasks.openUserTasks', async () => { - const resource = this.getResourceForKind(TaskSourceKind.User); + const resource = this._getResourceForKind(TaskSourceKind.User); if (resource) { - this.openTaskFile(resource, TaskSourceKind.User); + this._openTaskFile(resource, TaskSourceKind.User); } }); CommandsRegistry.registerCommand('workbench.action.tasks.openWorkspaceFileTasks', async () => { - const resource = this.getResourceForKind(TaskSourceKind.WorkspaceFile); + const resource = this._getResourceForKind(TaskSourceKind.WorkspaceFile); if (resource) { - this.openTaskFile(resource, TaskSourceKind.WorkspaceFile); + this._openTaskFile(resource, TaskSourceKind.WorkspaceFile); } }); } private get workspaceFolders(): IWorkspaceFolder[] { if (!this._workspaceFolders) { - this.updateSetup(); + this._updateSetup(); } return this._workspaceFolders!; } private get ignoredWorkspaceFolders(): IWorkspaceFolder[] { if (!this._ignoredWorkspaceFolders) { - this.updateSetup(); + this._updateSetup(); } return this._ignoredWorkspaceFolders!; } protected get executionEngine(): ExecutionEngine { if (this._executionEngine === undefined) { - this.updateSetup(); + this._updateSetup(); } return this._executionEngine!; } private get schemaVersion(): JsonSchemaVersion { if (this._schemaVersion === undefined) { - this.updateSetup(); + this._updateSetup(); } return this._schemaVersion!; } private get showIgnoreMessage(): boolean { if (this._showIgnoreMessage === undefined) { - this._showIgnoreMessage = !this.storageService.getBoolean(AbstractTaskService.IgnoreTask010DonotShowAgain_key, StorageScope.WORKSPACE, false); + this._showIgnoreMessage = !this._storageService.getBoolean(AbstractTaskService.IgnoreTask010DonotShowAgain_key, StorageScope.WORKSPACE, false); } return this._showIgnoreMessage; } @@ -500,16 +500,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private async _activateTaskProviders(type: string | undefined): Promise { // We need to first wait for extensions to be registered because we might read // the `TaskDefinitionRegistry` in case `type` is `undefined` - await this.extensionService.whenInstalledExtensionsRegistered(); + await this._extensionService.whenInstalledExtensionsRegistered(); await Promise.all( - this._getActivationEvents(type).map(activationEvent => this.extensionService.activateByEvent(activationEvent)) + this._getActivationEvents(type).map(activationEvent => this._extensionService.activateByEvent(activationEvent)) ); } - private updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void { + private _updateSetup(setup?: [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined]): void { if (!setup) { - setup = this.computeWorkspaceFolderSetup(); + setup = this._computeWorkspaceFolderSetup(); } this._workspaceFolders = setup[0]; if (this._ignoredWorkspaceFolders) { @@ -532,19 +532,19 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._workspace = setup[4]; } - protected showOutput(runSource: TaskRunSource = TaskRunSource.User): void { - if (!VirtualWorkspaceContext.getValue(this.contextKeyService) && ((runSource === TaskRunSource.User) || (runSource === TaskRunSource.ConfigurationChange))) { - this.notificationService.prompt(Severity.Warning, nls.localize('taskServiceOutputPrompt', 'There are task errors. See the output for details.'), + protected _showOutput(runSource: TaskRunSource = TaskRunSource.User): void { + if (!VirtualWorkspaceContext.getValue(this._contextKeyService) && ((runSource === TaskRunSource.User) || (runSource === TaskRunSource.ConfigurationChange))) { + this._notificationService.prompt(Severity.Warning, nls.localize('taskServiceOutputPrompt', 'There are task errors. See the output for details.'), [{ label: nls.localize('showOutput', "Show output"), run: () => { - this.outputService.showChannel(this._outputChannel.id, true); + this._outputService.showChannel(this._outputChannel.id, true); } }]); } } - protected disposeTaskSystemListeners(): void { + protected _disposeTaskSystemListeners(): void { if (this._taskSystemListener) { this._taskSystemListener.dispose(); } @@ -556,7 +556,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer dispose: () => { } }; } - const handle = AbstractTaskService.nextHandle++; + const handle = AbstractTaskService._nextHandle++; this._providers.set(handle, provider); this._providerTypes.set(handle, type); return { @@ -571,7 +571,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const infosCount = Array.from(this._taskSystemInfos.values()).flat().length; // If there's a remoteAuthority, then we end up with 2 taskSystemInfos, // one for each extension host. - if (this.environmentService.remoteAuthority) { + if (this._environmentService.remoteAuthority) { return infosCount > 1; } return infosCount > 0; @@ -600,7 +600,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private getTaskSystemInfo(key: string): ITaskSystemInfo | undefined { + private _getTaskSystemInfo(key: string): ITaskSystemInfo | undefined { const infos = this._taskSystemInfos.get(key); return (infos && infos.length) ? infos[0] : undefined; } @@ -651,7 +651,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public async getTask(folder: IWorkspace | IWorkspaceFolder | string, identifier: string | ITaskIdentifier, compareId: boolean = false): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return; } const name = Types.isString(folder) ? folder : isWorkspaceFolder(folder) ? folder.name : folder.configuration ? resources.basename(folder.configuration) : undefined; @@ -687,7 +687,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } // We didn't find the task, so we need to ask all resolvers about it - return this.getGroupedTasks().then((map) => { + return this._getGroupedTasks().then((map) => { let values = map.get(folder); values = values.concat(map.get(USER_TASKS_GROUP_KEY)); @@ -700,7 +700,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public async tryResolveTask(configuringTask: ConfiguringTask): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return; } await this._activateTaskProviders(configuringTask.type); @@ -709,7 +709,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const [handle, provider] of this._providers) { const providerType = this._providerTypes.get(handle); if (configuringTask.type === providerType) { - if (providerType && !this.isTaskProviderEnabled(providerType)) { + if (providerType && !this._isTaskProviderEnabled(providerType)) { matchingProviderUnavailable = true; continue; } @@ -750,16 +750,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } - protected abstract versionAndEngineCompatible(filter?: ITaskFilter): boolean; + protected abstract _versionAndEngineCompatible(filter?: ITaskFilter): boolean; public async tasks(filter?: ITaskFilter): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return []; } - if (!this.versionAndEngineCompatible(filter)) { + if (!this._versionAndEngineCompatible(filter)) { return Promise.resolve([]); } - return this.getGroupedTasks(filter ? filter.type : undefined).then((map) => { + return this._getGroupedTasks(filter ? filter.type : undefined).then((map) => { if (!filter || !filter.type) { return map.all(); } @@ -786,9 +786,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer public taskTypes(): string[] { const types: string[] = []; - if (this.isProvideTasksEnabled()) { + if (this._isProvideTasksEnabled()) { for (const definition of TaskDefinitionRegistry.all()) { - if (this.isTaskProviderEnabled(definition.taskType)) { + if (this._isTaskProviderEnabled(definition.taskType)) { types.push(definition.taskType); } } @@ -797,10 +797,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public createSorter(): TaskSorter { - return new TaskSorter(this.contextService.getWorkspace() ? this.contextService.getWorkspace().folders : []); + return new TaskSorter(this._contextService.getWorkspace() ? this._contextService.getWorkspace().folders : []); } - private isActive(): Promise { + private _isActive(): Promise { if (!this._taskSystem) { return Promise.resolve(false); } @@ -825,10 +825,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (this._recentlyUsedTasksV1) { return this._recentlyUsedTasksV1; } - const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); + const quickOpenHistoryLimit = this._configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); this._recentlyUsedTasksV1 = new LRUCache(quickOpenHistoryLimit); - const storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); + const storageValue = this._storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); if (storageValue) { try { const values: string[] = JSON.parse(storageValue); @@ -844,14 +844,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this._recentlyUsedTasksV1; } - private getRecentlyUsedTasks(): LRUCache { + private _getRecentlyUsedTasks(): LRUCache { if (this._recentlyUsedTasks) { return this._recentlyUsedTasks; } - const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); + const quickOpenHistoryLimit = this._configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); this._recentlyUsedTasks = new LRUCache(quickOpenHistoryLimit); - const storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_KeyV2, StorageScope.WORKSPACE); + const storageValue = this._storageService.get(AbstractTaskService.RecentlyUsedTasks_KeyV2, StorageScope.WORKSPACE); if (storageValue) { try { const values: [string, string][] = JSON.parse(storageValue); @@ -867,7 +867,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this._recentlyUsedTasks; } - private getFolderFromTaskKey(key: string): { folder: string | undefined; isWorkspaceFile: boolean | undefined } { + private _getFolderFromTaskKey(key: string): { folder: string | undefined; isWorkspaceFile: boolean | undefined } { const keyValue: { folder: string | undefined; id: string | undefined } = JSON.parse(key); return { folder: keyValue.folder, isWorkspaceFile: keyValue.id?.endsWith(TaskSourceKind.WorkspaceFile) @@ -881,7 +881,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); const folderToTasksMap: Map = new Map(); const workspaceToTaskMap: Map = new Map(); - const recentlyUsedTasks = this.getRecentlyUsedTasks(); + const recentlyUsedTasks = this._getRecentlyUsedTasks(); const tasks: (Task | ConfiguringTask)[] = []; function addTaskToMap(map: Map, folder: string | undefined, task: any) { @@ -896,7 +896,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const entry of recentlyUsedTasks.entries()) { const key = entry[0]; const task = JSON.parse(entry[1]); - const folderInfo = this.getFolderFromTaskKey(key); + const folderInfo = this._getFolderFromTaskKey(key); addTaskToMap(folderInfo.isWorkspaceFile ? workspaceToTaskMap : folderToTasksMap, folderInfo.folder, task); } @@ -909,7 +909,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer ? (isWorkspaceFile ? TaskConfig.TaskConfigSource.WorkspaceFile : TaskConfig.TaskConfigSource.TasksJson) : TaskConfig.TaskConfigSource.User); - await that.computeTasksForSingleConfig(folderMap[key] ?? await that.getAFolder(), { + await that._computeTasksForSingleConfig(folderMap[key] ?? await that._getAFolder(), { version: '2.0.0', tasks: map.get(key) }, TaskRunSource.System, custom, customized, taskConfigSource, true); @@ -939,27 +939,27 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public removeRecentlyUsedTask(taskRecentlyUsedKey: string) { - if (this.getRecentlyUsedTasks().has(taskRecentlyUsedKey)) { - this.getRecentlyUsedTasks().delete(taskRecentlyUsedKey); - this.saveRecentlyUsedTasks(); + if (this._getRecentlyUsedTasks().has(taskRecentlyUsedKey)) { + this._getRecentlyUsedTasks().delete(taskRecentlyUsedKey); + this._saveRecentlyUsedTasks(); } } - private setTaskLRUCacheLimit() { - const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); + private _setTaskLRUCacheLimit() { + const quickOpenHistoryLimit = this._configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); if (this._recentlyUsedTasks) { this._recentlyUsedTasks.limit = quickOpenHistoryLimit; } } - private async setRecentlyUsedTask(task: Task): Promise { + private async _setRecentlyUsedTask(task: Task): Promise { let key = task.getRecentlyUsedKey(); if (!InMemoryTask.is(task) && key) { - const customizations = this.createCustomizableTask(task); + const customizations = this._createCustomizableTask(task); if (ContributedTask.is(task) && customizations) { const custom: CustomTask[] = []; const customized: IStringDictionary = Object.create(null); - await this.computeTasksForSingleConfig(task._source.workspaceFolder ?? this.workspaceFolders[0], { + await this._computeTasksForSingleConfig(task._source.workspaceFolder ?? this.workspaceFolders[0], { version: '2.0.0', tasks: [customizations] }, TaskRunSource.System, custom, customized, TaskConfig.TaskConfigSource.TasksJson, true); @@ -967,16 +967,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer key = customized[configuration].getRecentlyUsedKey()!; } } - this.getRecentlyUsedTasks().set(key, JSON.stringify(customizations)); - this.saveRecentlyUsedTasks(); + this._getRecentlyUsedTasks().set(key, JSON.stringify(customizations)); + this._saveRecentlyUsedTasks(); } } - private saveRecentlyUsedTasks(): void { + private _saveRecentlyUsedTasks(): void { if (!this._recentlyUsedTasks) { return; } - const quickOpenHistoryLimit = this.configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); + const quickOpenHistoryLimit = this._configurationService.getValue(QUICKOPEN_HISTORY_LIMIT_CONFIG); // setting history limit to 0 means no LRU sorting if (quickOpenHistoryLimit === 0) { return; @@ -989,11 +989,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const key of keys) { keyValues.push([key, this._recentlyUsedTasks.get(key, Touch.None)!]); } - this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE, StorageTarget.USER); + this._storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(keyValues), StorageScope.WORKSPACE, StorageTarget.USER); } - private openDocumentation(): void { - this.openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher')); + private _openDocumentation(): void { + this._openerService.open(URI.parse('https://code.visualstudio.com/docs/editor/tasks#_defining-a-problem-matcher')); } private async _findSingleWorkspaceTaskOfGroup(group: TaskGroup): Promise { @@ -1012,14 +1012,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return undefined; } - private async build(): Promise { + private async _build(): Promise { const tryBuildShortcut = await this._findSingleWorkspaceTaskOfGroup(TaskGroup.Build); if (tryBuildShortcut) { return tryBuildShortcut; } - return this.getGroupedTasks().then((tasks) => { - const runnable = this.createRunnableTask(tasks, TaskGroup.Build); + return this._getGroupedTasks().then((tasks) => { + const runnable = this._createRunnableTask(tasks, TaskGroup.Build); if (!runnable || !runnable.task) { if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask1', 'No build task defined. Mark a task with \'isBuildCommand\' in the tasks.json file.'), TaskErrors.NoBuildTask); @@ -1027,21 +1027,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer throw new TaskError(Severity.Info, nls.localize('TaskService.noBuildTask2', 'No build task defined. Mark a task with as a \'build\' group in the tasks.json file.'), TaskErrors.NoBuildTask); } } - return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User); + return this._executeTask(runnable.task, runnable.resolver, TaskRunSource.User); }).then(value => value, (error) => { - this.handleError(error); + this._handleError(error); return Promise.reject(error); }); } - private async runTest(): Promise { + private async _runTest(): Promise { const tryTestShortcut = await this._findSingleWorkspaceTaskOfGroup(TaskGroup.Test); if (tryTestShortcut) { return tryTestShortcut; } - return this.getGroupedTasks().then((tasks) => { - const runnable = this.createRunnableTask(tasks, TaskGroup.Test); + return this._getGroupedTasks().then((tasks) => { + const runnable = this._createRunnableTask(tasks, TaskGroup.Test); if (!runnable || !runnable.task) { if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask1', 'No test task defined. Mark a task with \'isTestCommand\' in the tasks.json file.'), TaskErrors.NoTestTask); @@ -1049,15 +1049,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer throw new TaskError(Severity.Info, nls.localize('TaskService.noTestTask2', 'No test task defined. Mark a task with as a \'test\' group in the tasks.json file.'), TaskErrors.NoTestTask); } } - return this.executeTask(runnable.task, runnable.resolver, TaskRunSource.User); + return this._executeTask(runnable.task, runnable.resolver, TaskRunSource.User); }).then(value => value, (error) => { - this.handleError(error); + this._handleError(error); return Promise.reject(error); }); } public async run(task: Task | undefined, options?: IProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return; } @@ -1066,38 +1066,38 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } return new Promise((resolve) => { - const resolver = this.createResolver(); - if (options && options.attachProblemMatcher && this.shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { - this.attachProblemMatcher(task).then(toExecute => { + const resolver = this._createResolver(); + if (options && options.attachProblemMatcher && this._shouldAttachProblemMatcher(task) && !InMemoryTask.is(task)) { + this._attachProblemMatcher(task).then(toExecute => { if (toExecute) { - resolve(this.executeTask(toExecute, resolver, runSource)); + resolve(this._executeTask(toExecute, resolver, runSource)); } else { resolve(undefined); } }); } else { - resolve(this.executeTask(task, resolver, runSource)); + resolve(this._executeTask(task, resolver, runSource)); } }).then((value) => { if (runSource === TaskRunSource.User) { this.getWorkspaceTasks().then(workspaceTasks => { - RunAutomaticTasks.promptForPermission(this, this.storageService, this.notificationService, this.workspaceTrustManagementService, this.openerService, workspaceTasks); + RunAutomaticTasks.promptForPermission(this, this._storageService, this._notificationService, this._workspaceTrustManagementService, this._openerService, workspaceTasks); }); } return value; }, (error) => { - this.handleError(error); + this._handleError(error); return Promise.reject(error); }); } - private isProvideTasksEnabled(): boolean { - const settingValue = this.configurationService.getValue('task.autoDetect'); + private _isProvideTasksEnabled(): boolean { + const settingValue = this._configurationService.getValue('task.autoDetect'); return settingValue === 'on'; } - private isProblemMatcherPromptEnabled(type?: string): boolean { - const settingValue = this.configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG); + private _isProblemMatcherPromptEnabled(type?: string): boolean { + const settingValue = this._configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG); if (Types.isBoolean(settingValue)) { return !settingValue; } @@ -1108,7 +1108,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return !settingValueMap[type]; } - private getTypeForTask(task: Task): string { + private _getTypeForTask(task: Task): string { let type: string; if (CustomTask.is(task)) { const configProperties: TaskConfig.IConfigurationProperties = task._source.config.element; @@ -1119,12 +1119,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return type; } - private shouldAttachProblemMatcher(task: Task): boolean { - const enabled = this.isProblemMatcherPromptEnabled(this.getTypeForTask(task)); + private _shouldAttachProblemMatcher(task: Task): boolean { + const enabled = this._isProblemMatcherPromptEnabled(this._getTypeForTask(task)); if (enabled === false) { return false; } - if (!this.canCustomize(task)) { + if (!this._canCustomize(task)) { return false; } if (task.configurationProperties.group !== undefined && task.configurationProperties.group !== TaskGroup.Build) { @@ -1143,8 +1143,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return false; } - private async updateNeverProblemMatcherSetting(type: string): Promise { - const current = this.configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG); + private async _updateNeverProblemMatcherSetting(type: string): Promise { + const current = this._configurationService.getValue(PROBLEM_MATCHER_NEVER_CONFIG); if (current === true) { return; } @@ -1155,10 +1155,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer newValue = Object.create(null); } newValue[type] = true; - return this.configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue); + return this._configurationService.updateValue(PROBLEM_MATCHER_NEVER_CONFIG, newValue); } - private attachProblemMatcher(task: ContributedTask | CustomTask): Promise { + private _attachProblemMatcher(task: ContributedTask | CustomTask): Promise { interface IProblemMatcherPickEntry extends IQuickPickItem { matcher: INamedProblemMatcher | undefined; never?: boolean; @@ -1203,12 +1203,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer { label: nls.localize('TaskService.attachProblemMatcher.neverType', 'Never scan the task output for {0} tasks', taskType), matcher: undefined, setting: taskType }, { label: nls.localize('TaskService.attachProblemMatcher.learnMoreAbout', 'Learn more about scanning the task output'), matcher: undefined, learnMore: true } ); - return this.quickInputService.pick(entries, { + return this._quickInputService.pick(entries, { placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the task output'), }).then(async (selected) => { if (selected) { if (selected.learnMore) { - this.openDocumentation(); + this._openDocumentation(); return undefined; } else if (selected.never) { this.customize(task, { problemMatcher: [] }, true); @@ -1226,7 +1226,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.customize(task, properties, true); return newTask; } else if (selected.setting) { - await this.updateNeverProblemMatcherSetting(selected.setting); + await this._updateNeverProblemMatcherSetting(selected.setting); return task; } else { return task; @@ -1239,8 +1239,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return Promise.resolve(task); } - private getTasksForGroup(group: TaskGroup): Promise { - return this.getGroupedTasks().then((groups) => { + private _getTasksForGroup(group: TaskGroup): Promise { + return this._getGroupedTasks().then((groups) => { const result: Task[] = []; groups.forEach((tasks) => { for (const task of tasks) { @@ -1255,10 +1255,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public needsFolderQualification(): boolean { - return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; + return this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; } - private canCustomize(task: Task): boolean { + private _canCustomize(task: Task): boolean { if (this.schemaVersion !== JsonSchemaVersion.V2_0_0) { return false; } @@ -1271,11 +1271,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return false; } - private async formatTaskForJson(resource: URI, task: TaskConfig.ICustomTask | TaskConfig.IConfiguringTask): Promise { + private async _formatTaskForJson(resource: URI, task: TaskConfig.ICustomTask | TaskConfig.IConfiguringTask): Promise { let reference: IReference | undefined; let stringValue: string = ''; try { - reference = await this.textModelResolverService.createModelReference(resource); + reference = await this._textModelResolverService.createModelReference(resource); const model = reference.object.textEditorModel; const { tabSize, insertSpaces } = model.getOptions(); const eol = model.getEOL(); @@ -1292,12 +1292,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return stringValue; } - private openEditorAtTask(resource: URI | undefined, task: TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | string | undefined, configIndex: number = -1): Promise { + private _openEditorAtTask(resource: URI | undefined, task: TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | string | undefined, configIndex: number = -1): Promise { if (resource === undefined) { return Promise.resolve(false); } let selection: ITextEditorSelection | undefined; - return this.fileService.readFile(resource).then(content => content.value).then(async content => { + return this._fileService.readFile(resource).then(content => content.value).then(async content => { if (!content) { return false; } @@ -1305,16 +1305,16 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const contentValue = content.toString(); let stringValue: string | undefined; if (configIndex !== -1) { - const json: TaskConfig.IExternalTaskRunnerConfiguration = this.configurationService.getValue('tasks', { resource }); + const json: TaskConfig.IExternalTaskRunnerConfiguration = this._configurationService.getValue('tasks', { resource }); if (json.tasks && (json.tasks.length > configIndex)) { - stringValue = await this.formatTaskForJson(resource, json.tasks[configIndex]); + stringValue = await this._formatTaskForJson(resource, json.tasks[configIndex]); } } if (!stringValue) { if (typeof task === 'string') { stringValue = task; } else { - stringValue = await this.formatTaskForJson(resource, task); + stringValue = await this._formatTaskForJson(resource, task); } } @@ -1334,7 +1334,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer selection = startLineNumber > 1 ? { startLineNumber, startColumn: startLineNumber === endLineNumber ? 4 : 3, endLineNumber, endColumn: startLineNumber === endLineNumber ? undefined : 4 } : undefined; } - return this.editorService.openEditor({ + return this._editorService.openEditor({ resource, options: { pinned: false, @@ -1346,7 +1346,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private createCustomizableTask(task: ContributedTask | CustomTask | ConfiguringTask): TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | undefined { + private _createCustomizableTask(task: ContributedTask | CustomTask | ConfiguringTask): TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | undefined { let toCustomize: TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | undefined; const taskConfig = CustomTask.is(task) || ConfiguringTask.is(task) ? task._source.config : undefined; if (taskConfig && taskConfig.element) { @@ -1380,7 +1380,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public async customize(task: ContributedTask | CustomTask | ConfiguringTask, properties?: ICustomizationProperties, openConfig?: boolean): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return; } @@ -1388,14 +1388,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!workspaceFolder) { return Promise.resolve(undefined); } - const configuration = this.getConfiguration(workspaceFolder, task._source.kind); + const configuration = this._getConfiguration(workspaceFolder, task._source.kind); if (configuration.hasParseErrors) { - this.notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.')); + this._notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.')); return Promise.resolve(undefined); } const fileConfig = configuration.config; - const toCustomize = this.createCustomizableTask(task); + const toCustomize = this._createCustomizableTask(task); if (!toCustomize) { return Promise.resolve(undefined); } @@ -1419,20 +1419,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer '{', nls.localize('tasksJsonComment', '\t// See https://go.microsoft.com/fwlink/?LinkId=733558 \n\t// for the documentation about the tasks.json format'), ].join('\n') + JSON.stringify(value, null, '\t').substr(1); - const editorConfig = this.configurationService.getValue(); + const editorConfig = this._configurationService.getValue(); if (editorConfig.editor.insertSpaces) { content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize)); } - promise = this.textFileService.create([{ resource: workspaceFolder.toResource('.vscode/tasks.json'), value: content }]).then(() => { }); + promise = this._textFileService.create([{ resource: workspaceFolder.toResource('.vscode/tasks.json'), value: content }]).then(() => { }); } else { // We have a global task configuration if ((index === -1) && properties) { if (properties.problemMatcher !== undefined) { fileConfig.problemMatcher = properties.problemMatcher; - promise = this.writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher, task._source.kind); + promise = this._writeConfiguration(workspaceFolder, 'tasks.problemMatchers', fileConfig.problemMatcher, task._source.kind); } else if (properties.group !== undefined) { fileConfig.group = properties.group; - promise = this.writeConfiguration(workspaceFolder, 'tasks.group', fileConfig.group, task._source.kind); + promise = this._writeConfiguration(workspaceFolder, 'tasks.group', fileConfig.group, task._source.kind); } } else { if (!Array.isArray(fileConfig.tasks)) { @@ -1443,7 +1443,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } else { fileConfig.tasks[index] = toCustomize; } - promise = this.writeConfiguration(workspaceFolder, 'tasks.tasks', fileConfig.tasks, task._source.kind); + promise = this._writeConfiguration(workspaceFolder, 'tasks.tasks', fileConfig.tasks, task._source.kind); } } if (!promise) { @@ -1451,34 +1451,34 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } return promise.then(() => { if (openConfig) { - this.openEditorAtTask(this.getResourceForTask(task), toCustomize); + this._openEditorAtTask(this._getResourceForTask(task), toCustomize); } }); } - private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any, source?: string): Promise | undefined { + private _writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any, source?: string): Promise | undefined { let target: ConfigurationTarget | undefined = undefined; switch (source) { case TaskSourceKind.User: target = ConfigurationTarget.USER; break; case TaskSourceKind.WorkspaceFile: target = ConfigurationTarget.WORKSPACE; break; - default: if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + default: if (this._contextService.getWorkbenchState() === WorkbenchState.FOLDER) { target = ConfigurationTarget.WORKSPACE; - } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + } else if (this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { target = ConfigurationTarget.WORKSPACE_FOLDER; } } if (target) { - return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, target); + return this._configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, target); } else { return undefined; } } - private getResourceForKind(kind: string): URI | undefined { - this.updateSetup(); + private _getResourceForKind(kind: string): URI | undefined { + this._updateSetup(); switch (kind) { case TaskSourceKind.User: { - return resources.joinPath(resources.dirname(this.preferencesService.userSettingsResource), 'tasks.json'); + return resources.joinPath(resources.dirname(this._preferencesService.userSettingsResource), 'tasks.json'); } case TaskSourceKind.WorkspaceFile: { if (this._workspace && this._workspace.configuration) { @@ -1491,9 +1491,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private getResourceForTask(task: CustomTask | ConfiguringTask | ContributedTask): URI { + private _getResourceForTask(task: CustomTask | ConfiguringTask | ContributedTask): URI { if (CustomTask.is(task)) { - let uri = this.getResourceForKind(task._source.kind); + let uri = this._getResourceForKind(task._source.kind); if (!uri) { const taskFolder = task.getWorkspaceFolder(); if (taskFolder) { @@ -1511,14 +1511,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer public async openConfig(task: CustomTask | ConfiguringTask | undefined): Promise { let resource: URI | undefined; if (task) { - resource = this.getResourceForTask(task); + resource = this._getResourceForTask(task); } else { resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined; } - return this.openEditorAtTask(resource, task ? task._label : undefined, task ? task._source.config.index : -1); + return this._openEditorAtTask(resource, task ? task._label : undefined, task ? task._source.config.index : -1); } - private createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined { + private _createRunnableTask(tasks: TaskMap, group: TaskGroup): { task: Task; resolver: ITaskResolver } | undefined { interface IResolverData { id: Map; label: Map; @@ -1594,7 +1594,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private createResolver(grouped?: TaskMap): ITaskResolver { + private _createResolver(grouped?: TaskMap): ITaskResolver { interface ResolverData { label: Map; identifier: Map; @@ -1631,7 +1631,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer async function getResolverData(that: AbstractTaskService) { if (resolverData === undefined) { resolverData = new Map(); - (grouped || await that.getGroupedTasks()).forEach((tasks, folder) => { + (grouped || await that._getGroupedTasks()).forEach((tasks, folder) => { let data = resolverData!.get(folder); if (!data) { data = { label: new Map(), identifier: new Map(), taskIdentifier: new Map() }; @@ -1680,19 +1680,19 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; } - private async saveBeforeRun(): Promise { + private async _saveBeforeRun(): Promise { enum SaveBeforeRunConfigOptions { Always = 'always', Never = 'never', Prompt = 'prompt' } - const saveBeforeRunTaskConfig: SaveBeforeRunConfigOptions = this.configurationService.getValue('task.saveBeforeRun'); + const saveBeforeRunTaskConfig: SaveBeforeRunConfigOptions = this._configurationService.getValue('task.saveBeforeRun'); if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Never) { return false; } else if (saveBeforeRunTaskConfig === SaveBeforeRunConfigOptions.Prompt) { - const dialogOptions = await this.dialogService.show( + const dialogOptions = await this._dialogService.show( Severity.Info, nls.localize('TaskSystem.saveBeforeRun.prompt.title', 'Save all editors?'), [nls.localize('saveBeforeRun.save', 'Save'), nls.localize('saveBeforeRun.dontSave', 'Don\'t save')], @@ -1706,15 +1706,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return false; } } - await this.editorService.saveAll({ reason: SaveReason.AUTO }); + await this._editorService.saveAll({ reason: SaveReason.AUTO }); return true; } - private async executeTask(task: Task, resolver: ITaskResolver, runSource: TaskRunSource): Promise { + private async _executeTask(task: Task, resolver: ITaskResolver, runSource: TaskRunSource): Promise { let taskToRun: Task = task; - if (await this.saveBeforeRun()) { - await this.configurationService.reloadConfiguration(); - await this.updateWorkspaceTasks(); + if (await this._saveBeforeRun()) { + await this._configurationService.reloadConfiguration(); + await this._updateWorkspaceTasks(); const taskFolder = task.getWorkspaceFolder(); const taskIdentifier = task.configurationProperties.identifier; // Since we save before running tasks, the task may have changed as part of the save. @@ -1724,28 +1724,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer ? await this.getTask(taskFolder, taskIdentifier) : task) ?? task; } await ProblemMatcherRegistry.onReady(); - const executeResult = this.getTaskSystem().run(taskToRun, resolver); - return this.handleExecuteResult(executeResult, runSource); + const executeResult = this._getTaskSystem().run(taskToRun, resolver); + return this._handleExecuteResult(executeResult, runSource); } - private async handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise { + private async _handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise { if (runSource === TaskRunSource.User) { - await this.setRecentlyUsedTask(executeResult.task); + await this._setRecentlyUsedTask(executeResult.task); } if (executeResult.kind === TaskExecuteKind.Active) { const active = executeResult.active; if (active && active.same) { if (this._taskSystem?.isTaskVisible(executeResult.task)) { const message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', executeResult.task.getQualifiedLabel()); - const lastInstance = this.getTaskSystem().getLastInstance(executeResult.task) ?? executeResult.task; - this.notificationService.prompt(Severity.Warning, message, + const lastInstance = this._getTaskSystem().getLastInstance(executeResult.task) ?? executeResult.task; + this._notificationService.prompt(Severity.Warning, message, [{ label: nls.localize('terminateTask', "Terminate Task"), run: () => this.terminate(lastInstance) }, { label: nls.localize('restartTask', "Restart Task"), - run: () => this.restart(lastInstance) + run: () => this._restart(lastInstance) }], { sticky: true } ); @@ -1759,7 +1759,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return executeResult.promise; } - private restart(task: Task): void { + private _restart(task: Task): void { if (!this._taskSystem) { return; } @@ -1769,14 +1769,14 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer // eat the error, it has already been surfaced to the user and we don't care about it here }); } else { - this.notificationService.warn(nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.configurationProperties.name)); + this._notificationService.warn(nls.localize('TaskSystem.restartFailed', 'Failed to terminate and restart task {0}', Types.isString(task) ? task : task.configurationProperties.name)); } return response; }); } public async terminate(task: Task): Promise { - if (!(await this.trust())) { + if (!(await this._trust())) { return { success: true, task: undefined }; } @@ -1786,24 +1786,24 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this._taskSystem.terminate(task); } - private terminateAll(): Promise { + private _terminateAll(): Promise { if (!this._taskSystem) { return Promise.resolve([]); } return this._taskSystem.terminateAll(); } - protected createTerminalTaskSystem(): ITaskSystem { + protected _createTerminalTaskSystem(): ITaskSystem { return new TerminalTaskSystem( - this.terminalService, this.terminalGroupService, this.outputService, this.paneCompositeService, this.viewsService, this.markerService, - this.modelService, this.configurationResolverService, - this.contextService, this.environmentService, - AbstractTaskService.OutputChannelId, this.fileService, this.terminalProfileResolverService, - this.pathService, this.viewDescriptorService, this.logService, this.configurationService, this.notificationService, + this._terminalService, this._terminalGroupService, this._outputService, this._paneCompositeService, this._viewsService, this._markerService, + this._modelService, this._configurationResolverService, + this._contextService, this._environmentService, + AbstractTaskService.OutputChannelId, this._fileService, this._terminalProfileResolverService, + this._pathService, this._viewDescriptorService, this._logService, this._configurationService, this._notificationService, this, (workspaceFolder: IWorkspaceFolder | undefined) => { if (workspaceFolder) { - return this.getTaskSystemInfo(workspaceFolder.uri.scheme); + return this._getTaskSystemInfo(workspaceFolder.uri.scheme); } else if (this._taskSystemInfos.size > 0) { const infos = Array.from(this._taskSystemInfos.entries()); const notFile = infos.filter(info => info[0] !== Schemas.file); @@ -1818,15 +1818,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer ); } - protected abstract getTaskSystem(): ITaskSystem; + protected abstract _getTaskSystem(): ITaskSystem; - private isTaskProviderEnabled(type: string) { + private _isTaskProviderEnabled(type: string) { const definition = TaskDefinitionRegistry.get(type); - return !definition || !definition.when || this.contextKeyService.contextMatchesRules(definition.when); + return !definition || !definition.when || this._contextKeyService.contextMatchesRules(definition.when); } - private getGroupedTasks(type?: string): Promise { - const needsRecentTasksMigration = this.needsRecentTasksMigration(); + private _getGroupedTasks(type?: string): Promise { + const needsRecentTasksMigration = this._needsRecentTasksMigration(); return this._activateTaskProviders(type).then(() => { const validTypes: IStringDictionary = Object.create(null); TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true); @@ -1849,10 +1849,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._outputChannel.append('Error: '); this._outputChannel.append(error.message); this._outputChannel.append('\n'); - this.showOutput(); + this._showOutput(); } else { this._outputChannel.append('Unknown error received while collecting tasks from providers.\n'); - this.showOutput(); + this._showOutput(); } } finally { if (--counter === 0) { @@ -1860,12 +1860,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } }; - if (this.isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) { + if (this._isProvideTasksEnabled() && (this.schemaVersion === JsonSchemaVersion.V2_0_0) && (this._providers.size > 0)) { let foundAnyProviders = false; for (const [handle, provider] of this._providers) { const providerType = this._providerTypes.get(handle); if ((type === undefined) || (type === providerType)) { - if (providerType && !this.isTaskProviderEnabled(providerType)) { + if (providerType && !this._isTaskProviderEnabled(providerType)) { continue; } foundAnyProviders = true; @@ -1876,7 +1876,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (task.type !== this._providerTypes.get(handle)) { this._outputChannel.append(nls.localize('unexpectedTaskType', "The task provider for \"{0}\" tasks unexpectedly provided a task of type \"{1}\".\n", this._providerTypes.get(handle), task.type)); if ((task.type !== 'shell') && (task.type !== 'process')) { - this.showOutput(); + this._showOutput(); } break; } @@ -1916,11 +1916,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + if (this._contextService.getWorkbenchState() === WorkbenchState.EMPTY) { result.add(key, ...folderTasks.set.tasks); } else { const configurations = folderTasks.configurations; - const legacyTaskConfigurations = folderTasks.set ? this.getLegacyTaskConfigurations(folderTasks.set) : undefined; + const legacyTaskConfigurations = folderTasks.set ? this._getLegacyTaskConfigurations(folderTasks.set) : undefined; const customTasksToDelete: Task[] = []; if (configurations || legacyTaskConfigurations) { const unUsedConfigurations: Set = new Set(); @@ -1979,7 +1979,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const [handle, provider] of this._providers) { const providerType = this._providerTypes.get(handle); if (configuringTask.type === providerType) { - if (providerType && !this.isTaskProviderEnabled(providerType)) { + if (providerType && !this._isTaskProviderEnabled(providerType)) { requiredTaskProviderUnavailable = true; continue; } @@ -2009,7 +2009,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer configuringTask.configures.type, JSON.stringify(configuringTask._source.config.element, undefined, 4) )); - this.showOutput(); + this._showOutput(); } }); @@ -2024,7 +2024,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer await Promise.all(customTasksPromises); if (needsRecentTasksMigration) { // At this point we have all the tasks and can migrate the recently used tasks. - await this.migrateRecentTasks(result.all()); + await this._migrateRecentTasks(result.all()); } return result; }, () => { @@ -2043,7 +2043,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private getLegacyTaskConfigurations(workspaceTasks: ITaskSet): IStringDictionary | undefined { + private _getLegacyTaskConfigurations(workspaceTasks: ITaskSet): IStringDictionary | undefined { let result: IStringDictionary | undefined; function getResult(): IStringDictionary { if (result) { @@ -2070,34 +2070,34 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } public async getWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { - if (!(await this.trust())) { + if (!(await this._trust())) { return new Map(); } await this._waitForSupportedExecutions; if (this._workspaceTasksPromise) { return this._workspaceTasksPromise; } - return this.updateWorkspaceTasks(runSource); + return this._updateWorkspaceTasks(runSource); } - private updateWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { - this._workspaceTasksPromise = this.computeWorkspaceTasks(runSource); + private _updateWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { + this._workspaceTasksPromise = this._computeWorkspaceTasks(runSource); return this._workspaceTasksPromise; } - private async getAFolder(): Promise { + private async _getAFolder(): Promise { let folder = this.workspaceFolders.length > 0 ? this.workspaceFolders[0] : undefined; if (!folder) { - const userhome = await this.pathService.userHome(); + const userhome = await this._pathService.userHome(); folder = new WorkspaceFolder({ uri: userhome, name: resources.basename(userhome), index: 0 }); } return folder; } - protected computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { + protected _computeWorkspaceTasks(runSource: TaskRunSource = TaskRunSource.User): Promise> { const promises: Promise[] = []; for (const folder of this.workspaceFolders) { - promises.push(this.computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); + promises.push(this._computeWorkspaceFolderTasks(folder, runSource).then((value) => value, () => undefined)); } return Promise.all(promises).then(async (values) => { const result = new Map(); @@ -2107,15 +2107,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - const folder = await this.getAFolder(); - if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - const workspaceFileTasks = await this.computeWorkspaceFileTasks(folder, runSource).then((value) => value, () => undefined); + const folder = await this._getAFolder(); + if (this._contextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + const workspaceFileTasks = await this._computeWorkspaceFileTasks(folder, runSource).then((value) => value, () => undefined); if (workspaceFileTasks && this._workspace && this._workspace.configuration) { result.set(this._workspace.configuration.toString(), workspaceFileTasks); } } - const userTasks = await this.computeUserTasks(folder, runSource).then((value) => value, () => undefined); + const userTasks = await this._computeUserTasks(folder, runSource).then((value) => value, () => undefined); if (userTasks) { result.set(USER_TASKS_GROUP_KEY, userTasks); } @@ -2123,26 +2123,26 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private get jsonTasksSupported(): boolean { - return !!ShellExecutionSupportedContext.getValue(this.contextKeyService) && !!ProcessExecutionSupportedContext.getValue(this.contextKeyService); + private get _jsonTasksSupported(): boolean { + return !!ShellExecutionSupportedContext.getValue(this._contextKeyService) && !!ProcessExecutionSupportedContext.getValue(this._contextKeyService); } - private computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { - return (this.executionEngine === ExecutionEngine.Process - ? this.computeLegacyConfiguration(workspaceFolder) - : this.computeConfiguration(workspaceFolder)). + private _computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { + return (this._executionEngine === ExecutionEngine.Process + ? this._computeLegacyConfiguration(workspaceFolder) + : this._computeConfiguration(workspaceFolder)). then((workspaceFolderConfiguration) => { if (!workspaceFolderConfiguration || !workspaceFolderConfiguration.config || workspaceFolderConfiguration.hasErrors) { return Promise.resolve({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false }); } return ProblemMatcherRegistry.onReady().then(async (): Promise => { - const taskSystemInfo: ITaskSystemInfo | undefined = this.getTaskSystemInfo(workspaceFolder.uri.scheme); + const taskSystemInfo: ITaskSystemInfo | undefined = this._getTaskSystemInfo(workspaceFolder.uri.scheme); const problemReporter = new ProblemReporter(this._outputChannel); - const parseResult = TaskConfig.parse(workspaceFolder, undefined, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson, this.contextKeyService); + const parseResult = TaskConfig.parse(workspaceFolder, undefined, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter, TaskConfig.TaskConfigSource.TasksJson, this._contextKeyService); let hasErrors = false; if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) { hasErrors = true; - this.showOutput(runSource); + this._showOutput(runSource); } if (problemReporter.status.isFatal()) { problemReporter.fatal(nls.localize('TaskSystem.configurationErrors', 'Error: the provided task configuration has validation errors and can\'t not be used. Please correct the errors first.')); @@ -2157,15 +2157,15 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer customizedTasks.byIdentifier[task.configures._key] = task; } } - if (!this.jsonTasksSupported && (parseResult.custom.length > 0)) { + if (!this._jsonTasksSupported && (parseResult.custom.length > 0)) { console.warn('Custom workspace tasks are not supported.'); } - return { workspaceFolder, set: { tasks: this.jsonTasksSupported ? parseResult.custom : [] }, configurations: customizedTasks, hasErrors }; + return { workspaceFolder, set: { tasks: this._jsonTasksSupported ? parseResult.custom : [] }, configurations: customizedTasks, hasErrors }; }); }); } - private testParseExternalConfig(config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, location: string): { config: TaskConfig.IExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } { + private _testParseExternalConfig(config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, location: string): { config: TaskConfig.IExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } { if (!config) { return { config: undefined, hasParseErrors: false }; } @@ -2180,67 +2180,67 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (isAffected) { this._outputChannel.append(nls.localize({ key: 'TaskSystem.invalidTaskJsonOther', comment: ['Message notifies of an error in one of several places there is tasks related json, not necessarily in a file named tasks.json'] }, 'Error: The content of the tasks json in {0} has syntax errors. Please correct them before executing a task.\n', location)); - this.showOutput(); + this._showOutput(); return { config, hasParseErrors: true }; } } return { config, hasParseErrors: false }; } - private async computeWorkspaceFileTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { - if (this.executionEngine === ExecutionEngine.Process) { - return this.emptyWorkspaceTaskResults(workspaceFolder); + private async _computeWorkspaceFileTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { + if (this._executionEngine === ExecutionEngine.Process) { + return this._emptyWorkspaceTaskResults(workspaceFolder); } - const workspaceFileConfig = this.getConfiguration(workspaceFolder, TaskSourceKind.WorkspaceFile); - const configuration = this.testParseExternalConfig(workspaceFileConfig.config, nls.localize('TasksSystem.locationWorkspaceConfig', 'workspace file')); + const workspaceFileConfig = this._getConfiguration(workspaceFolder, TaskSourceKind.WorkspaceFile); + const configuration = this._testParseExternalConfig(workspaceFileConfig.config, nls.localize('TasksSystem.locationWorkspaceConfig', 'workspace file')); const customizedTasks: { byIdentifier: IStringDictionary } = { byIdentifier: Object.create(null) }; const custom: CustomTask[] = []; - await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.WorkspaceFile); + await this._computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.WorkspaceFile); const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal; if (engine === ExecutionEngine.Process) { - this.notificationService.warn(nls.localize('TaskSystem.versionWorkspaceFile', 'Only tasks version 2.0.0 permitted in workspace configuration files.')); - return this.emptyWorkspaceTaskResults(workspaceFolder); + this._notificationService.warn(nls.localize('TaskSystem.versionWorkspaceFile', 'Only tasks version 2.0.0 permitted in workspace configuration files.')); + return this._emptyWorkspaceTaskResults(workspaceFolder); } return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors }; } - private async computeUserTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { - if (this.executionEngine === ExecutionEngine.Process) { - return this.emptyWorkspaceTaskResults(workspaceFolder); + private async _computeUserTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { + if (this._executionEngine === ExecutionEngine.Process) { + return this._emptyWorkspaceTaskResults(workspaceFolder); } - const userTasksConfig = this.getConfiguration(workspaceFolder, TaskSourceKind.User); - const configuration = this.testParseExternalConfig(userTasksConfig.config, nls.localize('TasksSystem.locationUserConfig', 'user settings')); + const userTasksConfig = this._getConfiguration(workspaceFolder, TaskSourceKind.User); + const configuration = this._testParseExternalConfig(userTasksConfig.config, nls.localize('TasksSystem.locationUserConfig', 'user settings')); const customizedTasks: { byIdentifier: IStringDictionary } = { byIdentifier: Object.create(null) }; const custom: CustomTask[] = []; - await this.computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.User); + await this._computeTasksForSingleConfig(workspaceFolder, configuration.config, runSource, custom, customizedTasks.byIdentifier, TaskConfig.TaskConfigSource.User); const engine = configuration.config ? TaskConfig.ExecutionEngine.from(configuration.config) : ExecutionEngine.Terminal; if (engine === ExecutionEngine.Process) { - this.notificationService.warn(nls.localize('TaskSystem.versionSettings', 'Only tasks version 2.0.0 permitted in user settings.')); - return this.emptyWorkspaceTaskResults(workspaceFolder); + this._notificationService.warn(nls.localize('TaskSystem.versionSettings', 'Only tasks version 2.0.0 permitted in user settings.')); + return this._emptyWorkspaceTaskResults(workspaceFolder); } return { workspaceFolder, set: { tasks: custom }, configurations: customizedTasks, hasErrors: configuration.hasParseErrors }; } - private emptyWorkspaceTaskResults(workspaceFolder: IWorkspaceFolder): IWorkspaceFolderTaskResult { + private _emptyWorkspaceTaskResults(workspaceFolder: IWorkspaceFolder): IWorkspaceFolderTaskResult { return { workspaceFolder, set: undefined, configurations: undefined, hasErrors: false }; } - private async computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { + private async _computeTasksForSingleConfig(workspaceFolder: IWorkspaceFolder, config: TaskConfig.IExternalTaskRunnerConfiguration | undefined, runSource: TaskRunSource, custom: CustomTask[], customized: IStringDictionary, source: TaskConfig.TaskConfigSource, isRecentTask: boolean = false): Promise { if (!config) { return false; } - const taskSystemInfo: ITaskSystemInfo | undefined = workspaceFolder ? this.getTaskSystemInfo(workspaceFolder.uri.scheme) : undefined; + const taskSystemInfo: ITaskSystemInfo | undefined = workspaceFolder ? this._getTaskSystemInfo(workspaceFolder.uri.scheme) : undefined; const problemReporter = new ProblemReporter(this._outputChannel); - const parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, this.contextKeyService, isRecentTask); + const parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, this._contextKeyService, isRecentTask); let hasErrors = false; if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) { - this.showOutput(runSource); + this._showOutput(runSource); hasErrors = true; } if (problemReporter.status.isFatal()) { @@ -2252,7 +2252,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer customized[task.configures._key] = task; } } - if (!this.jsonTasksSupported && (parseResult.custom.length > 0)) { + if (!this._jsonTasksSupported && (parseResult.custom.length > 0)) { console.warn('Custom workspace tasks are not supported.'); } else { for (const task of parseResult.custom) { @@ -2262,23 +2262,23 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return hasErrors; } - private computeConfiguration(workspaceFolder: IWorkspaceFolder): Promise { - const { config, hasParseErrors } = this.getConfiguration(workspaceFolder); + private _computeConfiguration(workspaceFolder: IWorkspaceFolder): Promise { + const { config, hasParseErrors } = this._getConfiguration(workspaceFolder); return Promise.resolve({ workspaceFolder, config, hasErrors: hasParseErrors }); } - protected abstract computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise; + protected abstract _computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise; - private computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] { + private _computeWorkspaceFolderSetup(): [IWorkspaceFolder[], IWorkspaceFolder[], ExecutionEngine, JsonSchemaVersion, IWorkspace | undefined] { const workspaceFolders: IWorkspaceFolder[] = []; const ignoredWorkspaceFolders: IWorkspaceFolder[] = []; let executionEngine = ExecutionEngine.Terminal; let schemaVersion = JsonSchemaVersion.V2_0_0; let workspace: IWorkspace | undefined; - if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { - const workspaceFolder: IWorkspaceFolder = this.contextService.getWorkspace().folders[0]; + if (this._contextService.getWorkbenchState() === WorkbenchState.FOLDER) { + const workspaceFolder: IWorkspaceFolder = this._contextService.getWorkspace().folders[0]; workspaceFolders.push(workspaceFolder); - executionEngine = this.computeExecutionEngine(workspaceFolder); + executionEngine = this._computeExecutionEngine(workspaceFolder); const telemetryData: { [key: string]: any } = { executionEngineVersion: executionEngine }; @@ -2289,12 +2289,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer "executionEngineVersion" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The engine version of tasks." } } */ - this.telemetryService.publicLog('taskService.engineVersion', telemetryData); - schemaVersion = this.computeJsonSchemaVersion(workspaceFolder); - } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - workspace = this.contextService.getWorkspace(); - for (const workspaceFolder of this.contextService.getWorkspace().folders) { - if (schemaVersion === this.computeJsonSchemaVersion(workspaceFolder)) { + this._telemetryService.publicLog('taskService.engineVersion', telemetryData); + schemaVersion = this._computeJsonSchemaVersion(workspaceFolder); + } else if (this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + workspace = this._contextService.getWorkspace(); + for (const workspaceFolder of this._contextService.getWorkspace().folders) { + if (schemaVersion === this._computeJsonSchemaVersion(workspaceFolder)) { workspaceFolders.push(workspaceFolder); } else { ignoredWorkspaceFolders.push(workspaceFolder); @@ -2308,28 +2308,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return [workspaceFolders, ignoredWorkspaceFolders, executionEngine, schemaVersion, workspace]; } - private computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine { - const { config } = this.getConfiguration(workspaceFolder); + private _computeExecutionEngine(workspaceFolder: IWorkspaceFolder): ExecutionEngine { + const { config } = this._getConfiguration(workspaceFolder); if (!config) { return ExecutionEngine._default; } return TaskConfig.ExecutionEngine.from(config); } - private computeJsonSchemaVersion(workspaceFolder: IWorkspaceFolder): JsonSchemaVersion { - const { config } = this.getConfiguration(workspaceFolder); + private _computeJsonSchemaVersion(workspaceFolder: IWorkspaceFolder): JsonSchemaVersion { + const { config } = this._getConfiguration(workspaceFolder); if (!config) { return JsonSchemaVersion.V2_0_0; } return TaskConfig.JsonSchemaVersion.from(config); } - protected getConfiguration(workspaceFolder: IWorkspaceFolder, source?: string): { config: TaskConfig.IExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } { + protected _getConfiguration(workspaceFolder: IWorkspaceFolder, source?: string): { config: TaskConfig.IExternalTaskRunnerConfiguration | undefined; hasParseErrors: boolean } { let result; - if ((source !== TaskSourceKind.User) && (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY)) { + if ((source !== TaskSourceKind.User) && (this._contextService.getWorkbenchState() === WorkbenchState.EMPTY)) { result = undefined; } else { - const wholeConfig = this.configurationService.inspect('tasks', { resource: workspaceFolder.uri }); + const wholeConfig = this._configurationService.inspect('tasks', { resource: workspaceFolder.uri }); switch (source) { case TaskSourceKind.User: { if (wholeConfig.userValue !== wholeConfig.workspaceFolderValue) { @@ -2339,7 +2339,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } case TaskSourceKind.Workspace: result = Objects.deepClone(wholeConfig.workspaceFolderValue); break; case TaskSourceKind.WorkspaceFile: { - if ((this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) + if ((this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) && (wholeConfig.workspaceFolderValue !== wholeConfig.workspaceValue)) { result = Objects.deepClone(wholeConfig.workspaceValue); } @@ -2362,7 +2362,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (isAffected) { this._outputChannel.append(nls.localize('TaskSystem.invalidTaskJson', 'Error: The content of the tasks.json file has syntax errors. Please correct them before executing a task.\n')); - this.showOutput(); + this._showOutput(); return { config: undefined, hasParseErrors: true }; } } @@ -2373,67 +2373,67 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (this._taskSystem) { return this._taskSystem instanceof TerminalTaskSystem; } - return this.executionEngine === ExecutionEngine.Terminal; + return this._executionEngine === ExecutionEngine.Terminal; } public configureAction(): Action { const thisCapture: AbstractTaskService = this; return new class extends Action { constructor() { - super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, () => { thisCapture.runConfigureTasks(); return Promise.resolve(undefined); }); + super(ConfigureTaskAction.ID, ConfigureTaskAction.TEXT, undefined, true, () => { thisCapture._runConfigureTasks(); return Promise.resolve(undefined); }); } }; } - private handleError(err: any): void { + private _handleError(err: any): void { let showOutput = true; if (err instanceof TaskError) { const buildError = err; const needsConfig = buildError.code === TaskErrors.NotConfigured || buildError.code === TaskErrors.NoBuildTask || buildError.code === TaskErrors.NoTestTask; const needsTerminate = buildError.code === TaskErrors.RunningTask; if (needsConfig || needsTerminate) { - this.notificationService.prompt(buildError.severity, buildError.message, [{ + this._notificationService.prompt(buildError.severity, buildError.message, [{ label: needsConfig ? ConfigureTaskAction.TEXT : nls.localize('TerminateAction.label', "Terminate Task"), run: () => { if (needsConfig) { - this.runConfigureTasks(); + this._runConfigureTasks(); } else { - this.runTerminateCommand(); + this._runTerminateCommand(); } } }]); } else { - this.notificationService.notify({ severity: buildError.severity, message: buildError.message }); + this._notificationService.notify({ severity: buildError.severity, message: buildError.message }); } } else if (err instanceof Error) { const error = err; - this.notificationService.error(error.message); + this._notificationService.error(error.message); showOutput = false; } else if (Types.isString(err)) { - this.notificationService.error(err); + this._notificationService.error(err); } else { - this.notificationService.error(nls.localize('TaskSystem.unknownError', 'An error has occurred while running a task. See task log for details.')); + this._notificationService.error(nls.localize('TaskSystem.unknownError', 'An error has occurred while running a task. See task log for details.')); } if (showOutput) { - this.showOutput(); + this._showOutput(); } } - private canRunCommand(): boolean { + private _canRunCommand(): boolean { return true; } - private showDetail(): boolean { - return this.configurationService.getValue(QUICKOPEN_DETAIL_CONFIG); + private _showDetail(): boolean { + return this._configurationService.getValue(QUICKOPEN_DETAIL_CONFIG); } - private async createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, includeRecents: boolean = true): Promise { + private async _createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, includeRecents: boolean = true): Promise { let encounteredTasks: { [key: string]: ITaskQuickPickEntry[] } = {}; if (tasks === undefined || tasks === null || tasks.length === 0) { return []; } const TaskQuickPickEntry = (task: Task): ITaskQuickPickEntry => { - const newEntry = { label: task._label, description: this.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + const newEntry = { label: task._label, description: this.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined }; if (encounteredTasks[task._id]) { if (encounteredTasks[task._id].length === 1) { encounteredTasks[task._id][0].label += ' (1)'; @@ -2518,23 +2518,23 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return entries; } - private async showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry) { - return TaskQuickPick.show(this, this.configurationService, this.quickInputService, this.notificationService, this.dialogService, placeHolder, defaultEntry); + private async _showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry) { + return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, placeHolder, defaultEntry); } - private async showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: ITaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, additionalEntries?: ITaskQuickPickEntry[]): Promise { + private async _showQuickPick(tasks: Promise | Task[], placeHolder: string, defaultEntry?: ITaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, additionalEntries?: ITaskQuickPickEntry[]): Promise { const tokenSource = new CancellationTokenSource(); const cancellationToken: CancellationToken = tokenSource.token; - const _createEntries = new Promise[]>((resolve) => { + const createEntries = new Promise[]>((resolve) => { if (Array.isArray(tasks)) { - resolve(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); + resolve(this._createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } else { - resolve(tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry))); + resolve(tasks.then((tasks) => this._createTaskQuickPickEntries(tasks, group, sort, selectedEntry))); } }); const timeout: boolean = await Promise.race([new Promise((resolve) => { - _createEntries.then(() => resolve(false)); + createEntries.then(() => resolve(false)); }), new Promise((resolve) => { const timer = setTimeout(() => { clearTimeout(timer); @@ -2542,12 +2542,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }, 200); })]); - if (!timeout && ((await _createEntries).length === 1) && this.configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { - return ((await _createEntries)[0]); + if (!timeout && ((await createEntries).length === 1) && this._configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { + return ((await createEntries)[0]); } - const pickEntries = _createEntries.then((entries) => { - if ((entries.length === 1) && this.configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { + const pickEntries = createEntries.then((entries) => { + if ((entries.length === 1) && this._configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { tokenSource.cancel(); } else if ((entries.length === 0) && defaultEntry) { entries.push(defaultEntry); @@ -2558,13 +2558,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return entries; }); - const picker: IQuickPick = this.quickInputService.createQuickPick(); + const picker: IQuickPick = this._quickInputService.createQuickPick(); picker.placeholder = placeHolder; picker.matchOnDescription = true; picker.onDidTriggerItemButton(context => { const task = context.item.task; - this.quickInputService.cancel(); + this._quickInputService.cancel(); if (ContributedTask.is(task)) { this.customize(task, undefined, true); } else if (CustomTask.is(task)) { @@ -2597,12 +2597,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private needsRecentTasksMigration(): boolean { - return (this.getRecentlyUsedTasksV1().size > 0) && (this.getRecentlyUsedTasks().size === 0); + private _needsRecentTasksMigration(): boolean { + return (this.getRecentlyUsedTasksV1().size > 0) && (this._getRecentlyUsedTasks().size === 0); } - private async migrateRecentTasks(tasks: Task[]) { - if (!this.needsRecentTasksMigration()) { + private async _migrateRecentTasks(tasks: Task[]) { + if (!this._needsRecentTasksMigration()) { return; } const recentlyUsedTasks = this.getRecentlyUsedTasksV1(); @@ -2617,25 +2617,25 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer for (const key in reversed) { const task = taskMap[key]; if (task) { - await this.setRecentlyUsedTask(task); + await this._setRecentlyUsedTask(task); } } - this.storageService.remove(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); + this._storageService.remove(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); } - private showIgnoredFoldersMessage(): Promise { + private _showIgnoredFoldersMessage(): Promise { if (this.ignoredWorkspaceFolders.length === 0 || !this.showIgnoreMessage) { return Promise.resolve(undefined); } - this.notificationService.prompt( + this._notificationService.prompt( Severity.Info, nls.localize('TaskService.ignoredFolder', 'The following workspace folders are ignored since they use task version 0.1.0: {0}', this.ignoredWorkspaceFolders.map(f => f.name).join(', ')), [{ label: nls.localize('TaskService.notAgain', "Don't Show Again"), isSecondary: true, run: () => { - this.storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE, StorageTarget.USER); + this._storageService.store(AbstractTaskService.IgnoreTask010DonotShowAgain_key, true, StorageScope.WORKSPACE, StorageTarget.USER); this._showIgnoreMessage = false; } }] @@ -2644,24 +2644,24 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return Promise.resolve(undefined); } - private async trust(): Promise { - return (await this.workspaceTrustRequestService.requestWorkspaceTrust( + private async _trust(): Promise { + return (await this._workspaceTrustRequestService.requestWorkspaceTrust( { message: nls.localize('TaskService.requestTrust', "Listing and running tasks requires that some of the files in this workspace be executed as code.") })) === true; } - private runTaskCommand(arg?: any): void { - if (!this.canRunCommand()) { + private _runTaskCommand(arg?: any): void { + if (!this._canRunCommand()) { return; } - const identifier = this.getTaskIdentifier(arg); + const identifier = this._getTaskIdentifier(arg); if (identifier !== undefined) { - this.getGroupedTasks().then(async (grouped) => { - const resolver = this.createResolver(grouped); - const folderURIs: (URI | string)[] = this.contextService.getWorkspace().folders.map(folder => folder.uri); - if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - folderURIs.push(this.contextService.getWorkspace().configuration!); + this._getGroupedTasks().then(async (grouped) => { + const resolver = this._createResolver(grouped); + const folderURIs: (URI | string)[] = this._contextService.getWorkspace().folders.map(folder => folder.uri); + if (this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + folderURIs.push(this._contextService.getWorkspace().configuration!); } folderURIs.push(USER_TASKS_GROUP_KEY); for (const uri of folderURIs) { @@ -2673,20 +2673,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } } - this.doRunTaskCommand(grouped.all()); + this._doRunTaskCommand(grouped.all()); }, () => { - this.doRunTaskCommand(); + this._doRunTaskCommand(); }); } else { - this.doRunTaskCommand(); + this._doRunTaskCommand(); } } - private tasksAndGroupedTasks(filter?: ITaskFilter): { tasks: Promise; grouped: Promise } { - if (!this.versionAndEngineCompatible(filter)) { + private _tasksAndGroupedTasks(filter?: ITaskFilter): { tasks: Promise; grouped: Promise } { + if (!this._versionAndEngineCompatible(filter)) { return { tasks: Promise.resolve([]), grouped: Promise.resolve(new TaskMap()) }; } - const grouped = this.getGroupedTasks(filter ? filter.type : undefined); + const grouped = this._getGroupedTasks(filter ? filter.type : undefined); const tasks = grouped.then((map) => { if (!filter || !filter.type) { return map.all(); @@ -2713,13 +2713,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return { tasks, grouped }; } - private doRunTaskCommand(tasks?: Task[]): void { + private _doRunTaskCommand(tasks?: Task[]): void { const pickThen = (task: Task | undefined | null) => { if (task === undefined) { return; } if (task === null) { - this.runConfigureTasks(); + this._runConfigureTasks(); } else { this.run(task, { attachProblemMatcher: true }, TaskRunSource.User).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here @@ -2729,13 +2729,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const placeholder = nls.localize('TaskService.pickRunTask', 'Select the task to run'); - this.showIgnoredFoldersMessage().then(() => { - if (this.configurationService.getValue(USE_SLOW_PICKER)) { + this._showIgnoredFoldersMessage().then(() => { + if (this._configurationService.getValue(USE_SLOW_PICKER)) { let taskResult: { tasks: Promise; grouped: Promise } | undefined = undefined; if (!tasks) { - taskResult = this.tasksAndGroupedTasks(); + taskResult = this._tasksAndGroupedTasks(); } - this.showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder, + this._showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder, { label: nls.localize('TaskService.noEntryToRunSlow', '$(plus) Configure a Task'), task: null @@ -2745,7 +2745,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return pickThen(entry ? entry.task : undefined); }); } else { - this.showTwoLevelQuickPick(placeholder, + this._showTwoLevelQuickPick(placeholder, { label: nls.localize('TaskService.noEntryToRun', '$(plus) Configure a Task'), task: null @@ -2755,18 +2755,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private reRunTaskCommand(): void { - if (!this.canRunCommand()) { + private _reRunTaskCommand(): void { + if (!this._canRunCommand()) { return; } ProblemMatcherRegistry.onReady().then(() => { - return this.editorService.saveAll({ reason: SaveReason.AUTO }).then(() => { // make sure all dirty editors are saved - const executeResult = this.getTaskSystem().rerun(); + return this._editorService.saveAll({ reason: SaveReason.AUTO }).then(() => { // make sure all dirty editors are saved + const executeResult = this._getTaskSystem().rerun(); if (executeResult) { - return this.handleExecuteResult(executeResult); + return this._handleExecuteResult(executeResult); } else { - this.doRunTaskCommand(); + this._doRunTaskCommand(); return Promise.resolve(undefined); } }); @@ -2780,7 +2780,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer * @param taskGlobsInList - This tells splitPerGroupType to filter out globbed tasks (into default), otherwise fall back to boolean * @returns */ - private splitPerGroupType(tasks: Task[], taskGlobsInList: boolean = false): { none: Task[]; defaults: Task[] } { + private _splitPerGroupType(tasks: Task[], taskGlobsInList: boolean = false): { none: Task[]; defaults: Task[] } { const none: Task[] = []; const defaults: Task[] = []; for (const task of tasks) { @@ -2796,12 +2796,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return { none, defaults }; } - private runTaskGroupCommand(taskGroup: TaskGroup, strings: { + private _runTaskGroupCommand(taskGroup: TaskGroup, strings: { fetching: string; select: string; notFoundConfigure: string; }, configure: () => void, legacyCommand: () => void): void { - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return; } if (this.schemaVersion === JsonSchemaVersion.V0_1_0) { @@ -2823,8 +2823,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const chooseAndRunTask = (tasks: Task[]) => { - this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, + this._showIgnoredFoldersMessage().then(() => { + this._showQuickPick(tasks, strings.select, { label: strings.notFoundConfigure, @@ -2845,9 +2845,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }; // First check for globs before checking for the default tasks of the task group - const absoluteURI = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor); + const absoluteURI = EditorResourceAccessor.getOriginalUri(this._editorService.activeEditor); if (absoluteURI) { - const workspaceFolder = this.contextService.getWorkspaceFolder(absoluteURI); + const workspaceFolder = this._contextService.getWorkspaceFolder(absoluteURI); // fallback to absolute path of the file if it is not in a workspace or relative path cannot be found const relativePath = workspaceFolder?.uri ? (resources.relativePath(workspaceFolder.uri, absoluteURI) ?? absoluteURI.path) : absoluteURI.path; @@ -2862,11 +2862,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const handleMultipleTasks = (areGlobTasks: boolean) => { - return this.getTasksForGroup(taskGroup).then((tasks) => { + return this._getTasksForGroup(taskGroup).then((tasks) => { if (tasks.length > 0) { // If we're dealing with tasks that were chosen because of a glob match, // then put globs in the defaults and everything else in none - const { none, defaults } = this.splitPerGroupType(tasks, areGlobTasks); + const { none, defaults } = this._splitPerGroupType(tasks, areGlobTasks); if (defaults.length === 1) { runSingleTask(defaults[0], undefined, this); return; @@ -2915,35 +2915,35 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer // Multiple default tasks returned, show the quickPicker return handleMultipleTasks(false); })(); - this.progressService.withProgress(options, () => promise); + this._progressService.withProgress(options, () => promise); } - private runBuildCommand(): void { - return this.runTaskGroupCommand(TaskGroup.Build, { + private _runBuildCommand(): void { + return this._runTaskGroupCommand(TaskGroup.Build, { fetching: nls.localize('TaskService.fetchingBuildTasks', 'Fetching build tasks...'), select: nls.localize('TaskService.pickBuildTask', 'Select the build task to run'), notFoundConfigure: nls.localize('TaskService.noBuildTask', 'No build task to run found. Configure Build Task...') - }, this.runConfigureDefaultBuildTask, this.build); + }, this._runConfigureDefaultBuildTask, this._build); } - private runTestCommand(): void { - return this.runTaskGroupCommand(TaskGroup.Test, { + private _runTestCommand(): void { + return this._runTaskGroupCommand(TaskGroup.Test, { fetching: nls.localize('TaskService.fetchingTestTasks', 'Fetching test tasks...'), select: nls.localize('TaskService.pickTestTask', 'Select the test task to run'), notFoundConfigure: nls.localize('TaskService.noTestTaskTerminal', 'No test task to run found. Configure Tasks...') - }, this.runConfigureDefaultTestTask, this.runTest); + }, this._runConfigureDefaultTestTask, this._runTest); } - private runTerminateCommand(arg?: any): void { - if (!this.canRunCommand()) { + private _runTerminateCommand(arg?: any): void { + if (!this._canRunCommand()) { return; } if (arg === 'terminateAll') { - this.terminateAll(); + this._terminateAll(); return; } const runQuickPick = (promise?: Promise) => { - this.showQuickPick(promise || this.getActiveTasks(), + this._showQuickPick(promise || this.getActiveTasks(), nls.localize('TaskService.taskToTerminate', 'Select a task to terminate'), { label: nls.localize('TaskService.noTaskRunning', 'No task is currently running'), @@ -2958,7 +2958,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }] ).then(entry => { if (entry && entry.id === 'terminateAll') { - this.terminateAll(); + this._terminateAll(); } const task: Task | undefined | null = entry ? entry.task : undefined; if (task === undefined || task === null) { @@ -2968,7 +2968,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); }; if (this.inTerminal()) { - const identifier = this.getTaskIdentifier(arg); + const identifier = this._getTaskIdentifier(arg); let promise: Promise; if (identifier !== undefined) { promise = this.getActiveTasks(); @@ -2985,18 +2985,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer runQuickPick(); } } else { - this.isActive().then((active) => { + this._isActive().then((active) => { if (active) { - this.terminateAll().then((responses) => { + this._terminateAll().then((responses) => { // the output runner has only one task const response = responses[0]; if (response.success) { return; } if (response.code && response.code === TerminateResponseCode.ProcessNotFound) { - this.notificationService.error(nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting VS Code might result in orphaned processes.')); + this._notificationService.error(nls.localize('TerminateAction.noProcess', 'The launched process doesn\'t exist anymore. If the task spawned background tasks exiting VS Code might result in orphaned processes.')); } else { - this.notificationService.error(nls.localize('TerminateAction.failed', 'Failed to terminate running task')); + this._notificationService.error(nls.localize('TerminateAction.failed', 'Failed to terminate running task')); } }); } @@ -3004,12 +3004,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private runRestartTaskCommand(arg?: any): void { - if (!this.canRunCommand()) { + private _runRestartTaskCommand(arg?: any): void { + if (!this._canRunCommand()) { return; } const runQuickPick = (promise?: Promise) => { - this.showQuickPick(promise || this.getActiveTasks(), + this._showQuickPick(promise || this.getActiveTasks(), nls.localize('TaskService.taskToRestart', 'Select the task to restart'), { label: nls.localize('TaskService.noTaskToRestart', 'No task to restart'), @@ -3021,18 +3021,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (task === undefined || task === null) { return; } - this.restart(task); + this._restart(task); }); }; if (this.inTerminal()) { - const identifier = this.getTaskIdentifier(arg); + const identifier = this._getTaskIdentifier(arg); let promise: Promise; if (identifier !== undefined) { promise = this.getActiveTasks(); promise.then((tasks) => { for (const task of tasks) { if (task.matches(identifier)) { - this.restart(task); + this._restart(task); return; } } @@ -3047,12 +3047,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } const task = activeTasks[0]; - this.restart(task); + this._restart(task); }); } } - private getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined { + private _getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined { let result: string | KeyedTaskIdentifier | undefined = undefined; if (Types.isString(arg)) { result = arg; @@ -3062,30 +3062,30 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return result; } - private configHasTasks(taskConfig?: TaskConfig.IExternalTaskRunnerConfiguration): boolean { + private _configHasTasks(taskConfig?: TaskConfig.IExternalTaskRunnerConfiguration): boolean { return !!taskConfig && !!taskConfig.tasks && taskConfig.tasks.length > 0; } - private openTaskFile(resource: URI, taskSource: string) { + private _openTaskFile(resource: URI, taskSource: string) { let configFileCreated = false; - this.fileService.stat(resource).then((stat) => stat, () => undefined).then(async (stat) => { + this._fileService.stat(resource).then((stat) => stat, () => undefined).then(async (stat) => { const fileExists: boolean = !!stat; - const configValue = this.configurationService.inspect('tasks'); + const configValue = this._configurationService.inspect('tasks'); let tasksExistInFile: boolean; let target: ConfigurationTarget; switch (taskSource) { - case TaskSourceKind.User: tasksExistInFile = this.configHasTasks(configValue.userValue); target = ConfigurationTarget.USER; break; - case TaskSourceKind.WorkspaceFile: tasksExistInFile = this.configHasTasks(configValue.workspaceValue); target = ConfigurationTarget.WORKSPACE; break; - default: tasksExistInFile = this.configHasTasks(configValue.workspaceFolderValue); target = ConfigurationTarget.WORKSPACE_FOLDER; + case TaskSourceKind.User: tasksExistInFile = this._configHasTasks(configValue.userValue); target = ConfigurationTarget.USER; break; + case TaskSourceKind.WorkspaceFile: tasksExistInFile = this._configHasTasks(configValue.workspaceValue); target = ConfigurationTarget.WORKSPACE; break; + default: tasksExistInFile = this._configHasTasks(configValue.workspaceFolderValue); target = ConfigurationTarget.WORKSPACE_FOLDER; } let content; if (!tasksExistInFile) { - const pickTemplateResult = await this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }); + const pickTemplateResult = await this._quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }); if (!pickTemplateResult) { return Promise.resolve(undefined); } content = pickTemplateResult.content; - const editorConfig = this.configurationService.getValue() as any; + const editorConfig = this._configurationService.getValue() as any; if (editorConfig.editor.insertSpaces) { content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + ' '.repeat(s2.length * editorConfig.editor.tabSize)); } @@ -3093,12 +3093,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } if (!fileExists && content) { - return this.textFileService.create([{ resource, value: content }]).then(result => { + return this._textFileService.create([{ resource, value: content }]).then(result => { return result[0].resource; }); } else if (fileExists && (tasksExistInFile || content)) { if (content) { - this.configurationService.updateValue('tasks', json.parse(content), target); + this._configurationService.updateValue('tasks', json.parse(content), target); } return stat?.resource; } @@ -3107,7 +3107,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!resource) { return; } - this.editorService.openEditor({ + this._editorService.openEditor({ resource, options: { pinned: configFileCreated // pin only if config file is created #8727 @@ -3116,17 +3116,17 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private isTaskEntry(value: IQuickPickItem): value is IQuickPickItem & { task: Task } { + private _isTaskEntry(value: IQuickPickItem): value is IQuickPickItem & { task: Task } { const candidate: IQuickPickItem & { task: Task } = value as any; return candidate && !!candidate.task; } - private isSettingEntry(value: IQuickPickItem): value is IQuickPickItem & { settingType: string } { + private _isSettingEntry(value: IQuickPickItem): value is IQuickPickItem & { settingType: string } { const candidate: IQuickPickItem & { settingType: string } = value as any; return candidate && !!candidate.settingType; } - private configureTask(task: Task) { + private _configureTask(task: Task) { if (ContributedTask.is(task)) { this.customize(task, undefined, true); } else if (CustomTask.is(task)) { @@ -3136,21 +3136,21 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private handleSelection(selection: TaskQuickPickEntryType | undefined) { + private _handleSelection(selection: TaskQuickPickEntryType | undefined) { if (!selection) { return; } - if (this.isTaskEntry(selection)) { - this.configureTask(selection.task); - } else if (this.isSettingEntry(selection)) { - const taskQuickPick = new TaskQuickPick(this, this.configurationService, this.quickInputService, this.notificationService, this.dialogService); + if (this._isTaskEntry(selection)) { + this._configureTask(selection.task); + } else if (this._isSettingEntry(selection)) { + const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService); taskQuickPick.handleSettingOption(selection.settingType); - } else if (selection.folder && (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { - this.openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace); + } else if (selection.folder && (this._contextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + this._openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace); } else { - const resource = this.getResourceForKind(TaskSourceKind.User); + const resource = this._getResourceForKind(TaskSourceKind.User); if (resource) { - this.openTaskFile(resource, TaskSourceKind.User); + this._openTaskFile(resource, TaskSourceKind.User); } } } @@ -3170,23 +3170,23 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return description; } - private async runConfigureTasks(): Promise { - if (!(await this.trust())) { + private async _runConfigureTasks(): Promise { + if (!(await this._trust())) { return; } - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return undefined; } let taskPromise: Promise; if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { - taskPromise = this.getGroupedTasks(); + taskPromise = this._getGroupedTasks(); } else { taskPromise = Promise.resolve(new TaskMap()); } - const stats = this.contextService.getWorkspace().folders.map>((folder) => { - return this.fileService.stat(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined); + const stats = this._contextService.getWorkspace().folders.map>((folder) => { + return this._fileService.stat(folder.toResource('.vscode/tasks.json')).then(stat => stat, () => undefined); }); const createLabel = nls.localize('TaskService.createJsonFile', 'Create tasks.json file from template'); @@ -3201,7 +3201,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (tasks.length > 0) { tasks = tasks.sort((a, b) => a._label.localeCompare(b._label)); for (const task of tasks) { - entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this.showDetail() ? task.configurationProperties.detail : undefined }); + entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this._showDetail() ? task.configurationProperties.detail : undefined }); if (!ContributedTask.is(task)) { configuredCount++; } @@ -3214,7 +3214,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (entries.length) { entries.push({ type: 'separator' }); } - entries.push({ label, folder: this.contextService.getWorkspace().folders[0] }); + entries.push({ label, folder: this._contextService.getWorkspace().folders[0] }); } if ((entries.length === 1) && !needsCreateOrOpen) { tokenSource.cancel(); @@ -3232,20 +3232,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }, 200); })]); - if (!timeout && ((await entries).length === 1) && this.configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { + if (!timeout && ((await entries).length === 1) && this._configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { const entry: any = ((await entries)[0]); if (entry.task) { - this.handleSelection(entry); + this._handleSelection(entry); return; } } const entriesWithSettings = entries.then(resolvedEntries => { - resolvedEntries.push(...TaskQuickPick.allSettingEntries(this.configurationService)); + resolvedEntries.push(...TaskQuickPick.allSettingEntries(this._configurationService)); return resolvedEntries; }); - this.quickInputService.pick(entriesWithSettings, + this._quickInputService.pick(entriesWithSettings, { placeHolder: nls.localize('TaskService.pickTask', 'Select a task to configure') }, cancellationToken). then(async (selection) => { if (cancellationToken.isCancellationRequested) { @@ -3255,18 +3255,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer selection = task; } } - this.handleSelection(selection); + this._handleSelection(selection); }); } - private runConfigureDefaultBuildTask(): void { - if (!this.canRunCommand()) { + private _runConfigureDefaultBuildTask(): void { + if (!this._canRunCommand()) { return; } if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { - this.runConfigureTasks(); + this._runConfigureTasks(); return; } let selectedTask: Task | undefined; @@ -3282,11 +3282,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer selectedEntry = { label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', selectedTask.getQualifiedLabel()), task: selectedTask, - detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined + detail: this._showDetail() ? selectedTask.configurationProperties.detail : undefined }; } - this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, + this._showIgnoredFoldersMessage().then(() => { + this._showQuickPick(tasks, nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry). then((entry) => { const task: Task | undefined | null = entry ? entry.task : undefined; @@ -3307,18 +3307,18 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); })); } else { - this.runConfigureTasks(); + this._runConfigureTasks(); } } - private runConfigureDefaultTestTask(): void { - if (!this.canRunCommand()) { + private _runConfigureDefaultTestTask(): void { + if (!this._canRunCommand()) { return; } if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { this.tasks().then((tasks => { if (tasks.length === 0) { - this.runConfigureTasks(); + this._runConfigureTasks(); return; } let selectedTask: Task | undefined; @@ -3335,12 +3335,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer selectedEntry = { label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', selectedTask.getQualifiedLabel()), task: selectedTask, - detail: this.showDetail() ? selectedTask.configurationProperties.detail : undefined + detail: this._showDetail() ? selectedTask.configurationProperties.detail : undefined }; } - this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, + this._showIgnoredFoldersMessage().then(() => { + this._showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((entry) => { const task: Task | undefined | null = entry ? entry.task : undefined; if (!task) { @@ -3360,12 +3360,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); })); } else { - this.runConfigureTasks(); + this._runConfigureTasks(); } } public async runShowTasks(): Promise { - if (!this.canRunCommand()) { + if (!this._canRunCommand()) { return; } const activeTasksPromise: Promise = this.getActiveTasks(); @@ -3385,7 +3385,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer })) { this._taskSystem!.revealTask(activeTasks[0]); } else { - this.showQuickPick(activeTasksPromise, + this._showQuickPick(activeTasksPromise, nls.localize('TaskService.pickShowTask', 'Select the task to show its output'), { label: nls.localize('TaskService.noTaskIsRunning', 'No task is running'), @@ -3402,17 +3402,17 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private async createTasksDotOld(folder: IWorkspaceFolder): Promise<[URI, URI] | undefined> { + private async _createTasksDotOld(folder: IWorkspaceFolder): Promise<[URI, URI] | undefined> { const tasksFile = folder.toResource('.vscode/tasks.json'); - if (await this.fileService.exists(tasksFile)) { + if (await this._fileService.exists(tasksFile)) { const oldFile = tasksFile.with({ path: `${tasksFile.path}.old` }); - await this.fileService.copy(tasksFile, oldFile, true); + await this._fileService.copy(tasksFile, oldFile, true); return [oldFile, tasksFile]; } return undefined; } - private upgradeTask(task: Task, suppressTaskName: boolean, globalConfig: { windows?: ICommandUpgrade; osx?: ICommandUpgrade; linux?: ICommandUpgrade }): TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | undefined { + private _upgradeTask(task: Task, suppressTaskName: boolean, globalConfig: { windows?: ICommandUpgrade; osx?: ICommandUpgrade; linux?: ICommandUpgrade }): TaskConfig.ICustomTask | TaskConfig.IConfiguringTask | undefined { if (!CustomTask.is(task)) { return; } @@ -3456,31 +3456,31 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer task._source.config.element = configElement; const tempTask = new CustomTask(task._id, task._source, task._label, task.type, task.command, task.hasDefinedMatchers, task.runOptions, task.configurationProperties); - const configTask = this.createCustomizableTask(tempTask); + const configTask = this._createCustomizableTask(tempTask); if (configTask) { return configTask; } return; } - private async upgrade(): Promise { + private async _upgrade(): Promise { if (this.schemaVersion === JsonSchemaVersion.V2_0_0) { return; } - if (!this.workspaceTrustManagementService.isWorkspaceTrusted()) { - this._register(Event.once(this.workspaceTrustManagementService.onDidChangeTrust)(isTrusted => { + if (!this._workspaceTrustManagementService.isWorkspaceTrusted()) { + this._register(Event.once(this._workspaceTrustManagementService.onDidChangeTrust)(isTrusted => { if (isTrusted) { - this.upgrade(); + this._upgrade(); } })); return; } - const tasks = await this.getGroupedTasks(); + const tasks = await this._getGroupedTasks(); const fileDiffs: [URI, URI][] = []; for (const folder of this.workspaceFolders) { - const diff = await this.createTasksDotOld(folder); + const diff = await this._createTasksDotOld(folder); if (diff) { fileDiffs.push(diff); } @@ -3489,35 +3489,35 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const configTasks: (TaskConfig.ICustomTask | TaskConfig.IConfiguringTask)[] = []; - const suppressTaskName = !!this.configurationService.getValue('tasks.suppressTaskName', { resource: folder.uri }); + const suppressTaskName = !!this._configurationService.getValue('tasks.suppressTaskName', { resource: folder.uri }); const globalConfig = { - windows: this.configurationService.getValue('tasks.windows', { resource: folder.uri }), - osx: this.configurationService.getValue('tasks.osx', { resource: folder.uri }), - linux: this.configurationService.getValue('tasks.linux', { resource: folder.uri }) + windows: this._configurationService.getValue('tasks.windows', { resource: folder.uri }), + osx: this._configurationService.getValue('tasks.osx', { resource: folder.uri }), + linux: this._configurationService.getValue('tasks.linux', { resource: folder.uri }) }; tasks.get(folder).forEach(task => { - const configTask = this.upgradeTask(task, suppressTaskName, globalConfig); + const configTask = this._upgradeTask(task, suppressTaskName, globalConfig); if (configTask) { configTasks.push(configTask); } }); this._taskSystem = undefined; this._workspaceTasksPromise = undefined; - await this.writeConfiguration(folder, 'tasks.tasks', configTasks); - await this.writeConfiguration(folder, 'tasks.version', '2.0.0'); - if (this.configurationService.getValue('tasks.showOutput', { resource: folder.uri })) { - await this.configurationService.updateValue('tasks.showOutput', undefined, { resource: folder.uri }); + await this._writeConfiguration(folder, 'tasks.tasks', configTasks); + await this._writeConfiguration(folder, 'tasks.version', '2.0.0'); + if (this._configurationService.getValue('tasks.showOutput', { resource: folder.uri })) { + await this._configurationService.updateValue('tasks.showOutput', undefined, { resource: folder.uri }); } - if (this.configurationService.getValue('tasks.isShellCommand', { resource: folder.uri })) { - await this.configurationService.updateValue('tasks.isShellCommand', undefined, { resource: folder.uri }); + if (this._configurationService.getValue('tasks.isShellCommand', { resource: folder.uri })) { + await this._configurationService.updateValue('tasks.isShellCommand', undefined, { resource: folder.uri }); } - if (this.configurationService.getValue('tasks.suppressTaskName', { resource: folder.uri })) { - await this.configurationService.updateValue('tasks.suppressTaskName', undefined, { resource: folder.uri }); + if (this._configurationService.getValue('tasks.suppressTaskName', { resource: folder.uri })) { + await this._configurationService.updateValue('tasks.suppressTaskName', undefined, { resource: folder.uri }); } } - this.updateSetup(); + this._updateSetup(); - this.notificationService.prompt(Severity.Warning, + this._notificationService.prompt(Severity.Warning, fileDiffs.length === 1 ? nls.localize('taskService.upgradeVersion', "The deprecated tasks version 0.1.0 has been removed. Your tasks have been upgraded to version 2.0.0. Open the diff to review the upgrade.") : nls.localize('taskService.upgradeVersionPlural', "The deprecated tasks version 0.1.0 has been removed. Your tasks have been upgraded to version 2.0.0. Open the diffs to review the upgrade."), @@ -3525,7 +3525,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer label: fileDiffs.length === 1 ? nls.localize('taskService.openDiff', "Open diff") : nls.localize('taskService.openDiffs', "Open diffs"), run: async () => { for (const upgrade of fileDiffs) { - await this.editorService.openEditor({ + await this._editorService.openEditor({ original: { resource: upgrade[0] }, modified: { resource: upgrade[1] } }); diff --git a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts index fec67cc2d51..3d91091af90 100644 --- a/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts +++ b/src/vs/workbench/contrib/tasks/browser/runAutomaticTasks.ts @@ -26,40 +26,40 @@ const ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE = 'tasks.run.allowAutomatic'; export class RunAutomaticTasks extends Disposable implements IWorkbenchContribution { constructor( - @ITaskService private readonly taskService: ITaskService, - @IStorageService private readonly storageService: IStorageService, - @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService, - @ILogService private readonly logService: ILogService) { + @ITaskService private readonly _taskService: ITaskService, + @IStorageService private readonly _storageService: IStorageService, + @IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService, + @ILogService private readonly _logService: ILogService) { super(); - this.tryRunTasks(); + this._tryRunTasks(); } - private async tryRunTasks() { - this.logService.trace('RunAutomaticTasks: Trying to run tasks.'); + private async _tryRunTasks() { + this._logService.trace('RunAutomaticTasks: Trying to run tasks.'); // Wait until we have task system info (the extension host and workspace folders are available). - if (!this.taskService.hasTaskSystemInfo) { - this.logService.trace('RunAutomaticTasks: Awaiting task system info.'); - await Event.toPromise(Event.once(this.taskService.onDidChangeTaskSystemInfo)); + if (!this._taskService.hasTaskSystemInfo) { + this._logService.trace('RunAutomaticTasks: Awaiting task system info.'); + await Event.toPromise(Event.once(this._taskService.onDidChangeTaskSystemInfo)); } - this.logService.trace('RunAutomaticTasks: Checking if automatic tasks should run.'); - const isFolderAutomaticAllowed = this.storageService.getBoolean(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, StorageScope.WORKSPACE, undefined); - await this.workspaceTrustManagementService.workspaceTrustInitialized; - const isWorkspaceTrusted = this.workspaceTrustManagementService.isWorkspaceTrusted(); + this._logService.trace('RunAutomaticTasks: Checking if automatic tasks should run.'); + const isFolderAutomaticAllowed = this._storageService.getBoolean(ARE_AUTOMATIC_TASKS_ALLOWED_IN_WORKSPACE, StorageScope.WORKSPACE, undefined); + await this._workspaceTrustManagementService.workspaceTrustInitialized; + const isWorkspaceTrusted = this._workspaceTrustManagementService.isWorkspaceTrusted(); // Only run if allowed. Prompting for permission occurs when a user first tries to run a task. if (isFolderAutomaticAllowed && isWorkspaceTrusted) { - this.taskService.getWorkspaceTasks(TaskRunSource.FolderOpen).then(workspaceTaskResult => { - const { tasks } = RunAutomaticTasks.findAutoTasks(this.taskService, workspaceTaskResult); - this.logService.trace(`RunAutomaticTasks: Found ${tasks.length} automatic tasks tasks`); + this._taskService.getWorkspaceTasks(TaskRunSource.FolderOpen).then(workspaceTaskResult => { + const { tasks } = RunAutomaticTasks._findAutoTasks(this._taskService, workspaceTaskResult); + this._logService.trace(`RunAutomaticTasks: Found ${tasks.length} automatic tasks tasks`); if (tasks.length > 0) { - RunAutomaticTasks.runTasks(this.taskService, tasks); + RunAutomaticTasks._runTasks(this._taskService, tasks); } }); } } - private static runTasks(taskService: ITaskService, tasks: Array>) { + private static _runTasks(taskService: ITaskService, tasks: Array>) { tasks.forEach(task => { if (task instanceof Promise) { task.then(promiseResult => { @@ -73,7 +73,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut }); } - private static getTaskSource(source: TaskSource): URI | undefined { + private static _getTaskSource(source: TaskSource): URI | undefined { const taskKind = TaskSourceKind.toConfigurationTarget(source.kind); switch (taskKind) { case ConfigurationTarget.WORKSPACE_FOLDER: { @@ -86,7 +86,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut return undefined; } - private static findAutoTasks(taskService: ITaskService, workspaceTaskResult: Map): { tasks: Array>; taskNames: Array; locations: Map } { + private static _findAutoTasks(taskService: ITaskService, workspaceTaskResult: Map): { tasks: Array>; taskNames: Array; locations: Map } { const tasks = new Array>(); const taskNames = new Array(); const locations = new Map(); @@ -98,7 +98,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut if (task.runOptions.runOn === RunOnOptions.folderOpen) { tasks.push(task); taskNames.push(task._label); - const location = RunAutomaticTasks.getTaskSource(task._source); + const location = RunAutomaticTasks._getTaskSource(task._source); if (location) { locations.set(location.fsPath, location); } @@ -116,7 +116,7 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut } else { taskNames.push(configedTask.value.configures.task); } - const location = RunAutomaticTasks.getTaskSource(configedTask.value._source); + const location = RunAutomaticTasks._getTaskSource(configedTask.value._source); if (location) { locations.set(location.fsPath, location); } @@ -140,18 +140,18 @@ export class RunAutomaticTasks extends Disposable implements IWorkbenchContribut return; } - const { tasks, taskNames, locations } = RunAutomaticTasks.findAutoTasks(taskService, workspaceTaskResult); + const { tasks, taskNames, locations } = RunAutomaticTasks._findAutoTasks(taskService, workspaceTaskResult); if (taskNames.length > 0) { // We have automatic tasks, prompt to allow. - this.showPrompt(notificationService, storageService, taskService, openerService, taskNames, locations).then(allow => { + this._showPrompt(notificationService, storageService, taskService, openerService, taskNames, locations).then(allow => { if (allow) { - RunAutomaticTasks.runTasks(taskService, tasks); + RunAutomaticTasks._runTasks(taskService, tasks); } }); } } - private static showPrompt(notificationService: INotificationService, storageService: IStorageService, taskService: ITaskService, + private static _showPrompt(notificationService: INotificationService, storageService: IStorageService, taskService: ITaskService, openerService: IOpenerService, taskNames: Array, locations: Map): Promise { return new Promise(resolve => { notificationService.prompt(Severity.Info, nls.localize('tasks.run.allowAutomatic', diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index a7d6aad1065..c9bdcd49adb 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -56,31 +56,31 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { }); export class TaskStatusBarContributions extends Disposable implements IWorkbenchContribution { - private runningTasksStatusItem: IStatusbarEntryAccessor | undefined; - private activeTasksCount: number = 0; + private _runningTasksStatusItem: IStatusbarEntryAccessor | undefined; + private _activeTasksCount: number = 0; constructor( - @ITaskService private readonly taskService: ITaskService, - @IStatusbarService private readonly statusbarService: IStatusbarService, - @IProgressService private readonly progressService: IProgressService + @ITaskService private readonly _taskService: ITaskService, + @IStatusbarService private readonly _statusbarService: IStatusbarService, + @IProgressService private readonly _progressService: IProgressService ) { super(); - this.registerListeners(); + this._registerListeners(); } - private registerListeners(): void { + private _registerListeners(): void { let promise: Promise | undefined = undefined; let resolver: (value?: void | Thenable) => void; - this.taskService.onDidStateChange(event => { + this._taskService.onDidStateChange(event => { if (event.kind === TaskEventKind.Changed) { - this.updateRunningTasksStatus(); + this._updateRunningTasksStatus(); } - if (!this.ignoreEventForUpdateRunningTasksCount(event)) { + if (!this._ignoreEventForUpdateRunningTasksCount(event)) { switch (event.kind) { case TaskEventKind.Active: - this.activeTasksCount++; - if (this.activeTasksCount === 1) { + this._activeTasksCount++; + if (this._activeTasksCount === 1) { if (!promise) { promise = new Promise((resolve) => { resolver = resolve; @@ -91,9 +91,9 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench case TaskEventKind.Inactive: // Since the exiting of the sub process is communicated async we can't order inactive and terminate events. // So try to treat them accordingly. - if (this.activeTasksCount > 0) { - this.activeTasksCount--; - if (this.activeTasksCount === 0) { + if (this._activeTasksCount > 0) { + this._activeTasksCount--; + if (this._activeTasksCount === 0) { if (promise && resolver!) { resolver!(); } @@ -101,8 +101,8 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench } break; case TaskEventKind.Terminated: - if (this.activeTasksCount !== 0) { - this.activeTasksCount = 0; + if (this._activeTasksCount !== 0) { + this._activeTasksCount = 0; if (promise && resolver!) { resolver!(); } @@ -111,8 +111,8 @@ 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' }, progress => { + if (promise && (event.kind === TaskEventKind.Active) && (this._activeTasksCount === 1)) { + this._progressService.withProgress({ location: ProgressLocation.Window, command: 'workbench.action.tasks.showTasks' }, progress => { progress.report({ message: nls.localize('building', 'Building...') }); return promise!; }).then(() => { @@ -122,12 +122,12 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench }); } - private async updateRunningTasksStatus(): Promise { - const tasks = await this.taskService.getActiveTasks(); + private async _updateRunningTasksStatus(): Promise { + const tasks = await this._taskService.getActiveTasks(); if (tasks.length === 0) { - if (this.runningTasksStatusItem) { - this.runningTasksStatusItem.dispose(); - this.runningTasksStatusItem = undefined; + if (this._runningTasksStatusItem) { + this._runningTasksStatusItem.dispose(); + this._runningTasksStatusItem = undefined; } } else { const itemProps: IStatusbarEntry = { @@ -138,16 +138,16 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench command: 'workbench.action.tasks.showTasks', }; - if (!this.runningTasksStatusItem) { - this.runningTasksStatusItem = this.statusbarService.addEntry(itemProps, 'status.runningTasks', StatusbarAlignment.LEFT, 49 /* Medium Priority, next to Markers */); + if (!this._runningTasksStatusItem) { + this._runningTasksStatusItem = this._statusbarService.addEntry(itemProps, 'status.runningTasks', StatusbarAlignment.LEFT, 49 /* Medium Priority, next to Markers */); } else { - this.runningTasksStatusItem.update(itemProps); + this._runningTasksStatusItem.update(itemProps); } } } - private ignoreEventForUpdateRunningTasksCount(event: ITaskEvent): boolean { - if (!this.taskService.inTerminal()) { + private _ignoreEventForUpdateRunningTasksCount(event: ITaskEvent): boolean { + if (!this._taskService.inTerminal()) { return false; } diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index 9d56c75024c..615a8a7e236 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -42,24 +42,24 @@ export const configureTaskIcon = registerIcon('tasks-list-configure', Codicon.ge const removeTaskIcon = registerIcon('tasks-remove', Codicon.close, nls.localize('removeTaskIcon', 'Icon for remove in the tasks selection list.')); export class TaskQuickPick extends Disposable { - private sorter: TaskSorter; - private topLevelEntries: QuickPickInput[] | undefined; + private _sorter: TaskSorter; + private _topLevelEntries: QuickPickInput[] | undefined; constructor( - private taskService: ITaskService, - private configurationService: IConfigurationService, - private quickInputService: IQuickInputService, - private notificationService: INotificationService, - private dialogService: IDialogService) { + private _taskService: ITaskService, + private _configurationService: IConfigurationService, + private _quickInputService: IQuickInputService, + private _notificationService: INotificationService, + private _dialogService: IDialogService) { super(); - this.sorter = this.taskService.createSorter(); + this._sorter = this._taskService.createSorter(); } - private showDetail(): boolean { + private _showDetail(): boolean { // Ensure invalid values get converted into boolean values - return !!this.configurationService.getValue(QUICKOPEN_DETAIL_CONFIG); + return !!this._configurationService.getValue(QUICKOPEN_DETAIL_CONFIG); } - private guessTaskLabel(task: Task | ConfiguringTask): string { + private _guessTaskLabel(task: Task | ConfiguringTask): string { if (task._label) { return task._label; } @@ -74,21 +74,21 @@ export class TaskQuickPick extends Disposable { return ''; } - private createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry { - const entry: ITaskTwoLevelQuickPickEntry = { label: this.guessTaskLabel(task), description: this.taskService.getTaskDescription(task), task, detail: this.showDetail() ? task.configurationProperties.detail : undefined }; + private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry { + const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined }; entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons]; return entry; } - private createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], + private _createEntriesForGroup(entries: QuickPickInput[], tasks: (Task | ConfiguringTask)[], groupLabel: string, extraButtons: IQuickInputButton[] = []) { entries.push({ type: 'separator', label: groupLabel }); tasks.forEach(task => { - entries.push(this.createTaskEntry(task, extraButtons)); + entries.push(this._createTaskEntry(task, extraButtons)); }); } - private createTypeEntries(entries: QuickPickInput[], types: string[]) { + private _createTypeEntries(entries: QuickPickInput[], types: string[]) { entries.push({ type: 'separator', label: nls.localize('contributedTasks', "contributed") }); types.forEach(type => { entries.push({ label: `$(folder) ${type}`, task: type, ariaLabel: nls.localize('taskType', "All {0} tasks", type) }); @@ -96,7 +96,7 @@ export class TaskQuickPick extends Disposable { entries.push({ label: SHOW_ALL, task: SHOW_ALL, alwaysShow: true }); } - private handleFolderTaskResult(result: Map): (Task | ConfiguringTask)[] { + private _handleFolderTaskResult(result: Map): (Task | ConfiguringTask)[] { const tasks: (Task | ConfiguringTask)[] = []; Array.from(result).forEach(([key, folderTasks]) => { if (folderTasks.set) { @@ -111,7 +111,7 @@ export class TaskQuickPick extends Disposable { return tasks; } - private dedupeConfiguredAndRecent(recentTasks: (Task | ConfiguringTask)[], configuredTasks: (Task | ConfiguringTask)[]): { configuredTasks: (Task | ConfiguringTask)[]; recentTasks: (Task | ConfiguringTask)[] } { + private _dedupeConfiguredAndRecent(recentTasks: (Task | ConfiguringTask)[], configuredTasks: (Task | ConfiguringTask)[]): { configuredTasks: (Task | ConfiguringTask)[]; recentTasks: (Task | ConfiguringTask)[] } { let dedupedConfiguredTasks: (Task | ConfiguringTask)[] = []; const foundRecentTasks: boolean[] = Array(recentTasks.length).fill(false); for (let j = 0; j < configuredTasks.length; j++) { @@ -132,7 +132,7 @@ export class TaskQuickPick extends Disposable { foundRecentTasks[findIndex] = true; } } - dedupedConfiguredTasks = dedupedConfiguredTasks.sort((a, b) => this.sorter.compare(a, b)); + dedupedConfiguredTasks = dedupedConfiguredTasks.sort((a, b) => this._sorter.compare(a, b)); const prunedRecentTasks: (Task | ConfiguringTask)[] = []; for (let i = 0; i < recentTasks.length; i++) { if (foundRecentTasks[i] || ConfiguringTask.is(recentTasks[i])) { @@ -143,15 +143,15 @@ export class TaskQuickPick extends Disposable { } public async getTopLevelEntries(defaultEntry?: ITaskQuickPickEntry): Promise<{ entries: QuickPickInput[]; isSingleConfigured?: Task | ConfiguringTask }> { - if (this.topLevelEntries !== undefined) { - return { entries: this.topLevelEntries }; + if (this._topLevelEntries !== undefined) { + return { entries: this._topLevelEntries }; } - let recentTasks: (Task | ConfiguringTask)[] = (await this.taskService.readRecentTasks()).reverse(); - const configuredTasks: (Task | ConfiguringTask)[] = this.handleFolderTaskResult(await this.taskService.getWorkspaceTasks()); - const extensionTaskTypes = this.taskService.taskTypes(); - this.topLevelEntries = []; + let recentTasks: (Task | ConfiguringTask)[] = (await this._taskService.readRecentTasks()).reverse(); + const configuredTasks: (Task | ConfiguringTask)[] = this._handleFolderTaskResult(await this._taskService.getWorkspaceTasks()); + const extensionTaskTypes = this._taskService.taskTypes(); + this._topLevelEntries = []; // Dedupe will update recent tasks if they've changed in tasks.json. - const dedupeAndPrune = this.dedupeConfiguredAndRecent(recentTasks, configuredTasks); + const dedupeAndPrune = this._dedupeConfiguredAndRecent(recentTasks, configuredTasks); const dedupedConfiguredTasks: (Task | ConfiguringTask)[] = dedupeAndPrune.configuredTasks; recentTasks = dedupeAndPrune.recentTasks; if (recentTasks.length > 0) { @@ -159,34 +159,34 @@ export class TaskQuickPick extends Disposable { iconClass: ThemeIcon.asClassName(removeTaskIcon), tooltip: nls.localize('removeRecent', 'Remove Recently Used Task') }; - this.createEntriesForGroup(this.topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]); + this._createEntriesForGroup(this._topLevelEntries, recentTasks, nls.localize('recentlyUsed', 'recently used'), [removeRecentButton]); } if (configuredTasks.length > 0) { if (dedupedConfiguredTasks.length > 0) { - this.createEntriesForGroup(this.topLevelEntries, dedupedConfiguredTasks, nls.localize('configured', 'configured')); + this._createEntriesForGroup(this._topLevelEntries, dedupedConfiguredTasks, nls.localize('configured', 'configured')); } } if (defaultEntry && (configuredTasks.length === 0)) { - this.topLevelEntries.push({ type: 'separator', label: nls.localize('configured', 'configured') }); - this.topLevelEntries.push(defaultEntry); + this._topLevelEntries.push({ type: 'separator', label: nls.localize('configured', 'configured') }); + this._topLevelEntries.push(defaultEntry); } if (extensionTaskTypes.length > 0) { - this.createTypeEntries(this.topLevelEntries, extensionTaskTypes); + this._createTypeEntries(this._topLevelEntries, extensionTaskTypes); } - return { entries: this.topLevelEntries, isSingleConfigured: configuredTasks.length === 1 ? configuredTasks[0] : undefined }; + return { entries: this._topLevelEntries, isSingleConfigured: configuredTasks.length === 1 ? configuredTasks[0] : undefined }; } public async handleSettingOption(selectedType: string) { const noButton = nls.localize('TaskQuickPick.changeSettingNo', "No"); const yesButton = nls.localize('TaskQuickPick.changeSettingYes', "Yes"); - const changeSettingResult = await this.dialogService.show(Severity.Warning, + const changeSettingResult = await this._dialogService.show(Severity.Warning, nls.localize('TaskQuickPick.changeSettingDetails', "Task detection for {0} tasks causes files in any workspace you open to be run as code. Enabling {0} task detection is a user setting and will apply to any workspace you open. Do you want to enable {0} task detection for all workspaces?", selectedType), [noButton, yesButton]); if (changeSettingResult.choice === 1) { - await this.configurationService.updateValue(`${selectedType}.autoDetect`, 'on'); + await this._configurationService.updateValue(`${selectedType}.autoDetect`, 'on'); await new Promise(resolve => setTimeout(() => resolve(), 100)); return this.show(nls.localize('TaskService.pickRunTask', 'Select the task to run'), undefined, selectedType); } @@ -194,7 +194,7 @@ export class TaskQuickPick extends Disposable { } public async show(placeHolder: string, defaultEntry?: ITaskQuickPickEntry, startAtType?: string): Promise { - const picker: IQuickPick = this.quickInputService.createQuickPick(); + const picker: IQuickPick = this._quickInputService.createQuickPick(); picker.placeholder = placeHolder; picker.matchOnDescription = true; picker.ignoreFocusOut = false; @@ -205,25 +205,25 @@ export class TaskQuickPick extends Disposable { if (context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) { const key = (task && !Types.isString(task)) ? task.getRecentlyUsedKey() : undefined; if (key) { - this.taskService.removeRecentlyUsedTask(key); + this._taskService.removeRecentlyUsedTask(key); } const indexToRemove = picker.items.indexOf(context.item); if (indexToRemove >= 0) { picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)]; } } else { - this.quickInputService.cancel(); + this._quickInputService.cancel(); if (ContributedTask.is(task)) { - this.taskService.customize(task, undefined, true); + this._taskService.customize(task, undefined, true); } else if (CustomTask.is(task) || ConfiguringTask.is(task)) { let canOpenConfig: boolean = false; try { - canOpenConfig = await this.taskService.openConfig(task); + canOpenConfig = await this._taskService.openConfig(task); } catch (e) { // do nothing. } if (!canOpenConfig) { - this.taskService.customize(task, undefined, true); + this._taskService.customize(task, undefined, true); } } } @@ -233,30 +233,30 @@ export class TaskQuickPick extends Disposable { if (!firstLevelTask) { // First show recent tasks configured tasks. Other tasks will be available at a second level const topLevelEntriesResult = await this.getTopLevelEntries(defaultEntry); - if (topLevelEntriesResult.isSingleConfigured && this.configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { + if (topLevelEntriesResult.isSingleConfigured && this._configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { picker.dispose(); - return this.toTask(topLevelEntriesResult.isSingleConfigured); + return this._toTask(topLevelEntriesResult.isSingleConfigured); } const taskQuickPickEntries: QuickPickInput[] = topLevelEntriesResult.entries; - firstLevelTask = await this.doPickerFirstLevel(picker, taskQuickPickEntries); + firstLevelTask = await this._doPickerFirstLevel(picker, taskQuickPickEntries); } do { if (Types.isString(firstLevelTask)) { // Proceed to second level of quick pick - const selectedEntry = await this.doPickerSecondLevel(picker, firstLevelTask); + const selectedEntry = await this._doPickerSecondLevel(picker, firstLevelTask); if (selectedEntry && !selectedEntry.settingType && selectedEntry.task === null) { // The user has chosen to go back to the first level - firstLevelTask = await this.doPickerFirstLevel(picker, (await this.getTopLevelEntries(defaultEntry)).entries); + firstLevelTask = await this._doPickerFirstLevel(picker, (await this.getTopLevelEntries(defaultEntry)).entries); } else if (selectedEntry && Types.isString(selectedEntry.settingType)) { picker.dispose(); return this.handleSettingOption(selectedEntry.settingType); } else { picker.dispose(); - return (selectedEntry?.task && !Types.isString(selectedEntry?.task)) ? this.toTask(selectedEntry?.task) : undefined; + return (selectedEntry?.task && !Types.isString(selectedEntry?.task)) ? this._toTask(selectedEntry?.task) : undefined; } } else if (firstLevelTask) { picker.dispose(); - return this.toTask(firstLevelTask); + return this._toTask(firstLevelTask); } else { picker.dispose(); return firstLevelTask; @@ -265,7 +265,7 @@ export class TaskQuickPick extends Disposable { return; } - private async doPickerFirstLevel(picker: IQuickPick, taskQuickPickEntries: QuickPickInput[]): Promise { + private async _doPickerFirstLevel(picker: IQuickPick, taskQuickPickEntries: QuickPickInput[]): Promise { picker.items = taskQuickPickEntries; const firstLevelPickerResult = await new Promise(resolve => { Event.once(picker.onDidAccept)(async () => { @@ -275,15 +275,15 @@ export class TaskQuickPick extends Disposable { return firstLevelPickerResult?.task; } - private async doPickerSecondLevel(picker: IQuickPick, type: string) { + private async _doPickerSecondLevel(picker: IQuickPick, type: string) { picker.busy = true; if (type === SHOW_ALL) { - const items = (await this.taskService.tasks()).sort((a, b) => this.sorter.compare(a, b)).map(task => this.createTaskEntry(task)); - items.push(...TaskQuickPick.allSettingEntries(this.configurationService)); + const items = (await this._taskService.tasks()).sort((a, b) => this._sorter.compare(a, b)).map(task => this._createTaskEntry(task)); + items.push(...TaskQuickPick.allSettingEntries(this._configurationService)); picker.items = items; } else { picker.value = ''; - picker.items = await this.getEntriesForProvider(type); + picker.items = await this._getEntriesForProvider(type); } picker.busy = false; const secondLevelPickerResult = await new Promise(resolve => { @@ -325,11 +325,11 @@ export class TaskQuickPick extends Disposable { return undefined; } - private async getEntriesForProvider(type: string): Promise[]> { - const tasks = (await this.taskService.tasks({ type })).sort((a, b) => this.sorter.compare(a, b)); + private async _getEntriesForProvider(type: string): Promise[]> { + const tasks = (await this._taskService.tasks({ type })).sort((a, b) => this._sorter.compare(a, b)); let taskQuickPickEntries: QuickPickInput[]; if (tasks.length > 0) { - taskQuickPickEntries = tasks.map(task => this.createTaskEntry(task)); + taskQuickPickEntries = tasks.map(task => this._createTaskEntry(task)); taskQuickPickEntries.push({ type: 'separator' }, { @@ -345,22 +345,22 @@ export class TaskQuickPick extends Disposable { }]; } - const settingEntry = TaskQuickPick.getSettingEntry(this.configurationService, type); + const settingEntry = TaskQuickPick.getSettingEntry(this._configurationService, type); if (settingEntry) { taskQuickPickEntries.push(settingEntry); } return taskQuickPickEntries; } - private async toTask(task: Task | ConfiguringTask): Promise { + private async _toTask(task: Task | ConfiguringTask): Promise { if (!ConfiguringTask.is(task)) { return task; } - const resolvedTask = await this.taskService.tryResolveTask(task); + const resolvedTask = await this._taskService.tryResolveTask(task); if (!resolvedTask) { - this.notificationService.error(nls.localize('noProviderForTask', "There is no task provider registered for tasks of type \"{0}\".", task.type)); + this._notificationService.error(nls.localize('noProviderForTask', "There is no task provider registered for tasks of type \"{0}\".", task.type)); } return resolvedTask; } diff --git a/src/vs/workbench/contrib/tasks/browser/taskService.ts b/src/vs/workbench/contrib/tasks/browser/taskService.ts index 65028c8ab75..efb6f0bb872 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskService.ts @@ -14,12 +14,12 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class TaskService extends AbstractTaskService { private static readonly ProcessTaskSystemSupportMessage = nls.localize('taskService.processTaskSystem', 'Process task system is not support in the web.'); - protected getTaskSystem(): ITaskSystem { + protected _getTaskSystem(): ITaskSystem { if (this._taskSystem) { return this._taskSystem; } if (this.executionEngine === ExecutionEngine.Terminal) { - this._taskSystem = this.createTerminalTaskSystem(); + this._taskSystem = this._createTerminalTaskSystem(); } else { throw new Error(TaskService.ProcessTaskSystemSupportMessage); } @@ -32,11 +32,11 @@ export class TaskService extends AbstractTaskService { return this._taskSystem!; } - protected computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise { + protected _computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise { throw new Error(TaskService.ProcessTaskSystemSupportMessage); } - protected versionAndEngineCompatible(filter?: ITaskFilter): boolean { + protected _versionAndEngineCompatible(filter?: ITaskFilter): boolean { return this.executionEngine === ExecutionEngine.Terminal; } } diff --git a/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts b/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts index 6c4080090bb..5f9af57d660 100644 --- a/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts +++ b/src/vs/workbench/contrib/tasks/browser/tasksQuickAccess.ts @@ -24,11 +24,11 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider = []; @@ -63,14 +63,14 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider { + private async _toTask(task: Task | ConfiguringTask): Promise { if (!ConfiguringTask.is(task)) { return task; } - return this.taskService.tryResolveTask(task); + return this._taskService.tryResolveTask(task); } } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 2d865c5247f..fbde4d9b819 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -84,21 +84,21 @@ class InstanceManager { } class VariableResolver { - private static regex = /\$\{(.*?)\}/g; + private static _regex = /\$\{(.*?)\}/g; constructor(public workspaceFolder: IWorkspaceFolder | undefined, public taskSystemInfo: ITaskSystemInfo | undefined, public readonly values: Map, private _service: IConfigurationResolverService | undefined) { } async resolve(value: string): Promise { const replacers: Promise[] = []; - value.replace(VariableResolver.regex, (match, ...args) => { - replacers.push(this.replacer(match, args)); + value.replace(VariableResolver._regex, (match, ...args) => { + replacers.push(this._replacer(match, args)); return match; }); const resolvedReplacers = await Promise.all(replacers); - return value.replace(VariableResolver.regex, () => resolvedReplacers.shift()!); + return value.replace(VariableResolver._regex, () => resolvedReplacers.shift()!); } - private async replacer(match: string, args: string[]): Promise { + private async _replacer(match: string, args: string[]): Promise { // Strip out the ${} because the map contains them variables without those characters. const result = this.values.get(match.substring(2, match.length - 1)); if ((result !== undefined) && (result !== null)) { @@ -149,7 +149,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private static readonly ProcessVarName = '__process__'; - private static shellQuotes: IStringDictionary = { + private static _shellQuotes: IStringDictionary = { 'cmd': { strong: '"' }, @@ -179,126 +179,127 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } }; - private static osShellQuotes: IStringDictionary = { - 'Linux': TerminalTaskSystem.shellQuotes['bash'], - 'Mac': TerminalTaskSystem.shellQuotes['bash'], - 'Windows': TerminalTaskSystem.shellQuotes['powershell'] + private static _osShellQuotes: IStringDictionary = { + 'Linux': TerminalTaskSystem._shellQuotes['bash'], + 'Mac': TerminalTaskSystem._shellQuotes['bash'], + 'Windows': TerminalTaskSystem._shellQuotes['powershell'] }; - private activeTasks: IStringDictionary; - private instances: IStringDictionary; - private busyTasks: IStringDictionary; - private terminals: IStringDictionary; - private idleTaskTerminals: LinkedMap; - private sameTaskTerminals: IStringDictionary; - private taskSystemInfoResolver: ITaskSystemInfoResolver; - private lastTask: VerifiedTask | undefined; + private _activeTasks: IStringDictionary; + private _instances: IStringDictionary; + private _busyTasks: IStringDictionary; + private _terminals: IStringDictionary; + private _idleTaskTerminals: LinkedMap; + private _sameTaskTerminals: IStringDictionary; + private _taskSystemInfoResolver: ITaskSystemInfoResolver; + private _lastTask: VerifiedTask | undefined; // Should always be set in run - private currentTask!: VerifiedTask; - private isRerun: boolean = false; - private previousPanelId: string | undefined; - private previousTerminalInstance: ITerminalInstance | undefined; - private terminalStatusManager: TaskTerminalStatus; - private terminalCreationQueue: Promise = Promise.resolve(); + private _currentTask!: VerifiedTask; + private _isRerun: boolean = false; + private _previousPanelId: string | undefined; + private _previousTerminalInstance: ITerminalInstance | undefined; + private _terminalStatusManager: TaskTerminalStatus; + private _terminalCreationQueue: Promise = Promise.resolve(); private readonly _onDidStateChange: Emitter; constructor( - private terminalService: ITerminalService, - private terminalGroupService: ITerminalGroupService, - private outputService: IOutputService, - private paneCompositeService: IPaneCompositePartService, - private viewsService: IViewsService, - private markerService: IMarkerService, private modelService: IModelService, - private configurationResolverService: IConfigurationResolverService, - private contextService: IWorkspaceContextService, - private environmentService: IWorkbenchEnvironmentService, - private outputChannelId: string, - private fileService: IFileService, - private terminalProfileResolverService: ITerminalProfileResolverService, - private pathService: IPathService, - private viewDescriptorService: IViewDescriptorService, - private logService: ILogService, - private configurationService: IConfigurationService, - private notificationService: INotificationService, + private _terminalService: ITerminalService, + private _terminalGroupService: ITerminalGroupService, + private _outputService: IOutputService, + private _paneCompositeService: IPaneCompositePartService, + private _viewsService: IViewsService, + private _markerService: IMarkerService, + private _modelService: IModelService, + private _configurationResolverService: IConfigurationResolverService, + private _contextService: IWorkspaceContextService, + private _environmentService: IWorkbenchEnvironmentService, + private _outputChannelId: string, + private _fileService: IFileService, + private _terminalProfileResolverService: ITerminalProfileResolverService, + private _pathService: IPathService, + private _viewDescriptorService: IViewDescriptorService, + private _logService: ILogService, + private _configurationService: IConfigurationService, + private _notificationService: INotificationService, taskService: ITaskService, taskSystemInfoResolver: ITaskSystemInfoResolver, ) { super(); - this.activeTasks = Object.create(null); - this.instances = Object.create(null); - this.busyTasks = Object.create(null); - this.terminals = Object.create(null); - this.idleTaskTerminals = new LinkedMap(); - this.sameTaskTerminals = Object.create(null); + this._activeTasks = Object.create(null); + this._instances = Object.create(null); + this._busyTasks = Object.create(null); + this._terminals = Object.create(null); + this._idleTaskTerminals = new LinkedMap(); + this._sameTaskTerminals = Object.create(null); this._onDidStateChange = new Emitter(); - this.taskSystemInfoResolver = taskSystemInfoResolver; - this._register(this.terminalStatusManager = new TaskTerminalStatus(taskService)); + this._taskSystemInfoResolver = taskSystemInfoResolver; + this._register(this._terminalStatusManager = new TaskTerminalStatus(taskService)); } public get onDidStateChange(): Event { return this._onDidStateChange.event; } - private log(value: string): void { - this.appendOutput(value + '\n'); + private _log(value: string): void { + this._appendOutput(value + '\n'); } - protected showOutput(): void { - this.outputService.showChannel(this.outputChannelId, true); + protected _showOutput(): void { + this._outputService.showChannel(this._outputChannelId, true); } public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { task = task.clone(); // A small amount of task state is stored in the task (instance) and tasks passed in to run may have that set already. const recentTaskKey = task.getRecentlyUsedKey() ?? ''; - const validInstance = task.runOptions && task.runOptions.instanceLimit && this.instances[recentTaskKey] && this.instances[recentTaskKey].instances < task.runOptions.instanceLimit; - const instance = this.instances[recentTaskKey] ? this.instances[recentTaskKey].instances : 0; - this.currentTask = new VerifiedTask(task, resolver, trigger); + const validInstance = task.runOptions && task.runOptions.instanceLimit && this._instances[recentTaskKey] && this._instances[recentTaskKey].instances < task.runOptions.instanceLimit; + const instance = this._instances[recentTaskKey] ? this._instances[recentTaskKey].instances : 0; + this._currentTask = new VerifiedTask(task, resolver, trigger); if (instance > 0) { - task.instance = this.instances[recentTaskKey].counter; + task.instance = this._instances[recentTaskKey].counter; } const lastTaskInstance = this.getLastInstance(task); - const terminalData = lastTaskInstance ? this.activeTasks[lastTaskInstance.getMapKey()] : undefined; + const terminalData = lastTaskInstance ? this._activeTasks[lastTaskInstance.getMapKey()] : undefined; if (terminalData && terminalData.promise && !validInstance) { - this.lastTask = this.currentTask; + this._lastTask = this._currentTask; return { kind: TaskExecuteKind.Active, task: terminalData.task, active: { same: true, background: task.configurationProperties.isBackground! }, promise: terminalData.promise }; } try { - const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger, new Set()) }; + const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this._executeTask(task, resolver, trigger, new Set()) }; executeResult.promise.then(summary => { - this.lastTask = this.currentTask; + this._lastTask = this._currentTask; }); - if (InMemoryTask.is(task) || !this.isTaskEmpty(task)) { - if (!this.instances[recentTaskKey]) { - this.instances[recentTaskKey] = new InstanceManager(); + if (InMemoryTask.is(task) || !this._isTaskEmpty(task)) { + if (!this._instances[recentTaskKey]) { + this._instances[recentTaskKey] = new InstanceManager(); } - this.instances[recentTaskKey].addInstance(); + this._instances[recentTaskKey].addInstance(); } return executeResult; } catch (error) { if (error instanceof TaskError) { throw error; } else if (error instanceof Error) { - this.log(error.message); + this._log(error.message); throw new TaskError(Severity.Error, error.message, TaskErrors.UnknownError); } else { - this.log(error.toString()); + this._log(error.toString()); throw new TaskError(Severity.Error, nls.localize('TerminalTaskSystem.unknownError', 'A unknown error has occurred while executing a task. See task output log for details.'), TaskErrors.UnknownError); } } } public rerun(): ITaskExecuteResult | undefined { - if (this.lastTask && this.lastTask.verify()) { - if ((this.lastTask.task.runOptions.reevaluateOnRerun !== undefined) && !this.lastTask.task.runOptions.reevaluateOnRerun) { - this.isRerun = true; + if (this._lastTask && this._lastTask.verify()) { + if ((this._lastTask.task.runOptions.reevaluateOnRerun !== undefined) && !this._lastTask.task.runOptions.reevaluateOnRerun) { + this._isRerun = true; } - const result = this.run(this.lastTask.task, this.lastTask.resolver); + const result = this.run(this._lastTask.task, this._lastTask.resolver); result.promise.then(summary => { - this.isRerun = false; + this._isRerun = false; }); return result; } else { @@ -306,59 +307,59 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } - private showTaskLoadErrors(task: Task) { + private _showTaskLoadErrors(task: Task) { if (task.taskLoadMessages && task.taskLoadMessages.length > 0) { task.taskLoadMessages.forEach(loadMessage => { - this.log(loadMessage + '\n'); + this._log(loadMessage + '\n'); }); const openOutput = 'Show Output'; - this.notificationService.prompt(Severity.Warning, + this._notificationService.prompt(Severity.Warning, nls.localize('TerminalTaskSystem.taskLoadReporting', "There are issues with task \"{0}\". See the output for more details.", task._label), [{ label: openOutput, - run: () => this.showOutput() + run: () => this._showOutput() }]); } } public isTaskVisible(task: Task): boolean { - const terminalData = this.activeTasks[task.getMapKey()]; + const terminalData = this._activeTasks[task.getMapKey()]; if (!terminalData) { return false; } - const activeTerminalInstance = this.terminalService.activeInstance; - const isPanelShowingTerminal = !!this.viewsService.getActiveViewWithId(TERMINAL_VIEW_ID); + const activeTerminalInstance = this._terminalService.activeInstance; + const isPanelShowingTerminal = !!this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID); return isPanelShowingTerminal && (activeTerminalInstance?.instanceId === terminalData.terminal.instanceId); } public revealTask(task: Task): boolean { - const terminalData = this.activeTasks[task.getMapKey()]; + const terminalData = this._activeTasks[task.getMapKey()]; if (!terminalData) { return false; } - const isTerminalInPanel: boolean = this.viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID) === ViewContainerLocation.Panel; + const isTerminalInPanel: boolean = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID) === ViewContainerLocation.Panel; if (isTerminalInPanel && this.isTaskVisible(task)) { - if (this.previousPanelId) { - if (this.previousTerminalInstance) { - this.terminalService.setActiveInstance(this.previousTerminalInstance); + if (this._previousPanelId) { + if (this._previousTerminalInstance) { + this._terminalService.setActiveInstance(this._previousTerminalInstance); } - this.paneCompositeService.openPaneComposite(this.previousPanelId, ViewContainerLocation.Panel); + this._paneCompositeService.openPaneComposite(this._previousPanelId, ViewContainerLocation.Panel); } else { - this.paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Panel); + this._paneCompositeService.hideActivePaneComposite(ViewContainerLocation.Panel); } - this.previousPanelId = undefined; - this.previousTerminalInstance = undefined; + this._previousPanelId = undefined; + this._previousTerminalInstance = undefined; } else { if (isTerminalInPanel) { - this.previousPanelId = this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)?.getId(); - if (this.previousPanelId === TERMINAL_VIEW_ID) { - this.previousTerminalInstance = this.terminalService.activeInstance ?? undefined; + this._previousPanelId = this._paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel)?.getId(); + if (this._previousPanelId === TERMINAL_VIEW_ID) { + this._previousTerminalInstance = this._terminalService.activeInstance ?? undefined; } } - this.terminalService.setActiveInstance(terminalData.terminal); + this._terminalService.setActiveInstance(terminalData.terminal); if (CustomTask.is(task) || ContributedTask.is(task)) { - this.terminalGroupService.showPanel(task.command.presentation!.focus); + this._terminalGroupService.showPanel(task.command.presentation!.focus); } } return true; @@ -369,34 +370,34 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } public isActiveSync(): boolean { - return Object.keys(this.activeTasks).length > 0; + return Object.keys(this._activeTasks).length > 0; } public canAutoTerminate(): boolean { - return Object.keys(this.activeTasks).every(key => !this.activeTasks[key].task.configurationProperties.promptOnClose); + return Object.keys(this._activeTasks).every(key => !this._activeTasks[key].task.configurationProperties.promptOnClose); } public getActiveTasks(): Task[] { - return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task); + return Object.keys(this._activeTasks).map(key => this._activeTasks[key].task); } public getLastInstance(task: Task): Task | undefined { let lastInstance = undefined; const recentKey = task.getRecentlyUsedKey(); - Object.keys(this.activeTasks).forEach((key) => { - if (recentKey && recentKey === this.activeTasks[key].task.getRecentlyUsedKey()) { - lastInstance = this.activeTasks[key].task; + Object.keys(this._activeTasks).forEach((key) => { + if (recentKey && recentKey === this._activeTasks[key].task.getRecentlyUsedKey()) { + lastInstance = this._activeTasks[key].task; } }); return lastInstance; } public getBusyTasks(): Task[] { - return Object.keys(this.busyTasks).map(key => this.busyTasks[key]); + return Object.keys(this._busyTasks).map(key => this._busyTasks[key]); } public customExecutionComplete(task: Task, result: number): Promise { - const activeTerminal = this.activeTasks[task.getMapKey()]; + const activeTerminal = this._activeTasks[task.getMapKey()]; if (!activeTerminal) { return Promise.reject(new Error('Expected to have a terminal for an custom execution task')); } @@ -407,27 +408,27 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { }); } - private removeInstances(task: Task) { + private _removeInstances(task: Task) { const recentTaskKey = task.getRecentlyUsedKey() ?? ''; - if (this.instances[recentTaskKey]) { - this.instances[recentTaskKey].removeInstance(); - if (this.instances[recentTaskKey].instances === 0) { - delete this.instances[recentTaskKey]; + if (this._instances[recentTaskKey]) { + this._instances[recentTaskKey].removeInstance(); + if (this._instances[recentTaskKey].instances === 0) { + delete this._instances[recentTaskKey]; } } } - private removeFromActiveTasks(task: Task): void { - if (!this.activeTasks[task.getMapKey()]) { + private _removeFromActiveTasks(task: Task): void { + if (!this._activeTasks[task.getMapKey()]) { return; } - delete this.activeTasks[task.getMapKey()]; - this.removeInstances(task); + delete this._activeTasks[task.getMapKey()]; + this._removeInstances(task); } - private fireTaskEvent(event: ITaskEvent) { + private _fireTaskEvent(event: ITaskEvent) { if (event.__task) { - const activeTask = this.activeTasks[event.__task.getMapKey()]; + const activeTask = this._activeTasks[event.__task.getMapKey()]; if (activeTask) { activeTask.state = event.kind; } @@ -436,7 +437,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } public terminate(task: Task): Promise { - const activeTerminal = this.activeTasks[task.getMapKey()]; + const activeTerminal = this._activeTasks[task.getMapKey()]; if (!activeTerminal) { return Promise.resolve({ success: false, task: undefined }); } @@ -447,7 +448,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const task = activeTerminal.task; try { onExit.dispose(); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task)); } catch (error) { // Do nothing. } @@ -459,15 +460,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { public terminateAll(): Promise { const promises: Promise[] = []; - Object.keys(this.activeTasks).forEach((key) => { - const terminalData = this.activeTasks[key]; + Object.keys(this._activeTasks).forEach((key) => { + const terminalData = this._activeTasks[key]; const terminal = terminalData.terminal; promises.push(new Promise((resolve, reject) => { const onExit = terminal.onExit(() => { const task = terminalData.task; try { onExit.dispose(); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task)); } catch (error) { // Do nothing. } @@ -476,26 +477,26 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { })); terminal.dispose(); }); - this.activeTasks = Object.create(null); + this._activeTasks = Object.create(null); return Promise.all(promises); } - private showDependencyCycleMessage(task: Task) { - this.log(nls.localize('dependencyCycle', + private _showDependencyCycleMessage(task: Task) { + this._log(nls.localize('dependencyCycle', 'There is a dependency cycle. See task "{0}".', task._label )); - this.showOutput(); + this._showOutput(); } - private async executeTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { + private async _executeTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { if (encounteredDependencies.has(task.getCommonTaskId())) { - this.showDependencyCycleMessage(task); + this._showDependencyCycleMessage(task); return {}; } - this.showTaskLoadErrors(task); + this._showTaskLoadErrors(task); alreadyResolved = alreadyResolved ?? new Map(); const promises: Promise[] = []; @@ -504,11 +505,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const dependencyTask = await resolver.resolve(dependency.uri, dependency.task!); if (dependencyTask) { const key = dependencyTask.getMapKey(); - let promise = this.activeTasks[key] ? this.getDependencyPromise(this.activeTasks[key]) : undefined; + let promise = this._activeTasks[key] ? this._getDependencyPromise(this._activeTasks[key]) : undefined; if (!promise) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); encounteredDependencies.add(task.getCommonTaskId()); - promise = this.executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved); + promise = this._executeDependencyTask(dependencyTask, resolver, trigger, encounteredDependencies, alreadyResolved); } promises.push(promise); if (task.configurationProperties.dependsOrder === DependsOrder.sequence) { @@ -522,12 +523,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } promises.push(promise); } else { - this.log(nls.localize('dependencyFailed', + this._log(nls.localize('dependencyFailed', 'Couldn\'t resolve dependent task \'{0}\' in workspace folder \'{1}\'', Types.isString(dependency.task) ? dependency.task : JSON.stringify(dependency.task, undefined, 0), dependency.uri.toString() )); - this.showOutput(); + this._showOutput(); } } } @@ -537,14 +538,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { encounteredDependencies.delete(task.getCommonTaskId()); for (const summary of summaries) { if (summary.exitCode !== 0) { - this.removeInstances(task); + this._removeInstances(task); return { exitCode: summary.exitCode }; } } - if (this.isRerun) { - return this.reexecuteCommand(task, trigger, alreadyResolved!); + if (this._isRerun) { + return this._reexecuteCommand(task, trigger, alreadyResolved!); } else { - return this.executeCommand(task, trigger, alreadyResolved!); + return this._executeCommand(task, trigger, alreadyResolved!); } }); } else { @@ -560,7 +561,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } - private createInactiveDependencyPromise(task: Task): Promise { + private _createInactiveDependencyPromise(task: Task): Promise { return new Promise(resolve => { const taskInactiveDisposable = this.onDidStateChange(taskEvent => { if ((taskEvent.kind === TaskEventKind.Inactive) && (taskEvent.__task === task)) { @@ -571,7 +572,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { }); } - private async getDependencyPromise(task: IActiveTerminalData): Promise { + private async _getDependencyPromise(task: IActiveTerminalData): Promise { if (!task.task.configurationProperties.isBackground) { return task.promise; } @@ -581,24 +582,24 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (task.state === TaskEventKind.Inactive) { return { exitCode: 0 }; } - return this.createInactiveDependencyPromise(task.task); + return this._createInactiveDependencyPromise(task.task); } - private async executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { + private async _executeDependencyTask(task: Task, resolver: ITaskResolver, trigger: string, encounteredDependencies: Set, alreadyResolved?: Map): Promise { // If the task is a background task with a watching problem matcher, we don't wait for the whole task to finish, // just for the problem matcher to go inactive. if (!task.configurationProperties.isBackground) { - return this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved); + return this._executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved); } - const inactivePromise = this.createInactiveDependencyPromise(task); - return Promise.race([inactivePromise, this.executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved)]); + const inactivePromise = this._createInactiveDependencyPromise(task); + return Promise.race([inactivePromise, this._executeTask(task, resolver, trigger, encounteredDependencies, alreadyResolved)]); } - private async resolveAndFindExecutable(systemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { - const command = await this.configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); - cwd = cwd ? await this.configurationResolverService.resolveAsync(workspaceFolder, cwd) : undefined; - const paths = envPath ? await Promise.all(envPath.split(path.delimiter).map(p => this.configurationResolverService.resolveAsync(workspaceFolder, p))) : undefined; + private async _resolveAndFindExecutable(systemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, cwd: string | undefined, envPath: string | undefined): Promise { + const command = await this._configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); + cwd = cwd ? await this._configurationResolverService.resolveAsync(workspaceFolder, cwd) : undefined; + const paths = envPath ? await Promise.all(envPath.split(path.delimiter).map(p => this._configurationResolverService.resolveAsync(workspaceFolder, p))) : undefined; let foundExecutable = await systemInfo?.findExecutable(command, cwd, paths); if (!foundExecutable) { foundExecutable = path.join(cwd ?? '', command); @@ -606,7 +607,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return foundExecutable; } - private findUnresolvedVariables(variables: Set, alreadyResolved: Map): Set { + private _findUnresolvedVariables(variables: Set, alreadyResolved: Map): Set { if (alreadyResolved.size === 0) { return variables; } @@ -619,7 +620,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return unresolved; } - private mergeMaps(mergeInto: Map, mergeFrom: Map) { + private _mergeMaps(mergeInto: Map, mergeFrom: Map) { for (const entry of mergeFrom) { if (!mergeInto.has(entry[0])) { mergeInto.set(entry[0], entry[1]); @@ -627,13 +628,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } - private async acquireInput(taskSystemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { - const resolved = await this.resolveVariablesFromSet(taskSystemInfo, workspaceFolder, task, variables, alreadyResolved); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.AcquiredInput, task)); + private async _acquireInput(taskSystemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { + const resolved = await this._resolveVariablesFromSet(taskSystemInfo, workspaceFolder, task, variables, alreadyResolved); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.AcquiredInput, task)); return resolved; } - private resolveVariablesFromSet(taskSystemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { + private _resolveVariablesFromSet(taskSystemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { const isProcess = task.command && task.command.runtime === RuntimeType.Process; const options = task.command && task.command.options ? task.command.options : undefined; const cwd = options ? options.cwd : undefined; @@ -648,7 +649,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } } - const unresolved = this.findUnresolvedVariables(variables, alreadyResolved); + const unresolved = this._findUnresolvedVariables(variables, alreadyResolved); let resolvedVariables: Promise; if (taskSystemInfo && workspaceFolder) { const resolveSet: IResolveSet = { @@ -669,12 +670,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return undefined; } - this.mergeMaps(alreadyResolved, resolved.variables); + this._mergeMaps(alreadyResolved, resolved.variables); resolved.variables = new Map(alreadyResolved); if (isProcess) { let process = CommandString.value(task.command.name!); if (taskSystemInfo.platform === Platform.Platform.Windows) { - process = await this.resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); + process = await this._resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); } resolved.variables.set(TerminalTaskSystem.ProcessVarName, process); } @@ -686,16 +687,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { unresolved.forEach(variable => variablesArray.push(variable)); return new Promise((resolve, reject) => { - this.configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, 'tasks', undefined, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolvedVariablesMap: Map | undefined) => { + this._configurationResolverService.resolveWithInteraction(workspaceFolder, variablesArray, 'tasks', undefined, TaskSourceKind.toConfigurationTarget(task._source.kind)).then(async (resolvedVariablesMap: Map | undefined) => { if (resolvedVariablesMap) { - this.mergeMaps(alreadyResolved, resolvedVariablesMap); + this._mergeMaps(alreadyResolved, resolvedVariablesMap); resolvedVariablesMap = new Map(alreadyResolved); if (isProcess) { let processVarValue: string; if (Platform.isWindows) { - processVarValue = await this.resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); + processVarValue = await this._resolveAndFindExecutable(taskSystemInfo, workspaceFolder, task, cwd, envPath); } else { - processVarValue = await this.configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); + processVarValue = await this._configurationResolverService.resolveAsync(workspaceFolder, CommandString.value(task.command.name!)); } resolvedVariablesMap.set(TerminalTaskSystem.ProcessVarName, processVarValue); } @@ -713,28 +714,28 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } - private executeCommand(task: CustomTask | ContributedTask, trigger: string, alreadyResolved: Map): Promise { + private _executeCommand(task: CustomTask | ContributedTask, trigger: string, alreadyResolved: Map): Promise { const taskWorkspaceFolder = task.getWorkspaceFolder(); let workspaceFolder: IWorkspaceFolder | undefined; if (taskWorkspaceFolder) { - workspaceFolder = this.currentTask.workspaceFolder = taskWorkspaceFolder; + workspaceFolder = this._currentTask.workspaceFolder = taskWorkspaceFolder; } else { - const folders = this.contextService.getWorkspace().folders; + const folders = this._contextService.getWorkspace().folders; workspaceFolder = folders.length > 0 ? folders[0] : undefined; } - const systemInfo: ITaskSystemInfo | undefined = this.currentTask.systemInfo = this.taskSystemInfoResolver(workspaceFolder); + const systemInfo: ITaskSystemInfo | undefined = this._currentTask.systemInfo = this._taskSystemInfoResolver(workspaceFolder); const variables = new Set(); - this.collectTaskVariables(variables, task); - const resolvedVariables = this.acquireInput(systemInfo, workspaceFolder, task, variables, alreadyResolved); + this._collectTaskVariables(variables, task); + const resolvedVariables = this._acquireInput(systemInfo, workspaceFolder, task, variables, alreadyResolved); return resolvedVariables.then((resolvedVariables) => { - if (resolvedVariables && !this.isTaskEmpty(task)) { - this.currentTask.resolvedVariables = resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder); + if (resolvedVariables && !this._isTaskEmpty(task)) { + this._currentTask.resolvedVariables = resolvedVariables; + return this._executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, systemInfo, resolvedVariables.variables, this._configurationResolverService), workspaceFolder); } else { // Allows the taskExecutions array to be updated in the extension host - this.fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); return Promise.resolve({ exitCode: 0 }); } }, reason => { @@ -742,19 +743,19 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { }); } - private isTaskEmpty(task: CustomTask | ContributedTask): boolean { + private _isTaskEmpty(task: CustomTask | ContributedTask): boolean { const isCustomExecution = (task.command.runtime === RuntimeType.CustomExecution); return !((task.command !== undefined) && task.command.runtime && (isCustomExecution || (task.command.name !== undefined))); } - private reexecuteCommand(task: CustomTask | ContributedTask, trigger: string, alreadyResolved: Map): Promise { - const lastTask = this.lastTask; + private _reexecuteCommand(task: CustomTask | ContributedTask, trigger: string, alreadyResolved: Map): Promise { + const lastTask = this._lastTask; if (!lastTask) { return Promise.reject(new Error('No task previously run')); } - const workspaceFolder = this.currentTask.workspaceFolder = lastTask.workspaceFolder; + const workspaceFolder = this._currentTask.workspaceFolder = lastTask.workspaceFolder; const variables = new Set(); - this.collectTaskVariables(variables, task); + this._collectTaskVariables(variables, task); // Check that the task hasn't changed to include new variables let hasAllVariables = true; @@ -765,33 +766,33 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { }); if (!hasAllVariables) { - return this.acquireInput(lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().workspaceFolder, task, variables, alreadyResolved).then((resolvedVariables) => { + return this._acquireInput(lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().workspaceFolder, task, variables, alreadyResolved).then((resolvedVariables) => { if (!resolvedVariables) { // Allows the taskExecutions array to be updated in the extension host - this.fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); return { exitCode: 0 }; } - this.currentTask.resolvedVariables = resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); + this._currentTask.resolvedVariables = resolvedVariables; + return this._executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this._configurationResolverService), workspaceFolder!); }, reason => { return Promise.reject(reason); }); } else { - this.currentTask.resolvedVariables = lastTask.getVerifiedTask().resolvedVariables; - return this.executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().resolvedVariables.variables, this.configurationResolverService), workspaceFolder!); + this._currentTask.resolvedVariables = lastTask.getVerifiedTask().resolvedVariables; + return this._executeInTerminal(task, trigger, new VariableResolver(lastTask.getVerifiedTask().workspaceFolder, lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().resolvedVariables.variables, this._configurationResolverService), workspaceFolder!); } } - private async executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise { + private async _executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise { let terminal: ITerminalInstance | undefined = undefined; let error: TaskError | undefined = undefined; let promise: Promise | undefined = undefined; if (task.configurationProperties.isBackground) { - const problemMatchers = await this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); - const watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService, this.fileService); + const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers); + const watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this._markerService, this._modelService, this._fileService); if ((problemMatchers.length > 0) && !watchingProblemMatcher.isWatching()) { - this.appendOutput(nls.localize('TerminalTaskSystem.nonWatchingMatcher', 'Task {0} is a background task but uses a problem matcher without a background pattern', task._label)); - this.showOutput(); + this._appendOutput(nls.localize('TerminalTaskSystem.nonWatchingMatcher', 'Task {0} is a background task but uses a problem matcher without a background pattern', task._label)); + this._showOutput(); } const toDispose = new DisposableStore(); let eventCounter: number = 0; @@ -799,24 +800,24 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { toDispose.add(watchingProblemMatcher.onDidStateChange((event) => { if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { eventCounter++; - this.busyTasks[mapKey] = task; - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task)); + this._busyTasks[mapKey] = task; + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task)); } else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) { eventCounter--; - if (this.busyTasks[mapKey]) { - delete this.busyTasks[mapKey]; + if (this._busyTasks[mapKey]) { + delete this._busyTasks[mapKey]; } - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); if (eventCounter === 0) { if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { const reveal = task.command.presentation!.reveal; const revealProblems = task.command.presentation!.revealProblems; if (revealProblems === RevealProblemKind.OnProblem) { - this.viewsService.openView(Markers.MARKERS_VIEW_ID, true); + this._viewsService.openView(Markers.MARKERS_VIEW_ID, true); } else if (reveal === RevealKind.Silent) { - this.terminalService.setActiveInstance(terminal!); - this.terminalGroupService.showPanel(false); + this._terminalService.setActiveInstance(terminal!); + this._terminalGroupService.showPanel(false); } } } @@ -824,7 +825,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { })); watchingProblemMatcher.aboutToStart(); let delayer: Async.Delayer | undefined = undefined; - [terminal, error] = await this.createTerminal(task, resolver, workspaceFolder); + [terminal, error] = await this._createTerminal(task, resolver, workspaceFolder); if (error) { return Promise.reject(new Error((error).message)); @@ -832,18 +833,18 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (!terminal) { return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`)); } - this.terminalStatusManager.addTerminal(task, terminal, watchingProblemMatcher); + this._terminalStatusManager.addTerminal(task, terminal, watchingProblemMatcher); let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { - this.logService.error('Task terminal process never got ready'); + this._logService.error('Task terminal process never got ready'); }); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId)); const onData = terminal.onLineData((line) => { watchingProblemMatcher.processLine(line); if (!delayer) { @@ -860,19 +861,19 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { onData.dispose(); onExit.dispose(); const key = task.getMapKey(); - if (this.busyTasks[mapKey]) { - delete this.busyTasks[mapKey]; + if (this._busyTasks[mapKey]) { + delete this._busyTasks[mapKey]; } - this.removeFromActiveTasks(task); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._removeFromActiveTasks(task); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); if (terminalLaunchResult !== undefined) { // Only keep a reference to the terminal if it is not being disposed. switch (task.command.presentation!.panel) { case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal!.instanceId.toString(); + this._sameTaskTerminals[key] = terminal!.instanceId.toString(); break; case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld); + this._idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld); break; } } @@ -880,8 +881,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { try { - this.terminalService.setActiveInstance(terminal!); - this.terminalGroupService.showPanel(false); + this._terminalService.setActiveInstance(terminal!); + this._terminalGroupService.showPanel(false); } catch (e) { // If the terminal has already been disposed, then setting the active instance will fail. #99828 // There is nothing else to do here. @@ -890,23 +891,23 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); if (!processStartedSignaled) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode ?? undefined)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode ?? undefined)); for (let i = 0; i < eventCounter; i++) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); } eventCounter = 0; - this.fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); toDispose.dispose(); resolve({ exitCode: exitCode ?? undefined }); }); }); } else { - [terminal, error] = await this.createTerminal(task, resolver, workspaceFolder); + [terminal, error] = await this._createTerminal(task, resolver, workspaceFolder); if (error) { return Promise.reject(new Error((error).message)); @@ -918,19 +919,19 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { // The process never got ready. Need to think how to handle this. }); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId, resolver.values)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId, resolver.values)); const mapKey = task.getMapKey(); - this.busyTasks[mapKey] = task; - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task)); - const problemMatchers = await this.resolveMatchers(resolver, task.configurationProperties.problemMatchers); - const startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService); - this.terminalStatusManager.addTerminal(task, terminal, startStopProblemMatcher); + this._busyTasks[mapKey] = task; + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task)); + const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers); + const startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this._markerService, this._modelService, ProblemHandlingStrategy.Clean, this._fileService); + this._terminalStatusManager.addTerminal(task, terminal, startStopProblemMatcher); const onData = terminal.onLineData((line) => { startStopProblemMatcher.processLine(line); }); @@ -939,16 +940,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const exitCode = typeof terminalLaunchResult === 'number' ? terminalLaunchResult : terminalLaunchResult?.code; onExit.dispose(); const key = task.getMapKey(); - this.removeFromActiveTasks(task); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._removeFromActiveTasks(task); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); if (terminalLaunchResult !== undefined) { // Only keep a reference to the terminal if it is not being disposed. switch (task.command.presentation!.panel) { case PanelKind.Dedicated: - this.sameTaskTerminals[key] = terminal!.instanceId.toString(); + this._sameTaskTerminals[key] = terminal!.instanceId.toString(); break; case PanelKind.Shared: - this.idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld); + this._idleTaskTerminals.set(key, terminal!.instanceId.toString(), Touch.AsOld); break; } } @@ -956,12 +957,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const revealProblems = task.command.presentation!.revealProblems; const revealProblemPanel = terminal && (revealProblems === RevealProblemKind.OnProblem) && (startStopProblemMatcher.numberOfMatches > 0); if (revealProblemPanel) { - this.viewsService.openView(Markers.MARKERS_VIEW_ID); + this._viewsService.openView(Markers.MARKERS_VIEW_ID); } else if (terminal && (reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && startStopProblemMatcher.maxMarkerSeverity && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { try { - this.terminalService.setActiveInstance(terminal); - this.terminalGroupService.showPanel(false); + this._terminalService.setActiveInstance(terminal); + this._terminalGroupService.showPanel(false); } catch (e) { // If the terminal has already been disposed, then setting the active instance will fail. #99828 // There is nothing else to do here. @@ -974,16 +975,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { startStopProblemMatcher.dispose(); }, 100); if (!processStartedSignaled && terminal) { - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId!)); processStartedSignaled = true; } - this.fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode ?? undefined)); - if (this.busyTasks[mapKey]) { - delete this.busyTasks[mapKey]; + this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode ?? undefined)); + if (this._busyTasks[mapKey]) { + delete this._busyTasks[mapKey]; } - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); - this.fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task)); + this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); resolve({ exitCode: exitCode ?? undefined }); }); }); @@ -991,26 +992,26 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const showProblemPanel = task.command.presentation && (task.command.presentation.revealProblems === RevealProblemKind.Always); if (showProblemPanel) { - this.viewsService.openView(Markers.MARKERS_VIEW_ID); + this._viewsService.openView(Markers.MARKERS_VIEW_ID); } else if (task.command.presentation && (task.command.presentation.reveal === RevealKind.Always)) { - this.terminalService.setActiveInstance(terminal); - this.terminalGroupService.showPanel(task.command.presentation.focus); + this._terminalService.setActiveInstance(terminal); + this._terminalGroupService.showPanel(task.command.presentation.focus); } - this.activeTasks[task.getMapKey()] = { terminal, task, promise }; - this.fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._activeTasks[task.getMapKey()] = { terminal, task, promise }; + this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); return promise; } - private createTerminalName(task: CustomTask | ContributedTask): string { - const needsFolderQualification = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; + private _createTerminalName(task: CustomTask | ContributedTask): string { + const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; return needsFolderQualification ? task.getQualifiedLabel() : (task.configurationProperties.name || ''); } - private async createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): Promise { + private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): Promise { let shellLaunchConfig: IShellLaunchConfig; const isShellCommand = task.command.runtime === RuntimeType.Shell; - const needsFolderQualification = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; - const terminalName = this.createTerminalName(task); + const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; + const terminalName = this._createTerminalName(task); const type = 'Task'; const originalCommand = task.command.name; if (isShellCommand) { @@ -1021,10 +1022,10 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { case Platform.Platform.Linux: default: os = Platform.OperatingSystem.Linux; break; } - const defaultProfile = await this.terminalProfileResolverService.getDefaultProfile({ + const defaultProfile = await this._terminalProfileResolverService.getDefaultProfile({ allowAutomationShell: true, os, - remoteAuthority: this.environmentService.remoteAuthority + remoteAuthority: this._environmentService.remoteAuthority }); shellLaunchConfig = { name: terminalName, @@ -1044,11 +1045,11 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (shellOptions.executable !== shellLaunchConfig.executable) { shellLaunchConfig.args = undefined; } - shellLaunchConfig.executable = await this.resolveVariable(variableResolver, shellOptions.executable); + shellLaunchConfig.executable = await this._resolveVariable(variableResolver, shellOptions.executable); shellSpecified = true; } if (shellOptions.args) { - shellLaunchConfig.args = await this.resolveVariables(variableResolver, shellOptions.args.slice()); + shellLaunchConfig.args = await this._resolveVariables(variableResolver, shellOptions.args.slice()); } } if (shellLaunchConfig.args === undefined) { @@ -1056,13 +1057,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } const shellArgs = Array.isArray(shellLaunchConfig.args!) ? shellLaunchConfig.args!.slice(0) : [shellLaunchConfig.args!]; const toAdd: string[] = []; - const basename = path.posix.basename((await this.pathService.fileURI(shellLaunchConfig.executable!)).path).toLowerCase(); - const commandLine = this.buildShellCommandLine(platform, basename, shellOptions, command, originalCommand, args); + const basename = path.posix.basename((await this._pathService.fileURI(shellLaunchConfig.executable!)).path).toLowerCase(); + const commandLine = this._buildShellCommandLine(platform, basename, shellOptions, command, originalCommand, args); let windowsShellArgs: boolean = false; if (platform === Platform.Platform.Windows) { windowsShellArgs = true; // If we don't have a cwd, then the terminal uses the home dir. - const userHome = await this.pathService.userHome(); + const userHome = await this._pathService.userHome(); if (basename === 'cmd.exe' && ((options.cwd && isUNC(options.cwd)) || (!options.cwd && isUNC(userHome.fsPath)))) { return undefined; } @@ -1089,7 +1090,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { // Under Mac remove -l to not start it as a login shell. if (platform === Platform.Platform.Mac) { // Background on -l on osx https://github.com/microsoft/vscode/issues/107563 - const osxShellArgs = this.configurationService.inspect(TerminalSettingId.ShellArgsMacOs); + const osxShellArgs = this._configurationService.inspect(TerminalSettingId.ShellArgsMacOs); if ((osxShellArgs.user === undefined) && (osxShellArgs.userLocal === undefined) && (osxShellArgs.userLocalValue === undefined) && (osxShellArgs.userRemote === undefined) && (osxShellArgs.userRemoteValue === undefined) && (osxShellArgs.userValue === undefined) && (osxShellArgs.workspace === undefined) @@ -1104,7 +1105,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { toAdd.push('-c'); } } - const combinedShellArgs = this.addAllArgument(toAdd, shellArgs); + const combinedShellArgs = this._addAllArgument(toAdd, shellArgs); combinedShellArgs.push(commandLine); shellLaunchConfig.args = windowsShellArgs ? combinedShellArgs.join(' ') : combinedShellArgs; if (task.command.presentation && task.command.presentation.echo) { @@ -1117,7 +1118,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } else { const commandExecutable = (task.command.runtime !== RuntimeType.CustomExecution) ? CommandString.value(command) : undefined; const executable = !isShellCommand - ? await this.resolveVariable(variableResolver, await this.resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}')) + ? await this._resolveVariable(variableResolver, await this._resolveVariable(variableResolver, '${' + TerminalTaskSystem.ProcessVarName + '}')) : commandExecutable; // When we have a process task there is no need to quote arguments. So we go ahead and take the string value. @@ -1154,7 +1155,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } // This must be normalized to the OS - shellLaunchConfig.cwd = isUNC(cwd) ? cwd : resources.toLocalResource(URI.from({ scheme: Schemas.file, path: cwd }), this.environmentService.remoteAuthority, this.pathService.defaultUriScheme); + shellLaunchConfig.cwd = isUNC(cwd) ? cwd : resources.toLocalResource(URI.from({ scheme: Schemas.file, path: cwd }), this._environmentService.remoteAuthority, this._pathService.defaultUriScheme); } if (options.env) { if (shellLaunchConfig.env) { @@ -1168,7 +1169,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return shellLaunchConfig; } - private addAllArgument(shellCommandArgs: string[], configuredShellArgs: string[]): string[] { + private _addAllArgument(shellCommandArgs: string[], configuredShellArgs: string[]): string[] { const combinedShellArgs: string[] = Objects.deepClone(configuredShellArgs); shellCommandArgs.forEach(element => { const shouldAddShellCommandArg = configuredShellArgs.every((arg, index) => { @@ -1186,31 +1187,31 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return combinedShellArgs; } - private async doCreateTerminal(group: string | undefined, launchConfigs: IShellLaunchConfig): Promise { + private async _doCreateTerminal(group: string | undefined, launchConfigs: IShellLaunchConfig): Promise { if (group) { // Try to find an existing terminal to split. // Even if an existing terminal is found, the split can fail if the terminal width is too small. - for (const terminal of values(this.terminals)) { + for (const terminal of values(this._terminals)) { if (terminal.group === group) { - this.logService.trace(`Found terminal to split for group ${group}`); + this._logService.trace(`Found terminal to split for group ${group}`); const originalInstance = terminal.terminal; - const result = await this.terminalService.createTerminal({ location: { parentTerminal: originalInstance }, config: launchConfigs }); + const result = await this._terminalService.createTerminal({ location: { parentTerminal: originalInstance }, config: launchConfigs }); if (result) { return result; } } } - this.logService.trace(`No terminal found to split for group ${group}`); + this._logService.trace(`No terminal found to split for group ${group}`); } // Either no group is used, no terminal with the group exists or splitting an existing terminal failed. - const createdTerminal = await this.terminalService.createTerminal({ location: TerminalLocation.Panel, config: launchConfigs }); - this.logService.trace('Created a new task terminal'); + const createdTerminal = await this._terminalService.createTerminal({ location: TerminalLocation.Panel, config: launchConfigs }); + this._logService.trace('Created a new task terminal'); return createdTerminal; } - private async createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise<[ITerminalInstance | undefined, TaskError | undefined]> { + private async _createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver, workspaceFolder: IWorkspaceFolder | undefined): Promise<[ITerminalInstance | undefined, TaskError | undefined]> { const platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform; - const options = await this.resolveOptions(resolver, task.command.options); + const options = await this._resolveOptions(resolver, task.command.options); const presentationOptions = task.command.presentation; let waitOnExit: boolean | string = false; @@ -1237,19 +1238,19 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let launchConfigs: IShellLaunchConfig | undefined; if (task.command.runtime === RuntimeType.CustomExecution) { - this.currentTask.shellLaunchConfig = launchConfigs = { - customPtyImplementation: (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this.terminalService), + this._currentTask.shellLaunchConfig = launchConfigs = { + customPtyImplementation: (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService), waitOnExit, - name: this.createTerminalName(task), + name: this._createTerminalName(task), initialText: task.command.presentation && task.command.presentation.echo ? `\x1b[1m> Executing task: ${task._label} <\x1b[0m\n` : undefined, isFeatureTerminal: true }; } else { - const resolvedResult: { command: CommandString; args: CommandString[] } = await this.resolveCommandAndArgs(resolver, task.command); + const resolvedResult: { command: CommandString; args: CommandString[] } = await this._resolveCommandAndArgs(resolver, task.command); command = resolvedResult.command; args = resolvedResult.args; - this.currentTask.shellLaunchConfig = launchConfigs = await this.createShellLaunchConfig(task, workspaceFolder, resolver, platform, options, command, args, waitOnExit); + this._currentTask.shellLaunchConfig = launchConfigs = await this._createShellLaunchConfig(task, workspaceFolder, resolver, platform, options, command, args, waitOnExit); if (launchConfigs === undefined) { return [undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)]; } @@ -1262,28 +1263,28 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const taskKey = task.getMapKey(); let terminalToReuse: ITerminalData | undefined; if (prefersSameTerminal) { - const terminalId = this.sameTaskTerminals[taskKey]; + const terminalId = this._sameTaskTerminals[taskKey]; if (terminalId) { - terminalToReuse = this.terminals[terminalId]; - delete this.sameTaskTerminals[taskKey]; + terminalToReuse = this._terminals[terminalId]; + delete this._sameTaskTerminals[taskKey]; } } else if (allowsSharedTerminal) { // Always allow to reuse the terminal previously used by the same task. - let terminalId = this.idleTaskTerminals.remove(taskKey); + let terminalId = this._idleTaskTerminals.remove(taskKey); if (!terminalId) { // There is no idle terminal which was used by the same task. // Search for any idle terminal used previously by a task of the same group // (or, if the task has no group, a terminal used by a task without group). - for (const taskId of this.idleTaskTerminals.keys()) { - const idleTerminalId = this.idleTaskTerminals.get(taskId)!; - if (idleTerminalId && this.terminals[idleTerminalId] && this.terminals[idleTerminalId].group === group) { - terminalId = this.idleTaskTerminals.remove(taskId); + for (const taskId of this._idleTaskTerminals.keys()) { + const idleTerminalId = this._idleTaskTerminals.get(taskId)!; + if (idleTerminalId && this._terminals[idleTerminalId] && this._terminals[idleTerminalId].group === group) { + terminalId = this._idleTaskTerminals.remove(taskId); break; } } } if (terminalId) { - terminalToReuse = this.terminals[terminalId]; + terminalToReuse = this._terminals[terminalId]; } } if (terminalToReuse) { @@ -1297,38 +1298,38 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (task.command.presentation && task.command.presentation.clear) { terminalToReuse.terminal.clearBuffer(); } - this.terminals[terminalToReuse.terminal.instanceId.toString()].lastTask = taskKey; + this._terminals[terminalToReuse.terminal.instanceId.toString()].lastTask = taskKey; return [terminalToReuse.terminal, undefined]; } - this.terminalCreationQueue = this.terminalCreationQueue.then(() => this.doCreateTerminal(group, launchConfigs!)); - const result: ITerminalInstance = (await this.terminalCreationQueue)!; + this._terminalCreationQueue = this._terminalCreationQueue.then(() => this._doCreateTerminal(group, launchConfigs!)); + const result: ITerminalInstance = (await this._terminalCreationQueue)!; const terminalKey = result.instanceId.toString(); result.onDisposed((terminal) => { - const terminalData = this.terminals[terminalKey]; + const terminalData = this._terminals[terminalKey]; if (terminalData) { - delete this.terminals[terminalKey]; - delete this.sameTaskTerminals[terminalData.lastTask]; - this.idleTaskTerminals.delete(terminalData.lastTask); + delete this._terminals[terminalKey]; + delete this._sameTaskTerminals[terminalData.lastTask]; + this._idleTaskTerminals.delete(terminalData.lastTask); // Delete the task now as a work around for cases when the onExit isn't fired. // This can happen if the terminal wasn't shutdown with an "immediate" flag and is expected. // For correct terminal re-use, the task needs to be deleted immediately. // Note that this shouldn't be a problem anymore since user initiated terminal kills are now immediate. const mapKey = task.getMapKey(); - this.removeFromActiveTasks(task); - if (this.busyTasks[mapKey]) { - delete this.busyTasks[mapKey]; + this._removeFromActiveTasks(task); + if (this._busyTasks[mapKey]) { + delete this._busyTasks[mapKey]; } } }); - this.terminals[terminalKey] = { terminal: result, lastTask: taskKey, group }; + this._terminals[terminalKey] = { terminal: result, lastTask: taskKey, group }; return [result, undefined]; } - private buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: IShellConfiguration | undefined, command: CommandString, originalCommand: CommandString | undefined, args: CommandString[]): string { + private _buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: IShellConfiguration | undefined, command: CommandString, originalCommand: CommandString | undefined, args: CommandString[]): string { const basename = path.parse(shellExecutable).name.toLowerCase(); - const shellQuoteOptions = this.getQuotingOptions(basename, shellOptions, platform); + const shellQuoteOptions = this._getQuotingOptions(basename, shellOptions, platform); function needsQuotes(value: string): boolean { if (value.length >= 2) { @@ -1425,18 +1426,18 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return commandLine; } - private getQuotingOptions(shellBasename: string, shellOptions: IShellConfiguration | undefined, platform: Platform.Platform): IShellQuotingOptions { + private _getQuotingOptions(shellBasename: string, shellOptions: IShellConfiguration | undefined, platform: Platform.Platform): IShellQuotingOptions { if (shellOptions && shellOptions.quoting) { return shellOptions.quoting; } - return TerminalTaskSystem.shellQuotes[shellBasename] || TerminalTaskSystem.osShellQuotes[Platform.PlatformToString(platform)]; + return TerminalTaskSystem._shellQuotes[shellBasename] || TerminalTaskSystem._osShellQuotes[Platform.PlatformToString(platform)]; } - private collectTaskVariables(variables: Set, task: CustomTask | ContributedTask): void { + private _collectTaskVariables(variables: Set, task: CustomTask | ContributedTask): void { if (task.command && task.command.name) { - this.collectCommandVariables(variables, task.command, task); + this._collectCommandVariables(variables, task.command, task); } - this.collectMatcherVariables(variables, task.configurationProperties.problemMatchers); + this._collectMatcherVariables(variables, task.configurationProperties.problemMatchers); if (task.command.runtime === RuntimeType.CustomExecution && (CustomTask.is(task) || ContributedTask.is(task))) { let definition: any; @@ -1447,23 +1448,23 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { delete definition._key; delete definition.type; } - this.collectDefinitionVariables(variables, definition); + this._collectDefinitionVariables(variables, definition); } } - private collectDefinitionVariables(variables: Set, definition: any): void { + private _collectDefinitionVariables(variables: Set, definition: any): void { if (Types.isString(definition)) { - this.collectVariables(variables, definition); + this._collectVariables(variables, definition); } else if (Types.isArray(definition)) { - definition.forEach((element: any) => this.collectDefinitionVariables(variables, element)); + definition.forEach((element: any) => this._collectDefinitionVariables(variables, element)); } else if (Types.isObject(definition)) { for (const key in definition) { - this.collectDefinitionVariables(variables, definition[key]); + this._collectDefinitionVariables(variables, definition[key]); } } } - private collectCommandVariables(variables: Set, command: ICommandConfiguration, task: CustomTask | ContributedTask): void { + private _collectCommandVariables(variables: Set, command: ICommandConfiguration, task: CustomTask | ContributedTask): void { // The custom execution should have everything it needs already as it provided // the callback. if (command.runtime === RuntimeType.CustomExecution) { @@ -1473,9 +1474,9 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (command.name === undefined) { throw new Error('Command name should never be undefined here.'); } - this.collectVariables(variables, command.name); + this._collectVariables(variables, command.name); if (command.args) { - command.args.forEach(arg => this.collectVariables(variables, arg)); + command.args.forEach(arg => this._collectVariables(variables, arg)); } // Try to get a scope. const scope = (task._source).scope; @@ -1485,29 +1486,29 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (command.options) { const options = command.options; if (options.cwd) { - this.collectVariables(variables, options.cwd); + this._collectVariables(variables, options.cwd); } const optionsEnv = options.env; if (optionsEnv) { Object.keys(optionsEnv).forEach((key) => { const value: any = optionsEnv[key]; if (Types.isString(value)) { - this.collectVariables(variables, value); + this._collectVariables(variables, value); } }); } if (options.shell) { if (options.shell.executable) { - this.collectVariables(variables, options.shell.executable); + this._collectVariables(variables, options.shell.executable); } if (options.shell.args) { - options.shell.args.forEach(arg => this.collectVariables(variables, arg)); + options.shell.args.forEach(arg => this._collectVariables(variables, arg)); } } } } - private collectMatcherVariables(variables: Set, values: Array | undefined): void { + private _collectMatcherVariables(variables: Set, values: Array | undefined): void { if (values === undefined || values === null || values.length === 0) { return; } @@ -1523,12 +1524,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { matcher = value; } if (matcher && matcher.filePrefix) { - this.collectVariables(variables, matcher.filePrefix); + this._collectVariables(variables, matcher.filePrefix); } }); } - private collectVariables(variables: Set, value: string | CommandString): void { + private _collectVariables(variables: Set, value: string | CommandString): void { const string: string = Types.isString(value) ? value : value.value; const r = /\$\{(.*?)\}/g; let matches: RegExpExecArray | null; @@ -1540,21 +1541,21 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } while (matches); } - private async resolveCommandAndArgs(resolver: VariableResolver, commandConfig: ICommandConfiguration): Promise<{ command: CommandString; args: CommandString[] }> { + private async _resolveCommandAndArgs(resolver: VariableResolver, commandConfig: ICommandConfiguration): Promise<{ command: CommandString; args: CommandString[] }> { // First we need to use the command args: let args: CommandString[] = commandConfig.args ? commandConfig.args.slice() : []; - args = await this.resolveVariables(resolver, args); - const command: CommandString = await this.resolveVariable(resolver, commandConfig.name); + args = await this._resolveVariables(resolver, args); + const command: CommandString = await this._resolveVariable(resolver, commandConfig.name); return { command, args }; } - private async resolveVariables(resolver: VariableResolver, value: string[]): Promise; - private async resolveVariables(resolver: VariableResolver, value: CommandString[]): Promise; - private async resolveVariables(resolver: VariableResolver, value: CommandString[]): Promise { - return Promise.all(value.map(s => this.resolveVariable(resolver, s))); + private async _resolveVariables(resolver: VariableResolver, value: string[]): Promise; + private async _resolveVariables(resolver: VariableResolver, value: CommandString[]): Promise; + private async _resolveVariables(resolver: VariableResolver, value: CommandString[]): Promise { + return Promise.all(value.map(s => this._resolveVariable(resolver, s))); } - private async resolveMatchers(resolver: VariableResolver, values: Array | undefined): Promise { + private async _resolveMatchers(resolver: VariableResolver, values: Array | undefined): Promise { if (values === undefined || values === null || values.length === 0) { return []; } @@ -1571,7 +1572,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { matcher = value; } if (!matcher) { - this.appendOutput(nls.localize('unknownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + this._appendOutput(nls.localize('unknownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); continue; } const taskSystemInfo: ITaskSystemInfo | undefined = resolver.taskSystemInfo; @@ -1585,7 +1586,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { copy.uriProvider = taskSystemInfo.uriProvider; } if (hasFilePrefix) { - copy.filePrefix = await this.resolveVariable(resolver, copy.filePrefix); + copy.filePrefix = await this._resolveVariable(resolver, copy.filePrefix); } result.push(copy); } @@ -1593,9 +1594,9 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return result; } - private async resolveVariable(resolver: VariableResolver, value: string | undefined): Promise; - private async resolveVariable(resolver: VariableResolver, value: CommandString | undefined): Promise; - private async resolveVariable(resolver: VariableResolver, value: CommandString | undefined): Promise { + private async _resolveVariable(resolver: VariableResolver, value: string | undefined): Promise; + private async _resolveVariable(resolver: VariableResolver, value: CommandString | undefined): Promise; + private async _resolveVariable(resolver: VariableResolver, value: CommandString | undefined): Promise { // TODO@Dirk Task.getWorkspaceFolder should return a WorkspaceFolder that is defined in workspace.ts if (Types.isString(value)) { return resolver.resolve(value); @@ -1609,25 +1610,25 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } - private async resolveOptions(resolver: VariableResolver, options: CommandOptions | undefined): Promise { + private async _resolveOptions(resolver: VariableResolver, options: CommandOptions | undefined): Promise { if (options === undefined || options === null) { let cwd: string | undefined; try { - cwd = await this.resolveVariable(resolver, '${workspaceFolder}'); + cwd = await this._resolveVariable(resolver, '${workspaceFolder}'); } catch (e) { // No workspace } return { cwd }; } const result: CommandOptions = Types.isString(options.cwd) - ? { cwd: await this.resolveVariable(resolver, options.cwd) } - : { cwd: await this.resolveVariable(resolver, '${workspaceFolder}') }; + ? { cwd: await this._resolveVariable(resolver, options.cwd) } + : { cwd: await this._resolveVariable(resolver, '${workspaceFolder}') }; if (options.env) { result.env = Object.create(null); for (const key of Object.keys(options.env)) { const value: any = options.env![key]; if (Types.isString(value)) { - result.env![key] = await this.resolveVariable(resolver, value); + result.env![key] = await this._resolveVariable(resolver, value); } else { result.env![key] = value.toString(); } @@ -1636,7 +1637,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return result; } - private static WellKnowCommands: IStringDictionary = { + static WellKnownCommands: IStringDictionary = { 'ant': true, 'cmake': true, 'eslint': true, @@ -1663,14 +1664,14 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (index !== -1) { result = result.substring(index + 1); } - if (TerminalTaskSystem.WellKnowCommands[result]) { + if (TerminalTaskSystem.WellKnownCommands[result]) { return result; } return 'other'; } - private appendOutput(output: string): void { - const outputChannel = this.outputService.getChannel(this.outputChannelId); + private _appendOutput(output: string): void { + const outputChannel = this._outputService.getChannel(this._outputChannelId); if (outputChannel) { outputChannel.append(output); } diff --git a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts index 3738cd5362b..ac7efc250fd 100644 --- a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts @@ -119,11 +119,11 @@ export class TaskService extends AbstractTaskService { this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(), 'veto.tasks'))); } - protected getTaskSystem(): ITaskSystem { + protected _getTaskSystem(): ITaskSystem { if (this._taskSystem) { return this._taskSystem; } - this._taskSystem = this.createTerminalTaskSystem(); + this._taskSystem = this._createTerminalTaskSystem(); this._taskSystemListener = this._taskSystem!.onDidStateChange((event) => { if (this._taskSystem) { this._taskRunningState.set(this._taskSystem.isActiveSync()); @@ -133,8 +133,8 @@ export class TaskService extends AbstractTaskService { return this._taskSystem; } - protected computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise { - const { config, hasParseErrors } = this.getConfiguration(workspaceFolder); + protected _computeLegacyConfiguration(workspaceFolder: IWorkspaceFolder): Promise { + const { config, hasParseErrors } = this._getConfiguration(workspaceFolder); if (hasParseErrors) { return Promise.resolve({ workspaceFolder: workspaceFolder, hasErrors: true, config: undefined }); } @@ -145,7 +145,7 @@ export class TaskService extends AbstractTaskService { } } - protected versionAndEngineCompatible(filter?: ITaskFilter): boolean { + protected _versionAndEngineCompatible(filter?: ITaskFilter): boolean { const range = filter && filter.version ? filter.version : undefined; const engine = this.executionEngine; @@ -169,7 +169,7 @@ export class TaskService extends AbstractTaskService { if (this._taskSystem.canAutoTerminate()) { terminatePromise = Promise.resolve({ confirmed: true }); } else { - terminatePromise = this.dialogService.confirm({ + terminatePromise = this._dialogService.confirm({ message: nls.localize('TaskSystem.runningTask', 'There is a task running. Do you want to terminate it?'), primaryButton: nls.localize({ key: 'TaskSystem.terminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task"), type: 'question' @@ -191,10 +191,10 @@ export class TaskService extends AbstractTaskService { } if (success) { this._taskSystem = undefined; - this.disposeTaskSystemListeners(); + this._disposeTaskSystemListeners(); return false; // no veto } else if (code && code === TerminateResponseCode.ProcessNotFound) { - return this.dialogService.confirm({ + return this._dialogService.confirm({ message: nls.localize('TaskSystem.noProcess', 'The launched task doesn\'t exist anymore. If the task spawned background processes exiting VS Code might result in orphaned processes. To avoid this start the last background process with a wait flag.'), primaryButton: nls.localize({ key: 'TaskSystem.exitAnyways', comment: ['&& denotes a mnemonic'] }, "&&Exit Anyways"), type: 'info' diff --git a/src/vs/workbench/contrib/terminal/browser/links/links.ts b/src/vs/workbench/contrib/terminal/browser/links/links.ts index a93a42c4b49..ad7f84b6141 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/links.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/links.ts @@ -79,30 +79,30 @@ export const enum TerminalBuiltinLinkType { /** * The link is validated to be a file on the file system and will open an editor. */ - LocalFile, + LocalFile = 'LocalFile', /** * The link is validated to be a folder on the file system and is outside the workspace. It will * reveal the folder within the explorer. */ - LocalFolderOutsideWorkspace, + LocalFolderOutsideWorkspace = 'LocalFolderOutsideWorkspace', /** * The link is validated to be a folder on the file system and is within the workspace and will * reveal the folder within the explorer. */ - LocalFolderInWorkspace, + LocalFolderInWorkspace = 'LocalFolderInWorkspace', /** * A low confidence link which will search for the file in the workspace. If there is a single * match, it will open the file; otherwise, it will present the matches in a quick pick. */ - Search, + Search = 'Search', /** * A link whose text is a valid URI. */ - Url + Url = 'Url' } export interface ITerminalExternalLinkType { diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts index ae9f9526f16..42b01b307bf 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkManager.ts @@ -86,7 +86,7 @@ export class TerminalLinkManager extends DisposableStore { this._openers.set(TerminalBuiltinLinkType.LocalFile, localFileOpener); this._openers.set(TerminalBuiltinLinkType.LocalFolderInWorkspace, localFolderInWorkspaceOpener); this._openers.set(TerminalBuiltinLinkType.LocalFolderOutsideWorkspace, this._instantiationService.createInstance(TerminalLocalFolderOutsideWorkspaceLinkOpener)); - this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, localFileOpener, localFolderInWorkspaceOpener, this._processManager.os || OS)); + this._openers.set(TerminalBuiltinLinkType.Search, this._instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, this._processManager.getInitialCwd(), localFileOpener, localFolderInWorkspaceOpener, this._processManager.os || OS)); this._openers.set(TerminalBuiltinLinkType.Url, this._instantiationService.createInstance(TerminalUrlLinkOpener, !!this._processManager.remoteAuthority)); this._registerStandardLinkProviders(); diff --git a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts index fed1eb2491b..835b57f934e 100644 --- a/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts +++ b/src/vs/workbench/contrib/terminal/browser/links/terminalLinkOpeners.ts @@ -125,6 +125,7 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener { constructor( private readonly _capabilities: ITerminalCapabilityStore, + private readonly _initialCwd: Promise, private readonly _localFileOpener: TerminalLocalFileLinkOpener, private readonly _localFolderInWorkspaceOpener: TerminalLocalFolderInWorkspaceLinkOpener, private readonly _os: OperatingSystem, @@ -182,10 +183,20 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener { } private async _getExactMatch(sanitizedLink: string): Promise { + // Make the link relative to the cwd if it isn't absolute + const pathModule = osPathModule(this._os); + const isAbsolute = pathModule.isAbsolute(sanitizedLink); + let absolutePath: string | undefined = isAbsolute ? sanitizedLink : undefined; + const initialCwd = await this._initialCwd; + if (!isAbsolute && initialCwd.length > 0) { + absolutePath = pathModule.join(initialCwd, sanitizedLink); + } + + // Try open as an absolute link let resourceMatch: IResourceMatch | undefined; - if (osPathModule(this._os).isAbsolute(sanitizedLink)) { + if (absolutePath) { + const slashNormalizedPath = this._os === OperatingSystem.Windows ? absolutePath.replace(/\\/g, '/') : absolutePath; const scheme = this._workbenchEnvironmentService.remoteAuthority ? Schemas.vscodeRemote : Schemas.file; - const slashNormalizedPath = this._os === OperatingSystem.Windows ? sanitizedLink.replace(/\\/g, '/') : sanitizedLink; const uri = URI.from({ scheme, path: slashNormalizedPath }); try { const fileStat = await this._fileService.stat(uri); @@ -194,6 +205,8 @@ export class TerminalSearchLinkOpener implements ITerminalLinkOpener { // File or dir doesn't exist, continue on } } + + // Search the workspace if an exact match based on the absolute path was not found if (!resourceMatch) { const results = await this._searchService.fileSearch( this._fileQueryBuilder.file(this._workspaceContextService.getWorkspace().folders, { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index d47d4fbad5d..65edb12ae16 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -669,6 +669,12 @@ export interface ITerminalInstance { */ clearSelection(): void; + /** + * When the panel is hidden or a terminal in the editor area becomes inactive, reset the focus context key + * to avoid issues like #147180. + */ + resetFocusContextKey(): void; + /** * Select all text in the terminal. */ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts index 77e38655595..aec68ee5d91 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts @@ -101,6 +101,14 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor } } })); + this._register(this._editorService.onDidActiveEditorChange(() => { + const instance = this._editorService.activeEditor instanceof TerminalEditorInput ? this._editorService.activeEditor : undefined; + if (!instance) { + for (const instance of this.instances) { + instance.resetFocusContextKey(); + } + } + })); } private _getActiveTerminalEditors(): EditorInput[] { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 2a7e0b6b7e0..ef333b0d323 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -385,7 +385,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); this._fixedRows = _shellLaunchConfig.attachPersistentProcess?.fixedDimensions?.rows; this._fixedCols = _shellLaunchConfig.attachPersistentProcess?.fixedDimensions?.cols; - this._icon = _shellLaunchConfig.attachPersistentProcess?.icon || _shellLaunchConfig.icon; // the resource is already set when it's been moved from another window this._resource = resource || getTerminalUri(this._workspaceContextService.getWorkspace().id, this.instanceId, this.title); @@ -431,10 +430,12 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Resolve just the icon ahead of time so that it shows up immediately in the tabs. This is // disabled in remote because this needs to be sync and the OS may differ on the remote // which would result in the wrong profile being selected and the wrong icon being - // permanently attached to the terminal. + // permanently attached to the terminal. This also doesn't work when the default profile + // setting is set to null, that's handled after the process is created. if (!this.shellLaunchConfig.executable && !workbenchEnvironmentService.remoteAuthority) { this._terminalProfileResolverService.resolveIcon(this._shellLaunchConfig, OS); } + this._icon = _shellLaunchConfig.attachPersistentProcess?.icon || _shellLaunchConfig.icon; // When a custom pty is used set the name immediately so it gets passed over to the exthost // and is available when Pseudoterminal.open fires. @@ -1150,6 +1151,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } + resetFocusContextKey(): void { + this._terminalFocusContextKey.reset(); + } + private _initDragAndDrop(container: HTMLElement) { this._dndObserver?.dispose(); const dndController = this._instantiationService.createInstance(TerminalInstanceDragAndDropController, container); @@ -1550,8 +1555,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this.xterm?.raw.resize(this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows); } - const hadIcon = !!this.shellLaunchConfig.icon; - + const originalIcon = this.shellLaunchConfig.icon; await this._processManager.createProcess(this._shellLaunchConfig, this._cols || Constants.DefaultCols, this._rows || Constants.DefaultRows, this._accessibilityService.isScreenReaderOptimized()).then(error => { if (error) { this._onProcessExit(error, error.code === ShellIntegrationExitCode); @@ -1560,7 +1564,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (this.xterm?.shellIntegration) { this.capabilities.add(this.xterm?.shellIntegration.capabilities); } - if (!hadIcon && this.shellLaunchConfig.icon || this.shellLaunchConfig.color) { + if (originalIcon !== this.shellLaunchConfig.icon || this.shellLaunchConfig.color) { + this._icon = this._shellLaunchConfig.attachPersistentProcess?.icon || this._shellLaunchConfig.icon; this._onIconChanged.fire(this); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 945cfd847d9..fb3dd56a967 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -175,6 +175,9 @@ export class TerminalViewPane extends ViewPane { this._terminalGroupService.activeGroup?.setVisible(visible); } } else { + for (const instance of this._terminalGroupService.instances) { + instance.resetFocusContextKey(); + } this._terminalGroupService.activeGroup?.setVisible(false); } })); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index cc3510f39dc..2b346c110b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -117,12 +117,14 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { this.clearDecorations(); } + private _clearPlaceholder(): void { + this._placeholderDecoration?.dispose(); + this._placeholderDecoration = undefined; + } + public clearDecorations(): void { - if (this._placeholderDecoration) { - this._placeholderDecoration.marker.dispose(); - this._placeholderDecoration.dispose(); - this._placeholderDecoration = undefined; - } + this._placeholderDecoration?.marker.dispose(); + this._clearPlaceholder(); for (const value of this._decorations.values()) { value.decoration.dispose(); dispose(value.disposables); @@ -183,10 +185,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { } })); // Current command invalidated - this._commandDetectionListeners.push(capability.onCurrentCommandInvalidated(() => { - this._placeholderDecoration?.dispose(); - this._placeholderDecoration = undefined; - })); + this._commandDetectionListeners.push(capability.onCurrentCommandInvalidated(() => this._clearPlaceholder())); } activate(terminal: Terminal): void { @@ -202,7 +201,7 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { throw new Error(`cannot add a decoration for a command ${JSON.stringify(command)} with no marker`); } - this._placeholderDecoration?.dispose(); + this._clearPlaceholder(); let color = command.exitCode === undefined ? defaultColor : command.exitCode ? errorColor : successColor; if (color && typeof color !== 'string') { color = color.toString(); @@ -223,7 +222,6 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { } if (beforeCommandExecution && !this._placeholderDecoration) { this._placeholderDecoration = decoration; - this._placeholderDecoration.onDispose(() => this._placeholderDecoration = undefined); } else if (!this._decorations.get(decoration.marker.id)) { decoration.onDispose(() => this._decorations.delete(decoration.marker.id)); this._decorations.set(decoration.marker.id, diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts index e37c554e6ef..a7559d081f0 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkManager.test.ts @@ -81,7 +81,11 @@ suite('TerminalLinkManager', () => { instantiationService.stub(IViewDescriptorService, viewDescriptorService); xterm = new Terminal({ cols: 80, rows: 30 }); - linkManager = instantiationService.createInstance(TestLinkManager, xterm, upcastPartial({}), { + linkManager = instantiationService.createInstance(TestLinkManager, xterm, upcastPartial({ + async getInitialCwd() { + return ''; + } + }), { get(capability: T): ITerminalCapabilityImplMap[T] | undefined { return undefined; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index a67d0f4b0d2..dcbdef59e2c 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -97,11 +97,30 @@ suite('Workbench - TerminalLinkOpeners', () => { capabilities.add(TerminalCapability.CommandDetection, commandDetection); }); + test('should open single exact match against cwd when searching if it exists', async () => { + localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Linux); + const localFolderOpener = instantiationService.createInstance(TerminalLocalFolderInWorkspaceLinkOpener); + opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve('/initial/cwd'), localFileOpener, localFolderOpener, OperatingSystem.Linux); + fileService.setFiles([ + URI.from({ scheme: Schemas.file, path: '/initial/cwd/foo/bar.txt' }), + URI.from({ scheme: Schemas.file, path: '/initial/cwd/foo2/bar.txt' }) + ]); + await opener.open({ + text: 'foo/bar.txt', + bufferRange: { start: { x: 1, y: 1 }, end: { x: 8, y: 1 } }, + type: TerminalBuiltinLinkType.Search + }); + deepStrictEqual(activationResult, { + link: 'file:///initial/cwd/foo/bar.txt', + source: 'editor' + }); + }); + suite('macOS/Linux', () => { setup(() => { localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Linux); const localFolderOpener = instantiationService.createInstance(TerminalLocalFolderInWorkspaceLinkOpener); - opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, localFileOpener, localFolderOpener, OperatingSystem.Linux); + opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve(''), localFileOpener, localFolderOpener, OperatingSystem.Linux); }); test('should apply the cwd to the link only when the file exists and cwdDetection is enabled', async () => { @@ -150,7 +169,7 @@ suite('Workbench - TerminalLinkOpeners', () => { setup(() => { localFileOpener = instantiationService.createInstance(TerminalLocalFileLinkOpener, OperatingSystem.Windows); const localFolderOpener = instantiationService.createInstance(TerminalLocalFolderInWorkspaceLinkOpener); - opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, localFileOpener, localFolderOpener, OperatingSystem.Windows); + opener = instantiationService.createInstance(TerminalSearchLinkOpener, capabilities, Promise.resolve(''), localFileOpener, localFolderOpener, OperatingSystem.Windows); }); test('should apply the cwd to the link only when the file exists and cwdDetection is enabled', async () => { diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 8e8740ea388..bafbd5e65a8 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -48,7 +48,7 @@ suite('ShellIntegrationAddon', () => { }); const instantiationService = new TestInstantiationService(); instantiationService.stub(ILogService, NullLogService); - shellIntegrationAddon = instantiationService.createInstance(TestShellIntegrationAddon); + shellIntegrationAddon = instantiationService.createInstance(TestShellIntegrationAddon, undefined, undefined); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; }); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts index eaec5127e56..a35b293a2a9 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/xtermTerminal.test.ts @@ -113,7 +113,7 @@ suite('XtermTerminal', () => { instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); configHelper = instantiationService.createInstance(TerminalConfigHelper); - xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore()); + xterm = instantiationService.createInstance(TestXtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore(), true); TestWebglAddon.shouldThrow = false; TestWebglAddon.isEnabled = false; @@ -130,7 +130,7 @@ suite('XtermTerminal', () => { [PANEL_BACKGROUND]: '#ff0000', [SIDE_BAR_BACKGROUND]: '#00ff00' })); - xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore()); + xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore(), true); strictEqual(xterm.raw.options.theme?.background, '#ff0000'); viewDescriptorService.moveTerminalToLocation(ViewContainerLocation.Sidebar); strictEqual(xterm.raw.options.theme?.background, '#00ff00'); @@ -164,7 +164,7 @@ suite('XtermTerminal', () => { 'terminal.ansiBrightCyan': '#150000', 'terminal.ansiBrightWhite': '#160000', })); - xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore()); + xterm = instantiationService.createInstance(XtermTerminal, Terminal, configHelper, 80, 30, TerminalLocation.Panel, new TerminalCapabilityStore(), true); deepStrictEqual(xterm.raw.options.theme, { background: '#000100', foreground: '#000200', diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 68f335784f7..a42283c1457 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -99,16 +99,18 @@ class MarketplaceThemesPicker { try { const installedExtensions = await this._installedExtensions; - const options = { text: `${this.marketplaceQuery} ${value}`, pageSize: 40 }; + const options = { text: `${this.marketplaceQuery} ${value}`, pageSize: 20 }; const pager = await this.extensionGalleryService.query(options, token); - for (let i = 0; i < pager.total && i < 1; i++) { + for (let i = 0; i < pager.total && i < 1; i++) { // loading multiple pages is turned of for now to avoid flickering if (token.isCancellationRequested) { break; } const nThemes = this._marketplaceThemes.length; + const gallery = i === 0 ? pager.firstPage : await pager.getPage(i, token); - const gallery = await pager.getPage(i, token); + const promises: Promise[] = []; + const promisesGalleries = []; for (let i = 0; i < gallery.length; i++) { if (token.isCancellationRequested) { break; @@ -116,12 +118,18 @@ class MarketplaceThemesPicker { const ext = gallery[i]; if (!installedExtensions.has(ext.identifier.id) && !this._marketplaceExtensions.has(ext.identifier.id)) { this._marketplaceExtensions.add(ext.identifier.id); - const themes = await this.getMarketplaceColorThemes(ext.publisher, ext.name, ext.version); - for (const theme of themes) { - this._marketplaceThemes.push({ id: theme.id, theme: theme, label: theme.label, description: `${ext.displayName} · ${ext.publisherDisplayName}`, galleryExtension: ext, buttons: [configureButton] }); - } + promises.push(this.getMarketplaceColorThemes(ext.publisher, ext.name, ext.version)); + promisesGalleries.push(ext); } } + const allThemes = await Promise.all(promises); + for (let i = 0; i < allThemes.length; i++) { + const ext = promisesGalleries[i]; + for (const theme of allThemes[i]) { + this._marketplaceThemes.push({ id: theme.id, theme: theme, label: theme.label, description: `${ext.displayName} · ${ext.publisherDisplayName}`, galleryExtension: ext, buttons: [configureButton] }); + } + } + if (nThemes !== this._marketplaceThemes.length) { this._marketplaceThemes.sort((t1, t2) => t1.label.localeCompare(t2.label)); this._onDidChange.fire(); diff --git a/src/vs/workbench/contrib/watermark/browser/media/watermark.css b/src/vs/workbench/contrib/watermark/browser/media/watermark.css index 2c45ebd4b16..29eba5b8a5f 100644 --- a/src/vs/workbench/contrib/watermark/browser/media/watermark.css +++ b/src/vs/workbench/contrib/watermark/browser/media/watermark.css @@ -23,6 +23,8 @@ text-align: center; white-space: nowrap; overflow: hidden; + /* Watermark should show even over opaque backgrounds */ + z-index: 1000; } .monaco-workbench .part.editor > .content.empty > .watermark > .watermark-box { diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts index 82ccdd7d3a9..01504f62f36 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/walkThroughPart.ts @@ -291,9 +291,7 @@ export class WalkThroughPart extends EditorPane { this.updateSizeClasses(); this.decorateContent(); this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent())); - if (input.onReady) { - input.onReady(this.content.firstElementChild as HTMLElement, store); - } + input.onReady?.(this.content.firstElementChild as HTMLElement, store); this.scrollbar.scanDomNode(); this.loadTextEditorViewState(input); this.updatedScrollPosition(); @@ -371,9 +369,7 @@ export class WalkThroughPart extends EditorPane { this.multiCursorModifier(); } })); - if (input.onReady) { - input.onReady(innerContent, store); - } + input.onReady?.(innerContent, store); this.scrollbar.scanDomNode(); this.loadTextEditorViewState(input); this.updatedScrollPosition(); diff --git a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts index b4c0e11cbeb..fe6eff44dd5 100644 --- a/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-sandbox/contextmenuService.ts @@ -83,9 +83,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService const actions = delegate.getActions(); if (actions.length) { const onHide = once(() => { - if (delegate.onHide) { - delegate.onHide(false); - } + delegate.onHide?.(false); dom.ModifierKeyEmitter.getInstance().resetKeyStatus(); this._onDidHideContextMenu.fire(); diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index 0d4818ac904..db4b01da54b 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -406,9 +406,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._logger.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`); } const callId = String(req); - if (this._cancelInvokedHandlers[callId]) { - this._cancelInvokedHandlers[callId](); - } + this._cancelInvokedHandlers[callId]?.(); } private _receiveReply(msgLength: number, req: number, value: any): void { diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 328e1917bc5..c40362672e2 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -134,9 +134,7 @@ export class SearchService extends Disposable implements ISearchService { return; } - if (onProgress) { - onProgress(item); - } + onProgress?.(item); }; const exists = await Promise.all(query.folderQueries.map(query => this.fileService.exists(query.folder))); diff --git a/src/vs/workbench/services/textfile/common/textEditorService.ts b/src/vs/workbench/services/textfile/common/textEditorService.ts index 9e40eb88f5a..c8c9050875b 100644 --- a/src/vs/workbench/services/textfile/common/textEditorService.ts +++ b/src/vs/workbench/services/textfile/common/textEditorService.ts @@ -227,9 +227,7 @@ export class TextEditorService extends Disposable implements ITextEditorService // Return early if already cached let input = this.editorInputCache.get(resource); if (input) { - if (cachedFn) { - cachedFn(input); - } + cachedFn?.(input); return input; } diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 88f7fdf67eb..730f0ad2014 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -10110,9 +10110,11 @@ declare module 'vscode' { /** * Allows iteration through the data transfer items. + * * @param callbackfn Callback for iteration through the data transfer items. + * @param thisArg The `this` context used when invoking the handler function. */ - forEach(callbackfn: (value: DataTransferItem, key: string) => void): void; + forEach(callbackfn: (value: DataTransferItem, key: string) => void, thisArg?: unknown): void; } /** diff --git a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts b/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts index a202f9a9ed6..68df3c07f4a 100644 --- a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts +++ b/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts @@ -7,15 +7,6 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/147481 - enum DataTransferItemKind { - String = 1, - File = 2, - } - - interface DataTransferItem { - readonly kind: DataTransferItemKind; - } - /** * A file associated with a {@linkcode DataTransferItem}. */ @@ -38,13 +29,42 @@ declare module 'vscode' { data(): Thenable; } + /** + * Identifies the kind of a {@link DataTransferItem}. May either be {@linkcode DataTransferItemKind.String String} or {@linkcode DataTransferItemKind.File File}. + */ + enum DataTransferItemKind { + + /** + * The {@link DataTransferItem} is a string. + * + * Use {@link DataTransferItem.asString} to get a string representation of this item or + * {@link DataTransferItem.value} to access the original value if them item was created + * by an extension. + */ + String = 1, + + /** + * The {@link DataTransferItem} is for a file. + * + * Use {@link DataTransferItem.asFile} to get the underlying file data. + */ + File = 2, + } + export interface DataTransferItem { + + /** + * The kind of the {@link DataTransferItem}. + */ + readonly kind: DataTransferItemKind; + /** * Try getting the file associated with this data transfer item. * * Note that the file object is only valid for the scope of the drag and drop operation. * - * @returns The file for the data transfer or `undefined` if the item is not a file. + * @returns The file for the data transfer or `undefined` if the item is either not a file or the + * file data cannot be accessed. */ asFile(): DataTransferFile | undefined; }