diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 3a0ff3188c75012cdfc9687ad77bac63b67d8aad..075860b55a5f5f45fb5ef13886684ac17f055ae3 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -450,7 +450,7 @@ export class CommandCenter { return; } - url = url.replace(/^\s*git\s+clone\s+/, ''); + url = url.trim().replace(/^git\s+clone\s+/, ''); const config = workspace.getConfiguration('git'); let defaultCloneDirectory = config.get('defaultCloneDirectory') || os.homedir(); @@ -1924,7 +1924,17 @@ export class CommandCenter { private async _sync(repository: Repository, rebase: boolean): Promise { const HEAD = repository.HEAD; - if (!HEAD || !HEAD.upstream) { + if (!HEAD) { + return; + } else if (!HEAD.upstream) { + const branchName = HEAD.name; + const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName); + const yes = localize('ok', "OK"); + const pick = await window.showWarningMessage(message, { modal: true }, yes); + + if (pick === yes) { + await this.publish(repository); + } return; } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 246d1fac324fe6e16036fe86a814bf5b9f096d98..ce2ac8b1f96958fd4aa3e45f11560e484360b4ab 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -11,12 +11,15 @@ import * as which from 'which'; import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; -import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; +import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter } from './util'; import { CancellationToken } from 'vscode'; import { URI } from 'vscode-uri'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; +// https://github.com/microsoft/vscode/issues/65693 +const MAX_CLI_LENGTH = 30000; + const readfile = denodeify(fs.readFile); export interface IGit { @@ -1139,13 +1142,14 @@ export class Repository { args.push(treeish); } - if (paths && paths.length) { - args.push('--'); - args.push.apply(args, paths); - } - try { - await this.run(args); + if (paths && paths.length > 0) { + for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + await this.run([...args, '--', ...chunk]); + } + } else { + await this.run(args); + } } catch (err) { if (/Please,? commit your changes or stash them/.test(err.stderr || '')) { err.gitErrorCode = GitErrorCodes.DirtyWorkTree; @@ -1276,11 +1280,17 @@ export class Repository { async clean(paths: string[]): Promise { const pathsByGroup = groupBy(paths, p => path.dirname(p)); const groups = Object.keys(pathsByGroup).map(k => pathsByGroup[k]); - const tasks = groups.map(paths => () => this.run(['clean', '-f', '-q', '--'].concat(paths))); - for (let task of tasks) { - await task(); + const limiter = new Limiter(5); + const promises: Promise[] = []; + + for (const paths of groups) { + for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + promises.push(limiter.queue(() => this.run(['clean', '-f', '-q', '--', ...chunk]))); + } } + + await Promise.all(promises); } async undo(): Promise { @@ -1746,8 +1756,11 @@ export class Repository { } async updateSubmodules(paths: string[]): Promise { - const args = ['submodule', 'update', '--', ...paths]; - await this.run(args); + const args = ['submodule', 'update', '--']; + + for (const chunk of splitInChunks(paths, MAX_CLI_LENGTH)) { + await this.run([...args, ...chunk]); + } } async getSubmodules(): Promise { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index e4debc05179c8c269bc755aae69f268f3bed47c5..07dc1c231e8acdeec416ffd64bab5eaaf831eb93 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env, ProgressOptions, CancellationToken } from 'vscode'; import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git'; -import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util'; +import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; import { AutoFetcher } from './autofetch'; @@ -14,6 +14,7 @@ import * as nls from 'vscode-nls'; import * as fs from 'fs'; import { StatusBarCommands } from './statusbar'; import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git'; +import { IFileWatcher, watch } from './watch'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -956,21 +957,9 @@ export class Repository implements Disposable { } }); - const promises: Promise[] = []; - - if (toClean.length > 0) { - promises.push(this.repository.clean(toClean)); - } - - if (toCheckout.length > 0) { - promises.push(this.repository.checkout('', toCheckout)); - } - - if (submodulesToUpdate.length > 0) { - promises.push(this.repository.updateSubmodules(submodulesToUpdate)); - } - - await Promise.all(promises); + await this.repository.clean(toClean); + await this.repository.checkout('', toCheckout); + await this.repository.updateSubmodules(submodulesToUpdate); }); } diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index f0444ce57956dbc551e6ad0020be35c33c0c3006..a860c82352ad512eb0634a08dfd19dda09e17aa4 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -6,6 +6,7 @@ import 'mocha'; import { GitStatusParser, parseGitCommit, parseGitmodules, parseLsTree, parseLsFiles } from '../git'; import * as assert from 'assert'; +import { splitInChunks } from '../util'; suite('git', () => { suite('GitStatusParser', () => { @@ -292,4 +293,78 @@ This is a commit message.`; ]); }); }); -}); \ No newline at end of file + + suite('splitInChunks', () => { + test('unit tests', function () { + assert.deepEqual( + [...splitInChunks(['hello', 'there', 'cool', 'stuff'], 6)], + [['hello'], ['there'], ['cool'], ['stuff']] + ); + + assert.deepEqual( + [...splitInChunks(['hello', 'there', 'cool', 'stuff'], 10)], + [['hello', 'there'], ['cool', 'stuff']] + ); + + assert.deepEqual( + [...splitInChunks(['hello', 'there', 'cool', 'stuff'], 12)], + [['hello', 'there'], ['cool', 'stuff']] + ); + + assert.deepEqual( + [...splitInChunks(['hello', 'there', 'cool', 'stuff'], 14)], + [['hello', 'there', 'cool'], ['stuff']] + ); + + assert.deepEqual( + [...splitInChunks(['hello', 'there', 'cool', 'stuff'], 2000)], + [['hello', 'there', 'cool', 'stuff']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 1)], + [['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 2)], + [['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 3)], + [['0', '01'], ['012'], ['0', '01'], ['012'], ['0', '01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 4)], + [['0', '01'], ['012', '0'], ['01'], ['012', '0'], ['01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 5)], + [['0', '01'], ['012', '0'], ['01', '012'], ['0', '01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 6)], + [['0', '01', '012'], ['0', '01', '012'], ['0', '01', '012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 7)], + [['0', '01', '012', '0'], ['01', '012', '0'], ['01', '012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 8)], + [['0', '01', '012', '0'], ['01', '012', '0', '01'], ['012']] + ); + + assert.deepEqual( + [...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 9)], + [['0', '01', '012', '0', '01'], ['012', '0', '01', '012']] + ); + }); + }); +}); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index c4e938506196a46b263e6e9aede342feaabb3bbc..d2e201ca01d4933ff0411e382400f94cc91387f8 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, EventEmitter, Uri } from 'vscode'; -import { dirname, sep, join } from 'path'; +import { Event } from 'vscode'; +import { dirname, sep } from 'path'; import { Readable } from 'stream'; import * as fs from 'fs'; import * as byline from 'byline'; @@ -345,18 +345,69 @@ export function pathEquals(a: string, b: string): boolean { return a === b; } -export interface IFileWatcher extends IDisposable { - readonly event: Event; +export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator { + let current: string[] = []; + let length = 0; + + for (const value of array) { + let newLength = length + value.length; + + if (newLength > maxChunkLength && current.length > 0) { + yield current; + current = []; + newLength = value.length; + } + + current.push(value); + length = newLength; + } + + if (current.length > 0) { + yield current; + } } -export function watch(location: string): IFileWatcher { - const dotGitWatcher = fs.watch(location); - const onDotGitFileChangeEmitter = new EventEmitter(); - dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string)))); - dotGitWatcher.on('error', err => console.error(err)); +interface ILimitedTaskFactory { + factory: () => Promise; + c: (value?: T | Promise) => void; + e: (error?: any) => void; +} - return new class implements IFileWatcher { - event = onDotGitFileChangeEmitter.event; - dispose() { dotGitWatcher.close(); } - }; +export class Limiter { + + private runningPromises: number; + private maxDegreeOfParalellism: number; + private outstandingPromises: ILimitedTaskFactory[]; + + constructor(maxDegreeOfParalellism: number) { + this.maxDegreeOfParalellism = maxDegreeOfParalellism; + this.outstandingPromises = []; + this.runningPromises = 0; + } + + queue(factory: () => Promise): Promise { + return new Promise((c, e) => { + this.outstandingPromises.push({ factory, c, e }); + this.consume(); + }); + } + + private consume(): void { + while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { + const iLimitedTask = this.outstandingPromises.shift()!; + this.runningPromises++; + + const promise = iLimitedTask.factory(); + promise.then(iLimitedTask.c, iLimitedTask.e); + promise.then(() => this.consumed(), () => this.consumed()); + } + } + + private consumed(): void { + this.runningPromises--; + + if (this.outstandingPromises.length > 0) { + this.consume(); + } + } } diff --git a/extensions/git/src/watch.ts b/extensions/git/src/watch.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6670cfa8847bfc0d5d12292a83ce61ad33a7152 --- /dev/null +++ b/extensions/git/src/watch.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, EventEmitter, Uri } from 'vscode'; +import { join } from 'path'; +import * as fs from 'fs'; +import { IDisposable } from './util'; + +export interface IFileWatcher extends IDisposable { + readonly event: Event; +} + +export function watch(location: string): IFileWatcher { + const dotGitWatcher = fs.watch(location); + const onDotGitFileChangeEmitter = new EventEmitter(); + dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string)))); + dotGitWatcher.on('error', err => console.error(err)); + + return new class implements IFileWatcher { + event = onDotGitFileChangeEmitter.event; + dispose() { dotGitWatcher.close(); } + }; +} diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index e41d130649932c77f0e39ee4f41aaeaca12c4642..67380ce77b6ff348952f045d8f1ceae535507d73 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -465,11 +465,11 @@ "c": ".IsInRole", "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell variable.other.readwrite.powershell variable.other.member.powershell", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "source.powershell variable.other.member: #DCDCAA", + "light_plus": "source.powershell variable.other.member: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "hc_black": "source.powershell variable.other.member: #DCDCAA" } }, { diff --git a/extensions/theme-defaults/themes/dark_plus.json b/extensions/theme-defaults/themes/dark_plus.json index 1898153ba8969f984539fa574133d7315573a7de..f12d3d7633e95271e64e8d3808b76ae0b0e5a6a8 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -8,7 +8,8 @@ "scope": [ "entity.name.function", "support.function", - "support.constant.handlebars" + "support.constant.handlebars", + "source.powershell variable.other.member" ], "settings": { "foreground": "#DCDCAA" @@ -171,4 +172,4 @@ } } ] -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index 8119256d5f24222b2735490a52cb62593727c726..f76d7bb960f26cd53ecaf4a51b45f78eb0d69cdb 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -13,7 +13,8 @@ "scope": [ "entity.name.function", "support.function", - "support.constant.handlebars" + "support.constant.handlebars", + "source.powershell variable.other.member" ], "settings": { "foreground": "#DCDCAA" @@ -115,4 +116,4 @@ } } ] -} \ No newline at end of file +} diff --git a/extensions/theme-defaults/themes/light_plus.json b/extensions/theme-defaults/themes/light_plus.json index 7138f045d589976fdffb1ea8b0693bb40b4d596b..3d30c5d17fbd69077f5f80c2013049b51bb24447 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -8,7 +8,8 @@ "scope": [ "entity.name.function", "support.function", - "support.constant.handlebars" + "support.constant.handlebars", + "source.powershell variable.other.member" ], "settings": { "foreground": "#795E26" @@ -172,4 +173,4 @@ } ] -} \ No newline at end of file +} diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 8598b8954480220a1731887cb188051a602df0ea..ca09cbc9cc902da8fa6455eac25ce9a6a1556ed0 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -81,7 +81,7 @@ export class StorageService extends Disposable implements IStorageService { const useInMemoryStorage = !!this.environmentService.extensionTestsLocationURI; // no storage during extension tests! - // Create workspace storage and initalize + // Create workspace storage and initialize mark('willInitWorkspaceStorage'); try { await this.createWorkspaceStorage(useInMemoryStorage ? SQLiteStorageDatabase.IN_MEMORY_PATH : join(result.path, StorageService.WORKSPACE_STORAGE_NAME), result.wasCreated ? StorageHint.STORAGE_DOES_NOT_EXIST : undefined).init(); diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index b7af867d10cd0597eb6004e0d40151bb439a37d0..bbd29b585a21355231872f5b90922b58dc3a8e54 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -16,9 +16,10 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; -import { CommentsPanel, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsPanel'; +import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape } from '../common/extHost.protocol'; +import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; export class MainThreadCommentThread implements modes.CommentThread { diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 85039369466303ca433327438b99c267335e52fb..20572fa77839c9a45426c25dc5d64be3f8629c11 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -153,13 +153,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb type: debugType }; if (hasProvide) { - provider.provideDebugConfigurations = (folder) => { - return this._proxy.$provideDebugConfigurations(handle, folder); + provider.provideDebugConfigurations = (folder, token) => { + return this._proxy.$provideDebugConfigurations(handle, folder, token); }; } if (hasResolve) { - provider.resolveDebugConfiguration = (folder, config) => { - return this._proxy.$resolveDebugConfiguration(handle, folder, config); + provider.resolveDebugConfiguration = (folder, config, token) => { + return this._proxy.$resolveDebugConfiguration(handle, folder, config, token); }; } if (hasProvideDebugAdapter) { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 01a1733917ab066d87ccd3f7187310320d105170..c992a30243fd3f6183b28fffc165f12c74d118f1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1242,8 +1242,8 @@ export interface ExtHostDebugServiceShape { $startDASession(handle: number, session: IDebugSessionDto): Promise; $stopDASession(handle: number): Promise; $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; - $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise; - $provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Promise; + $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; + $provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise; $legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise; // TODO@AW legacy $provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise; $acceptDebugSessionStarted(session: IDebugSessionDto): void; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index be353ca5bdec3db78d2863655318f9c046017248..9b26206924de2258b986676e0541b241a8d2e1b0 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -599,7 +599,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this.fireBreakpointChanges(a, r, c); } - public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise { + public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise { return asPromise(async () => { const provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { @@ -609,7 +609,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations'); } const folder = await this.getFolder(folderUri); - return provider.provideDebugConfigurations(folder, CancellationToken.None); + return provider.provideDebugConfigurations(folder, token); }).then(debugConfigurations => { if (!debugConfigurations) { throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations'); @@ -618,7 +618,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise { + public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise { return asPromise(async () => { const provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { @@ -628,7 +628,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration'); } const folder = await this.getFolder(folderUri); - return provider.resolveDebugConfiguration(folder, debugConfiguration, CancellationToken.None); + return provider.resolveDebugConfiguration(folder, debugConfiguration, token); }); } diff --git a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts index 27440e3e996b6c2b18d8f5da5b66db0364feaa23..5f0e33758a410e2cc553965562efd25db397a96a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsPanel.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsPanel.ts @@ -4,34 +4,31 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/panel'; +import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; -import { IAction } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; -import { CollapseAllAction, DefaultAccessibilityProvider, DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IAction, Action } from 'vs/base/common/actions'; +import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Panel } from 'vs/workbench/browser/panel'; import { CommentNode, CommentsModel, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel'; import { ReviewController } from 'vs/workbench/contrib/comments/browser/commentsEditorContribution'; -import { CommentsDataFilter, CommentsDataSource, CommentsModelRenderer } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/contrib/comments/browser/commentService'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { CommentsList, COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; -export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; -export const COMMENTS_PANEL_TITLE = 'Comments'; export class CommentsPanel extends Panel { private treeLabels: ResourceLabels; - private tree: WorkbenchTree; + private tree: CommentsList; private treeContainer: HTMLElement; private messageBoxContainer: HTMLElement; private messageBox: HTMLElement; @@ -42,7 +39,6 @@ export class CommentsPanel extends Panel { @IInstantiationService private readonly instantiationService: IInstantiationService, @ICommentService private readonly commentService: ICommentService, @IEditorService private readonly editorService: IEditorService, - @IOpenerService private readonly openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService @@ -113,7 +109,7 @@ export class CommentsPanel extends Panel { public getActions(): IAction[] { if (!this.collapseAllAction) { - this.collapseAllAction = this.instantiationService.createInstance(CollapseAllAction, this.tree, this.commentsModel.hasCommentThreads()); + this.collapseAllAction = new Action('vs.tree.collapse', nls.localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve()); this._register(this.collapseAllAction); } @@ -141,22 +137,11 @@ export class CommentsPanel extends Panel { private createTree(): void { this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + this.tree = this._register(this.instantiationService.createInstance(CommentsList, this.treeLabels, this.treeContainer)); - this.tree = this._register(this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { - dataSource: new CommentsDataSource(), - renderer: new CommentsModelRenderer(this.treeLabels, this.openerService), - accessibilityProvider: new DefaultAccessibilityProvider, - controller: new DefaultController(), - dnd: new DefaultDragAndDrop(), - filter: new CommentsDataFilter() - }, { - twistiePixels: 20, - ariaLabel: COMMENTS_PANEL_TITLE - })); - - const commentsNavigator = this._register(new TreeResourceNavigator(this.tree, { openOnFocus: true })); - this._register(Event.debounce(commentsNavigator.openResource, (last, event) => event, 100, true)(options => { - this.openFile(options.element, options.editorOptions.pinned, options.editorOptions.preserveFocus, options.sideBySide); + const commentsNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true })); + this._register(commentsNavigator.onDidOpenResource(e => { + this.openFile(e.element, e.editorOptions.pinned, e.editorOptions.preserveFocus, e.sideBySide); })); } @@ -213,7 +198,7 @@ export class CommentsPanel extends Panel { this.collapseAllAction.enabled = this.commentsModel.hasCommentThreads(); dom.toggleClass(this.treeContainer, 'hidden', !this.commentsModel.hasCommentThreads()); - this.tree.refresh().then(() => { + this.tree.updateChildren().then(() => { this.renderMessage(); }, (e) => { console.log(e); @@ -243,4 +228,4 @@ CommandsRegistry.registerCommand({ panelService.openPanel(COMMENTS_PANEL_ID, true); } } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 090ffa54650998325bae13280614cb9e0f4cf33f..7ab435e96ca243128b74f44cbd2d7a343596a4cd 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -9,30 +9,28 @@ import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IDataSource, IFilter, IRenderer as ITreeRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/contrib/comments/common/commentModel'; - -export class CommentsDataSource implements IDataSource { - public getId(tree: ITree, element: any): string { - if (element instanceof CommentsModel) { - return 'root'; - } - if (element instanceof ResourceWithCommentThreads) { - return `${element.owner}-${element.id}`; - } - if (element instanceof CommentNode) { - return `${element.owner}-${element.resource.toString()}-${element.threadId}-${element.comment.uniqueIdInThread}` + (element.isRoot ? '-root' : ''); - } - return ''; - } - - public hasChildren(tree: ITree, element: any): boolean { +import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; +export const COMMENTS_PANEL_TITLE = 'Comments'; + +export class CommentsAsyncDataSource implements IAsyncDataSource { + hasChildren(element: any): boolean { return element instanceof CommentsModel || element instanceof ResourceWithCommentThreads || (element instanceof CommentNode && !!element.replies.length); } - public getChildren(tree: ITree, element: any): Promise { + getChildren(element: any): any[] | Promise { if (element instanceof CommentsModel) { return Promise.resolve(element.resourceCommentThreads); } @@ -44,14 +42,6 @@ export class CommentsDataSource implements IDataSource { } return Promise.resolve([]); } - - public getParent(tree: ITree, element: any): Promise { - return Promise.resolve(undefined); - } - - public shouldAutoexpand(tree: ITree, element: any): boolean { - return true; - } } interface IResourceTemplateData { @@ -65,61 +55,36 @@ interface ICommentThreadTemplateData { disposables: IDisposable[]; } -export class CommentsModelRenderer implements ITreeRenderer { +export class CommentsModelVirualDelegate implements IListVirtualDelegate { private static RESOURCE_ID = 'resource-with-comments'; private static COMMENT_ID = 'comment-node'; - constructor( - private labels: ResourceLabels, - @IOpenerService private readonly openerService: IOpenerService - ) { - } - public getHeight(tree: ITree, element: any): number { + getHeight(element: any): number { return 22; } - public getTemplateId(tree: ITree, element: any): string { + public getTemplateId(element: any): string { if (element instanceof ResourceWithCommentThreads) { - return CommentsModelRenderer.RESOURCE_ID; + return CommentsModelVirualDelegate.RESOURCE_ID; } if (element instanceof CommentNode) { - return CommentsModelRenderer.COMMENT_ID; + return CommentsModelVirualDelegate.COMMENT_ID; } return ''; } +} - public renderTemplate(ITree: ITree, templateId: string, container: HTMLElement): any { - switch (templateId) { - case CommentsModelRenderer.RESOURCE_ID: - return this.renderResourceTemplate(container); - case CommentsModelRenderer.COMMENT_ID: - return this.renderCommentTemplate(container); - } - } - - public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { - switch (templateId) { - case CommentsModelRenderer.RESOURCE_ID: - (templateData).resourceLabel.dispose(); - break; - case CommentsModelRenderer.COMMENT_ID: - (templateData).disposables.forEach(disposeable => disposeable.dispose()); - break; - } - } +export class ResourceWithCommentsRenderer implements IListRenderer, IResourceTemplateData> { + templateId: string = 'resource-with-comments'; - public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { - switch (templateId) { - case CommentsModelRenderer.RESOURCE_ID: - return this.renderResourceElement(tree, element, templateData); - case CommentsModelRenderer.COMMENT_ID: - return this.renderCommentElement(tree, element, templateData); - } + constructor( + private labels: ResourceLabels + ) { } - private renderResourceTemplate(container: HTMLElement): IResourceTemplateData { + renderTemplate(container: HTMLElement) { const data = Object.create(null); const labelContainer = dom.append(container, dom.$('.resource-container')); data.resourceLabel = this.labels.create(labelContainer); @@ -127,7 +92,23 @@ export class CommentsModelRenderer implements ITreeRenderer { return data; } - private renderCommentTemplate(container: HTMLElement): ICommentThreadTemplateData { + renderElement(node: ITreeNode, index: number, templateData: IResourceTemplateData, height: number | undefined): void { + templateData.resourceLabel.setFile(node.element.resource); + } + + disposeTemplate(templateData: IResourceTemplateData): void { + templateData.resourceLabel.dispose(); + } +} + +export class CommentNodeRenderer implements IListRenderer, ICommentThreadTemplateData> { + templateId: string = 'comment-node'; + + constructor( + @IOpenerService private readonly openerService: IOpenerService + ) { } + + renderTemplate(container: HTMLElement) { const data = Object.create(null); const labelContainer = dom.append(container, dom.$('.comment-container')); data.userName = dom.append(labelContainer, dom.$('.user')); @@ -137,16 +118,12 @@ export class CommentsModelRenderer implements ITreeRenderer { return data; } - private renderResourceElement(tree: ITree, element: ResourceWithCommentThreads, templateData: IResourceTemplateData) { - templateData.resourceLabel.setFile(element.resource); - } - - private renderCommentElement(tree: ITree, element: CommentNode, templateData: ICommentThreadTemplateData) { - templateData.userName.textContent = element.comment.userName; + renderElement(node: ITreeNode, index: number, templateData: ICommentThreadTemplateData, height: number | undefined): void { + templateData.userName.textContent = node.element.comment.userName; templateData.commentText.innerHTML = ''; const disposables = new DisposableStore(); templateData.disposables.push(disposables); - const renderedComment = renderMarkdown(element.comment.body, { + const renderedComment = renderMarkdown(node.element.comment.body, { inline: true, actionHandler: { callback: (content) => { @@ -171,16 +148,71 @@ export class CommentsModelRenderer implements ITreeRenderer { templateData.commentText.appendChild(renderedComment); } + + disposeTemplate(templateData: ICommentThreadTemplateData): void { + templateData.disposables.forEach(disposeable => disposeable.dispose()); + } } -export class CommentsDataFilter implements IFilter { - public isVisible(tree: ITree, element: any): boolean { - if (element instanceof CommentsModel) { - return element.resourceCommentThreads.length > 0; - } - if (element instanceof ResourceWithCommentThreads) { - return element.commentThreads.length > 0; - } - return true; +export class CommentsList extends WorkbenchAsyncDataTree { + constructor( + labels: ResourceLabels, + container: HTMLElement, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService + ) { + const delegate = new CommentsModelVirualDelegate(); + const dataSource = new CommentsAsyncDataSource(); + + const renderers = [ + instantiationService.createInstance(ResourceWithCommentsRenderer, labels), + instantiationService.createInstance(CommentNodeRenderer) + ]; + + super( + container, + delegate, + renderers, + dataSource, + { + ariaLabel: COMMENTS_PANEL_TITLE, + keyboardSupport: true, + identityProvider: { + getId: (element: any) => { + if (element instanceof CommentsModel) { + return 'root'; + } + if (element instanceof ResourceWithCommentThreads) { + return `${element.owner}-${element.id}`; + } + if (element instanceof CommentNode) { + return `${element.owner}-${element.resource.toString()}-${element.threadId}-${element.comment.uniqueIdInThread}` + (element.isRoot ? '-root' : ''); + } + return ''; + } + }, + expandOnlyOnTwistieClick: (element: any) => { + if (element instanceof CommentsModel || element instanceof ResourceWithCommentThreads) { + return false; + } + + return true; + }, + collapseByDefault: () => { + return false; + } + }, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService + ); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 6ace957453ca973e156e5b7176046225ec9560bf..637086088b3c679e548fb19160f68470de43fb30 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -34,6 +34,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { CancellationToken } from 'vs/base/common/cancellation'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -180,7 +181,7 @@ export class ConfigurationManager implements IConfigurationManager { return providers.length > 0; } - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Promise { + resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise { return this.activateDebuggers('onDebugResolve', type).then(() => { // pipe the config through the promises sequentially. Append at the end the '*' types const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration) @@ -189,7 +190,7 @@ export class ConfigurationManager implements IConfigurationManager { return providers.reduce((promise, provider) => { return promise.then(config => { if (config) { - return provider.resolveDebugConfiguration!(folderUri, config); + return provider.resolveDebugConfiguration!(folderUri, config, token); } else { return Promise.resolve(config); } @@ -198,9 +199,9 @@ export class ConfigurationManager implements IConfigurationManager { }); } - provideDebugConfigurations(folderUri: uri | undefined, type: string): Promise { + provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise { return this.activateDebuggers('onDebugInitialConfigurations') - .then(() => Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri))) + .then(() => Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations!(folderUri, token))) .then(results => results.reduce((first, second) => first.concat(second), []))); } @@ -531,7 +532,7 @@ class Launch extends AbstractLaunch implements ILaunch { return this.configurationService.inspect('launch', { resource: this.workspace.uri }).workspaceFolder; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }> { + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }> { const resource = this.uri; let created = false; @@ -539,7 +540,7 @@ class Launch extends AbstractLaunch implements ILaunch { // launch.json not found: create one by collecting launch configs from debugConfigProviders return this.configurationManager.guessDebugger(type).then(adapter => { if (adapter) { - return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type).then(initialConfigs => { + return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type, token || CancellationToken.None).then(initialConfigs => { return adapter.getInitialConfigurationContent(initialConfigs); }); } else { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index da261aa003295382bb2352511a73eb27486d46fc..eddc8876af7271b4d4367904dd9d8425f1f5d00d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -47,6 +47,7 @@ import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/error import { RunOnceScheduler } from 'vs/base/common/async'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated'; @@ -88,6 +89,7 @@ export class DebugService implements IDebugService { private breakpointsToSendOnResourceSaved: Set; private initializing = false; private previousState: State | undefined; + private initCancellationToken: CancellationTokenSource | undefined; constructor( @IStorageService private readonly storageService: IStorageService, @@ -211,6 +213,10 @@ export class DebugService implements IDebugService { } private endInitializingState() { + if (this.initCancellationToken) { + this.initCancellationToken.cancel(); + this.initCancellationToken = undefined; + } if (this.initializing) { this.initializing = false; this.onStateChange(); @@ -355,8 +361,9 @@ export class DebugService implements IDebugService { } const debuggerThenable: Promise = type ? Promise.resolve() : this.configurationManager.guessDebugger().then(dbgr => { type = dbgr && dbgr.type; }); - return debuggerThenable.then(() => - this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!).then(config => { + return debuggerThenable.then(() => { + this.initCancellationToken = new CancellationTokenSource(); + return this.configurationManager.resolveConfigurationByProviders(launch && launch.workspace ? launch.workspace.uri : undefined, type, config!, this.initCancellationToken.token).then(config => { // a falsy config indicates an aborted launch if (config && config.type) { return this.substituteVariables(launch, config).then(resolvedConfig => { @@ -396,17 +403,17 @@ export class DebugService implements IDebugService { .then(() => false); } - return launch && launch.openConfigFile(false, true).then(() => false); + return launch && launch.openConfigFile(false, true, undefined, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); }); } if (launch && type && config === null) { // show launch.json only for "config" being "null". - return launch.openConfigFile(false, true, type).then(() => false); + return launch.openConfigFile(false, true, type, this.initCancellationToken ? this.initCancellationToken.token : undefined).then(() => false); } return false; - }) - ); + }); + }); } /** @@ -587,7 +594,8 @@ export class DebugService implements IDebugService { let substitutionThenable: Promise = Promise.resolve(session.configuration); if (launch && needsToSubstitute && unresolved) { - substitutionThenable = this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved) + this.initCancellationToken = new CancellationTokenSource(); + substitutionThenable = this.configurationManager.resolveConfigurationByProviders(launch.workspace ? launch.workspace.uri : undefined, unresolved.type, unresolved, this.initCancellationToken.token) .then(resolved => { if (resolved) { // start debugging diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 320be3360ce944c7c32d0588fec9392ccfc2d899..d454894401968dd2b0c782b95f9628cb7e7ddb53 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -169,7 +169,7 @@ export class DebugSession implements IDebugSession { this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService); - return this.raw!.start().then(() => { + return this.raw.start().then(() => { this.registerListeners(); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 8fa024396103e84620d87c82991aefef420ba821..65e28660dc9aab3485e1cac4f6338d631dde8116 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -25,6 +25,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const VIEWLET_ID = 'workbench.view.debug'; export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); @@ -557,8 +558,8 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut export interface IDebugConfigurationProvider { readonly type: string; - resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise; - provideDebugConfigurations?(folderUri: uri | undefined): Promise; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise; + provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise; debugAdapterExecutable?(folderUri: uri | undefined): Promise; // TODO@AW legacy } @@ -610,7 +611,7 @@ export interface IConfigurationManager { registerDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): IDisposable; unregisterDebugAdapterDescriptorFactory(debugAdapterDescriptorFactory: IDebugAdapterDescriptorFactory): void; - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): Promise; + resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any, token: CancellationToken): Promise; getDebugAdapterDescriptor(session: IDebugSession): Promise; registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable; @@ -663,7 +664,7 @@ export interface ILaunch { /** * Opens the launch.json file. Creates if it does not exist. */ - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Promise<{ editor: IEditor | null, created: boolean }>; + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string, token?: CancellationToken): Promise<{ editor: IEditor | null, created: boolean }>; } // Debug service interfaces diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 8b120c0a125f76ee55ca041adfeb5d099776c893..a59f40dd2f4a9b2c75b238dcc408733013e09c21 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -502,7 +502,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!this.versionAndEngineCompatible(filter)) { return Promise.resolve([]); } - return this.getGroupedTasks().then((map) => { + return this.getGroupedTasks(filter ? filter.type : undefined).then((map) => { if (!filter || !filter.type) { return map.all(); } @@ -1117,7 +1117,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected abstract getTaskSystem(): ITaskSystem; - private getGroupedTasks(): Promise { + private getGroupedTasks(type?: string): Promise { return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), TaskDefinitionRegistry.onReady()]).then(() => { let validTypes: IStringDictionary = Object.create(null); TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true); @@ -1152,10 +1152,12 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } }; if (this.schemaVersion === JsonSchemaVersion.V2_0_0 && this._providers.size > 0) { - this._providers.forEach((provider) => { - counter++; - provider.provideTasks(validTypes).then(done, error); - }); + for (const [handle, provider] of this._providers) { + if ((type === undefined) || (type === this._providerTypes.get(handle))) { + counter++; + provider.provideTasks(validTypes).then(done, error); + } + } } else { resolve(result); } diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 696f4681b4ee952653b06ab7969da337935a2347..28fb4787d24a103d47b097c7721a08b4a89ac0fe 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -762,8 +762,8 @@ export class RemoteFileDialog { private pathAppend(uri: URI, additional: string): string { if ((additional === '..') || (additional === '.')) { - const basePath = this.pathFromUri(uri); - return basePath + this.separator + additional; + const basePath = this.pathFromUri(uri, true); + return basePath + additional; } else { return this.pathFromUri(resources.joinPath(uri, additional)); }