mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 10:38:31 -05:00
Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
src/vs/workbench/api/browser/mainThreadKeytar.ts
Normal file
61
src/vs/workbench/api/browser/mainThreadKeytar.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 } });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>[] = [];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 }; }
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: () => {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}; }`);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -178,7 +178,7 @@ export abstract class ViewletPanel extends Panel implements IView {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getActionsContext(): any {
|
||||
getActionsContext(): unknown {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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', []),
|
||||
];
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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', '');
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -558,6 +558,7 @@ export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolea
|
||||
preserveFocus,
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealIfOpened: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: !preserveFocus
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -424,6 +424,7 @@ export interface IDebugConfiguration {
|
||||
internalConsoleOptions: 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart';
|
||||
extensionHostDebugAdapter: boolean;
|
||||
enableAllHovers: boolean;
|
||||
hideSubSessions: boolean;
|
||||
console: {
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
|
||||
@@ -93,6 +93,7 @@ export class Source {
|
||||
preserveFocus,
|
||||
selection,
|
||||
revealIfVisible: true,
|
||||
revealIfOpened: true,
|
||||
revealInCenterIfOutsideViewport: true,
|
||||
pinned: pinned || (!preserveFocus && !this.inMemory)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user