未验证 提交 fcb78f59 编写于 作者: D Daniel Imms 提交者: GitHub

Merge branch 'master' into tyriar/69865_remove_renderers

......@@ -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<string>('defaultCloneDirectory') || os.homedir();
......@@ -1924,7 +1924,17 @@ export class CommandCenter {
private async _sync(repository: Repository, rebase: boolean): Promise<void> {
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;
}
......
......@@ -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<string, string | null, string>(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<void> {
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<any>[] = [];
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<void> {
......@@ -1746,8 +1756,11 @@ export class Repository {
}
async updateSubmodules(paths: string[]): Promise<void> {
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<Submodule[]> {
......
......@@ -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<void>[] = [];
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);
});
}
......
......@@ -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']]
);
});
});
});
......@@ -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<Uri>;
export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator<string[]> {
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<Uri>();
dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
dotGitWatcher.on('error', err => console.error(err));
interface ILimitedTaskFactory<T> {
factory: () => Promise<T>;
c: (value?: T | Promise<T>) => void;
e: (error?: any) => void;
}
return new class implements IFileWatcher {
event = onDotGitFileChangeEmitter.event;
dispose() { dotGitWatcher.close(); }
};
export class Limiter<T> {
private runningPromises: number;
private maxDegreeOfParalellism: number;
private outstandingPromises: ILimitedTaskFactory<T>[];
constructor(maxDegreeOfParalellism: number) {
this.maxDegreeOfParalellism = maxDegreeOfParalellism;
this.outstandingPromises = [];
this.runningPromises = 0;
}
queue(factory: () => Promise<T>): Promise<T> {
return new Promise<T>((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();
}
}
}
/*---------------------------------------------------------------------------------------------
* 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<Uri>;
}
export function watch(location: string): IFileWatcher {
const dotGitWatcher = fs.watch(location);
const onDotGitFileChangeEmitter = new EventEmitter<Uri>();
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(); }
};
}
......@@ -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"
}
},
{
......
......@@ -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
}
......@@ -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
}
......@@ -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
}
......@@ -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();
......
......@@ -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 {
......
......@@ -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) {
......
......@@ -1242,8 +1242,8 @@ export interface ExtHostDebugServiceShape {
$startDASession(handle: number, session: IDebugSessionDto): Promise<void>;
$stopDASession(handle: number): Promise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise<IConfig | null | undefined>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Promise<IConfig[]>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined, token: CancellationToken): Promise<IConfig[]>;
$legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
$provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise<IAdapterDescriptor>;
$acceptDebugSessionStarted(session: IDebugSessionDto): void;
......
......@@ -599,7 +599,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this.fireBreakpointChanges(a, r, c);
}
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<vscode.DebugConfiguration[]> {
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined, token: CancellationToken): Promise<vscode.DebugConfiguration[]> {
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<vscode.DebugConfiguration | null | undefined> {
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration, token: CancellationToken): Promise<vscode.DebugConfiguration | null | undefined> {
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);
});
}
......
......@@ -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<any, any>(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
});
......@@ -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<any, any> {
hasChildren(element: any): boolean {
return element instanceof CommentsModel || element instanceof ResourceWithCommentThreads || (element instanceof CommentNode && !!element.replies.length);
}
public getChildren(tree: ITree, element: any): Promise<ResourceWithCommentThreads[] | CommentNode[]> {
getChildren(element: any): any[] | Promise<any[]> {
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<void> {
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<any> {
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:
(<IResourceTemplateData>templateData).resourceLabel.dispose();
break;
case CommentsModelRenderer.COMMENT_ID:
(<ICommentThreadTemplateData>templateData).disposables.forEach(disposeable => disposeable.dispose());
break;
}
}
export class ResourceWithCommentsRenderer implements IListRenderer<ITreeNode<ResourceWithCommentThreads>, 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 = <IResourceTemplateData>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<ResourceWithCommentThreads>, 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<ITreeNode<CommentNode>, ICommentThreadTemplateData> {
templateId: string = 'comment-node';
constructor(
@IOpenerService private readonly openerService: IOpenerService
) { }
renderTemplate(container: HTMLElement) {
const data = <ICommentThreadTemplateData>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<CommentNode>, 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<any, any> {
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
);
}
}
......@@ -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<IJSONContributionRegistry>(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<IConfig | null | undefined> {
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined> {
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<any[]> {
provideDebugConfigurations(folderUri: uri | undefined, type: string, token: CancellationToken): Promise<any[]> {
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<IGlobalConfig>('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 {
......
......@@ -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<string>;
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<void> = 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<IConfig | null | undefined> = 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
......
......@@ -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();
......
......@@ -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<IViewContainersRegistry>(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<IConfig | null | undefined>;
provideDebugConfigurations?(folderUri: uri | undefined): Promise<IConfig[]>;
resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig, token: CancellationToken): Promise<IConfig | null | undefined>;
provideDebugConfigurations?(folderUri: uri | undefined, token: CancellationToken): Promise<IConfig[]>;
debugAdapterExecutable?(folderUri: uri | undefined): Promise<IAdapterDescriptor>; // 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<any>;
resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any, token: CancellationToken): Promise<any>;
getDebugAdapterDescriptor(session: IDebugSession): Promise<IAdapterDescriptor | undefined>;
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
......
......@@ -502,7 +502,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (!this.versionAndEngineCompatible(filter)) {
return Promise.resolve<Task[]>([]);
}
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<TaskMap> {
private getGroupedTasks(type?: string): Promise<TaskMap> {
return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), TaskDefinitionRegistry.onReady()]).then(() => {
let validTypes: IStringDictionary<boolean> = 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);
}
......
......@@ -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));
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册