Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)

This commit is contained in:
Karl Burtram
2019-04-05 10:09:18 -07:00
committed by GitHub
parent 9bd7e30d18
commit cb5bcf2248
433 changed files with 8915 additions and 8361 deletions

View File

@@ -109,40 +109,48 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
private _onDidChangeLabel = new Emitter<string>();
get onDidChangeLabel(): Event<string> { return this._onDidChangeLabel.event; }
private _comments: modes.Comment[] | undefined;
public get comments(): modes.Comment[] {
public get comments(): modes.Comment[] | undefined {
return this._comments;
}
public set comments(newComments: modes.Comment[]) {
public set comments(newComments: modes.Comment[] | undefined) {
this._comments = newComments;
this._onDidChangeComments.fire(this._comments);
}
private _onDidChangeComments = new Emitter<modes.Comment[]>();
get onDidChangeComments(): Event<modes.Comment[]> { return this._onDidChangeComments.event; }
private _onDidChangeComments = new Emitter<modes.Comment[] | undefined>();
get onDidChangeComments(): Event<modes.Comment[] | undefined> { return this._onDidChangeComments.event; }
set acceptInputCommand(newCommand: modes.Command) {
private _acceptInputCommand: modes.Command | undefined;
set acceptInputCommand(newCommand: modes.Command | undefined) {
this._acceptInputCommand = newCommand;
this._onDidChangeAcceptInputCommand.fire(this._acceptInputCommand);
}
get acceptInputCommand(): modes.Command {
get acceptInputCommand(): modes.Command | undefined {
return this._acceptInputCommand!;
}
private _onDidChangeAcceptInputCommand = new Emitter<modes.Command>();
get onDidChangeAcceptInputCommand(): Event<modes.Command> { return this._onDidChangeAcceptInputCommand.event; }
private _onDidChangeAcceptInputCommand = new Emitter<modes.Command | undefined>();
get onDidChangeAcceptInputCommand(): Event<modes.Command | undefined> { return this._onDidChangeAcceptInputCommand.event; }
set additionalCommands(newCommands: modes.Command[]) {
private _additionalCommands: modes.Command[] | undefined;
set additionalCommands(newCommands: modes.Command[] | undefined) {
this._additionalCommands = newCommands;
this._onDidChangeAdditionalCommands.fire(this._additionalCommands);
}
get additionalCommands(): modes.Command[] {
get additionalCommands(): modes.Command[] | undefined {
return this._additionalCommands;
}
private _onDidChangeAdditionalCommands = new Emitter<modes.Command[] | undefined>();
get onDidChangeAdditionalCommands(): Event<modes.Command[] | undefined> { return this._onDidChangeAdditionalCommands.event; }
private _deleteCommand: modes.Command | undefined;
set deleteCommand(newCommand: modes.Command | undefined) {
this._deleteCommand = newCommand;
}
@@ -151,9 +159,6 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
return this._deleteCommand;
}
private _onDidChangeAdditionalCommands = new Emitter<modes.Command[]>();
get onDidChangeAdditionalCommands(): Event<modes.Command[]> { return this._onDidChangeAdditionalCommands.event; }
set range(range: IRange) {
this._range = range;
this._onDidChangeRange.fire(this._range);
@@ -166,16 +171,17 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
private _onDidChangeRange = new Emitter<IRange>();
public onDidChangeRange = this._onDidChangeRange.event;
private _collapsibleState: modes.CommentThreadCollapsibleState | undefined;
get collapsibleState() {
return this._collapsibleState;
}
set collapsibleState(newState: modes.CommentThreadCollapsibleState) {
set collapsibleState(newState: modes.CommentThreadCollapsibleState | undefined) {
this._collapsibleState = newState;
this._onDidChangeCollasibleState.fire(this._collapsibleState);
}
private _onDidChangeCollasibleState = new Emitter<modes.CommentThreadCollapsibleState>();
private _onDidChangeCollasibleState = new Emitter<modes.CommentThreadCollapsibleState | undefined>();
public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event;
constructor(
@@ -184,14 +190,24 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
public extensionId: string,
public threadId: string,
public resource: string,
private _range: IRange,
private _comments: modes.Comment[],
private _acceptInputCommand: modes.Command | undefined,
private _additionalCommands: modes.Command[],
private _deleteCommand: modes.Command | undefined,
private _collapsibleState: modes.CommentThreadCollapsibleState
) {
private _range: IRange
) { }
batchUpdate(
range: IRange,
label: string,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command | undefined,
collapsibleState: modes.CommentThreadCollapsibleState) {
this._range = range;
this._label = label;
this._comments = comments;
this._acceptInputCommand = acceptInputCommand;
this._additionalCommands = additionalCommands;
this._deleteCommand = deleteCommand;
this._collapsibleState = collapsibleState;
}
dispose() { }
@@ -254,23 +270,14 @@ export class MainThreadCommentController {
threadId: string,
resource: UriComponents,
range: IRange,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command | undefined,
collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 {
): modes.CommentThread2 {
let thread = new MainThreadCommentThread(
commentThreadHandle,
this,
'',
threadId,
URI.revive(resource).toString(),
range,
comments,
acceptInputCommand,
additionalCommands,
deleteCommand,
collapseState
range
);
this._threads.set(commentThreadHandle, thread);
@@ -284,6 +291,27 @@ export class MainThreadCommentController {
return thread;
}
updateCommentThread(commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command | undefined,
collapsibleState: modes.CommentThreadCollapsibleState): void {
let thread = this.getKnownThread(commentThreadHandle);
thread.batchUpdate(range, label, comments, acceptInputCommand, additionalCommands, deleteCommand, collapsibleState);
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [],
changed: [thread],
draftMode: modes.DraftMode.NotSupported
});
}
deleteCommentThread(commentThreadHandle: number) {
let thread = this.getKnownThread(commentThreadHandle);
this._threads.delete(commentThreadHandle);
@@ -298,48 +326,6 @@ export class MainThreadCommentController {
thread.dispose();
}
updateComments(commentThreadHandle: number, comments: modes.Comment[]) {
let thread = this.getKnownThread(commentThreadHandle);
thread.comments = comments;
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [],
changed: [thread],
draftMode: modes.DraftMode.NotSupported
});
}
updateAcceptInputCommand(commentThreadHandle: number, acceptInputCommand: modes.Command) {
let thread = this.getKnownThread(commentThreadHandle);
thread.acceptInputCommand = acceptInputCommand;
}
updateAdditionalCommands(commentThreadHandle: number, additionalCommands: modes.Command[]) {
let thread = this.getKnownThread(commentThreadHandle);
thread.additionalCommands = additionalCommands;
}
updateDeleteCommand(commentThreadHandle: number, deleteCommand: modes.Command) {
const thread = this.getKnownThread(commentThreadHandle);
thread.deleteCommand = deleteCommand;
}
updateCollapsibleState(commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState) {
let thread = this.getKnownThread(commentThreadHandle);
thread.collapsibleState = collapseState;
}
updateCommentThreadRange(commentThreadHandle: number, range: IRange) {
let thread = this.getKnownThread(commentThreadHandle);
thread.range = range;
}
updateCommentThreadLabel(commentThreadHandle: number, label: string) {
let thread = this.getKnownThread(commentThreadHandle);
thread.label = label;
}
updateInput(input: string) {
let thread = this.activeCommentThread;
@@ -358,7 +344,6 @@ export class MainThreadCommentController {
return thread;
}
async getDocumentComments(resource: URI, token: CancellationToken) {
let ret: modes.CommentThread2[] = [];
for (let thread of keys(this._threads)) {
@@ -499,19 +484,35 @@ export class MainThreadComments extends Disposable implements MainThreadComments
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command,
collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined {
range: IRange
): modes.CommentThread2 | undefined {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.createCommentThread(commentThreadHandle, threadId, resource, range, comments, acceptInputCommand, additionalCommands, deleteCommand, collapseState);
return provider.createCommentThread(commentThreadHandle, threadId, resource, range);
}
$updateCommentThread(handle: number,
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
label: string,
comments: modes.Comment[],
acceptInputCommand: modes.Command | undefined,
additionalCommands: modes.Command[],
deleteCommand: modes.Command,
collapsibleState: modes.CommentThreadCollapsibleState): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.updateCommentThread(commentThreadHandle, threadId, resource, range, label, comments, acceptInputCommand, additionalCommands, deleteCommand, collapsibleState);
}
$deleteCommentThread(handle: number, commentThreadHandle: number) {
@@ -524,16 +525,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return provider.deleteCommentThread(commentThreadHandle);
}
$updateComments(handle: number, commentThreadHandle: number, comments: modes.Comment[]) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateComments(commentThreadHandle, comments);
}
$setInputValue(handle: number, input: string) {
let provider = this._commentControllers.get(handle);
@@ -544,66 +535,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments
provider.updateInput(input);
}
$updateCommentThreadAcceptInputCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateAcceptInputCommand(commentThreadHandle, acceptInputCommand);
}
$updateCommentThreadAdditionalCommands(handle: number, commentThreadHandle: number, additionalCommands: modes.Command[]) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateAdditionalCommands(commentThreadHandle, additionalCommands);
}
$updateCommentThreadDeleteCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateDeleteCommand(commentThreadHandle, acceptInputCommand);
}
$updateCommentThreadCollapsibleState(handle: number, commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCollapsibleState(commentThreadHandle, collapseState);
}
$updateCommentThreadRange(handle: number, commentThreadHandle: number, range: any): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCommentThreadRange(commentThreadHandle, range);
}
$updateCommentThreadLabel(handle: number, commentThreadHandle: number, label: string): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCommentThreadLabel(commentThreadHandle, label);
}
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void {
this._documentProviders.set(handle, undefined);
const handler = new MainThreadDocumentCommentProvider(this._proxy, handle, features);

View File

@@ -9,8 +9,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast';
import { EXTENSION_LOG_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug';
@extHostNamedCustomer(MainContext.MainThreadConsole)
export class MainThreadConsole implements MainThreadConsoleShape {
@@ -22,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
extHostContext: IExtHostContext,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IWindowsService private readonly _windowsService: IWindowsService,
@IBroadcastService private readonly _broadcastService: IBroadcastService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
) {
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
@@ -45,14 +44,8 @@ export class MainThreadConsole implements MainThreadConsoleShape {
}
// Broadcast to other windows if we are in development mode
else if (!this._environmentService.isBuilt || this._isExtensionDevHost) {
this._broadcastService.broadcast({
channel: EXTENSION_LOG_BROADCAST_CHANNEL,
payload: {
logEntry: entry,
debugId: this._environmentService.debugExtensionHost.debugId
}
});
else if (this._environmentService.debugExtensionHost.debugId && (!this._environmentService.isBuilt || this._isExtensionDevHost)) {
this._extensionHostDebugService.logToSession(this._environmentService.debugExtensionHost.debugId, entry);
}
}
}

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
}
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
private _keytar: IKeytarModule | null;
constructor(
extHostContext: IExtHostContext
) {
try {
this._keytar = <IKeytarModule>require.__$__nodeRequire('keytar');
} catch (e) {
this._keytar = null;
}
}
dispose(): void {
//
}
async $getPassword(service: string, account: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.getPassword(service, account);
}
return null;
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
if (this._keytar) {
return this._keytar.setPassword(service, account, password);
}
}
async $deletePassword(service: string, account: string): Promise<boolean> {
if (this._keytar) {
return this._keytar.deletePassword(service, account);
}
return false;
}
async $findPassword(service: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.findPassword(service);
}
return null;
}
}

View File

@@ -10,8 +10,8 @@ import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto } from '../common/extHost.protocol';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -22,6 +22,7 @@ import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IHeapService } from 'vs/workbench/services/heap/common/heap';
import { mixin } from 'vs/base/common/objects';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
@@ -150,8 +151,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return dto;
});
},
resolveCodeLens: (model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, model.uri, codeLens, token).then(obj => {
resolveCodeLens: (_model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token).then(obj => {
if (obj) {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
@@ -359,29 +360,56 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- suggest
private static _inflateSuggestDto(defaultRange: IRange, data: SuggestDataDto): modes.CompletionItem {
return {
label: data.a,
kind: data.b,
detail: data.c,
documentation: data.d,
sortText: data.e,
filterText: data.f,
preselect: data.g,
insertText: data.h || data.a,
insertTextRules: data.i,
range: data.j || defaultRange,
commitCharacters: data.k,
additionalTextEdits: data.l,
command: data.m,
// not-standard
_id: data.x,
_pid: data.y
};
}
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void {
this._registrations[handle] = modes.CompletionProviderRegistry.register(selector, <modes.CompletionItemProvider>{
const provider: modes.CompletionItemProvider = {
triggerCharacters,
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.CompletionContext, token: CancellationToken): Promise<modes.CompletionList | undefined> => {
return this._proxy.$provideCompletionItems(handle, model.uri, position, context, token).then(result => {
if (!result) {
return result;
// {{SQL CARBON EDIT}} @todo anthonydresser required because of strict null checks
return undefined;
}
return {
suggestions: result.suggestions,
incomplete: result.incomplete,
dispose: () => {
if (typeof result._id === 'number') {
this._proxy.$releaseCompletionItems(handle, result._id);
}
}
suggestions: result.b.map(d => MainThreadLanguageFeatures._inflateSuggestDto(result.a, d)),
incomplete: result.c,
dispose: () => this._proxy.$releaseCompletionItems(handle, result.x)
};
});
},
resolveCompletionItem: supportsResolveDetails
? (model, position, suggestion, token) => this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion, token)
: undefined
});
}
};
if (supportsResolveDetails) {
provider.resolveCompletionItem = (model, position, suggestion, token) => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, suggestion._pid, token).then(result => {
if (!result) {
return suggestion;
}
let newSuggestion = MainThreadLanguageFeatures._inflateSuggestDto(suggestion.range, result);
return mixin(suggestion, newSuggestion, true);
});
};
}
this._registrations[handle] = modes.CompletionProviderRegistry.register(selector, provider);
}
// --- parameter hints

View File

@@ -220,7 +220,7 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac
const runningExtensions = await extensionService.getExtensions();
// If requested extension to disable is running, then reload window with given workspace
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => ExtensionIdentifier.equals(runningExtension.identifier, id)))) {
return windowService.openWindow([{ uri: workspace, typeHint: 'file' }], { args: { _: [], 'disable-extension': disableExtensions } });
return windowService.openWindow([{ workspaceUri: workspace }], { args: { _: [], 'disable-extension': disableExtensions } });
}
}

View File

@@ -126,16 +126,10 @@ export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], deleteCommand: modes.Command | undefined, collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange): modes.CommentThread2 | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], deleteCommand: modes.Command | undefined, collapseState: modes.CommentThreadCollapsibleState): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$updateComments(handle: number, commentThreadHandle: number, comments: modes.Comment[]): void;
$setInputValue(handle: number, input: string): void;
$updateCommentThreadAcceptInputCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command): void;
$updateCommentThreadAdditionalCommands(handle: number, commentThreadHandle: number, additionalCommands: modes.Command[]): void;
$updateCommentThreadDeleteCommand(handle: number, commentThreadHandle: number, deleteCommand: modes.Command): void;
$updateCommentThreadCollapsibleState(handle: number, commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState): void;
$updateCommentThreadRange(handle: number, commentThreadHandle: number, range: IRange): void;
$updateCommentThreadLabel(handle: number, commentThreadHandle: number, label: string): void;
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void;
@@ -266,6 +260,13 @@ export interface MainThreadConsoleShape extends IDisposable {
}): void;
}
export interface MainThreadKeytarShape extends IDisposable {
$getPassword(service: string, account: string): Promise<string | null>;
$setPassword(service: string, account: string, password: string): Promise<void>;
$deletePassword(service: string, account: string): Promise<boolean>;
$findPassword(service: string): Promise<string | null>;
}
export interface ISerializedRegExp {
pattern: string;
flags?: string;
@@ -862,14 +863,30 @@ export class IdObject {
}
}
export interface SuggestionDto extends modes.CompletionItem {
_id: number;
_parentId: number;
export interface SuggestDataDto {
a/* label */: string;
b/* kind */: modes.CompletionItemKind;
c/* detail */?: string;
d/* documentation */?: string | IMarkdownString;
e/* sortText */?: string;
f/* filterText */?: string;
g/* preselect */?: boolean;
h/* insertText */?: string;
i/* insertTextRules */?: modes.CompletionItemInsertTextRule;
j/* range */?: IRange;
k/* commitCharacters */?: string[];
l/* additionalTextEdits */?: ISingleEditOperation[];
m/* command */?: modes.Command;
// not-standard
x: number;
y: number;
}
export interface SuggestResultDto extends IdObject {
suggestions: SuggestionDto[];
incomplete?: boolean;
export interface SuggestResultDto {
x: number;
a: IRange;
b: SuggestDataDto[];
c?: boolean;
}
export interface LocationDto {
@@ -970,7 +987,7 @@ export interface CallHierarchyDto {
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeInsetDto[] | undefined>;
$resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise<CodeInsetDto>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
@@ -990,7 +1007,7 @@ export interface ExtHostLanguageFeaturesShape {
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinkDto[] | undefined>;
@@ -1199,6 +1216,7 @@ export const MainContext = {
MainThreadTextEditors: createMainId<MainThreadTextEditorsShape>('MainThreadTextEditors'),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadKeytar: createMainId<MainThreadKeytarShape>('MainThreadKeytar'),
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages'),
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService'),

View File

@@ -34,7 +34,6 @@ namespace schema {
case 'explorer/context': return MenuId.ExplorerContext;
case 'editor/title/context': return MenuId.EditorTitleContext;
case 'debug/callstack/context': return MenuId.DebugCallStackContext;
case 'debug/toolbar': return MenuId.DebugToolBar;
case 'debug/toolBar': return MenuId.DebugToolBar;
case 'menuBar/file': return MenuId.MenubarFileMenu;
case 'scm/title': return MenuId.SCMTitle;

View File

@@ -33,6 +33,7 @@ import '../browser/mainThreadExtensionService';
import '../browser/mainThreadFileSystem';
import '../browser/mainThreadFileSystemEventService';
import '../browser/mainThreadHeapService';
import '../browser/mainThreadKeytar';
import '../browser/mainThreadLanguageFeatures';
import '../browser/mainThreadLanguages';
import '../browser/mainThreadLogService';

View File

@@ -7,30 +7,38 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IWebviewOptions } from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import product from 'vs/platform/product/node/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution';
import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorService';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IWebviewOptions } from 'vs/editor/common/modes';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
private static readonly standardSupportedLinkSchemes = new Set([
'http',
'https',
'mailto',
product.urlProtocol,
'vscode',
'vscode-insiders'
]);
private static revivalPool = 0;
@@ -104,7 +112,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
};
this._webviews.set(handle, webview);
this._activeWebview = handle;
/* __GDPR__
"webviews:createWebviewPanel" : {
@@ -200,9 +207,9 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
return;
}
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn));
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0);
if (targetGroup) {
this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.getGroup(webview.group || ACTIVE_GROUP), !!showOptions.preserveFocus);
this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
}
}
@@ -366,12 +373,18 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
const webview = this.getWebview(handle);
const enableCommandUris = webview.options.enableCommandUris;
if (MainThreadWebviews.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0 || enableCommandUris && link.scheme === 'command') {
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);
}
}
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
return !!webview.options.enableCommandUris && link.scheme === 'command';
}
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
const webview = this._webviews.get(handle);
if (!webview) {

View File

@@ -11,9 +11,9 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWindowsService, IOpenSettings } from 'vs/platform/windows/common/windows';
import { IWindowsService, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows';
import { IDownloadService } from 'vs/platform/download/common/download';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { IRecent } from 'vs/platform/history/common/history';
// -----------------------------------------------------------------
@@ -51,7 +51,8 @@ export class OpenFolderAPICommand {
}
const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
return executor.executeCommand('_files.windowOpen', [{ uri }], options);
const uriToOpen: IURIToOpen = hasWorkspaceFileExtension(uri.path) ? { workspaceUri: uri } : { folderUri: uri };
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
}
}
CommandsRegistry.registerCommand({

View File

@@ -19,7 +19,7 @@ import { score } from 'vs/editor/common/modes/languageSelector';
import * as files from 'vs/platform/files/common/files';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostContext, IInitData, IMainContext, MainContext, MainThreadKeytarShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
@@ -237,6 +237,7 @@ export function createApiFactory(
get language() { return platform.language!; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot!.fsPath; },
get uriScheme() { return product.urlProtocol; },
get logLevel() {
checkProposedApiEnabled(extension);
return typeConverters.LogLevel.to(extHostLogService.getLevel());
@@ -883,41 +884,105 @@ class Extension<T> implements vscode.Extension<T> {
}
}
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): Promise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry, configProvider));
interface INodeModuleFactory {
readonly nodeModuleName: string;
load(request: string, parent: { filename: string; }): any;
}
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree<IExtensionDescription>, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): void {
export class NodeModuleRequireInterceptor {
public static INSTANCE = new NodeModuleRequireInterceptor();
// each extension is meant to get its own api implementation
const extApiImpl = new Map<string, typeof vscode>();
let defaultApiImpl: typeof vscode;
private readonly _factories: Map<string, INodeModuleFactory>;
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: any, isMain: any) {
if (request !== 'vscode') {
return original.apply(this, arguments);
}
constructor() {
this._factories = new Map<string, INodeModuleFactory>();
this._installInterceptor(this._factories);
}
private _installInterceptor(factories: Map<string, INodeModuleFactory>): void {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
if (!factories.has(request)) {
return original.apply(this, arguments);
}
return factories.get(request)!.load(request, parent);
};
}
public register(interceptor: INodeModuleFactory): void {
this._factories.set(interceptor.nodeModuleName, interceptor);
}
}
export class VSCodeNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'vscode';
private readonly _extApiImpl = new Map<string, typeof vscode>();
private _defaultApiImpl: typeof vscode;
constructor(
private readonly _apiFactory: IExtensionApiFactory,
private readonly _extensionPaths: TernarySearchTree<IExtensionDescription>,
private readonly _extensionRegistry: ExtensionDescriptionRegistry,
private readonly _configProvider: ExtHostConfigProvider
) {
}
public load(request: string, parent: { filename: string; }): any {
// get extension id from filename and api for extension
const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
const ext = this._extensionPaths.findSubstr(URI.file(parent.filename).fsPath);
if (ext) {
let apiImpl = extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
let apiImpl = this._extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier));
if (!apiImpl) {
apiImpl = factory(ext, extensionRegistry, configProvider);
extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);
apiImpl = this._apiFactory(ext, this._extensionRegistry, this._configProvider);
this._extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl);
}
return apiImpl;
}
// fall back to a default implementation
if (!defaultApiImpl) {
if (!this._defaultApiImpl) {
let extensionPathsPretty = '';
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
this._extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
defaultApiImpl = factory(nullExtensionDescription, extensionRegistry, configProvider);
this._defaultApiImpl = this._apiFactory(nullExtensionDescription, this._extensionRegistry, this._configProvider);
}
return defaultApiImpl;
};
return this._defaultApiImpl;
}
}
interface IKeytarModule {
getPassword(service: string, account: string): Promise<string | null>;
setPassword(service: string, account: string, password: string): Promise<void>;
deletePassword(service: string, account: string): Promise<boolean>;
findPassword(service: string): Promise<string | null>;
}
export class KeytarNodeModuleFactory implements INodeModuleFactory {
public readonly nodeModuleName = 'keytar';
private _impl: IKeytarModule;
constructor(mainThreadKeytar: MainThreadKeytarShape) {
this._impl = {
getPassword: (service: string, account: string): Promise<string | null> => {
return mainThreadKeytar.$getPassword(service, account);
},
setPassword: (service: string, account: string, password: string): Promise<void> => {
return mainThreadKeytar.$setPassword(service, account, password);
},
deletePassword: (service: string, account: string): Promise<boolean> => {
return mainThreadKeytar.$deletePassword(service, account);
},
findPassword: (service: string): Promise<string | null> => {
return mainThreadKeytar.$findPassword(service);
}
};
}
public load(request: string, parent: { filename: string; }): any {
return this._impl;
}
}

View File

@@ -7,7 +7,7 @@ import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
import * as http from 'http';
import * as fs from 'fs';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IURIToOpen, URIType, IOpenSettings } from 'vs/platform/windows/common/windows';
import { IURIToOpen, IOpenSettings } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
@@ -55,17 +55,6 @@ export class CLIServer {
return this._ipcHandlePath;
}
private collectURIToOpen(strs: string[] | undefined, typeHint: URIType, result: IURIToOpen[]): void {
if (Array.isArray(strs)) {
for (const s of strs) {
try {
result.push({ uri: URI.parse(s), typeHint });
} catch (e) {
// ignore
}
}
}
}
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
const chunks: string[] = [];
@@ -95,13 +84,32 @@ export class CLIServer {
private open(data: OpenCommandPipeArgs, res: http.ServerResponse) {
let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow, waitMarkerFilePath } = data;
if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) {
const urisToOpen: IURIToOpen[] = [];
this.collectURIToOpen(folderURIs, 'folder', urisToOpen);
this.collectURIToOpen(fileURIs, 'file', urisToOpen);
if (!forceReuseWindow && urisToOpen.some(o => o.typeHint === 'folder' || (o.typeHint === 'file' && hasWorkspaceFileExtension(o.uri.path)))) {
forceNewWindow = true;
const urisToOpen: IURIToOpen[] = [];
if (Array.isArray(folderURIs)) {
for (const s of folderURIs) {
try {
urisToOpen.push({ folderUri: URI.parse(s) });
forceNewWindow = true;
} catch (e) {
// ignore
}
}
}
if (Array.isArray(fileURIs)) {
for (const s of fileURIs) {
try {
if (hasWorkspaceFileExtension(s)) {
urisToOpen.push({ workspaceUri: URI.parse(s) });
forceNewWindow = true;
} else {
urisToOpen.push({ fileUri: URI.parse(s) });
}
} catch (e) {
// ignore
}
}
}
if (urisToOpen.length) {
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
const windowOpenArgs: IOpenSettings = { forceNewWindow, diffMode, addMode, forceReuseWindow, waitMarkerFileURI };
this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs);

View File

@@ -16,6 +16,7 @@ import { IRange } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
interface HandlerData<T> {
@@ -360,10 +361,13 @@ export class ExtHostCommentThread implements vscode.CommentThread {
return this._resource;
}
private _onDidUpdateCommentThread = new Emitter<void>();
readonly onDidUpdateCommentThread = this._onDidUpdateCommentThread.event;
set range(range: vscode.Range) {
if (range.isEqual(this._range)) {
this._range = range;
this._proxy.$updateCommentThreadRange(this._commentController.handle, this.handle, extHostTypeConverter.Range.from(this._range));
this._onDidUpdateCommentThread.fire();
}
}
@@ -379,7 +383,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set label(label: string) {
this._label = label;
this._proxy.$updateCommentThreadLabel(this._commentController.handle, this.handle, this._label);
this._onDidUpdateCommentThread.fire();
}
get comments(): vscode.Comment[] {
@@ -387,8 +391,8 @@ export class ExtHostCommentThread implements vscode.CommentThread {
}
set comments(newComments: vscode.Comment[]) {
this._proxy.$updateComments(this._commentController.handle, this.handle, newComments.map(cmt => { return convertToModeComment(this._commentController, cmt, this._commandsConverter); }));
this._comments = newComments;
this._onDidUpdateCommentThread.fire();
}
private _acceptInputCommand: vscode.Command;
@@ -398,9 +402,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set acceptInputCommand(acceptInputCommand: vscode.Command) {
this._acceptInputCommand = acceptInputCommand;
const internal = this._commandsConverter.toInternal(acceptInputCommand);
this._proxy.$updateCommentThreadAcceptInputCommand(this._commentController.handle, this.handle, internal);
this._onDidUpdateCommentThread.fire();
}
private _additionalCommands: vscode.Command[] = [];
@@ -410,9 +412,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set additionalCommands(additionalCommands: vscode.Command[]) {
this._additionalCommands = additionalCommands;
const internals = additionalCommands.map(x => this._commandsConverter.toInternal(x));
this._proxy.$updateCommentThreadAdditionalCommands(this._commentController.handle, this.handle, internals);
this._onDidUpdateCommentThread.fire();
}
private _deleteCommand?: vscode.Command;
@@ -422,9 +422,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set deleteCommand(deleteCommand: vscode.Command) {
this._deleteCommand = deleteCommand;
const internal = this._commandsConverter.toInternal(deleteCommand);
this._proxy.$updateCommentThreadDeleteCommand(this._commentController.handle, this.handle, internal);
this._onDidUpdateCommentThread.fire();
}
private _collapseState?: vscode.CommentThreadCollapsibleState;
@@ -435,9 +433,11 @@ export class ExtHostCommentThread implements vscode.CommentThread {
set collapsibleState(newState: vscode.CommentThreadCollapsibleState) {
this._collapseState = newState;
this._proxy.$updateCommentThreadCollapsibleState(this._commentController.handle, this.handle, convertToCollapsibleState(newState));
this._onDidUpdateCommentThread.fire();
}
private _localDisposables: types.Disposable[];
constructor(
private _proxy: MainThreadCommentsShape,
private readonly _commandsConverter: CommandsConverter,
@@ -452,12 +452,41 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this.handle,
this._threadId,
this._resource,
extHostTypeConverter.Range.from(this._range),
this._comments.map(comment => { return convertToModeComment(this._commentController, comment, this._commandsConverter); }),
this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand) : undefined,
this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x)) : [],
this._deleteCommand ? this._commandsConverter.toInternal(this._deleteCommand) : undefined,
this._collapseState!
extHostTypeConverter.Range.from(this._range)
);
this._localDisposables = [];
this._localDisposables.push(this.onDidUpdateCommentThread(() => {
this.eventuallyUpdateCommentThread();
}));
// set up comments after ctor to batch update events.
this.comments = _comments;
}
@debounce(100)
eventuallyUpdateCommentThread(): void {
const commentThreadRange = extHostTypeConverter.Range.from(this._range);
const label = this.label;
const comments = this._comments.map(cmt => { return convertToModeComment(this._commentController, cmt, this._commandsConverter); });
const acceptInputCommand = this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand) : undefined;
const additionalCommands = this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x)) : [];
const deleteCommand = this._deleteCommand ? this._commandsConverter.toInternal(this._deleteCommand) : undefined;
const collapsibleState = convertToCollapsibleState(this._collapseState);
this._proxy.$updateCommentThread(
this._commentController.handle,
this.handle,
this._threadId,
this._resource,
commentThreadRange,
label,
comments,
acceptInputCommand,
additionalCommands,
deleteCommand,
collapsibleState
);
}
@@ -472,6 +501,7 @@ export class ExtHostCommentThread implements vscode.CommentThread {
}
dispose() {
this._localDisposables.forEach(disposable => disposable.dispose());
this._proxy.$deleteCommentThread(
this._commentController.handle,
this.handle
@@ -607,7 +637,7 @@ function convertFromCommentThread(commentThread: modes.CommentThread): vscode.Co
threadId: commentThread.threadId!,
resource: URI.parse(commentThread.resource!),
range: extHostTypeConverter.Range.to(commentThread.range),
comments: commentThread.comments.map(convertFromComment),
comments: commentThread.comments ? commentThread.comments.map(convertFromComment) : [],
collapsibleState: commentThread.collapsibleState
};
}

View File

@@ -15,6 +15,8 @@ import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports
//import { createApiFactory, IExtensionApiFactory, NodeModuleRequireInterceptor, VSCodeNodeModuleFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IStaticWorkspaceData } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule, HostExtension } from 'vs/workbench/api/node/extHostExtensionActivator';
@@ -32,6 +34,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { withNullAsUndefined } from 'vs/base/common/types';
import { realpath } from 'vs/base/node/extpath';
class ExtensionMemento implements IExtensionMemento {
@@ -243,7 +246,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private async _initialize(): Promise<void> {
try {
const configProvider = await this._extHostConfiguration.getConfigProvider();
// {{SQL CARBON EDIT}} - disable VSCodeNodeModuleFactory and use older initializeExtensionApi
// const extensionPaths = await this.getExtensionPathIndex();
// NodeModuleRequireInterceptor.INSTANCE.register(new VSCodeNodeModuleFactory(this._extensionApiFactory, extensionPaths, this._registry, configProvider));
await initializeExtensionApi(this, this._extensionApiFactory, this._registry, configProvider);
NodeModuleRequireInterceptor.INSTANCE.register(new KeytarNodeModuleFactory(this._extHostContext.getProxy(MainContext.MainThreadKeytar)));
// Do this when extension service exists, but extensions are not being activated yet.
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy);
this._almostReadyToRunExtensions.open();
@@ -318,7 +327,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!ext.main) {
return undefined;
}
return pfs.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
@@ -735,13 +744,13 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
if (!extensionDescription) {
return;
}
const realpath = await pfs.realpath(extensionDescription.extensionLocation.fsPath);
trie.delete(URI.file(realpath).fsPath);
const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
trie.delete(URI.file(realpathValue).fsPath);
}));
await Promise.all(toAdd.map(async (extensionDescription) => {
const realpath = await pfs.realpath(extensionDescription.extensionLocation.fsPath);
trie.set(URI.file(realpath).fsPath, extensionDescription);
const realpathValue = await realpath(extensionDescription.extensionLocation.fsPath);
trie.set(URI.file(realpathValue).fsPath, extensionDescription);
}));
this._registry.deltaExtensions(toAdd, toRemove);

View File

@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto } from '../common/extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto } from '../common/extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
@@ -129,7 +129,7 @@ class CodeLensAdapter {
});
}
resolveCodeLens(resource: URI, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
resolveCodeLens(symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
if (!lens) {
@@ -637,15 +637,18 @@ class SuggestAdapter {
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
return asPromise<vscode.CompletionItem[] | vscode.CompletionList | null | undefined>(
() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))
).then(value => {
return asPromise(() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))).then(value => {
const _id = this._idPool++;
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
.with({ end: pos });
const result: SuggestResultDto = {
_id,
suggestions: [],
x: _id,
b: [],
a: typeConvert.Range.from(wordRangeBeforePos),
};
let list: CompletionList;
@@ -658,54 +661,45 @@ class SuggestAdapter {
} else {
list = value;
result.incomplete = list.isIncomplete;
result.c = list.isIncomplete;
}
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
.with({ end: pos });
for (let i = 0; i < list.items.length; i++) {
const suggestion = this._convertCompletionItem(list.items[i], pos, wordRangeBeforePos, i, _id);
const suggestion = this._convertCompletionItem2(list.items[i], pos, i, _id);
// check for bad completion item
// for the converter did warn
if (suggestion) {
result.suggestions.push(suggestion);
result.b.push(suggestion);
}
}
this._cache.set(_id, list.items);
if (SuggestAdapter.supportsResolving(this._provider)) {
this._cache.set(_id, list.items);
}
return result;
});
}
resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem> {
resolveCompletionItem(_resource: URI, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined> {
if (typeof this._provider.resolveCompletionItem !== 'function') {
return Promise.resolve(suggestion);
return Promise.resolve(undefined);
}
const { _parentId, _id } = (<SuggestionDto>suggestion);
const item = this._cache.has(_parentId) ? this._cache.get(_parentId)![_id] : undefined;
const item = this._cache.has(pid) ? this._cache.get(pid)![id] : undefined;
if (!item) {
return Promise.resolve(suggestion);
return Promise.resolve(undefined);
}
return asPromise(() => this._provider.resolveCompletionItem!(item, token)).then(resolvedItem => {
if (!resolvedItem) {
return suggestion;
return undefined;
}
const doc = this._documents.getDocument(resource);
const pos = typeConvert.Position.to(position);
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos)).with({ end: pos });
const newSuggestion = this._convertCompletionItem(resolvedItem, pos, wordRangeBeforePos, _id, _parentId);
if (newSuggestion) {
mixin(suggestion, newSuggestion, true);
}
return suggestion;
return this._convertCompletionItem2(resolvedItem, pos, id, pid);
});
}
@@ -713,60 +707,52 @@ class SuggestAdapter {
this._cache.delete(id);
}
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, defaultRange: vscode.Range, _id: number, _parentId: number): SuggestionDto | undefined {
private _convertCompletionItem2(item: vscode.CompletionItem, position: vscode.Position, id: number, pid: number): SuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
return undefined;
}
const result: SuggestionDto = {
const result: SuggestDataDto = {
//
_id,
_parentId,
x: id,
y: pid,
//
label: item.label,
kind: typeConvert.CompletionItemKind.from(item.kind),
detail: item.detail,
documentation: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
filterText: item.filterText,
sortText: item.sortText,
preselect: item.preselect,
//
range: undefined!, // populated below
insertText: undefined!, // populated below
insertTextRules: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
command: this._commands.toInternal(item.command),
commitCharacters: item.commitCharacters
a: item.label,
b: typeConvert.CompletionItemKind.from(item.kind),
c: item.detail,
d: typeof item.documentation === 'undefined' ? undefined : typeConvert.MarkdownString.fromStrict(item.documentation),
e: item.sortText,
f: item.filterText,
g: item.preselect,
i: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command),
};
// 'insertText'-logic
if (item.textEdit) {
result.insertText = item.textEdit.newText;
result.h = item.textEdit.newText;
} else if (typeof item.insertText === 'string') {
result.insertText = item.insertText;
result.h = item.insertText;
} else if (item.insertText instanceof SnippetString) {
result.insertText = item.insertText.value;
result.insertTextRules! |= modes.CompletionItemInsertTextRule.InsertAsSnippet;
} else {
result.insertText = item.label;
result.h = item.insertText.value;
result.i! |= modes.CompletionItemInsertTextRule.InsertAsSnippet;
}
// 'overwrite[Before|After]'-logic
let range: vscode.Range;
let range: vscode.Range | undefined;
if (item.textEdit) {
range = item.textEdit.range;
} else if (item.range) {
range = item.range;
} else {
range = defaultRange;
}
result.range = typeConvert.Range.from(range);
result.j = typeConvert.Range.from(range);
if (!range.isSingleLine || range.start.line !== position.line) {
if (range && (!range.isSingleLine || range.start.line !== position.line)) {
console.warn('INVALID text edit -> must be single line and on the same line');
return undefined;
}
@@ -1181,8 +1167,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), []);
}
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token), undefined);
$resolveCodeLens(handle: number, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined);
}
// --- code insets
@@ -1387,8 +1373,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined);
}
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion, token), suggestion);
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, id: number, pid: number, token: CancellationToken): Promise<SuggestDataDto | undefined> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, id, pid, token), undefined);
}
$releaseCompletionItems(handle: number, id: number): void {

View File

@@ -163,7 +163,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
constructor(logsLocation: URI, mainContext: IMainContext) {
const outputDirPath = join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
this._outputDir = dirExists(outputDirPath).then(exists => exists ? exists : mkdirp(outputDirPath)).then(() => outputDirPath);
this._outputDir = dirExists(outputDirPath).then(exists => exists ? exists : mkdirp(outputDirPath).then(() => true)).then(() => outputDirPath);
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
}

View File

@@ -6,7 +6,7 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as extfs from 'vs/base/node/extfs';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery, isSerializedFileMatch, ISerializedSearchProgressItem } from 'vs/workbench/services/search/common/search';
import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager';
@@ -35,7 +35,7 @@ export class ExtHostSearch implements ExtHostSearchShape {
private _fileSearchManager: FileSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer | null, private _logService: ILogService, private _extfs = extfs) {
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer | null, private _logService: ILogService, private _pfs = pfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
}
@@ -146,7 +146,7 @@ export class ExtHostSearch implements ExtHostSearchShape {
}
const query = reviveQuery(rawQuery);
const engine = new TextSearchManager(query, provider, this._extfs);
const engine = new TextSearchManager(query, provider, this._pfs);
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
}
}

View File

@@ -3,7 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import { coalesce, equals } from 'vs/base/common/arrays';
import { illegalArgument } from 'vs/base/common/errors';
import { IRelativePattern } from 'vs/base/common/glob';
@@ -1545,6 +1544,14 @@ export class TaskGroup implements vscode.TaskGroup {
}
}
function computeTaskExecutionId(values: string[]): string {
let id: string = '';
for (let i = 0; i < values.length; i++) {
id += values[i].replace(/,/g, ',,') + ',';
}
return id;
}
@es5ClassCompat
export class ProcessExecution implements vscode.ProcessExecution {
@@ -1604,17 +1611,17 @@ export class ProcessExecution implements vscode.ProcessExecution {
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('process');
const props: string[] = [];
props.push('process');
if (this._process !== undefined) {
hash.update(this._process);
props.push(this._process);
}
if (this._args && this._args.length > 0) {
for (let arg of this._args) {
hash.update(arg);
props.push(arg);
}
}
return hash.digest('hex');
return computeTaskExecutionId(props);
}
}
@@ -1687,20 +1694,20 @@ export class ShellExecution implements vscode.ShellExecution {
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('shell');
const props: string[] = [];
props.push('shell');
if (this._commandLine !== undefined) {
hash.update(this._commandLine);
props.push(this._commandLine);
}
if (this._command !== undefined) {
hash.update(typeof this._command === 'string' ? this._command : this._command.value);
props.push(typeof this._command === 'string' ? this._command : this._command.value);
}
if (this._args && this._args.length > 0) {
for (let arg of this._args) {
hash.update(typeof arg === 'string' ? arg : arg.value);
props.push(typeof arg === 'string' ? arg : arg.value);
}
}
return hash.digest('hex');
return computeTaskExecutionId(props);
}
}
@@ -1723,10 +1730,7 @@ export class CustomExecution implements vscode.CustomExecution {
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('customExecution');
hash.update(generateUuid());
return hash.digest('hex');
return 'customExecution' + generateUuid();
}
public set callback(value: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {

View File

@@ -17,14 +17,14 @@ export class ActionBarContributor {
/**
* Returns true if this contributor has actions for the given context.
*/
hasActions(context: any): boolean {
hasActions(context: unknown): boolean {
return false;
}
/**
* Returns an array of primary actions in the given context.
*/
getActions(context: any): IAction[] {
getActions(context: unknown): IAction[] {
return [];
}
}
@@ -46,14 +46,14 @@ export const Scope = {
export class ContributableActionProvider implements IActionProvider {
private readonly registry: IActionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
private toContext(tree: ITree, element: any): any {
private toContext(tree: ITree, element: unknown): unknown {
return {
viewer: tree,
element: element
};
}
hasActions(tree: ITree, element: any): boolean {
hasActions(tree: ITree, element: unknown): boolean {
const context = this.toContext(tree, element);
const contributors = this.registry.getActionBarContributors(Scope.VIEWER);
@@ -66,7 +66,7 @@ export class ContributableActionProvider implements IActionProvider {
return false;
}
getActions(tree: ITree, element: any): IAction[] {
getActions(tree: ITree, element: unknown): IAction[] {
const actions: IAction[] = [];
const context = this.toContext(tree, element);

View File

@@ -233,12 +233,10 @@ export class ToggleEditorVisibilityAction extends Action {
return Promise.resolve();
}
}
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory, ContextKeyExpr.equals('config.workbench.useExperimentalGridLayout', true));
export class ToggleSidebarVisibilityAction extends Action {
static readonly ID = 'workbench.action.toggleSidebarVisibility';

View File

@@ -80,7 +80,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler: (accessor, arg2) => focusDown(accessor, arg2)
});
function expandMultiSelection(focused: List<any> | PagedList<any> | ITree | ObjectTree<any, any> | DataTree<any, any, any> | AsyncDataTree<any, any, any>, previousFocus: any): void {
function expandMultiSelection(focused: List<unknown> | PagedList<unknown> | ITree | ObjectTree<unknown, unknown> | DataTree<unknown, unknown, unknown> | AsyncDataTree<unknown, unknown, unknown>, previousFocus: unknown): void {
// List
if (focused instanceof List || focused instanceof PagedList) {
@@ -625,7 +625,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const selection = tree.getSelection();
// Which element should be considered to start selecting all?
let start: any | undefined = undefined;
let start: unknown | undefined = undefined;
if (focus.length > 0 && (selection.length === 0 || selection.indexOf(focus[0]) === -1)) {
start = focus[0];
@@ -636,7 +636,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
// What is the scope of select all?
let scope: any | undefined = undefined;
let scope: unknown | undefined = undefined;
if (!start) {
scope = undefined;
@@ -651,8 +651,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
}
}
const newSelection: any[] = [];
const visit = (node: ITreeNode<any, any>) => {
const newSelection: unknown[] = [];
const visit = (node: ITreeNode<unknown, unknown>) => {
for (const child of node.children) {
if (child.visible) {
newSelection.push(child.element);

View File

@@ -300,7 +300,7 @@ export class DuplicateWorkspaceInNewWindowAction extends Action {
return this.workspacesService.createUntitledWorkspace(folders, remoteAuthority).then(newWorkspace => {
return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => {
return this.windowService.openWindow([{ uri: newWorkspace.configPath, typeHint: 'file' }], { forceNewWindow: true });
return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true });
});
});
}

View File

@@ -89,13 +89,13 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc
return undefined;
}
const folderPicks = folders.map(folder => {
const folderPicks: IQuickPickItem[] = folders.map(folder => {
return {
label: folder.name,
description: labelService.getUriLabel(resources.dirname(folder.uri), { relative: true }),
folder,
iconClasses: getIconClasses(modelService, modeService, folder.uri, FileKind.ROOT_FOLDER)
} as IQuickPickItem;
};
});
const options: IPickOptions<IQuickPickItem> = (args ? args[0] : undefined) || Object.create(null);

View File

@@ -176,7 +176,7 @@ export abstract class Composite extends Component implements IComposite {
/**
* Provide a context to be passed to the toolbar.
*/
getActionsContext(): any {
getActionsContext(): unknown {
return null;
}
@@ -210,10 +210,10 @@ export abstract class Composite extends Component implements IComposite {
}
/**
* Returns the underlying composite control or null if it is not accessible.
* Returns the underlying composite control or `undefined` if it is not accessible.
*/
getControl(): ICompositeControl | null {
return null;
getControl(): ICompositeControl | undefined {
return undefined;
}
}
@@ -257,7 +257,7 @@ export abstract class CompositeRegistry<T extends Composite> extends Disposable
protected deregisterComposite(id: string): void {
const descriptor = this.compositeById(id);
if (descriptor === null) {
if (!descriptor) {
return;
}

View File

@@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContext } from 'vs/platform/contextkey/common/contextkeys';
import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows';
import { IWindowService, IWindowsConfiguration } from 'vs/platform/windows/common/windows';
import { ActiveEditorContext, EditorsVisibleContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, TEXT_DIFF_EDITOR_ID, SplitEditorsVertically, InEditorZenModeContext } from 'vs/workbench/common/editor';
import { IsMacContext, IsLinuxContext, IsWindowsContext, HasMacNativeTabsContext, IsDevelopmentContext, SupportsWorkspacesContext, SupportsOpenFileFolderContext, WorkbenchStateContext, WorkspaceFolderCountContext, IsRemoteContext } from 'vs/workbench/common/contextkeys';
import { trackFocus, addDisposableListener, EventType } from 'vs/base/browser/dom';
@@ -16,7 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { SidebarVisibleContext, SideBarVisibleContext } from 'vs/workbench/common/viewlet';
import { SideBarVisibleContext } from 'vs/workbench/common/viewlet';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -38,8 +38,6 @@ export class WorkbenchContextKeysHandler extends Disposable {
private inZenModeContext: IContextKey<boolean>;
private sideBarVisibleContext: IContextKey<boolean>;
//TODO@Isidor remove in May
private sidebarVisibleContext: IContextKey<boolean>;
constructor(
@IContextKeyService private contextKeyService: IContextKeyService,
@@ -93,7 +91,7 @@ export class WorkbenchContextKeysHandler extends Disposable {
IsRemoteContext.bindTo(this.contextKeyService).set(!!this.windowService.getConfiguration().remoteAuthority);
// macOS Native Tabs
const windowConfig = this.configurationService.getValue<IWindowConfiguration>();
const windowConfig = this.configurationService.getValue<IWindowsConfiguration>();
HasMacNativeTabsContext.bindTo(this.contextKeyService).set(windowConfig && windowConfig.window && windowConfig.window.nativeTabs);
// Development
@@ -131,7 +129,6 @@ export class WorkbenchContextKeysHandler extends Disposable {
// Sidebar
this.sideBarVisibleContext = SideBarVisibleContext.bindTo(this.contextKeyService);
this.sidebarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService);
}
private updateEditorContextKeys(): void {
@@ -208,6 +205,5 @@ export class WorkbenchContextKeysHandler extends Disposable {
private updateSideBarContextKeys(): void {
this.sideBarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));
this.sidebarVisibleContext.set(this.layoutService.isVisible(Parts.SIDEBAR_PART));
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { normalize } from 'vs/base/common/path';
import { basename } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
@@ -79,7 +79,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
const rawEditorsData = e.dataTransfer.getData(CodeDataTransfers.EDITORS);
if (rawEditorsData) {
try {
const draggedEditors = JSON.parse(rawEditorsData) as ISerializedDraggedEditor[];
const draggedEditors: ISerializedDraggedEditor[] = JSON.parse(rawEditorsData);
draggedEditors.forEach(draggedEditor => {
resources.push({ resource: URI.parse(draggedEditor.resource), backupResource: draggedEditor.backupResource ? URI.parse(draggedEditor.backupResource) : undefined, viewState: draggedEditor.viewState, isExternal: false });
});
@@ -105,7 +105,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
// Check for native file transfer
if (e.dataTransfer && e.dataTransfer.files) {
for (let i = 0; i < e.dataTransfer.files.length; i++) {
const file = e.dataTransfer.files[i] as { path: string };
const file = e.dataTransfer.files[i];
if (file && file.path && !resources.some(r => r.resource.fsPath === file.path) /* prevent duplicates */) {
try {
resources.push({ resource: URI.file(file.path), isExternal: true });
@@ -120,7 +120,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array<ID
const rawCodeFiles = e.dataTransfer.getData(CodeDataTransfers.FILES);
if (rawCodeFiles) {
try {
const codeFiles = JSON.parse(rawCodeFiles) as string[];
const codeFiles: string[] = JSON.parse(rawCodeFiles);
codeFiles.forEach(codeFile => {
if (!resources.some(r => r.resource.fsPath === codeFile) /* prevent duplicates */) {
resources.push({ resource: URI.file(codeFile), isExternal: true });
@@ -254,16 +254,14 @@ export class ResourcesDropHandler {
}
private handleWorkspaceFileDrop(fileOnDiskResources: URI[]): Promise<boolean> {
const workspaceResources: { workspaces: IURIToOpen[], folders: IURIToOpen[] } = {
workspaces: [],
folders: []
};
const urisToOpen: IURIToOpen[] = [];
const folderURIs: IWorkspaceFolderCreationData[] = [];
return Promise.all(fileOnDiskResources.map(fileOnDiskResource => {
// Check for Workspace
if (hasWorkspaceFileExtension(fileOnDiskResource.fsPath)) {
workspaceResources.workspaces.push({ uri: fileOnDiskResource, typeHint: 'file' });
urisToOpen.push({ workspaceUri: fileOnDiskResource });
return undefined;
}
@@ -271,14 +269,14 @@ export class ResourcesDropHandler {
// Check for Folder
return this.fileService.resolve(fileOnDiskResource).then(stat => {
if (stat.isDirectory) {
workspaceResources.folders.push({ uri: stat.resource, typeHint: 'folder' });
urisToOpen.push({ folderUri: stat.resource });
folderURIs.push({ uri: stat.resource });
}
}, error => undefined);
})).then(_ => {
const { workspaces, folders } = workspaceResources;
// Return early if no external resource is a folder or workspace
if (workspaces.length === 0 && folders.length === 0) {
if (urisToOpen.length === 0) {
return false;
}
@@ -286,12 +284,12 @@ export class ResourcesDropHandler {
this.windowService.focusWindow();
// Open in separate windows if we drop workspaces or just one folder
if (workspaces.length > 0 || folders.length === 1) {
return this.windowService.openWindow([...workspaces, ...folders], { forceReuseWindow: true }).then(_ => true);
if (urisToOpen.length > folderURIs.length || folderURIs.length === 1) {
return this.windowService.openWindow(urisToOpen, { forceReuseWindow: true }).then(_ => true);
}
// folders.length > 1: Multiple folders: Create new workspace with folders and open
return this.workspaceEditingService.createAndEnterWorkspace(folders).then(_ => true);
return this.workspaceEditingService.createAndEnterWorkspace(folderURIs).then(_ => true);
});
}
}

View File

@@ -16,7 +16,7 @@ export interface IEditorDescriptor {
getId(): string;
getName(): string;
describes(obj: any): boolean;
describes(obj: unknown): boolean;
}
export interface IEditorRegistry {
@@ -76,7 +76,7 @@ export class EditorDescriptor implements IEditorDescriptor {
return this.name;
}
describes(obj: any): boolean {
describes(obj: unknown): boolean {
return obj instanceof BaseEditor && (<BaseEditor>obj).getId() === this.id;
}
}
@@ -88,7 +88,7 @@ class EditorRegistry implements IEditorRegistry {
registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>): void;
registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>[]): void;
registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: any): void {
registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput> | SyncDescriptor<EditorInput>[]): void {
// Support both non-array and array parameter
let inputDescriptors: SyncDescriptor<EditorInput>[] = [];

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI as uri } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -27,7 +27,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IResourceLabelProps {
resource?: uri;
resource?: URI;
name?: string;
description?: string;
}
@@ -61,7 +61,7 @@ export interface IResourceLabel extends IDisposable {
/**
* Convinient method to render a file label based on a resource.
*/
setFile(resource: uri, options?: IFileLabelOptions): void;
setFile(resource: URI, options?: IFileLabelOptions): void;
/**
* Convinient method to apply a label by passing an editor along.
@@ -151,7 +151,7 @@ export class ResourceLabels extends Disposable {
setLabel: (label?: string, description?: string, options?: IIconLabelValueOptions) => widget.setLabel(label, description, options),
setResource: (label: IResourceLabelProps, options?: IResourceLabelOptions) => widget.setResource(label, options),
setEditor: (editor: IEditorInput, options?: IResourceLabelOptions) => widget.setEditor(editor, options),
setFile: (resource: uri, options?: IFileLabelOptions) => widget.setFile(resource, options),
setFile: (resource: URI, options?: IFileLabelOptions) => widget.setFile(resource, options),
clear: () => widget.clear(),
dispose: () => this.disposeWidget(widget)
};
@@ -338,7 +338,7 @@ class ResourceLabelWidget extends IconLabel {
}, options);
}
setFile(resource: uri, options?: IFileLabelOptions): void {
setFile(resource: URI, options?: IFileLabelOptions): void {
const hideLabel = options && options.hideLabel;
let name: string | undefined;
if (!hideLabel) {

View File

@@ -457,9 +457,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
let input: IResourceInput | IUntitledResourceInput;
if (isNew) {
input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;
input = { filePath: resource.fsPath, options: { pinned: true } };
} else {
input = { resource, options: { pinned: true }, forceFile: true } as IResourceInput;
input = { resource, options: { pinned: true }, forceFile: true };
}
if (!isNew && typeof p.lineNumber === 'number') {

View File

@@ -669,6 +669,8 @@ export class SimpleRemoteAuthorityResolverService implements IRemoteAuthorityRes
return Promise.resolve(undefined);
}
clearResolvedAuthority(authority: string): void { }
setResolvedAuthority(resolvedAuthority: ResolvedAuthority): void { }
setResolvedAuthorityError(authority: string, err: any): void { }
@@ -694,6 +696,7 @@ export class SimpleRemoteFileService implements IFileService {
readonly onAfterOperation = Event.None;
readonly onDidChangeFileSystemProviderRegistrations = Event.None;
readonly onWillActivateFileSystemProvider = Event.None;
readonly onError = Event.None;
resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStatWithMetadata> {
// @ts-ignore
@@ -798,13 +801,11 @@ export class SimpleRemoteFileService implements IFileService {
canHandleResource(resource: URI): boolean { return resource.scheme === 'file'; }
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): Promise<boolean> { return Promise.resolve(false); }
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean { return false; }
del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { return Promise.resolve(); }
watch(_resource: URI): void { }
unwatch(_resource: URI): void { }
watch(_resource: URI): IDisposable { return Disposable.None; }
getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }

View File

@@ -45,7 +45,7 @@ export class PanelRegistry extends CompositeRegistry<Panel> {
* Returns an array of registered panels known to the platform.
*/
getPanels(): PanelDescriptor[] {
return this.getComposites() as PanelDescriptor[];
return this.getComposites();
}
/**

View File

@@ -14,7 +14,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { prepareActions } from 'vs/workbench/browser/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { IAction } from 'vs/base/common/actions';
import { Part, IPartOptions } from 'vs/workbench/browser/part';
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
@@ -399,7 +399,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Toolbar
this.toolBar = this._register(new ToolBar(titleActionsContainer, this.contextMenuService, {
actionItemProvider: action => this.actionItemProvider(action as Action),
actionItemProvider: action => this.actionItemProvider(action),
orientation: ActionsOrientation.HORIZONTAL,
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment()
@@ -432,7 +432,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
this.titleLabel.updateStyles();
}
protected actionItemProvider(action: Action): IActionItem | undefined {
protected actionItemProvider(action: IAction): IActionItem | undefined {
// Check Active Composite
if (this.activeComposite) {
@@ -442,7 +442,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
return undefined;
}
protected actionsContextProvider(): any {
protected actionsContextProvider(): unknown {
// Check Active Composite
if (this.activeComposite) {

View File

@@ -248,7 +248,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
}
}
private doGetResource(resourceOrEditor: URI | EditorInput): URI | null {
private doGetResource(resourceOrEditor: URI | EditorInput): URI | undefined {
if (resourceOrEditor instanceof EditorInput) {
return resourceOrEditor.getResource();
}

View File

@@ -40,7 +40,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
private metadata: string | undefined;
private binaryContainer: HTMLElement;
private scrollbar: DomScrollableElement;
private resourceViewerContext: ResourceViewerContext;
private resourceViewerContext: ResourceViewerContext | undefined;
constructor(
id: string,
@@ -127,7 +127,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Clear Resource Viewer
clearNode(this.binaryContainer);
this.resourceViewerContext = dispose(this.resourceViewerContext);
dispose(this.resourceViewerContext);
this.resourceViewerContext = undefined;
super.clearInput();
}
@@ -149,7 +150,8 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
dispose(): void {
this.binaryContainer.remove();
this.resourceViewerContext = dispose(this.resourceViewerContext);
dispose(this.resourceViewerContext);
this.resourceViewerContext = undefined;
super.dispose();
}

View File

@@ -30,7 +30,7 @@ export class ExecuteCommandAction extends Action {
label: string,
private commandId: string,
private commandService: ICommandService,
private commandArgs?: any
private commandArgs?: unknown
) {
super(id, label);
}
@@ -429,14 +429,16 @@ export class OpenToSideFromQuickOpenAction extends Action {
const entry = toEditorQuickOpenEntry(context);
if (entry) {
const input = entry.getInput();
if (input instanceof EditorInput) {
return this.editorService.openEditor(input, entry.getOptions() || undefined, SIDE_GROUP);
if (input) {
if (input instanceof EditorInput) {
return this.editorService.openEditor(input, entry.getOptions() || undefined, SIDE_GROUP);
}
const resourceInput = input as IResourceInput;
resourceInput.options = mixin(resourceInput.options, entry.getOptions());
return this.editorService.openEditor(resourceInput, SIDE_GROUP);
}
const resourceInput = input as IResourceInput;
resourceInput.options = mixin(resourceInput.options, entry.getOptions());
return this.editorService.openEditor(resourceInput, SIDE_GROUP);
}
return Promise.resolve(false);

View File

@@ -762,13 +762,13 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
return !!editorContext ? [editorContext] : [];
}
function isEditorGroup(thing: any): thing is IEditorGroup {
function isEditorGroup(thing: unknown): thing is IEditorGroup {
const group = thing as IEditorGroup;
return group && typeof group.id === 'number' && Array.isArray(group.editors);
}
function isEditorIdentifier(thing: any): thing is IEditorIdentifier {
function isEditorIdentifier(thing: unknown): thing is IEditorIdentifier {
const identifier = thing as IEditorIdentifier;
return identifier && typeof identifier.groupId === 'number';

View File

@@ -68,7 +68,7 @@ export class EditorControl extends Disposable {
const control = this.doShowEditorControl(descriptor);
// Set input
return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged }))));
}
private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {

View File

@@ -257,12 +257,16 @@ class DropOverlay extends Themable {
// Check for URI transfer
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup!.focus());
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => {
if (targetGroup) {
targetGroup.focus();
}
});
}
}
private isCopyOperation(e: DragEvent, draggedEditor?: IEditorIdentifier): boolean {
if (draggedEditor && !(draggedEditor.editor as EditorInput).supportsSplitEditor()) {
if (draggedEditor && draggedEditor.editor instanceof EditorInput && !draggedEditor.editor.supportsSplitEditor()) {
return false;
}

View File

@@ -49,7 +49,7 @@ import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/
import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { hash } from 'vs/base/common/hash';
import { guessMimeTypes } from 'vs/base/common/mime';
import { extname } from 'vs/base/common/path';
@@ -132,7 +132,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IHashService private readonly hashService: IHashService,
// {{SQL CARBON EDIT}}
@ICommandService private commandService: ICommandService
) {
@@ -224,7 +223,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
let activeEditorListener: IDisposable;
const observeActiveEditor = () => {
activeEditorListener = dispose(activeEditorListener);
dispose(activeEditorListener);
const activeEditor = this._group.activeEditor;
if (activeEditor) {
@@ -467,16 +466,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private onDidEditorOpen(editor: EditorInput): void {
// Telemetry
this.toEditorTelemetryDescriptor(editor).then(descriptor => {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', descriptor);
});
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', this.toEditorTelemetryDescriptor(editor));
// Update container
this.updateContainer();
@@ -508,16 +505,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
});
// Telemetry
this.toEditorTelemetryDescriptor(event.editor).then(descriptor => {
/* __GDPR__
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', descriptor);
});
this.telemetryService.publicLog('editorClosed', this.toEditorTelemetryDescriptor(event.editor));
// Update container
this.updateContainer();
@@ -527,24 +522,22 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_CLOSE, editor, editorIndex: event.index });
}
private toEditorTelemetryDescriptor(editor: EditorInput): Thenable<object> {
private toEditorTelemetryDescriptor(editor: EditorInput): object {
const descriptor = editor.getTelemetryDescriptor();
const resource = editor.getResource();
if (resource && resource.fsPath) {
return this.hashService.createSHA1(resource.fsPath).then(hashedPath => {
descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hashedPath };
descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hash(resource.fsPath) };
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
"resource": { "${inline}": [ "${URIDescriptor}" ] }
}
*/
return descriptor;
});
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
"resource": { "${inline}": [ "${URIDescriptor}" ] }
}
*/
return descriptor;
}
return Promise.resolve(descriptor);
return descriptor;
}
private onDidEditorDispose(editor: EditorInput): void {
@@ -1229,10 +1222,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Filter: direction (left / right)
else if (hasDirection) {
else if (hasDirection && filter.except) {
editorsToClose = (filter.direction === CloseDirection.LEFT) ?
editorsToClose.slice(0, this._group.indexOf(filter.except as EditorInput)) :
editorsToClose.slice(this._group.indexOf(filter.except as EditorInput) + 1);
editorsToClose.slice(0, this._group.indexOf(filter.except)) :
editorsToClose.slice(this._group.indexOf(filter.except) + 1);
}
// Filter: except

View File

@@ -844,7 +844,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
}
private doCreateGridControlWithPreviousState(): boolean {
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
const uiState: IEditorPartUIState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY];
if (uiState && uiState.serializedGrid) {
try {

View File

@@ -6,10 +6,10 @@
import 'vs/css!./media/editorstatus';
import * as nls from 'vs/nls';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import { format } from 'vs/base/common/strings';
import { extname, basename } from 'vs/base/common/resources';
import * as types from 'vs/base/common/types';
import { URI as uri } from 'vs/base/common/uri';
import { areFunctions, withNullAsUndefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { Language } from 'vs/base/common/platform';
@@ -87,7 +87,7 @@ function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | nu
// File or Resource Editor
let encodingSupport = input as IFileEditorInput;
if (types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
if (areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
return encodingSupport;
}
@@ -457,18 +457,18 @@ export class EditorStatus implements IStatusbarItem {
if (info.selections.length === 1) {
if (info.charactersSelected) {
return strings.format(nlsSingleSelectionRange, info.selections[0].positionLineNumber, info.selections[0].positionColumn, info.charactersSelected);
return format(nlsSingleSelectionRange, info.selections[0].positionLineNumber, info.selections[0].positionColumn, info.charactersSelected);
}
return strings.format(nlsSingleSelection, info.selections[0].positionLineNumber, info.selections[0].positionColumn);
return format(nlsSingleSelection, info.selections[0].positionLineNumber, info.selections[0].positionColumn);
}
if (info.charactersSelected) {
return strings.format(nlsMultiSelectionRange, info.selections.length, info.charactersSelected);
return format(nlsMultiSelectionRange, info.selections.length, info.charactersSelected);
}
if (info.selections.length > 0) {
return strings.format(nlsMultiSelection, info.selections.length);
return format(nlsMultiSelection, info.selections.length);
}
return undefined;
@@ -536,7 +536,7 @@ export class EditorStatus implements IStatusbarItem {
private updateStatusBar(): void {
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? types.withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
// Update all states
this.onScreenReaderModeChange(activeCodeEditor);
@@ -769,7 +769,7 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(info);
}
private onResourceEncodingChange(resource: uri): void {
private onResourceEncodingChange(resource: URI): void {
const activeControl = this.editorService.activeControl;
if (activeControl) {
const activeResource = toResource(activeControl.input, { supportSideBySide: true });
@@ -876,14 +876,14 @@ export class ChangeModeAction extends Action {
}
// construct a fake resource to be able to show nice icons if any
let fakeResource: uri | undefined;
let fakeResource: URI | undefined;
const extensions = this.modeService.getExtensions(lang);
if (extensions && extensions.length) {
fakeResource = uri.file(extensions[0]);
fakeResource = URI.file(extensions[0]);
} else {
const filenames = this.modeService.getFilenames(lang);
if (filenames && filenames.length) {
fakeResource = uri.file(filenames[0]);
fakeResource = URI.file(filenames[0]);
}
}
@@ -997,7 +997,7 @@ export class ChangeModeAction extends Action {
});
}
private configureFileAssociation(resource: uri): void {
private configureFileAssociation(resource: URI): void {
const extension = extname(resource);
const base = basename(resource);
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(base);
@@ -1208,7 +1208,7 @@ export class ChangeEncodingAction extends Action {
.then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const configuredEncoding = this.textResourceConfigurationService.getValue(types.withNullAsUndefined(resource), 'files.encoding');
const configuredEncoding = this.textResourceConfigurationService.getValue(withNullAsUndefined(resource), 'files.encoding');
let directMatchIndex: number | undefined;
let aliasMatchIndex: number | undefined;

View File

@@ -101,7 +101,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
private static readonly ID = 'editor.contrib.openWorkspaceButton';
private openWorkspaceButton: FloatingClickWidget;
private openWorkspaceButton: FloatingClickWidget | undefined;
constructor(
private editor: ICodeEditor,
@@ -163,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
this._register(this.openWorkspaceButton.onClick(() => {
const model = this.editor.getModel();
if (model) {
this.windowService.openWindow([{ uri: model.uri, typeHint: 'file' }]);
this.windowService.openWindow([{ fileUri: model.uri }]);
}
}));
@@ -172,7 +172,8 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
}
private disposeOpenWorkspaceWidgetRenderer(): void {
this.openWorkspaceButton = dispose(this.openWorkspaceButton);
dispose(this.openWorkspaceButton);
this.openWorkspaceButton = undefined;
}
dispose(): void {

View File

@@ -141,12 +141,12 @@ export class SideBySideEditor extends BaseEditor {
this.splitview.layout(dimension.width);
}
getControl(): IEditorControl | null {
getControl(): IEditorControl | undefined {
if (this.masterEditor) {
return this.masterEditor.getControl();
}
return null;
return undefined;
}
getMasterEditor(): IEditor | undefined {

View File

@@ -227,7 +227,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
private isFileBinaryError(error: Error[]): boolean;
private isFileBinaryError(error: Error): boolean;
private isFileBinaryError(error: any): boolean {
private isFileBinaryError(error: Error | Error[]): boolean {
if (types.isArray(error)) {
const errors = <Error[]>error;
return errors.some(e => this.isFileBinaryError(e));
@@ -312,9 +312,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return control.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | null {
let original: URI | null;
let modified: URI | null;
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | undefined {
let original: URI | undefined;
let modified: URI | undefined;
if (modelOrInput instanceof DiffEditorInput) {
original = modelOrInput.originalInput.getResource();
@@ -325,7 +325,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
}
if (!original || !modified) {
return null;
return undefined;
}
// create a URI that is the Base64 concatenation of original + modified resource

View File

@@ -19,7 +19,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { isDiffEditor, isCodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -241,7 +241,11 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
}
protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null {
const control = this.getControl() as ICodeEditor;
const control = this.getControl();
if (!isCodeEditor(control)) {
return null;
}
const model = control.getModel();
if (!model) {
return null; // view state always needs a model
@@ -302,7 +306,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
}
}
protected getResource(): URI | null {
protected getResource(): URI | undefined {
const codeEditor = getCodeEditor(this.editorControl);
if (codeEditor) {
const model = codeEditor.getModel();
@@ -315,7 +319,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditor {
return this.input.getResource();
}
return null;
return undefined;
}
protected abstract getAriaLabel(): string;

View File

@@ -8,7 +8,7 @@ import { addDisposableListener, Dimension, EventType } from 'vs/base/browser/dom
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { Action, IAction, IRunEvent } from 'vs/base/common/actions';
import { IAction, IRunEvent } from 'vs/base/common/actions';
import * as arrays from 'vs/base/common/arrays';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@@ -123,10 +123,10 @@ export abstract class TitleControl extends Themable {
protected abstract handleBreadcrumbsEnablementChange(): void;
protected createEditorActionsToolBar(container: HTMLElement): void {
const context = { groupId: this.group.id } as IEditorCommandsContext;
const context: IEditorCommandsContext = { groupId: this.group.id };
this.editorActionsToolbar = this._register(new ToolBar(container, this.contextMenuService, {
actionItemProvider: action => this.actionItemProvider(action as Action),
actionItemProvider: action => this.actionItemProvider(action),
orientation: ActionsOrientation.HORIZONTAL,
ariaLabel: localize('araLabelEditorActions', "Editor actions"),
getKeyBinding: action => this.getKeybinding(action),
@@ -156,7 +156,7 @@ export abstract class TitleControl extends Themable {
}));
}
private actionItemProvider(action: Action): IActionItem | undefined {
private actionItemProvider(action: IAction): IActionItem | undefined {
const activeControl = this.group.activeControl;
// Check Active Editor
@@ -303,7 +303,7 @@ export abstract class TitleControl extends Themable {
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => actions,
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext),
getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) }),
getKeyBinding: (action) => this.getKeybinding(action),
onHide: () => {

View File

@@ -57,7 +57,7 @@ export interface INotificationsToastController {
export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController): void {
function getNotificationFromContext(listService: IListService, context?: any): INotificationViewItem | undefined {
function getNotificationFromContext(listService: IListService, context?: unknown): INotificationViewItem | undefined {
if (isNotificationViewItem(context)) {
return context;
}

View File

@@ -16,7 +16,7 @@ import { IAction, IActionRunner } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { DropdownMenuActionItem, IContextMenuProvider } from 'vs/base/browser/ui/dropdown/dropdown';
import { DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown';
import { INotificationViewItem, NotificationViewItem, NotificationViewItemLabelKind, INotificationMessage, ChoiceAction } from 'vs/workbench/common/notifications';
import { ClearNotificationAction, ExpandNotificationAction, CollapseNotificationAction, ConfigureNotificationAction } from 'vs/workbench/browser/parts/notifications/notificationsActions';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -221,7 +221,7 @@ export class NotificationRenderer implements IListRenderer<INotificationViewItem
ariaLabel: localize('notificationActions', "Notification Actions"),
actionItemProvider: action => {
if (action && action instanceof ConfigureNotificationAction) {
const item = new DropdownMenuActionItem(action, action.configurationActions, this.contextMenuService as IContextMenuProvider, undefined, this.actionRunner, undefined, action.class as string);
const item = new DropdownMenuActionItem(action, action.configurationActions, this.contextMenuService, undefined, this.actionRunner, undefined, action.class);
data.toDispose.push(item);
return item;

View File

@@ -19,7 +19,7 @@ import { StatusbarAlignment, IStatusbarService, IStatusbarEntry } from 'vs/platf
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Action } from 'vs/base/common/actions';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme';
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { isThemeColor } from 'vs/editor/common/editorCommon';
@@ -329,11 +329,7 @@ class StatusBarEntryItem implements IStatusbarItem {
el.appendChild(textContainer);
return {
dispose: () => {
toDispose = dispose(toDispose);
}
};
return toDisposable(() => toDispose = dispose(toDispose));
}
private applyColor(container: HTMLElement, color: string | ThemeColor | undefined, isBackground?: boolean): IDisposable {
@@ -354,7 +350,7 @@ class StatusBarEntryItem implements IStatusbarItem {
return combinedDisposable(disposable);
}
private executeCommand(id: string, args?: any[]) {
private executeCommand(id: string, args?: unknown[]) {
args = args || [];
// Maintain old behaviour of always focusing the editor here
@@ -398,6 +394,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`);
}
const statusBarProminentItemForeground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_FOREGROUND);
if (statusBarProminentItemForeground) {
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .status-bar-info { color: ${statusBarProminentItemForeground}; }`);
}
const statusBarProminentItemBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND);
if (statusBarProminentItemBackground) {
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .status-bar-info { background-color: ${statusBarProminentItemBackground}; }`);

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar';
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, URIType } from 'vs/platform/windows/common/windows';
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, IURIToOpen } from 'vs/platform/windows/common/windows';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IAction, Action } from 'vs/base/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -355,36 +355,35 @@ export class MenubarControl extends Disposable {
return label;
}
private createOpenRecentMenuAction(recent: IRecent, isFile: boolean): IAction & { uri: URI } {
private createOpenRecentMenuAction(recent: IRecent): IAction & { uri: URI } {
let label: string;
let uri: URI;
let commandId: string;
let typeHint: URIType | undefined;
let uriToOpen: IURIToOpen;
if (isRecentFolder(recent)) {
uri = recent.folderUri;
label = recent.label || this.labelService.getWorkspaceLabel(uri, { verbose: true });
commandId = 'openRecentFolder';
typeHint = 'folder';
uriToOpen = { folderUri: uri };
} else if (isRecentWorkspace(recent)) {
uri = recent.workspace.configPath;
label = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: true });
commandId = 'openRecentWorkspace';
typeHint = 'file';
uriToOpen = { workspaceUri: uri };
} else {
uri = recent.fileUri;
label = recent.label || this.labelService.getUriLabel(uri);
commandId = 'openRecentFile';
typeHint = 'file';
uriToOpen = { fileUri: uri };
}
const ret: IAction = new Action(commandId, unmnemonicLabel(label), undefined, undefined, (event) => {
const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
return this.windowService.openWindow([{ uri, typeHint }], {
forceNewWindow: openInNewWindow,
forceOpenWorkspaceAsFile: isFile
return this.windowService.openWindow([uriToOpen], {
forceNewWindow: openInNewWindow
});
});
@@ -403,7 +402,7 @@ export class MenubarControl extends Disposable {
if (workspaces.length > 0) {
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) {
result.push(this.createOpenRecentMenuAction(workspaces[i], false));
result.push(this.createOpenRecentMenuAction(workspaces[i]));
}
result.push(new Separator());
@@ -411,7 +410,7 @@ export class MenubarControl extends Disposable {
if (files.length > 0) {
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
result.push(this.createOpenRecentMenuAction(files[i], true));
result.push(this.createOpenRecentMenuAction(files[i]));
}
result.push(new Separator());

View File

@@ -178,7 +178,7 @@ export abstract class ViewletPanel extends Panel implements IView {
return undefined;
}
getActionsContext(): any {
getActionsContext(): unknown {
return undefined;
}

View File

@@ -138,7 +138,7 @@ export class QuickOpenHandlerDescriptor {
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, description: string, instantProgress?: boolean);
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, helpEntries: QuickOpenHandlerHelpEntry[], instantProgress?: boolean);
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, param: any, instantProgress: boolean = false) {
constructor(ctor: IConstructorSignature0<QuickOpenHandler>, id: string, prefix: string, contextKey: string | undefined, param: string | QuickOpenHandlerHelpEntry[], instantProgress: boolean = false) {
this.ctor = ctor;
this.id = id;
this.prefix = prefix;
@@ -325,7 +325,7 @@ export class QuickOpenAction extends Action {
this.enabled = !!this.quickOpenService;
}
run(context?: any): Promise<void> {
run(): Promise<void> {
// Show with prefix
this.quickOpenService.show(this.prefix);

View File

@@ -93,7 +93,7 @@ export class Workbench extends Layout {
}
private previousUnexpectedError: { message: string | undefined, time: number } = { message: undefined, time: 0 };
private handleUnexpectedError(error: any, logService: ILogService): void {
private handleUnexpectedError(error: unknown, logService: ILogService): void {
const message = toErrorMessage(error, true);
if (!message) {
return;

View File

@@ -25,8 +25,8 @@ exports.collectModules = function () {
createModuleDescription('vs/workbench/services/search/node/searchApp', []),
createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []),
createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []),
createModuleDescription('vs/workbench/services/files2/node/watcher/unix/watcherApp', []),
createModuleDescription('vs/workbench/services/files2/node/watcher/nsfw/watcherApp', []),
createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []),
];

View File

@@ -40,7 +40,7 @@ export interface IComposite {
/**
* Returns the underlying control of this composite.
*/
getControl(): ICompositeControl | null;
getControl(): ICompositeControl | undefined;
/**
* Asks the underlying control to focus.

View File

@@ -91,7 +91,7 @@ export interface IEditor {
/**
* Returns the underlying control of this editor.
*/
getControl(): IEditorControl | null;
getControl(): IEditorControl | undefined;
/**
* Asks the underlying control to focus.
@@ -279,7 +279,7 @@ export interface IEditorInput extends IDisposable {
/**
* Returns the associated resource of this input.
*/
getResource(): URI | null;
getResource(): URI | undefined;
/**
* Unique type identifier for this inpput.
@@ -319,7 +319,7 @@ export interface IEditorInput extends IDisposable {
/**
* Returns if the other object matches this input.
*/
matches(other: any): boolean;
matches(other: unknown): boolean;
}
/**
@@ -347,8 +347,8 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
/**
* Returns the associated resource of this input if any.
*/
getResource(): URI | null {
return null;
getResource(): URI | undefined {
return undefined;
}
/**
@@ -392,7 +392,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
*
* Subclasses should extend if they can contribute.
*/
getTelemetryDescriptor(): { [key: string]: any } {
getTelemetryDescriptor(): { [key: string]: unknown } {
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
"typeId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
@@ -453,7 +453,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
/**
* Returns true if this input is identical to the otherInput.
*/
matches(otherInput: any): boolean {
matches(otherInput: unknown): boolean {
return this === otherInput;
}
@@ -623,7 +623,7 @@ export class SideBySideEditorInput extends EditorInput {
return this.description;
}
matches(otherInput: any): boolean {
matches(otherInput: unknown): boolean {
if (super.matches(otherInput) === true) {
return true;
}
@@ -684,7 +684,7 @@ export interface IEditorInputWithOptions {
options?: IEditorOptions | ITextEditorOptions;
}
export function isEditorInputWithOptions(obj: any): obj is IEditorInputWithOptions {
export function isEditorInputWithOptions(obj: unknown): obj is IEditorInputWithOptions {
const editorInputWithOptions = obj as IEditorInputWithOptions;
return !!editorInputWithOptions && !!editorInputWithOptions.editor;
@@ -948,7 +948,7 @@ export class EditorCommandsContextActionRunner extends ActionRunner {
super();
}
run(action: IAction, context?: any): Promise<void> {
run(action: IAction): Promise<void> {
return super.run(action, this.context);
}
}
@@ -1007,7 +1007,7 @@ export function toResource(editor: IEditorInput | null | undefined, options?: IR
const resource = editor.getResource();
if (!options || !options.filter) {
return resource; // return early if no filter is specified
return types.withUndefinedAsNull(resource); // return early if no filter is specified
}
if (!resource) {

View File

@@ -72,7 +72,7 @@ export class BinaryEditorModel extends EditorModel {
return this.etag;
}
load(): Promise<EditorModel> {
load(): Promise<BinaryEditorModel> {
// Make sure to resolve up to date stat for file resources
if (this.fileService.canHandleResource(this.resource)) {

View File

@@ -60,10 +60,10 @@ export class DataUriEditorInput extends EditorInput {
}
resolve(): Promise<BinaryEditorModel> {
return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load().then(m => m as BinaryEditorModel);
return this.instantiationService.createInstance(BinaryEditorModel, this.resource, this.getName()).load();
}
matches(otherInput: any): boolean {
matches(otherInput: unknown): boolean {
if (super.matches(otherInput) === true) {
return true;
}

View File

@@ -21,18 +21,20 @@ export class DiffEditorModel extends EditorModel {
this._modifiedModel = modifiedModel;
}
get originalModel(): EditorModel | null {
get originalModel(): IEditorModel | null {
if (!this._originalModel) {
return null;
}
return this._originalModel as EditorModel;
return this._originalModel;
}
get modifiedModel(): EditorModel | null {
get modifiedModel(): IEditorModel | null {
if (!this._modifiedModel) {
return null;
}
return this._modifiedModel as EditorModel;
return this._modifiedModel;
}
load(): Promise<EditorModel> {
@@ -43,7 +45,7 @@ export class DiffEditorModel extends EditorModel {
}
isResolved(): boolean {
return !!this.originalModel && this.originalModel.isResolved() && !!this.modifiedModel && this.modifiedModel.isResolved();
return this.originalModel instanceof EditorModel && this.originalModel.isResolved() && this.modifiedModel instanceof EditorModel && this.modifiedModel.isResolved();
}
dispose(): void {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection } from 'vs/workbench/common/editor';
import { Extensions, IEditorInputFactoryRegistry, EditorInput, toResource, IEditorIdentifier, IEditorCloseEvent, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorInput } from 'vs/workbench/common/editor';
import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
@@ -54,7 +54,7 @@ export interface ISerializedEditorGroup {
}
export function isSerializedEditorGroup(obj?: any): obj is ISerializedEditorGroup {
const group = obj as ISerializedEditorGroup;
const group: ISerializedEditorGroup = obj;
return obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru);
}
@@ -146,7 +146,7 @@ export class EditorGroup extends Disposable {
getEditor(index: number): EditorInput | null;
getEditor(resource: URI): EditorInput | null;
getEditor(arg1: any): EditorInput | null {
getEditor(arg1: number | URI): EditorInput | null {
if (typeof arg1 === 'number') {
return this.editors[arg1];
}
@@ -507,7 +507,7 @@ export class EditorGroup extends Disposable {
private splice(index: number, del: boolean, editor?: EditorInput): void {
const editorToDeleteOrReplace = this.editors[index];
const args: any[] = [index, del ? 1 : 0];
const args: (number | EditorInput)[] = [index, del ? 1 : 0];
if (editor) {
args.push(editor);
}
@@ -567,7 +567,7 @@ export class EditorGroup extends Disposable {
}
}
indexOf(candidate: EditorInput | null, editors = this.editors): number {
indexOf(candidate: IEditorInput | null, editors = this.editors): number {
if (!candidate) {
return -1;
}
@@ -620,7 +620,7 @@ export class EditorGroup extends Disposable {
this.mru.unshift(editor);
}
private matches(editorA: EditorInput | null, editorB: EditorInput | null): boolean {
private matches(editorA: IEditorInput | null, editorB: IEditorInput | null): boolean {
return !!editorA && !!editorB && editorA.matches(editorB);
}
@@ -710,10 +710,11 @@ export class EditorGroup extends Disposable {
this.editors = coalesce(data.editors.map(e => {
const factory = registry.getEditorInputFactory(e.id);
if (factory) {
const editor = factory.deserialize(this.instantiationService, e.value)!;
this.registerEditorListeners(editor);
this.updateResourceMap(editor, false /* add */);
const editor = factory.deserialize(this.instantiationService, e.value);
if (editor) {
this.registerEditorListeners(editor);
this.updateResourceMap(editor, false /* add */);
}
// {{SQL CARBON EDIT}}
return CustomInputConverter.convertEditorInput(editor, undefined, this.instantiationService);

View File

@@ -74,14 +74,14 @@ export class ResourceEditorInput extends EditorInput {
ref.dispose();
this.modelReference = null;
return Promise.reject<any>(new Error(`Unexpected model for ResourceInput: ${this.resource}`));
return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`));
}
return model;
});
}
matches(otherInput: any): boolean {
matches(otherInput: unknown): boolean {
if (super.matches(otherInput) === true) {
return true;
}

View File

@@ -13,6 +13,10 @@ import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel';
* and the modified version.
*/
export class TextDiffEditorModel extends DiffEditorModel {
protected readonly _originalModel: BaseTextEditorModel;
protected readonly _modifiedModel: BaseTextEditorModel;
private _textDiffEditorModel: IDiffEditorModel | null;
constructor(originalModel: BaseTextEditorModel, modifiedModel: BaseTextEditorModel) {
@@ -22,11 +26,11 @@ export class TextDiffEditorModel extends DiffEditorModel {
}
get originalModel(): BaseTextEditorModel {
return this._originalModel as BaseTextEditorModel;
return this._originalModel;
}
get modifiedModel(): BaseTextEditorModel {
return this._modifiedModel as BaseTextEditorModel;
return this._modifiedModel;
}
load(): Promise<EditorModel> {

View File

@@ -224,7 +224,7 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport
return model;
}
matches(otherInput: any): boolean {
matches(otherInput: unknown): boolean {
if (super.matches(otherInput) === true) {
return true;
}

View File

@@ -206,7 +206,7 @@ export interface INotificationViewItem {
equals(item: INotificationViewItem): boolean;
}
export function isNotificationViewItem(obj: any): obj is INotificationViewItem {
export function isNotificationViewItem(obj: unknown): obj is INotificationViewItem {
return obj instanceof NotificationViewItem;
}

View File

@@ -291,6 +291,12 @@ export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hov
hc: Color.white.transparent(0.12)
}, nls.localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window."));
export const STATUS_BAR_PROMINENT_ITEM_FOREGROUND = registerColor('statusBarItem.prominentForeground', {
dark: STATUS_BAR_FOREGROUND,
light: STATUS_BAR_FOREGROUND,
hc: STATUS_BAR_FOREGROUND
}, nls.localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. Change mode `Toggle Tab Key Moves Focus` from command palette to see an example. The status bar is shown in the bottom of the window."));
export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', {
dark: Color.black.transparent(0.5),
light: Color.black.transparent(0.5),

View File

@@ -6,7 +6,6 @@
import { IComposite } from 'vs/workbench/common/composite';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
export const SidebarVisibleContext = new RawContextKey<boolean>('sidebarVisible', false);
export const SideBarVisibleContext = new RawContextKey<boolean>('sideBarVisible', false);
export const SidebarFocusContext = new RawContextKey<boolean>('sideBarFocus', false);
export const ActiveViewletContext = new RawContextKey<string>('activeViewlet', '');

View File

@@ -49,7 +49,7 @@ export class BackupRestorer implements IWorkbenchContribution {
}
private doResolveOpenedBackups(backups: URI[]): Promise<URI[]> {
const restorePromises: Promise<any>[] = [];
const restorePromises: Promise<unknown>[] = [];
const unresolved: URI[] = [];
backups.forEach(backup => {

View File

@@ -8,7 +8,7 @@ import * as path from 'vs/base/common/path';
import * as cp from 'child_process';
import * as pfs from 'vs/base/node/pfs';
import * as platform from 'vs/base/common/platform';
import { nfcall } from 'vs/base/common/async';
import { promisify } from 'util';
import { Action } from 'vs/base/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -105,7 +105,7 @@ class InstallAction extends Action {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"mkdir -p /usr/local/bin && ln -sf \'' + getSource() + '\' \'' + this.target + '\'\\" with administrator privileges"';
nfcall(cp.exec, command, {})
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantCreateBinFolder', "Unable to create '/usr/local/bin'."))))
.then(resolve, reject);
break;
@@ -172,7 +172,7 @@ class UninstallAction extends Action {
case 0 /* OK */:
const command = 'osascript -e "do shell script \\"rm \'' + this.target + '\'\\" with administrator privileges"';
nfcall(cp.exec, command, {})
promisify(cp.exec)(command, {})
.then(undefined, _ => Promise.reject(new Error(nls.localize('cantUninstall', "Unable to uninstall the shell command '{0}'.", this.target))))
.then(resolve, reject);
break;

View File

@@ -42,7 +42,7 @@ export interface ICommentService {
readonly onDidSetDataProvider: Event<void>;
readonly onDidDeleteDataProvider: Event<string>;
setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void;
setWorkspaceComments(owner: string, commentsByResource: CommentThread[]): void;
setWorkspaceComments(owner: string, commentsByResource: CommentThread[] | CommentThread2[]): void;
removeWorkspaceComments(owner: string): void;
registerCommentController(owner: string, commentControl: MainThreadCommentController): void;
unregisterCommentController(owner: string): void;

View File

@@ -60,7 +60,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
private _resizeObserver: any;
private _onDidClose = new Emitter<ReviewZoneWidget | undefined>();
private _onDidCreateThread = new Emitter<ReviewZoneWidget>();
private _isCollapsed: boolean;
private _isExpanded?: boolean;
private _collapseAction: Action;
private _commentGlyph?: CommentGlyphWidget;
private _submitActionsDisposables: IDisposable[];
@@ -74,7 +74,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
return this._owner;
}
public get commentThread(): modes.CommentThread {
return this._commentThread;
return this._commentThread as modes.CommentThread;
}
public get extensionId(): string | undefined {
@@ -101,7 +101,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
) {
super(editor, { keepEditorSelection: true });
this._resizeObserver = null;
this._isCollapsed = _commentThread.collapsibleState !== modes.CommentThreadCollapsibleState.Expanded;
this._isExpanded = _commentThread.collapsibleState ? _commentThread.collapsibleState === modes.CommentThreadCollapsibleState.Expanded : undefined;
this._globalToDispose = [];
this._submitActionsDisposables = [];
this._formActions = null;
@@ -144,7 +144,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
public reveal(commentId?: string) {
if (this._isCollapsed) {
if (!this._isExpanded) {
this.show({ lineNumber: this._commentThread.range.startLineNumber, column: 1 }, 2);
}
@@ -205,7 +205,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
public collapse(): Promise<void> {
if (this._commentThread.comments.length === 0) {
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
if ((this._commentThread as modes.CommentThread2).commentThreadHandle === undefined) {
this.dispose();
return Promise.resolve();
@@ -217,7 +217,6 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
}
this._isCollapsed = true;
this.hide();
return Promise.resolve();
}
@@ -230,25 +229,25 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
toggleExpand(lineNumber: number) {
if (this._isCollapsed) {
this.show({ lineNumber: lineNumber, column: 1 }, 2);
} else {
if (this._isExpanded) {
this.hide();
if (this._commentThread === null || this._commentThread.threadId === null) {
this.dispose();
}
} else {
this.show({ lineNumber: lineNumber, column: 1 }, 2);
}
}
async update(commentThread: modes.CommentThread | modes.CommentThread2) {
const oldCommentsLen = this._commentElements.length;
const newCommentsLen = commentThread.comments.length;
const newCommentsLen = commentThread.comments ? commentThread.comments.length : 0;
let commentElementsToDel: CommentNode[] = [];
let commentElementsToDelIndex: number[] = [];
for (let i = 0; i < oldCommentsLen; i++) {
let comment = this._commentElements[i].comment;
let newComment = commentThread.comments.filter(c => c.commentId === comment.commentId);
let newComment = commentThread.comments ? commentThread.comments.filter(c => c.commentId === comment.commentId) : [];
if (newComment.length) {
this._commentElements[i].update(newComment[0]);
@@ -267,7 +266,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
let lastCommentElement: HTMLElement | null = null;
let newCommentNodeList: CommentNode[] = [];
for (let i = newCommentsLen - 1; i >= 0; i--) {
let currentComment = commentThread.comments[i];
let currentComment = commentThread.comments![i];
let oldCommentNode = this._commentElements.filter(commentNode => commentNode.comment.commentId === currentComment.commentId);
if (oldCommentNode.length) {
oldCommentNode[0].update(currentComment);
@@ -291,6 +290,12 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this._commentElements = newCommentNodeList;
this.createThreadLabel();
if (this._formActions && this._commentEditor.hasModel()) {
dom.clearNode(this._formActions);
const model = this._commentEditor.getModel();
this.createCommentWidgetActions2(this._formActions, model);
}
// Move comment glyph widget and show position if the line has changed.
const lineNumber = this._commentThread.range.startLineNumber;
let shouldMoveWidget = false;
@@ -305,9 +310,22 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this.createReplyButton();
}
if (shouldMoveWidget && !this._isCollapsed) {
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
this.expandReplyArea();
}
if (shouldMoveWidget && this._isExpanded) {
this.show({ lineNumber, column: 1 }, 2);
}
// The collapsible state is not initialized yet.
if (this._isExpanded === undefined) {
if (this._commentThread.collapsibleState === modes.CommentThreadCollapsibleState.Expanded) {
this.show({ lineNumber, column: 1 }, 2);
} else {
this.hide();
}
}
}
updateDraftMode(draftMode: modes.DraftMode | undefined) {
@@ -344,14 +362,16 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this._commentsElement.setAttribute('role', 'presentation');
this._commentElements = [];
for (const comment of this._commentThread.comments) {
const newCommentNode = this.createNewCommentNode(comment);
if (this._commentThread.comments) {
for (const comment of this._commentThread.comments) {
const newCommentNode = this.createNewCommentNode(comment);
this._commentElements.push(newCommentNode);
this._commentsElement.appendChild(newCommentNode.domNode);
this._commentElements.push(newCommentNode);
this._commentsElement.appendChild(newCommentNode.domNode);
}
}
const hasExistingComments = this._commentThread.comments.length > 0;
const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0;
this._commentForm = dom.append(this._bodyElement, dom.$('.comment-form'));
this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions(), this._parentEditor, this);
@@ -426,9 +446,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
if (hasExistingComments) {
this.createReplyButton();
} else {
if (!dom.hasClass(this._commentForm, 'expand')) {
dom.addClass(this._commentForm, 'expand');
this._commentEditor.focus();
if (this._commentThread.comments && this._commentThread.comments.length === 0) {
this.expandReplyArea();
}
}
@@ -463,20 +482,20 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
}
if (shouldMoveWidget && !this._isCollapsed) {
if (shouldMoveWidget && this._isExpanded) {
this.show({ lineNumber, column: 1 }, 2);
}
}));
this._disposables.push((this._commentThread as modes.CommentThread2).onDidChangeCollasibleState(state => {
if (state === modes.CommentThreadCollapsibleState.Expanded && this._isCollapsed) {
if (state === modes.CommentThreadCollapsibleState.Expanded && !this._isExpanded) {
const lineNumber = this._commentThread.range.startLineNumber;
this.show({ lineNumber, column: 1 }, 2);
return;
}
if (state === modes.CommentThreadCollapsibleState.Collapsed && !this._isCollapsed) {
if (state === modes.CommentThreadCollapsibleState.Collapsed && this._isExpanded) {
this.hide();
return;
}
@@ -499,13 +518,11 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
// If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus.
if ((this._commentThread as modes.CommentThread).reply && !this._commentThread.comments.length) {
// if this._commentThread.comments is undefined, it doesn't finish initialization yet, so we don't focus the editor immediately.
if ((this._commentThread as modes.CommentThread).reply && this._commentThread.comments && !this._commentThread.comments.length) {
this._commentEditor.focus();
} else if (this._commentEditor.getModel()!.getValueLength() > 0) {
if (!dom.hasClass(this._commentForm, 'expand')) {
dom.addClass(this._commentForm, 'expand');
}
this._commentEditor.focus();
this.expandReplyArea();
}
}
@@ -616,8 +633,8 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
*/
private createCommentWidgetActions2(container: HTMLElement, model: ITextModel) {
let commentThread = this._commentThread as modes.CommentThread2;
const { acceptInputCommand, additionalCommands } = commentThread;
const { acceptInputCommand } = commentThread;
if (acceptInputCommand) {
const button = new Button(container);
this._disposables.push(attachButtonStyler(button, this.themeService));
@@ -642,20 +659,22 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}));
}
commentThread.additionalCommands.reverse().forEach(command => {
const button = new Button(container);
this._disposables.push(attachButtonStyler(button, this.themeService));
if (additionalCommands) {
additionalCommands.reverse().forEach(command => {
const button = new Button(container);
this._disposables.push(attachButtonStyler(button, this.themeService));
button.label = command.title;
this._disposables.push(button.onDidClick(async () => {
commentThread.input = {
uri: this._commentEditor.getModel()!.uri,
value: this._commentEditor.getValue()
};
this.commentService.setActiveCommentThread(this._commentThread);
await this.commandService.executeCommand(command.id, ...(command.arguments || []));
}));
});
button.label = command.title;
this._disposables.push(button.onDidClick(async () => {
commentThread.input = {
uri: this._commentEditor.getModel()!.uri,
value: this._commentEditor.getValue()
};
this.commentService.setActiveCommentThread(this._commentThread);
await this.commandService.executeCommand(command.id, ...(command.arguments || []));
}));
});
}
}
private createNewCommentNode(comment: modes.Comment): CommentNode {
@@ -676,15 +695,15 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
this._commentElements.splice(deletedElementIndex, 1);
}
const deletedCommentIndex = arrays.firstIndex(this._commentThread.comments, comment => comment.commentId === deletedNodeId);
const deletedCommentIndex = arrays.firstIndex(this._commentThread.comments!, comment => comment.commentId === deletedNodeId);
if (deletedCommentIndex > -1) {
this._commentThread.comments.splice(deletedCommentIndex, 1);
this._commentThread.comments!.splice(deletedCommentIndex, 1);
}
this._commentsElement.removeChild(deletedNode.domNode);
deletedNode.dispose();
if (this._commentThread.comments.length === 0) {
if (this._commentThread.comments!.length === 0) {
this.dispose();
}
}));
@@ -786,7 +805,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
if (label === undefined) {
if (this._commentThread.comments.length) {
if (this._commentThread.comments && this._commentThread.comments.length) {
const participantsList = this._commentThread.comments.filter(arrays.uniqueFilter(comment => comment.userName)).map(comment => `@${comment.userName}`).join(', ');
label = nls.localize('commentThreadParticipants', "Participants: {0}", participantsList);
} else {
@@ -825,7 +844,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
_refresh() {
if (!this._isCollapsed && this._bodyElement) {
if (this._isExpanded && this._bodyElement) {
let dimensions = dom.getClientArea(this._bodyElement);
const headHeight = Math.ceil(this.editor.getConfiguration().lineHeight * 1.2);
const lineHeight = this.editor.getConfiguration().lineHeight;
@@ -841,7 +860,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
const model = this._commentEditor && this._commentEditor.getModel();
if (model) {
const valueLength = model.getValueLength();
const hasExistingComments = this._commentThread.comments.length > 0;
const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0;
const placeholder = valueLength > 0
? ''
: hasExistingComments
@@ -996,13 +1015,13 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
}
show(rangeOrPos: IRange | IPosition, heightInLines: number): void {
this._isCollapsed = false;
this._isExpanded = true;
super.show(rangeOrPos, heightInLines);
this._refresh();
}
hide() {
this._isCollapsed = true;
this._isExpanded = false;
// Focus the container so that the comment editor will be blurred before it is hidden
this.editor.focus();
super.hide();

View File

@@ -265,9 +265,15 @@ export class ReviewController implements IEditorContribution {
if (commentThreadWidget.length === 1) {
commentThreadWidget[0].reveal(commentId);
} else if (fetchOnceIfNotExist) {
this.beginCompute().then(_ => {
this.revealCommentThread(threadId, commentId, false);
});
if (this._computePromise) {
this._computePromise.then(_ => {
this.revealCommentThread(threadId, commentId, false);
});
} else {
this.beginCompute().then(_ => {
this.revealCommentThread(threadId, commentId, false);
});
}
}
}

View File

@@ -41,12 +41,12 @@ export class ResourceWithCommentThreads {
constructor(resource: URI, commentThreads: CommentThread[]) {
this.id = resource.toString();
this.resource = resource;
this.commentThreads = commentThreads.filter(thread => thread.comments.length).map(thread => ResourceWithCommentThreads.createCommentNode(resource, thread));
this.commentThreads = commentThreads.filter(thread => thread.comments && thread.comments.length).map(thread => ResourceWithCommentThreads.createCommentNode(resource, thread));
}
public static createCommentNode(resource: URI, commentThread: CommentThread): CommentNode {
const { threadId, comments, range } = commentThread;
const commentNodes: CommentNode[] = comments.map(comment => new CommentNode(threadId!, resource, comment, range));
const commentNodes: CommentNode[] = comments!.map(comment => new CommentNode(threadId!, resource, comment, range));
if (commentNodes.length > 1) {
commentNodes[0].replies = commentNodes.slice(1, commentNodes.length);
}
@@ -107,7 +107,7 @@ export class CommentsModel {
const existingResource = threadsForOwner.filter(resourceWithThreads => resourceWithThreads.resource.toString() === thread.resource);
if (existingResource.length) {
const resource = existingResource[0];
if (thread.comments.length) {
if (thread.comments && thread.comments.length) {
resource.commentThreads.push(ResourceWithCommentThreads.createCommentNode(resource.resource, thread));
}
} else {

View File

@@ -558,6 +558,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea
preserveFocus,
selection,
revealIfVisible: true,
revealIfOpened: true,
revealInCenterIfOutsideViewport: true,
pinned: !preserveFocus
}

View File

@@ -12,7 +12,7 @@ import { SelectBox, ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selec
import { SelectActionItem, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IDebugSession, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
@@ -192,9 +192,10 @@ export class StartDebugActionItem implements IActionItem {
export class FocusSessionActionItem extends SelectActionItem {
constructor(
action: IAction,
@IDebugService protected debugService: IDebugService,
@IDebugService protected readonly debugService: IDebugService,
@IThemeService themeService: IThemeService,
@IContextViewService contextViewService: IContextViewService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(null, action, [], -1, contextViewService, { ariaLabel: nls.localize('debugSession', 'Debug Session') });
@@ -234,6 +235,9 @@ export class FocusSessionActionItem extends SelectActionItem {
}
protected getSessions(): ReadonlyArray<IDebugSession> {
return this.debugService.getModel().getSessions();
const hideSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
const sessions = this.debugService.getModel().getSessions();
return hideSubSessions ? sessions.filter(s => !s.parentSession) : sessions;
}
}

View File

@@ -9,7 +9,7 @@ import { List } from 'vs/base/browser/ui/list/listWidget';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IListService } from 'vs/platform/list/browser/listService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
import { Expression, Variable, Breakpoint, FunctionBreakpoint, Thread } from 'vs/workbench/contrib/debug/common/debugModel';
import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
@@ -30,6 +30,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { startDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export const ADD_CONFIGURATION_ID = 'debug.addConfiguration';
export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint';
@@ -183,6 +184,12 @@ export function registerCommands(): void {
const debugService = accessor.get(IDebugService);
if (!session || !session.getId) {
session = debugService.getViewModel().focusedSession;
const configurationService = accessor.get(IConfigurationService);
const hideSubSessions = configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
// Stop should be sent to the root parent session
while (hideSubSessions && session && session.parentSession) {
session = session.parentSession;
}
}
debugService.stopSession(session).then(undefined, onUnexpectedError);

View File

@@ -23,7 +23,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { registerColor, contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { localize } from 'vs/nls';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -67,7 +67,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
@IConfigurationService private readonly configurationService: IConfigurationService,
@IThemeService themeService: IThemeService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IContextViewService contextViewService: IContextViewService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IMenuService menuService: IMenuService,
@IContextMenuService contextMenuService: IContextMenuService,
@@ -89,7 +88,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
orientation: ActionsOrientation.HORIZONTAL,
actionItemProvider: (action: IAction) => {
if (action.id === FocusSessionAction.ID) {
return new FocusSessionActionItem(action, this.debugService, this.themeService, contextViewService);
return this.instantiationService.createInstance(FocusSessionActionItem, action);
}
if (action instanceof MenuItemAction) {
return new MenuItemActionItem(action, this.keybindingService, this.notificationService, contextMenuService);

View File

@@ -136,7 +136,7 @@ export class DebugViewlet extends ViewContainerViewlet {
return this.startDebugActionItem;
}
if (action.id === FocusSessionAction.ID) {
return new FocusSessionActionItem(action, this.debugService, this.themeService, this.contextViewService);
return new FocusSessionActionItem(action, this.debugService, this.themeService, this.contextViewService, this.configurationService);
}
if (action instanceof MenuItemAction) {
return new MenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);

View File

@@ -138,7 +138,7 @@ export class LinkDetector {
private onLinkClick(event: IMouseEvent, resource: uri, line: number, column: number = 0): void {
const selection = window.getSelection();
if (selection.type === 'Range') {
if (!selection || selection.type === 'Range') {
return; // do not navigate when user is selecting
}

View File

@@ -23,7 +23,7 @@ import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ltrim } from 'vs/base/common/strings';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels';
import { ResourceLabels, IResourceLabelProps, IResourceLabelOptions, IResourceLabel } from 'vs/workbench/browser/labels';
import { FileKind } from 'vs/platform/files/common/files';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
@@ -416,7 +416,7 @@ export class LoadedScriptsView extends ViewletPanel {
const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService, this.labelService);
this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer);
this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility });
this.disposables.push(this.treeLabels);
this.tree = this.instantiationService.createInstance(WorkbenchAsyncDataTree, this.treeContainer, new LoadedScriptsDelegate(),

View File

@@ -158,9 +158,15 @@
display: flex;
}
.debug-viewlet .debug-call-stack .thread > .name,
.debug-viewlet .debug-call-stack .session > .name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.debug-viewlet .debug-call-stack .thread > .state,
.debug-viewlet .debug-call-stack .session > .state {
flex: 1;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -424,6 +424,7 @@ export interface IDebugConfiguration {
internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
extensionHostDebugAdapter: boolean;
enableAllHovers: boolean;
hideSubSessions: boolean;
console: {
fontSize: number;
fontFamily: string;

View File

@@ -93,6 +93,7 @@ export class Source {
preserveFocus,
selection,
revealIfVisible: true,
revealIfOpened: true,
revealInCenterIfOutsideViewport: true,
pinned: pinned || (!preserveFocus && !this.inMemory)
}

View File

@@ -68,7 +68,7 @@ export class ViewModel implements IViewModel {
this.loadedScriptsSupportedContextKey.set(session ? !!session.capabilities.supportsLoadedSourcesRequest : false);
this.stepBackSupportedContextKey.set(session ? !!session.capabilities.supportsStepBack : false);
this.restartFrameSupportedContextKey.set(session ? !!session.capabilities.supportsRestartFrame : false);
const attach = !!session && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
const attach = !!session && !session.parentSession && session.configuration.request === 'attach' && !isExtensionHostDebugging(session.configuration);
this.focusedSessionIsAttach.set(attach);
if (shouldEmitForSession) {

View File

@@ -33,9 +33,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
import { IBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast';
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/common/console';
import { parse, getFirstFrame } from 'vs/base/common/console';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -47,6 +45,7 @@ import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
@@ -98,7 +97,6 @@ export class DebugService implements IDebugService {
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IBroadcastService private readonly broadcastService: IBroadcastService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IContextKeyService contextKeyService: IContextKeyService,
@@ -109,6 +107,7 @@ export class DebugService implements IDebugService {
@ITaskService private readonly taskService: ITaskService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService
) {
this.toDispose = [];
@@ -136,34 +135,31 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.storageService.onWillSaveState(this.saveState, this));
this.lifecycleService.onShutdown(this.dispose, this);
this.toDispose.push(this.broadcastService.onBroadcast(broadcast => {
const session = this.model.getSession(broadcast.payload.debugId, true);
this.toDispose.push(this.extensionHostDebugService.onAttachSession(data => {
const session = this.model.getSession(data.id, true);
if (session) {
switch (broadcast.channel) {
case EXTENSION_ATTACH_BROADCAST_CHANNEL:
// EH was started in debug mode -> attach to it
session.configuration.request = 'attach';
session.configuration.port = broadcast.payload.port;
this.launchOrAttachToSession(session);
break;
case EXTENSION_TERMINATE_BROADCAST_CHANNEL:
// EH was terminated
session.disconnect();
break;
case EXTENSION_LOG_BROADCAST_CHANNEL:
// extension logged output -> show it in REPL
const extensionOutput = <IRemoteConsoleLog>broadcast.payload.logEntry;
const sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(extensionOutput);
const frame = !!stack ? getFirstFrame(stack) : undefined;
session.logToRepl(sev, args, frame);
break;
}
// EH was started in debug mode -> attach to it
session.configuration.request = 'attach';
session.configuration.port = data.port;
this.launchOrAttachToSession(session).then(undefined, errors.onUnexpectedError);
}
}, this));
}));
this.toDispose.push(this.extensionHostDebugService.onTerminateSession(sessionId => {
const session = this.model.getSession(sessionId);
if (session) {
session.disconnect().then(undefined, errors.onUnexpectedError);
}
}));
this.toDispose.push(this.extensionHostDebugService.onLogToSession(data => {
const session = this.model.getSession(data.id, true);
if (session) {
// extension logged output -> show it in REPL
const sev = data.log.severity === 'warn' ? severity.Warning : data.log.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(data.log);
const frame = !!stack ? getFirstFrame(stack) : undefined;
session.logToRepl(sev, args, frame);
}
}));
this.toDispose.push(this.viewModel.onDidFocusStackFrame(() => {
this.onStateChange();
@@ -443,8 +439,10 @@ export class DebugService implements IDebugService {
}
this.viewModel.firstSessionStart = false;
if (this.model.getSessions().length > 1) {
const hideSubSessions = this.configurationService.getValue<IDebugConfiguration>('debug').hideSubSessions;
const sessions = this.model.getSessions();
const shownSessions = hideSubSessions ? sessions.filter(s => !s.parentSession) : sessions;
if (shownSessions.length > 1) {
this.viewModel.setMultiSessionView(true);
}
@@ -510,10 +508,7 @@ export class DebugService implements IDebugService {
// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
if (isExtensionHostDebugging(session.configuration) && session.state === State.Running && session.configuration.noDebug) {
this.broadcastService.broadcast({
channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
payload: [session.root.uri.toString()]
});
this.extensionHostDebugService.close(session.root.uri);
}
this.telemetryDebugSessionStop(session, adapterExitEvent);
@@ -561,10 +556,7 @@ export class DebugService implements IDebugService {
}
if (isExtensionHostDebugging(session.configuration) && session.root) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.broadcastService.broadcast({
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
payload: [session.root.uri.toString()]
}) : undefined);
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.root.uri) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();

View File

@@ -5,19 +5,20 @@
import * as dom from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDataSource, ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
import { Action } from 'vs/base/common/actions';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
import { Event } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { WorkbenchTreeController, WorkbenchTree, IListService } from 'vs/platform/list/browser/listService';
import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { IAsyncDataSource, ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
export interface IExtensionTemplateData {
icon: HTMLImageElement;
@@ -39,49 +40,40 @@ export interface IExtensionData {
parent: IExtensionData | null;
}
export class DataSource implements IDataSource {
export class AsyncDataSource implements IAsyncDataSource<IExtensionData, any> {
public getId(tree: ITree, { extension, parent }: IExtensionData): string {
return parent ? this.getId(tree, parent) + '/' + extension.identifier.id : extension.identifier.id;
}
public hasChildren(tree: ITree, { hasChildren }: IExtensionData): boolean {
public hasChildren({ hasChildren }: IExtensionData): boolean {
return hasChildren;
}
public getChildren(tree: ITree, extensionData: IExtensionData): Promise<any> {
public getChildren(extensionData: IExtensionData): Promise<any> {
return extensionData.getChildren();
}
public getParent(tree: ITree, { parent }: IExtensionData): Promise<any> {
return Promise.resolve(parent);
}
export class VirualDelegate implements IListVirtualDelegate<IExtensionData> {
public getHeight(element: IExtensionData): number {
return 62;
}
public getTemplateId({ extension }: IExtensionData): string {
return extension ? ExtensionRenderer.TEMPLATE_ID : UnknownExtensionRenderer.TEMPLATE_ID;
}
}
export class Renderer implements IRenderer {
export class ExtensionRenderer implements IListRenderer<ITreeNode<IExtensionData>, IExtensionTemplateData> {
private static readonly EXTENSION_TEMPLATE_ID = 'extension-template';
private static readonly UNKNOWN_EXTENSION_TEMPLATE_ID = 'unknown-extension-template';
static readonly TEMPLATE_ID = 'extension-template';
constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) {
}
public getHeight(tree: ITree, element: IExtensionData): number {
return 62;
public get templateId(): string {
return ExtensionRenderer.TEMPLATE_ID;
}
public getTemplateId(tree: ITree, { extension }: IExtensionData): string {
return extension ? Renderer.EXTENSION_TEMPLATE_ID : Renderer.UNKNOWN_EXTENSION_TEMPLATE_ID;
}
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
if (Renderer.EXTENSION_TEMPLATE_ID === templateId) {
return this.renderExtensionTemplate(tree, container);
}
return this.renderUnknownExtensionTemplate(tree, container);
}
private renderExtensionTemplate(tree: ITree, container: HTMLElement): IExtensionTemplateData {
public renderTemplate(container: HTMLElement): IExtensionTemplateData {
dom.addClass(container, 'extension');
const icon = dom.append(container, dom.$<HTMLImageElement>('img.icon'));
@@ -91,8 +83,6 @@ export class Renderer implements IRenderer {
const name = dom.append(header, dom.$('span.name'));
const openExtensionAction = this.instantiationService.createInstance(OpenExtensionAction);
const extensionDisposables = [dom.addDisposableListener(name, 'click', (e: MouseEvent) => {
tree.setFocus(openExtensionAction.extensionData);
tree.setSelection([openExtensionAction.extensionData]);
openExtensionAction.run(e.ctrlKey || e.metaKey);
e.stopPropagation();
e.preventDefault();
@@ -113,25 +103,8 @@ export class Renderer implements IRenderer {
};
}
private renderUnknownExtensionTemplate(tree: ITree, container: HTMLElement): IUnknownExtensionTemplateData {
const messageContainer = dom.append(container, dom.$('div.unknown-extension'));
dom.append(messageContainer, dom.$('span.error-marker')).textContent = localize('error', "Error");
dom.append(messageContainer, dom.$('span.message')).textContent = localize('Unknown Extension', "Unknown Extension:");
const identifier = dom.append(messageContainer, dom.$('span.message'));
return { identifier };
}
public renderElement(tree: ITree, element: IExtensionData, templateId: string, templateData: any): void {
if (templateId === Renderer.EXTENSION_TEMPLATE_ID) {
this.renderExtension(tree, element, templateData);
return;
}
this.renderUnknownExtension(tree, element, templateData);
}
private renderExtension(tree: ITree, extensionData: IExtensionData, data: IExtensionTemplateData): void {
const extension = extensionData.extension;
public renderElement(node: ITreeNode<IExtensionData>, index: number, data: IExtensionTemplateData): void {
const extension = node.element.extension;
const onError = Event.once(domEvent(data.icon, 'error'));
onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables);
data.icon.src = extension.iconUrl;
@@ -146,54 +119,36 @@ export class Renderer implements IRenderer {
data.name.textContent = extension.displayName;
data.identifier.textContent = extension.identifier.id;
data.author.textContent = extension.publisherDisplayName;
data.extensionData = extensionData;
data.extensionData = node.element;
}
private renderUnknownExtension(tree: ITree, { extension }: IExtensionData, data: IUnknownExtensionTemplateData): void {
data.identifier.textContent = extension.identifier.id;
}
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
if (templateId === Renderer.EXTENSION_TEMPLATE_ID) {
templateData.extensionDisposables = dispose((<IExtensionTemplateData>templateData).extensionDisposables);
}
public disposeTemplate(templateData: IExtensionTemplateData): void {
templateData.extensionDisposables = dispose((<IExtensionTemplateData>templateData).extensionDisposables);
}
}
export class Controller extends WorkbenchTreeController {
export class UnknownExtensionRenderer implements IListRenderer<ITreeNode<IExtensionData>, IUnknownExtensionTemplateData> {
constructor(
@IExtensionsWorkbenchService private readonly extensionsWorkdbenchService: IExtensionsWorkbenchService,
@IConfigurationService configurationService: IConfigurationService
) {
super({}, configurationService);
static readonly TEMPLATE_ID = 'unknown-extension-template';
// TODO@Sandeep this should be a command
this.downKeyBindingDispatcher.set(KeyMod.CtrlCmd | KeyCode.Enter, (tree: ITree, event: any) => this.openExtension(tree, true));
public get templateId(): string {
return UnknownExtensionRenderer.TEMPLATE_ID;
}
protected onLeftClick(tree: ITree, element: IExtensionData, event: IMouseEvent): boolean {
let currentFocused = tree.getFocus();
if (super.onLeftClick(tree, element, event)) {
if (element.parent === null) {
if (currentFocused) {
tree.setFocus(currentFocused);
} else {
tree.focusFirst();
}
return true;
}
}
return false;
public renderTemplate(container: HTMLElement): IUnknownExtensionTemplateData {
const messageContainer = dom.append(container, dom.$('div.unknown-extension'));
dom.append(messageContainer, dom.$('span.error-marker')).textContent = localize('error', "Error");
dom.append(messageContainer, dom.$('span.message')).textContent = localize('Unknown Extension', "Unknown Extension:");
const identifier = dom.append(messageContainer, dom.$('span.message'));
return { identifier };
}
public openExtension(tree: ITree, sideByside: boolean): boolean {
const element: IExtensionData = tree.getFocus();
if (element.extension) {
this.extensionsWorkdbenchService.open(element.extension, sideByside);
return true;
}
return false;
public renderElement(node: ITreeNode<IExtensionData>, index: number, data: IUnknownExtensionTemplateData): void {
data.identifier.textContent = node.element.extension.identifier.id;
}
public disposeTemplate(data: IUnknownExtensionTemplateData): void {
}
}
@@ -218,7 +173,7 @@ class OpenExtensionAction extends Action {
}
}
export class ExtensionsTree extends WorkbenchTree {
export class ExtensionsTree extends WorkbenchAsyncDataTree<IExtensionData, any> {
constructor(
input: IExtensionData,
@@ -227,30 +182,37 @@ export class ExtensionsTree extends WorkbenchTree {
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
@IKeybindingService keybindingService: IKeybindingService,
@IAccessibilityService accessibilityService: IAccessibilityService,
@IExtensionsWorkbenchService extensionsWorkdbenchService: IExtensionsWorkbenchService
) {
const renderer = instantiationService.createInstance(Renderer);
const controller = instantiationService.createInstance(Controller);
const delegate = new VirualDelegate();
const dataSource = new AsyncDataSource();
const renderers = [instantiationService.createInstance(ExtensionRenderer), instantiationService.createInstance(UnknownExtensionRenderer)];
const identityProvider = {
getId({ extension, parent }: IExtensionData): string {
return parent ? this.getId(parent) + '/' + extension.identifier.id : extension.identifier.id;
}
};
super(
container,
delegate,
renderers,
dataSource,
{
dataSource: new DataSource(),
renderer,
controller
}, {
indentPixels: 40,
twistiePixels: 20
indent: 40,
identityProvider,
multipleSelectionSupport: false
},
contextKeyService, listService, themeService, instantiationService, configurationService
contextKeyService, listService, themeService, configurationService, keybindingService, accessibilityService
);
this.setInput(input);
this.disposables.push(this.onDidChangeSelection(event => {
if (event && event.payload && event.payload.origin === 'keyboard') {
controller.openExtension(this, false);
}
extensionsWorkdbenchService.open(event.elements[0], event.browserEvent instanceof MouseEvent && (event.browserEvent.ctrlKey || event.browserEvent.metaKey || event.browserEvent.altKey));
}));
}
}

View File

@@ -27,7 +27,7 @@ export class ExtensionsInput extends EditorInput {
return localize('extensionsInputName', "Extension: {0}", this.extension.displayName);
}
matches(other: any): boolean {
matches(other: unknown): boolean {
if (!(other instanceof ExtensionsInput)) {
return false;
}

View File

@@ -33,7 +33,6 @@ import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/we
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
@@ -136,7 +135,7 @@ class NavBar {
}
dispose(): void {
this.actionbar = dispose(this.actionbar);
dispose(this.actionbar);
}
}
@@ -671,7 +670,7 @@ export class ExtensionEditor extends BaseEditor {
});
}
private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): Tree {
private renderDependencies(container: HTMLElement, extensionDependencies: IExtensionDependencies): ExtensionsTree {
class ExtensionData implements IExtensionData {
private readonly extensionDependencies: IExtensionDependencies;
@@ -720,7 +719,7 @@ export class ExtensionEditor extends BaseEditor {
return Promise.resolve({ focus() { extensionsPackTree.domFocus(); } });
}
private renderExtensionPack(container: HTMLElement, extension: IExtension): Tree {
private renderExtensionPack(container: HTMLElement, extension: IExtension): ExtensionsTree {
const extensionsWorkbenchService = this.extensionsWorkbenchService;
class ExtensionData implements IExtensionData {

View File

@@ -10,17 +10,17 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { tmpdir } from 'os';
import * as os from 'os';
import { join } from 'vs/base/common/path';
import { writeFile } from 'vs/base/node/pfs';
import { IExtensionHostProfileService, ReportExtensionIssueAction } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor';
import { IExtensionHostProfileService } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { createSlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution {
@@ -30,11 +30,11 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@IExtensionHostProfileService private readonly _extensionProfileService: IExtensionHostProfileService,
@IExtensionsWorkbenchService private readonly _anotherExtensionService: IExtensionsWorkbenchService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILogService private readonly _logService: ILogService,
@INotificationService private readonly _notificationService: INotificationService,
@IEditorService private readonly _editorService: IEditorService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super();
this._register(_extensionService.onDidChangeResponsiveChange(this._onDidChangeResponsiveChange, this));
@@ -132,16 +132,12 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
return;
}
// add to running extensions view
this._extensionProfileService.setUnresponsiveProfile(extension.identifier, profile);
// print message to log
const path = join(tmpdir(), `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`);
const path = join(os.tmpdir(), `exthost-${Math.random().toString(16).slice(2, 8)}.cpuprofile`);
await writeFile(path, JSON.stringify(profile.data));
this._logService.warn(`UNRESPONSIVE extension host, '${top.id}' took ${top!.percentage}% of ${duration / 1e3}ms, saved PROFILE here: '${path}'`, data);
// send telemetry
const id = generateUuid();
/* __GDPR__
"exthostunresponsive" : {
@@ -151,24 +147,22 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
}
*/
this._telemetryService.publicLog('exthostunresponsive', {
id,
duration,
data,
});
// add to running extensions view
this._extensionProfileService.setUnresponsiveProfile(extension.identifier, profile);
// prompt: when really slow/greedy
if (!(top.percentage >= 99 && top.total >= 5e6)) {
return;
}
// prompt: only when you can file an issue
const reportAction = new ReportExtensionIssueAction({
marketplaceInfo: this._anotherExtensionService.local.filter(value => ExtensionIdentifier.equals(value.identifier.id, extension.identifier))[0],
description: extension,
unresponsiveProfile: profile,
status: undefined,
});
if (!reportAction.enabled) {
const action = await this._instantiationService.invokeFunction(createSlowExtensionAction, extension, profile);
if (!action) {
// cannot report issues against this extension...
return;
}
@@ -190,18 +184,8 @@ export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchCont
label: localize('show', 'Show Extensions'),
run: () => this._editorService.openEditor(new RuntimeExtensionsInput())
},
{
label: localize('report', "Report Issue"),
run: () => {
/* __GDPR__
"exthostunresponsive/report" : {
"id" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
this._telemetryService.publicLog('exthostunresponsive/report', { id });
return reportAction.run();
}
}],
action
],
{ silent: true }
);
}

View File

@@ -0,0 +1,185 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import pkg from 'vs/platform/product/node/package';
import { Action } from 'vs/base/common/actions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { localize } from 'vs/nls';
import { IRequestService } from 'vs/platform/request/node/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { asText } from 'vs/base/node/request';
import { join } from 'vs/base/common/path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import Severity from 'vs/base/common/severity';
abstract class RepoInfo {
readonly base: string;
readonly owner: string;
readonly repo: string;
static fromExtension(desc: IExtensionDescription): RepoInfo | undefined {
let result: RepoInfo | undefined;
// scheme:auth/OWNER/REPO/issues/
if (desc.bugs && typeof desc.bugs.url === 'string') {
const base = URI.parse(desc.bugs.url);
const match = /\/([^/]+)\/([^/]+)\/issues\/?$/.exec(desc.bugs.url);
if (match) {
result = {
base: base.with({ path: null, fragment: null, query: null }).toString(true),
owner: match[1],
repo: match[2]
};
}
}
// scheme:auth/OWNER/REPO.git
if (!result && desc.repository && typeof desc.repository.url === 'string') {
const base = URI.parse(desc.repository.url);
const match = /\/([^/]+)\/([^/]+)(\.git)?$/.exec(desc.repository.url);
if (match) {
result = {
base: base.with({ path: null, fragment: null, query: null }).toString(true),
owner: match[1],
repo: match[2]
};
}
}
// for now only GH is supported
if (result && result.base.indexOf('github') === -1) {
result = undefined;
}
return result;
}
}
export class SlowExtensionAction extends Action {
constructor(
readonly extension: IExtensionDescription,
readonly profile: IExtensionHostProfile,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) {
super('report.slow', localize('cmd.reportOrShow', "Performance Issue"), 'extension-action report-issue');
this.enabled = Boolean(RepoInfo.fromExtension(extension));
}
async run(): Promise<void> {
const action = await this._instantiationService.invokeFunction(createSlowExtensionAction, this.extension, this.profile);
if (action) {
await action.run();
}
}
}
export async function createSlowExtensionAction(
accessor: ServicesAccessor,
extension: IExtensionDescription,
profile: IExtensionHostProfile
): Promise<Action | undefined> {
const info = RepoInfo.fromExtension(extension);
if (!info) {
return undefined;
}
const requestService = accessor.get(IRequestService);
const instaService = accessor.get(IInstantiationService);
const url = `https://api.github.com/search/issues?q=is:issue+state:open+in:title+repo:${info.owner}/${info.repo}+%22Extension+causes+high+cpu+load%22`;
const res = await requestService.request({ url }, CancellationToken.None);
const rawText = await asText(res);
if (!rawText) {
return undefined;
}
const data = <{ total_count: number; }>JSON.parse(rawText);
if (!data || typeof data.total_count !== 'number') {
return undefined;
} else if (data.total_count === 0) {
return instaService.createInstance(ReportExtensionSlowAction, extension, info, profile);
} else {
return instaService.createInstance(ShowExtensionSlowAction, extension, info, profile);
}
}
class ReportExtensionSlowAction extends Action {
constructor(
readonly extension: IExtensionDescription,
readonly repoInfo: RepoInfo,
readonly profile: IExtensionHostProfile,
@IDialogService private readonly _dialogService: IDialogService,
) {
super('report.slow', localize('cmd.report', "Report Issue"));
}
async run(): Promise<void> {
// rewrite pii (paths) and store on disk
const profiler = await import('v8-inspect-profiler');
const data = profiler.rewriteAbsolutePaths({ profile: <any>this.profile.data }, 'pii_removed');
const path = join(os.homedir(), `${this.extension.identifier.value}-unresponsive.cpuprofile.txt`);
await profiler.writeProfile(data, path).then(undefined, onUnexpectedError);
// build issue
const title = encodeURIComponent('Extension causes high cpu load');
const osVersion = `${os.type()} ${os.arch()} ${os.release()}`;
const message = `:warning: Make sure to **attach** this file from your *home*-directory:\n:warning:\`${path}\`\n\nFind more details here: https://github.com/Microsoft/vscode/wiki/Explain:-extension-causes-high-cpu-load`;
const body = encodeURIComponent(`- Issue Type: \`Performance\`
- Extension Name: \`${this.extension.name}\`
- Extension Version: \`${this.extension.version}\`
- OS Version: \`${osVersion}\`
- VSCode version: \`${pkg.version}\`\n\n${message}`);
const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`;
window.open(url);
this._dialogService.show(
Severity.Info,
localize('attach.title', "Did you attach the CPU-Profile?"),
[localize('ok', 'OK')],
{ detail: localize('attach.msg', "This is a reminder to make sure that you have not forgotten to attach '{0}' to the issue you have just created.", path) }
);
}
}
class ShowExtensionSlowAction extends Action {
constructor(
readonly extension: IExtensionDescription,
readonly repoInfo: RepoInfo,
readonly profile: IExtensionHostProfile,
@IDialogService private readonly _dialogService: IDialogService,
) {
super('show.slow', localize('cmd.show', "Show Issues"));
}
async run(): Promise<void> {
// rewrite pii (paths) and store on disk
const profiler = await import('v8-inspect-profiler');
const data = profiler.rewriteAbsolutePaths({ profile: <any>this.profile.data }, 'pii_removed');
const path = join(os.homedir(), `${this.extension.identifier.value}-unresponsive.cpuprofile.txt`);
await profiler.writeProfile(data, path).then(undefined, onUnexpectedError);
// show issues
const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues?utf8=✓&q=is%3Aissue+state%3Aopen+%22Extension+causes+high+cpu+load%22`;
window.open(url);
this._dialogService.show(
Severity.Info,
localize('attach.title', "Did you attach the CPU-Profile?"),
[localize('ok', 'OK')],
{ detail: localize('attach.msg2', "This is a reminder to make sure that you have not forgotten to attach '{0}' to an existing performance issue.", path) }
);
}
}

View File

@@ -300,11 +300,11 @@
border-color: rgb(238, 238, 238);
}
.extension-editor .subcontent .monaco-tree-row .content .unknown-extension {
.extension-editor .subcontent .monaco-list-row .content .unknown-extension {
line-height: 62px;
}
.extension-editor .subcontent .monaco-tree-row .content .unknown-extension > .error-marker {
.extension-editor .subcontent .monaco-list-row .content .unknown-extension > .error-marker {
background-color: #BE1100;
padding: 2px 4px;
font-weight: bold;
@@ -312,46 +312,46 @@
color: #CCC;
}
.extension-editor .subcontent .monaco-tree-row .unknown-extension > .message {
.extension-editor .subcontent .monaco-list-row .unknown-extension > .message {
padding-left: 10px;
font-weight: bold;
font-size: 14px;
}
.extension-editor .subcontent .monaco-tree-row .extension {
.extension-editor .subcontent .monaco-list-row .extension {
display: flex;
align-items: center;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details {
.extension-editor .subcontent .monaco-list-row .extension > .details {
flex: 1;
overflow: hidden;
padding-left: 10px;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .header {
.extension-editor .subcontent .monaco-list-row .extension > .details > .header {
display: flex;
align-items: center;
height: 19px;
line-height: 19px;
overflow: hidden;
}
.extension-editor .subcontent .monaco-tree-row .extension > .icon {
.extension-editor .subcontent .monaco-list-row .extension > .icon {
height: 40px;
width: 40px;
object-fit: contain;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .header > .name {
.extension-editor .subcontent .monaco-list-row .extension > .details > .header > .name {
font-weight: bold;
font-size: 16px;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .header > .name:hover {
.extension-editor .subcontent .monaco-list-row .extension > .details > .header > .name:hover {
text-decoration: underline;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .header > .identifier {
.extension-editor .subcontent .monaco-list-row .extension > .details > .header > .identifier {
font-size: 90%;
opacity: 0.6;
margin-left: 10px;
@@ -360,14 +360,14 @@
border-radius: 4px;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .footer {
.extension-editor .subcontent .monaco-list-row .extension > .details > .footer {
display: flex;
height: 19px;
line-height: 19px;
overflow: hidden;
padding-top: 5px;
}
.extension-editor .subcontent .monaco-tree-row .extension > .details > .footer > .author {
.extension-editor .subcontent .monaco-list-row .extension > .details > .footer > .author {
font-size: 90%;
font-weight: 600;
opacity: 0.6;

View File

@@ -40,10 +40,9 @@ import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/cont
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ILabelService } from 'vs/platform/label/common/label';
import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { join } from 'vs/base/common/path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions';
export const IExtensionHostProfileService = createDecorator<IExtensionHostProfileService>('extensionHostProfileService');
export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey<string>('profileSessionState', 'none');
@@ -308,7 +307,10 @@ export class RuntimeExtensionsEditor extends BaseEditor {
data.activationTime.textContent = activationTimes.startup ? `Startup Activation: ${syncTime}ms` : `Activation: ${syncTime}ms`;
data.actionbar.clear();
if (element.unresponsiveProfile || isNonEmptyArray(element.status.runtimeErrors)) {
if (element.unresponsiveProfile) {
data.actionbar.push(this._instantiationService.createInstance(SlowExtensionAction, element.description, element.unresponsiveProfile), { icon: true, label: true });
}
if (isNonEmptyArray(element.status.runtimeErrors)) {
data.actionbar.push(new ReportExtensionIssueAction(element), { icon: true, label: true });
}
@@ -471,7 +473,6 @@ export class ReportExtensionIssueAction extends Action {
private static _label = nls.localize('reportExtensionIssue', "Report Issue");
private readonly _url: string;
private readonly _task?: () => Promise<any>;
constructor(extension: {
description: IExtensionDescription;
@@ -484,15 +485,10 @@ export class ReportExtensionIssueAction extends Action {
&& extension.marketplaceInfo.type === ExtensionType.User
&& !!extension.description.repository && !!extension.description.repository.url;
const { url, task } = ReportExtensionIssueAction._generateNewIssueUrl(extension);
this._url = url;
this._task = task;
this._url = ReportExtensionIssueAction._generateNewIssueUrl(extension);
}
async run(): Promise<void> {
if (this._task) {
await this._task();
}
window.open(this._url);
}
@@ -501,9 +497,9 @@ export class ReportExtensionIssueAction extends Action {
marketplaceInfo: IExtension;
status?: IExtensionsStatus;
unresponsiveProfile?: IExtensionHostProfile
}): { url: string, task?: () => Promise<any> } {
}): string {
let task: (() => Promise<any>) | undefined;
let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined;
if (!!baseUrl) {
baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`;
@@ -511,28 +507,10 @@ export class ReportExtensionIssueAction extends Action {
baseUrl = product.reportIssueUrl;
}
let title: string;
let message: string;
let reason: string;
if (extension.unresponsiveProfile) {
// unresponsive extension host caused
reason = 'Performance';
title = 'Extension causes high cpu load';
let path = join(os.homedir(), `${extension.description.identifier.value}-unresponsive.cpuprofile.txt`);
task = async () => {
const profiler = await import('v8-inspect-profiler');
const data = profiler.rewriteAbsolutePaths({ profile: <any>extension.unresponsiveProfile!.data }, 'pii_removed');
profiler.writeProfile(data, path).then(undefined, onUnexpectedError);
};
message = `:warning: Make sure to **attach** this file from your *home*-directory:\n:warning:\`${path}\`\n\nFind more details here: https://github.com/Microsoft/vscode/wiki/Explain:-extension-causes-high-cpu-load`;
} else {
// generic
reason = 'Bug';
title = 'Extension issue';
message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:';
clipboard.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```');
}
let reason = 'Bug';
let title = 'Extension issue';
let message = ':warning: We have written the needed data into your clipboard. Please paste! :warning:';
clipboard.writeText('```json \n' + JSON.stringify(extension.status, null, '\t') + '\n```');
const osVersion = `${os.type()} ${os.arch()} ${os.release()}`;
const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&';
@@ -544,10 +522,7 @@ export class ReportExtensionIssueAction extends Action {
- VSCode version: \`${pkg.version}\`\n\n${message}`
);
return {
url: `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`,
task
};
return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`;
}
}

View File

@@ -23,7 +23,7 @@ export class RuntimeExtensionsInput extends EditorInput {
return nls.localize('extensionsInputName', "Running Extensions");
}
matches(other: any): boolean {
matches(other: unknown): boolean {
if (!(other instanceof RuntimeExtensionsInput)) {
return false;
}

View File

@@ -9,6 +9,7 @@ import * as path from 'vs/base/common/path';
import * as fs from 'fs';
import * as os from 'os';
import * as uuid from 'vs/base/common/uuid';
import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs';
import {
IExtensionGalleryService, IGalleryExtensionAssets, IGalleryExtension, IExtensionManagementService,
IExtensionEnablementService, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier
@@ -25,9 +26,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/workbench/services/files/node/fileService';
import * as extfs from 'vs/base/node/extfs';
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IPager } from 'vs/base/common/paging';
import { assign } from 'vs/base/common/objects';

View File

@@ -99,7 +99,7 @@ export class FeedbackDropdown extends Dropdown {
y: position.top - 9, // above status bar
width: position.width,
height: position.height
} as IAnchor;
};
}
protected renderContents(container: HTMLElement): IDisposable {

View File

@@ -5,7 +5,7 @@
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { FeedbackDropdown, IFeedback, IFeedbackDelegate, FEEDBACK_VISIBLE_CONFIG, IFeedbackDropdownOptions } from 'vs/workbench/contrib/feedback/electron-browser/feedback';
import { FeedbackDropdown, IFeedback, IFeedbackDelegate, FEEDBACK_VISIBLE_CONFIG } from 'vs/workbench/contrib/feedback/electron-browser/feedback';
import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import product from 'vs/platform/product/node/product';
@@ -125,14 +125,14 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem {
this.dropdown = this._register(this.instantiationService.createInstance(FeedbackDropdown, this.container, {
contextViewProvider: this.contextViewService,
feedbackService: this.instantiationService.createInstance(TwitterFeedbackService),
onFeedbackVisibilityChange: visible => {
onFeedbackVisibilityChange: (visible: boolean) => {
if (visible) {
addClass(this.container, 'has-beak');
} else {
removeClass(this.container, 'has-beak');
}
}
} as IFeedbackDropdownOptions));
}));
this.updateStyles();

View File

@@ -12,7 +12,7 @@ import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/te
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -34,10 +34,9 @@ import { QueryInput } from 'sql/parts/query/common/queryInput';
export class FileEditorTracker extends Disposable implements IWorkbenchContribution {
protected closeOnFileDelete: boolean;
private modelLoadQueue: ResourceQueue;
private activeOutOfWorkspaceWatchers: ResourceMap<URI>;
private closeOnFileDelete: boolean;
private modelLoadQueue = new ResourceQueue();
private activeOutOfWorkspaceWatchers = new ResourceMap<IDisposable>();
constructor(
@IEditorService private readonly editorService: IEditorService,
@@ -52,9 +51,6 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
) {
super();
this.modelLoadQueue = new ResourceQueue();
this.activeOutOfWorkspaceWatchers = new ResourceMap<URI>();
this.onConfigurationUpdated(configurationService.getValue<IWorkbenchEditorConfiguration>());
this.registerListeners();
@@ -358,9 +354,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
});
// Handle no longer visible out of workspace resources
this.activeOutOfWorkspaceWatchers.forEach(resource => {
this.activeOutOfWorkspaceWatchers.keys().forEach(resource => {
if (!visibleOutOfWorkspacePaths.get(resource)) {
this.fileService.unwatch(resource);
dispose(this.activeOutOfWorkspaceWatchers.get(resource));
this.activeOutOfWorkspaceWatchers.delete(resource);
}
});
@@ -368,8 +364,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
// Handle newly visible out of workspace resources
visibleOutOfWorkspacePaths.forEach(resource => {
if (!this.activeOutOfWorkspaceWatchers.get(resource)) {
this.fileService.watch(resource);
this.activeOutOfWorkspaceWatchers.set(resource, resource);
const disposable = this.fileService.watch(resource);
this.activeOutOfWorkspaceWatchers.set(resource, disposable);
}
});
}
@@ -377,8 +373,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
dispose(): void {
super.dispose();
// Dispose watchers if any
this.activeOutOfWorkspaceWatchers.forEach(resource => this.fileService.unwatch(resource));
// Dispose remaining watchers if any
this.activeOutOfWorkspaceWatchers.forEach(disposable => dispose(disposable));
this.activeOutOfWorkspaceWatchers.clear();
}
}

View File

@@ -99,7 +99,7 @@ export class TextFileEditor extends BaseTextEditor {
// React to editors closing to preserve or clear view state. This needs to happen
// in the onWillCloseEditor because at that time the editor has not yet
// been disposed and we can safely persist the view state still as needed.
this.groupListener = dispose(this.groupListener);
dispose(this.groupListener);
this.groupListener = ((group as IEditorGroupView).onWillCloseEditor(e => this.onWillCloseEditorInGroup(e)));
}
@@ -178,7 +178,7 @@ export class TextFileEditor extends BaseTextEditor {
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_DIRECTORY) {
this.openAsFolder(input);
return Promise.reject<any>(new Error(nls.localize('openFolderError', "File is a directory")));
return Promise.reject(new Error(nls.localize('openFolderError', "File is a directory")));
}
// Offer to create a file from the error if we have a file not found and the name is valid
@@ -296,7 +296,7 @@ export class TextFileEditor extends BaseTextEditor {
}
dispose(): void {
this.groupListener = dispose(this.groupListener);
dispose(this.groupListener);
super.dispose();
}

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/explorerviewlet';
import { localize } from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition, VIEW_CONTAINER } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, VIEW_CONTAINER } from 'vs/workbench/contrib/files/common/files';
import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ExplorerView } from 'vs/workbench/contrib/files/browser/views/explorerView';
@@ -103,7 +103,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor
name: OpenEditorsView.NAME,
ctorDescriptor: { ctor: OpenEditorsView },
order: 0,
when: OpenEditorsVisibleCondition,
when: OpenEditorsVisibleContext,
canToggleVisibility: true,
focusCommand: {
id: 'workbench.files.action.focusOpenEditorsView',

View File

@@ -15,7 +15,7 @@ import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/c
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { isWindows, isMacintosh } from 'vs/base/common/platform';
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash } from 'vs/workbench/contrib/files/common/files';
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands';
import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { AutoSaveContext } from 'vs/workbench/services/textfile/common/textfiles';
@@ -63,7 +63,7 @@ const MOVE_FILE_TO_TRASH_ID = 'moveFileToTrash';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: MOVE_FILE_TO_TRASH_ID,
weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus,
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ContextKeyExpr.has('config.files.enableTrash')),
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash),
primary: KeyCode.Delete,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.Backspace
@@ -75,7 +75,7 @@ const DELETE_FILE_ID = 'deleteFile';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: DELETE_FILE_ID,
weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus,
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ContextKeyExpr.has('config.files.enableTrash')),
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext),
primary: KeyMod.Shift | KeyCode.Delete,
mac: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace
@@ -86,7 +86,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: DELETE_FILE_ID,
weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus,
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ContextKeyExpr.not('config.files.enableTrash')),
when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext, ExplorerResourceMoveableToTrash.toNegated()),
primary: KeyCode.Delete,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.Backspace
@@ -517,7 +517,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
title: nls.localize('deleteFile', "Delete Permanently"),
precondition: ExplorerResourceNotReadonlyContext
},
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ContextKeyExpr.has('config.files.enableTrash'))
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ExplorerResourceMoveableToTrash)
});
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
@@ -528,7 +528,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, {
title: nls.localize('deleteFile', "Delete Permanently"),
precondition: ExplorerResourceNotReadonlyContext
},
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ContextKeyExpr.not('config.files.enableTrash'))
when: ContextKeyExpr.and(ExplorerRootContext.toNegated(), ExplorerResourceMoveableToTrash.toNegated())
});
// Empty Editor Group Context Menu

View File

@@ -17,12 +17,12 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { VIEWLET_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService, AutoSaveConfiguration } from 'vs/platform/files/common/files';
import { toResource, IUntitledResourceInput, ITextEditor } from 'vs/workbench/common/editor';
import { toResource, ITextEditor } from 'vs/workbench/common/editor';
import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IInstantiationService, ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileCommands';
@@ -68,36 +68,14 @@ export const PASTE_FILE_LABEL = nls.localize('pasteFile', "Paste");
export const FileCopiedContext = new RawContextKey<boolean>('fileCopied', false);
export class BaseErrorReportingAction extends Action {
const CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete';
constructor(
id: string,
label: string,
private _notificationService: INotificationService
) {
super(id, label);
function onError(notificationService: INotificationService, error: any): void {
if (error.message === 'string') {
error = error.message;
}
public get notificationService() {
return this._notificationService;
}
protected onError(error: any): void {
if (error.message === 'string') {
error = error.message;
}
this._notificationService.error(toErrorMessage(error, false));
}
protected onErrorWithRetry(error: any, retry: () => Promise<any>): void {
this._notificationService.prompt(Severity.Error, toErrorMessage(error, false),
[{
label: nls.localize('retry', "Retry"),
run: () => retry()
}]
);
}
notificationService.error(toErrorMessage(error, false));
}
function refreshIfSeparator(value: string, explorerService: IExplorerService): void {
@@ -108,66 +86,26 @@ function refreshIfSeparator(value: string, explorerService: IExplorerService): v
}
/* New File */
export class NewFileAction extends BaseErrorReportingAction {
export class NewFileAction extends Action {
static readonly ID = 'workbench.files.action.createFileFromExplorer';
static readonly LABEL = nls.localize('createNewFile', "New File");
private toDispose: IDisposable[] = [];
constructor(
private getElement: () => ExplorerItem,
@INotificationService notificationService: INotificationService,
@IExplorerService private explorerService: IExplorerService,
@IFileService private fileService: IFileService,
@IEditorService private editorService: IEditorService
@IExplorerService explorerService: IExplorerService,
@ICommandService private commandService: ICommandService
) {
super('explorer.newFile', NEW_FILE_LABEL, notificationService);
super('explorer.newFile', NEW_FILE_LABEL);
this.class = 'explorer-action new-file';
this.toDispose.push(this.explorerService.onDidChangeEditable(e => {
const elementIsBeingEdited = this.explorerService.isEditable(e);
this.toDispose.push(explorerService.onDidChangeEditable(e => {
const elementIsBeingEdited = explorerService.isEditable(e);
this.enabled = !elementIsBeingEdited;
}));
}
run(): Promise<any> {
let folder: ExplorerItem;
const element = this.getElement();
if (element) {
folder = element.isDirectory ? element : element.parent!;
} else {
folder = this.explorerService.roots[0];
}
if (folder.isReadonly) {
return Promise.reject(new Error('Parent folder is readonly.'));
}
const stat = new NewExplorerItem(folder, false);
return folder.fetchChildren(this.fileService, this.explorerService).then(() => {
folder.addChild(stat);
const onSuccess = (value: string) => {
return this.fileService.createFile(resources.joinPath(folder.resource, value)).then(stat => {
refreshIfSeparator(value, this.explorerService);
return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } });
}, (error) => {
this.onErrorWithRetry(error, () => onSuccess(value));
});
};
this.explorerService.setEditable(stat, {
validationMessage: value => validateFileName(stat, value),
onFinish: (value, success) => {
folder.removeChild(stat);
this.explorerService.setEditable(stat, null);
if (success) {
onSuccess(value);
} else {
this.explorerService.select(folder.resource).then(undefined, onUnexpectedError);
}
}
});
});
return this.commandService.executeCommand(NEW_FILE_COMMAND_ID);
}
dispose(): void {
@@ -177,65 +115,26 @@ export class NewFileAction extends BaseErrorReportingAction {
}
/* New Folder */
export class NewFolderAction extends BaseErrorReportingAction {
export class NewFolderAction extends Action {
static readonly ID = 'workbench.files.action.createFolderFromExplorer';
static readonly LABEL = nls.localize('createNewFolder', "New Folder");
private toDispose: IDisposable[] = [];
constructor(
private getElement: () => ExplorerItem,
@INotificationService notificationService: INotificationService,
@IFileService private fileService: IFileService,
@IExplorerService private explorerService: IExplorerService
@IExplorerService explorerService: IExplorerService,
@ICommandService private commandService: ICommandService
) {
super('explorer.newFolder', NEW_FOLDER_LABEL, notificationService);
super('explorer.newFolder', NEW_FOLDER_LABEL);
this.class = 'explorer-action new-folder';
this.toDispose.push(this.explorerService.onDidChangeEditable(e => {
const elementIsBeingEdited = this.explorerService.isEditable(e);
this.toDispose.push(explorerService.onDidChangeEditable(e => {
const elementIsBeingEdited = explorerService.isEditable(e);
this.enabled = !elementIsBeingEdited;
}));
}
run(): Promise<any> {
let folder: ExplorerItem;
const element = this.getElement();
if (element) {
folder = element.isDirectory ? element : element.parent!;
} else {
folder = this.explorerService.roots[0];
}
if (folder.isReadonly) {
return Promise.reject(new Error('Parent folder is readonly.'));
}
const stat = new NewExplorerItem(folder, true);
return folder.fetchChildren(this.fileService, this.explorerService).then(() => {
folder.addChild(stat);
const onSuccess = (value: string) => {
return this.fileService.createFolder(resources.joinPath(folder.resource, value)).then(stat => {
refreshIfSeparator(value, this.explorerService);
return this.explorerService.select(stat.resource, true);
}, (error) => {
this.onErrorWithRetry(error, () => onSuccess(value));
});
};
this.explorerService.setEditable(stat, {
validationMessage: value => validateFileName(stat, value),
onFinish: (value, success) => {
folder.removeChild(stat);
this.explorerService.setEditable(stat, null);
if (success) {
onSuccess(value);
} else {
this.explorerService.select(folder.resource).then(undefined, onUnexpectedError);
}
}
});
});
return this.commandService.executeCommand(NEW_FOLDER_COMMAND_ID);
}
dispose(): void {
@@ -267,229 +166,210 @@ export class GlobalNewUntitledFileAction extends Action {
}
}
class BaseDeleteFileAction extends BaseErrorReportingAction {
private static readonly CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete';
private skipConfirm: boolean;
constructor(
private elements: ExplorerItem[],
private useTrash: boolean,
@IFileService private readonly fileService: IFileService,
@INotificationService notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@ITextFileService private readonly textFileService: ITextFileService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super('moveFileToTrash', MOVE_FILE_TO_TRASH_LABEL, notificationService);
this.useTrash = useTrash && elements.every(e => !extpath.isUNC(e.resource.fsPath)); // on UNC shares there is no trash
this.enabled = this.elements && this.elements.every(e => !e.isReadonly);
function deleteFiles(serviceAccesor: ServicesAccessor, elements: ExplorerItem[], useTrash: boolean, skipConfirm = false): Promise<void> {
let primaryButton: string;
if (useTrash) {
primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash");
} else {
primaryButton = nls.localize({ key: 'deleteButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete");
}
public run(): Promise<any> {
const distinctElements = resources.distinctParents(elements, e => e.resource);
const textFileService = serviceAccesor.get(ITextFileService);
const dialogService = serviceAccesor.get(IDialogService);
const configurationService = serviceAccesor.get(IConfigurationService);
const fileService = serviceAccesor.get(IFileService);
let primaryButton: string;
if (this.useTrash) {
primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash");
} else {
primaryButton = nls.localize({ key: 'deleteButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete");
}
const distinctElements = resources.distinctParents(this.elements, e => e.resource);
// Handle dirty
let confirmDirtyPromise: Promise<boolean> = Promise.resolve(true);
const dirty = this.textFileService.getDirty().filter(d => distinctElements.some(e => resources.isEqualOrParent(d, e.resource, !isLinux /* ignorecase */)));
if (dirty.length) {
let message: string;
if (distinctElements.length > 1) {
message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?");
} else if (distinctElements[0].isDirectory) {
if (dirty.length === 1) {
message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?");
} else {
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
}
// Handle dirty
let confirmDirtyPromise: Promise<boolean> = Promise.resolve(true);
const dirty = textFileService.getDirty().filter(d => distinctElements.some(e => resources.isEqualOrParent(d, e.resource, !isLinux /* ignorecase */)));
if (dirty.length) {
let message: string;
if (distinctElements.length > 1) {
message = nls.localize('dirtyMessageFilesDelete', "You are deleting files with unsaved changes. Do you want to continue?");
} else if (distinctElements[0].isDirectory) {
if (dirty.length === 1) {
message = nls.localize('dirtyMessageFolderOneDelete', "You are deleting a folder with unsaved changes in 1 file. Do you want to continue?");
} else {
message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?");
message = nls.localize('dirtyMessageFolderDelete', "You are deleting a folder with unsaved changes in {0} files. Do you want to continue?", dirty.length);
}
confirmDirtyPromise = this.dialogService.confirm({
message,
type: 'warning',
detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
primaryButton
}).then(res => {
if (!res.confirmed) {
return false;
}
this.skipConfirm = true; // since we already asked for confirmation
return this.textFileService.revertAll(dirty).then(() => true);
});
} else {
message = nls.localize('dirtyMessageFileDelete', "You are deleting a file with unsaved changes. Do you want to continue?");
}
// Check if file is dirty in editor and save it to avoid data loss
return confirmDirtyPromise.then(confirmed => {
if (!confirmed) {
return null;
confirmDirtyPromise = dialogService.confirm({
message,
type: 'warning',
detail: nls.localize('dirtyWarning', "Your changes will be lost if you don't save them."),
primaryButton
}).then(res => {
if (!res.confirmed) {
return false;
}
let confirmDeletePromise: Promise<IConfirmationResult>;
// Check if we need to ask for confirmation at all
if (this.skipConfirm || (this.useTrash && this.configurationService.getValue<boolean>(BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY) === false)) {
confirmDeletePromise = Promise.resolve({ confirmed: true } as IConfirmationResult);
}
// Confirm for moving to trash
else if (this.useTrash) {
const message = this.getMoveToTrashMessage(distinctElements);
confirmDeletePromise = this.dialogService.confirm({
message,
detail: isWindows ? nls.localize('undoBin', "You can restore from the Recycle Bin.") : nls.localize('undoTrash', "You can restore from the Trash."),
primaryButton,
checkbox: {
label: nls.localize('doNotAskAgain', "Do not ask me again")
},
type: 'question'
});
}
// Confirm for deleting permanently
else {
const message = this.getDeleteMessage(distinctElements);
confirmDeletePromise = this.dialogService.confirm({
message,
detail: nls.localize('irreversible', "This action is irreversible!"),
primaryButton,
type: 'warning'
});
}
return confirmDeletePromise.then(confirmation => {
// Check for confirmation checkbox
let updateConfirmSettingsPromise: Promise<void> = Promise.resolve(undefined);
if (confirmation.confirmed && confirmation.checkboxChecked === true) {
updateConfirmSettingsPromise = this.configurationService.updateValue(BaseDeleteFileAction.CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER);
}
return updateConfirmSettingsPromise.then(() => {
// Check for confirmation
if (!confirmation.confirmed) {
return Promise.resolve(null);
}
// Call function
const servicePromise = Promise.all(distinctElements.map(e => this.fileService.del(e.resource, { useTrash: this.useTrash, recursive: true })))
.then(undefined, (error: any) => {
// Handle error to delete file(s) from a modal confirmation dialog
let errorMessage: string;
let detailMessage: string | undefined;
let primaryButton: string;
if (this.useTrash) {
errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?");
detailMessage = nls.localize('irreversible', "This action is irreversible!");
primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently");
} else {
errorMessage = toErrorMessage(error, false);
primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry");
}
return this.dialogService.confirm({
message: errorMessage,
detail: detailMessage,
type: 'warning',
primaryButton
}).then(res => {
if (res.confirmed) {
if (this.useTrash) {
this.useTrash = false; // Delete Permanently
}
this.skipConfirm = true;
return this.run();
}
return Promise.resolve(undefined);
});
});
return servicePromise;
});
});
skipConfirm = true; // since we already asked for confirmation
return textFileService.revertAll(dirty).then(() => true);
});
}
private getMoveToTrashMessage(distinctElements: ExplorerItem[]): string {
if (this.containsBothDirectoryAndFile(distinctElements)) {
return getConfirmMessage(nls.localize('confirmMoveTrashMessageFilesAndDirectories', "Are you sure you want to delete the following {0} files/directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
// Check if file is dirty in editor and save it to avoid data loss
return confirmDirtyPromise.then(confirmed => {
if (!confirmed) {
return undefined;
}
if (distinctElements.length > 1) {
if (distinctElements[0].isDirectory) {
return getConfirmMessage(nls.localize('confirmMoveTrashMessageMultipleDirectories', "Are you sure you want to delete the following {0} directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
let confirmDeletePromise: Promise<IConfirmationResult>;
// Check if we need to ask for confirmation at all
if (skipConfirm || (useTrash && configurationService.getValue<boolean>(CONFIRM_DELETE_SETTING_KEY) === false)) {
confirmDeletePromise = Promise.resolve({ confirmed: true });
}
// Confirm for moving to trash
else if (useTrash) {
const message = getMoveToTrashMessage(distinctElements);
confirmDeletePromise = dialogService.confirm({
message,
detail: isWindows ? nls.localize('undoBin', "You can restore from the Recycle Bin.") : nls.localize('undoTrash', "You can restore from the Trash."),
primaryButton,
checkbox: {
label: nls.localize('doNotAskAgain', "Do not ask me again")
},
type: 'question'
});
}
// Confirm for deleting permanently
else {
const message = getDeleteMessage(distinctElements);
confirmDeletePromise = dialogService.confirm({
message,
detail: nls.localize('irreversible', "This action is irreversible!"),
primaryButton,
type: 'warning'
});
}
return confirmDeletePromise.then(confirmation => {
// Check for confirmation checkbox
let updateConfirmSettingsPromise: Promise<void> = Promise.resolve(undefined);
if (confirmation.confirmed && confirmation.checkboxChecked === true) {
updateConfirmSettingsPromise = configurationService.updateValue(CONFIRM_DELETE_SETTING_KEY, false, ConfigurationTarget.USER);
}
return getConfirmMessage(nls.localize('confirmMoveTrashMessageMultiple', "Are you sure you want to delete the following {0} files?", distinctElements.length), distinctElements.map(e => e.resource));
}
return updateConfirmSettingsPromise.then(() => {
// Check for confirmation
if (!confirmation.confirmed) {
return Promise.resolve(undefined);
}
// Call function
const servicePromise = Promise.all(distinctElements.map(e => fileService.del(e.resource, { useTrash: useTrash, recursive: true })))
.then(undefined, (error: any) => {
// Handle error to delete file(s) from a modal confirmation dialog
let errorMessage: string;
let detailMessage: string | undefined;
let primaryButton: string;
if (useTrash) {
errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?");
detailMessage = nls.localize('irreversible', "This action is irreversible!");
primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently");
} else {
errorMessage = toErrorMessage(error, false);
primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry");
}
return dialogService.confirm({
message: errorMessage,
detail: detailMessage,
type: 'warning',
primaryButton
}).then(res => {
if (res.confirmed) {
if (useTrash) {
useTrash = false; // Delete Permanently
}
skipConfirm = true;
return deleteFiles(serviceAccesor, elements, useTrash, skipConfirm);
}
return Promise.resolve();
});
});
return servicePromise;
});
});
});
}
function getMoveToTrashMessage(distinctElements: ExplorerItem[]): string {
if (containsBothDirectoryAndFile(distinctElements)) {
return getConfirmMessage(nls.localize('confirmMoveTrashMessageFilesAndDirectories', "Are you sure you want to delete the following {0} files/directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
if (distinctElements.length > 1) {
if (distinctElements[0].isDirectory) {
return nls.localize('confirmMoveTrashMessageFolder', "Are you sure you want to delete '{0}' and its contents?", distinctElements[0].name);
return getConfirmMessage(nls.localize('confirmMoveTrashMessageMultipleDirectories', "Are you sure you want to delete the following {0} directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
return nls.localize('confirmMoveTrashMessageFile', "Are you sure you want to delete '{0}'?", distinctElements[0].name);
return getConfirmMessage(nls.localize('confirmMoveTrashMessageMultiple', "Are you sure you want to delete the following {0} files?", distinctElements.length), distinctElements.map(e => e.resource));
}
private getDeleteMessage(distinctElements: ExplorerItem[]): string {
if (this.containsBothDirectoryAndFile(distinctElements)) {
return getConfirmMessage(nls.localize('confirmDeleteMessageFilesAndDirectories', "Are you sure you want to permanently delete the following {0} files/directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
if (distinctElements[0].isDirectory) {
return nls.localize('confirmMoveTrashMessageFolder', "Are you sure you want to delete '{0}' and its contents?", distinctElements[0].name);
}
if (distinctElements.length > 1) {
if (distinctElements[0].isDirectory) {
return getConfirmMessage(nls.localize('confirmDeleteMessageMultipleDirectories', "Are you sure you want to permanently delete the following {0} directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
return nls.localize('confirmMoveTrashMessageFile', "Are you sure you want to delete '{0}'?", distinctElements[0].name);
}
return getConfirmMessage(nls.localize('confirmDeleteMessageMultiple', "Are you sure you want to permanently delete the following {0} files?", distinctElements.length), distinctElements.map(e => e.resource));
}
function getDeleteMessage(distinctElements: ExplorerItem[]): string {
if (containsBothDirectoryAndFile(distinctElements)) {
return getConfirmMessage(nls.localize('confirmDeleteMessageFilesAndDirectories', "Are you sure you want to permanently delete the following {0} files/directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
if (distinctElements.length > 1) {
if (distinctElements[0].isDirectory) {
return nls.localize('confirmDeleteMessageFolder', "Are you sure you want to permanently delete '{0}' and its contents?", distinctElements[0].name);
return getConfirmMessage(nls.localize('confirmDeleteMessageMultipleDirectories', "Are you sure you want to permanently delete the following {0} directories and their contents?", distinctElements.length), distinctElements.map(e => e.resource));
}
return nls.localize('confirmDeleteMessageFile', "Are you sure you want to permanently delete '{0}'?", distinctElements[0].name);
return getConfirmMessage(nls.localize('confirmDeleteMessageMultiple', "Are you sure you want to permanently delete the following {0} files?", distinctElements.length), distinctElements.map(e => e.resource));
}
private containsBothDirectoryAndFile(distinctElements: ExplorerItem[]): boolean {
const directories = distinctElements.filter(element => element.isDirectory);
const files = distinctElements.filter(element => !element.isDirectory);
return directories.length > 0 && files.length > 0;
if (distinctElements[0].isDirectory) {
return nls.localize('confirmDeleteMessageFolder', "Are you sure you want to permanently delete '{0}' and its contents?", distinctElements[0].name);
}
return nls.localize('confirmDeleteMessageFile', "Are you sure you want to permanently delete '{0}'?", distinctElements[0].name);
}
function containsBothDirectoryAndFile(distinctElements: ExplorerItem[]): boolean {
const directories = distinctElements.filter(element => element.isDirectory);
const files = distinctElements.filter(element => !element.isDirectory);
return directories.length > 0 && files.length > 0;
}
let pasteShouldMove = false;
// Paste File/Folder
class PasteFileAction extends BaseErrorReportingAction {
class PasteFileAction extends Action {
public static readonly ID = 'filesExplorer.paste';
constructor(
private element: ExplorerItem,
@IFileService private fileService: IFileService,
@INotificationService notificationService: INotificationService,
@INotificationService private notificationService: INotificationService,
@IEditorService private readonly editorService: IEditorService,
@IExplorerService private readonly explorerService: IExplorerService
) {
super(PasteFileAction.ID, PASTE_FILE_LABEL, notificationService);
super(PasteFileAction.ID, PASTE_FILE_LABEL);
if (!this.element) {
this.element = this.explorerService.roots[0];
@@ -528,9 +408,9 @@ class PasteFileAction extends BaseErrorReportingAction {
}
return undefined;
}, e => this.onError(e));
}, e => onError(this.notificationService, e));
}, error => {
this.onError(new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile")));
onError(this.notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile")));
});
}
}
@@ -701,7 +581,7 @@ export class ToggleAutoSaveAction extends Action {
}
}
export abstract class BaseSaveAllAction extends BaseErrorReportingAction {
export abstract class BaseSaveAllAction extends Action {
private toDispose: IDisposable[];
private lastIsDirty: boolean;
@@ -711,9 +591,9 @@ export abstract class BaseSaveAllAction extends BaseErrorReportingAction {
@ITextFileService private readonly textFileService: ITextFileService,
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
@ICommandService protected commandService: ICommandService,
@INotificationService notificationService: INotificationService,
@INotificationService private notificationService: INotificationService,
) {
super(id, label, notificationService);
super(id, label);
this.toDispose = [];
this.lastIsDirty = this.textFileService.isDirty();
@@ -747,7 +627,7 @@ export abstract class BaseSaveAllAction extends BaseErrorReportingAction {
public run(context?: any): Promise<boolean> {
return this.doRun(context).then(() => true, error => {
this.onError(error);
onError(this.notificationService, error);
return false;
});
}
@@ -918,7 +798,7 @@ export class ShowOpenedFileInNewWindow extends Action {
const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
if (fileResource) {
if (this.fileService.canHandleResource(fileResource)) {
this.windowService.openWindow([{ uri: fileResource, typeHint: 'file' }], { forceNewWindow: true, forceOpenWorkspaceAsFile: true });
this.windowService.openWindow([{ fileUri: fileResource }], { forceNewWindow: true });
} else {
this.notificationService.info(nls.localize('openFileToShowInNewWindow.unsupportedschema', "The active editor must contain an openable resource."));
}
@@ -1002,7 +882,7 @@ export class CompareWithClipboardAction extends Action {
private static readonly SCHEME = 'clipboardCompare';
private registrationDisposal: IDisposable;
private registrationDisposal: IDisposable | undefined;
constructor(
id: string,
@@ -1029,7 +909,8 @@ export class CompareWithClipboardAction extends Action {
const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name);
return this.editorService.openEditor({ leftResource: resource.with({ scheme: CompareWithClipboardAction.SCHEME }), rightResource: resource, label: editorLabel }).finally(() => {
this.registrationDisposal = dispose(this.registrationDisposal);
dispose(this.registrationDisposal);
this.registrationDisposal = undefined;
});
}
@@ -1039,7 +920,8 @@ export class CompareWithClipboardAction extends Action {
public dispose(): void {
super.dispose();
this.registrationDisposal = dispose(this.registrationDisposal);
dispose(this.registrationDisposal);
this.registrationDisposal = undefined;
}
}
@@ -1073,43 +955,82 @@ function getContext(listWidget: ListWidget): IExplorerContext {
return { stat, selection: selection && typeof stat !== 'undefined' && selection.indexOf(stat) >= 0 ? selection : [] };
}
// TODO@isidor these commands are calling into actions due to the complex inheritance action structure.
// It should be the other way around, that actions call into commands.
function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature1<() => ExplorerItem, Action>): Promise<any> {
const instantiationService = accessor.get(IInstantiationService);
function onErrorWithRetry(notificationService: INotificationService, error: any, retry: () => Promise<any>): void {
notificationService.prompt(Severity.Error, toErrorMessage(error, false),
[{
label: nls.localize('retry', "Retry"),
run: () => retry()
}]
);
}
async function openExplorerAndCreate(accessor: ServicesAccessor, isFolder: boolean): Promise<void> {
const listService = accessor.get(IListService);
const explorerService = accessor.get(IExplorerService);
const fileService = accessor.get(IFileService);
const editorService = accessor.get(IEditorService);
const viewletService = accessor.get(IViewletService);
const activeViewlet = viewletService.getActiveViewlet();
let explorerPromise = Promise.resolve(activeViewlet);
if (!activeViewlet || activeViewlet.getId() !== VIEWLET_ID) {
explorerPromise = viewletService.openViewlet(VIEWLET_ID, true);
if (!activeViewlet || activeViewlet.getId() !== VIEWLET_ID || !listService.lastFocusedList) {
await viewletService.openViewlet(VIEWLET_ID, true);
}
return explorerPromise.then((explorer: ExplorerViewlet) => {
const explorerView = explorer.getExplorerView();
if (explorerView && explorerView.isBodyVisible() && listService.lastFocusedList) {
explorerView.focus();
const { stat } = getContext(listService.lastFocusedList);
const action = instantiationService.createInstance(constructor, () => stat);
return action.run();
const list = listService.lastFocusedList;
if (list) {
const { stat } = getContext(list);
let folder: ExplorerItem;
if (stat) {
folder = stat.isDirectory ? stat : stat.parent!;
} else {
folder = explorerService.roots[0];
}
return undefined;
});
if (folder.isReadonly) {
throw new Error('Parent folder is readonly.');
}
const newStat = new NewExplorerItem(folder, isFolder);
await folder.fetchChildren(fileService, explorerService);
folder.addChild(newStat);
const onSuccess = async (value: string) => {
const createPromise = isFolder ? fileService.createFolder(resources.joinPath(folder.resource, value)) : fileService.createFile(resources.joinPath(folder.resource, value));
return createPromise.then(created => {
refreshIfSeparator(value, explorerService);
return isFolder ? explorerService.select(created.resource, true)
: editorService.openEditor({ resource: created.resource, options: { pinned: true } }).then(() => undefined);
}, (error) => {
onErrorWithRetry(accessor.get(INotificationService), error, () => onSuccess(value));
});
};
explorerService.setEditable(newStat, {
validationMessage: value => validateFileName(newStat, value),
onFinish: (value, success) => {
folder.removeChild(newStat);
explorerService.setEditable(newStat, null);
if (success) {
onSuccess(value);
} else {
explorerService.select(folder.resource).then(undefined, onUnexpectedError);
}
}
});
}
}
CommandsRegistry.registerCommand({
id: NEW_FILE_COMMAND_ID,
handler: (accessor) => {
return openExplorerAndRunAction(accessor, NewFileAction);
openExplorerAndCreate(accessor, false).then(undefined, onUnexpectedError);
}
});
CommandsRegistry.registerCommand({
id: NEW_FOLDER_COMMAND_ID,
handler: (accessor) => {
return openExplorerAndRunAction(accessor, NewFolderAction);
openExplorerAndCreate(accessor, true).then(undefined, onUnexpectedError);
}
});
@@ -1140,29 +1061,25 @@ export const renameHandler = (accessor: ServicesAccessor) => {
};
export const moveFileToTrashHandler = (accessor: ServicesAccessor) => {
const instantiationService = accessor.get(IInstantiationService);
const listService = accessor.get(IListService);
if (!listService.lastFocusedList) {
return Promise.resolve();
}
const explorerContext = getContext(listService.lastFocusedList);
const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat];
const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat!];
const moveFileToTrashAction = instantiationService.createInstance(BaseDeleteFileAction, stats, true);
return moveFileToTrashAction.run();
return deleteFiles(accessor, stats, true);
};
export const deleteFileHandler = (accessor: ServicesAccessor) => {
const instantiationService = accessor.get(IInstantiationService);
const listService = accessor.get(IListService);
if (!listService.lastFocusedList) {
return Promise.resolve();
}
const explorerContext = getContext(listService.lastFocusedList);
const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat];
const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat!];
const deleteFileAction = instantiationService.createInstance(BaseDeleteFileAction, stats, false);
return deleteFileAction.run();
return deleteFiles(accessor, stats, false);
};
export const copyFileHandler = (accessor: ServicesAccessor) => {

Some files were not shown because too many files have changed in this diff Show More