Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)

* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79

* Fix breaks

* Extension management fixes

* Fix breaks in windows bundling

* Fix/skip failing tests

* Update distro

* Add clear to nuget.config

* Add hygiene task

* Bump distro

* Fix hygiene issue

* Add build to hygiene exclusion

* Update distro

* Update hygiene

* Hygiene exclusions

* Update tsconfig

* Bump distro for server breaks

* Update build config

* Update darwin path

* Add done calls to notebook tests

* Skip failing tests

* Disable smoke tests
This commit is contained in:
Karl Burtram
2021-02-09 16:15:05 -08:00
committed by GitHub
parent 6f192f9af5
commit ce612a3d96
1929 changed files with 68012 additions and 34564 deletions

View File

@@ -19,11 +19,12 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import Severity from 'vs/base/common/severity';
import { coalesce } from 'vs/base/common/arrays';
import { coalesce, distinct } from 'vs/base/common/arrays';
import { trim } from 'vs/base/common/strings';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ILabelService } from 'vs/platform/label/common/label';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { Schemas } from 'vs/base/common/network';
export abstract class AbstractFileDialogService implements IFileDialogService {
@@ -45,7 +46,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
@IPathService private readonly pathService: IPathService
) { }
defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
async defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): Promise<URI> {
// Check for last active file first...
let candidate = this.historyService.getLastActiveFile(schemeFilter);
@@ -57,10 +58,14 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
candidate = candidate && resources.dirname(candidate);
}
return candidate || undefined;
if (!candidate) {
candidate = await this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file });
}
return candidate;
}
defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
async defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): Promise<URI> {
// Check for last active file root first...
let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
@@ -70,21 +75,33 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
candidate = this.historyService.getLastActiveFile(schemeFilter);
}
return candidate && resources.dirname(candidate) || undefined;
if (!candidate) {
return this.pathService.userHome({ preferLocal: schemeFilter === Schemas.file });
} else {
return resources.dirname(candidate);
}
}
defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
async defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow(), filename?: string): Promise<URI> {
let defaultWorkspacePath: URI | undefined;
// Check for current workspace config file first...
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
const configuration = this.contextService.getWorkspace().configuration;
if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) {
return resources.dirname(configuration) || undefined;
if (configuration && configuration.scheme === schemeFilter && !isUntitledWorkspace(configuration, this.environmentService)) {
defaultWorkspacePath = resources.dirname(configuration) || undefined;
}
}
// ...then fallback to default file path
return this.defaultFilePath(schemeFilter);
if (!defaultWorkspacePath) {
defaultWorkspacePath = await this.defaultFilePath(schemeFilter);
}
if (defaultWorkspacePath && filename) {
defaultWorkspacePath = resources.joinPath(defaultWorkspacePath, filename);
}
return defaultWorkspacePath;
}
async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
@@ -147,7 +164,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
} else {
return this.openerService.open(uri, { fromUserGesture: true });
return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
}
}
}
@@ -164,7 +181,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
if (options.forceNewWindow || preferNewWindow) {
return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
} else {
return this.openerService.open(uri, { fromUserGesture: true });
return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
}
}
}
@@ -264,7 +281,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
return null;
}
const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
const filter: IFilter = { name: languageName, extensions: distinct(extensions).slice(0, 10).map(e => trim(e, '.')) };
if (ext && extensions.indexOf(ext) >= 0) {
matchingFilter = filter;

View File

@@ -1,147 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IDialogService, IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult } from 'vs/platform/dialogs/common/dialogs';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ILogService } from 'vs/platform/log/common/log';
import Severity from 'vs/base/common/severity';
import { Dialog } from 'vs/base/browser/ui/dialog/dialog';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachDialogStyler } from 'vs/platform/theme/common/styler';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventHelper } from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IProductService } from 'vs/platform/product/common/productService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { fromNow } from 'vs/base/common/date';
export class DialogService implements IDialogService {
declare readonly _serviceBrand: undefined;
private allowableCommands = ['copy', 'cut', 'editor.action.clipboardCopyAction', 'editor.action.clipboardCutAction'];
constructor(
@ILogService private readonly logService: ILogService,
@ILayoutService private readonly layoutService: ILayoutService,
@IThemeService private readonly themeService: IThemeService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IProductService private readonly productService: IProductService,
@IClipboardService private readonly clipboardService: IClipboardService
) { }
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
this.logService.trace('DialogService#confirm', confirmation.message);
const buttons: string[] = [];
if (confirmation.primaryButton) {
buttons.push(confirmation.primaryButton);
} else {
buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes"));
}
if (confirmation.secondaryButton) {
buttons.push(confirmation.secondaryButton);
} else if (typeof confirmation.secondaryButton === 'undefined') {
buttons.push(nls.localize('cancelButton', "Cancel"));
}
const dialogDisposables = new DisposableStore();
const dialog = new Dialog(
this.layoutService.container,
confirmation.message,
buttons,
{
detail: confirmation.detail,
cancelId: 1,
type: confirmation.type,
keyEventProcessor: (event: StandardKeyboardEvent) => {
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
if (resolved && resolved.commandId) {
if (this.allowableCommands.indexOf(resolved.commandId) === -1) {
EventHelper.stop(event, true);
}
}
},
checkboxChecked: confirmation.checkbox ? confirmation.checkbox.checked : undefined,
checkboxLabel: confirmation.checkbox ? confirmation.checkbox.label : undefined
});
dialogDisposables.add(dialog);
dialogDisposables.add(attachDialogStyler(dialog, this.themeService));
const result = await dialog.show();
dialogDisposables.dispose();
return { confirmed: result.button === 0, checkboxChecked: result.checkboxChecked };
}
private getDialogType(severity: Severity): DialogType {
return (severity === Severity.Info) ? 'question' : (severity === Severity.Error) ? 'error' : (severity === Severity.Warning) ? 'warning' : 'none';
}
async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult> {
this.logService.trace('DialogService#show', message);
const dialogDisposables = new DisposableStore();
const dialog = new Dialog(
this.layoutService.container,
message,
buttons,
{
detail: options ? options.detail : undefined,
cancelId: options ? options.cancelId : undefined,
type: this.getDialogType(severity),
keyEventProcessor: (event: StandardKeyboardEvent) => {
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
if (resolved && resolved.commandId) {
if (this.allowableCommands.indexOf(resolved.commandId) === -1) {
EventHelper.stop(event, true);
}
}
},
checkboxLabel: options && options.checkbox ? options.checkbox.label : undefined,
checkboxChecked: options && options.checkbox ? options.checkbox.checked : undefined
});
dialogDisposables.add(dialog);
dialogDisposables.add(attachDialogStyler(dialog, this.themeService));
const result = await dialog.show();
dialogDisposables.dispose();
return {
choice: result.button,
checkboxChecked: result.checkboxChecked
};
}
async about(): Promise<void> {
const detailString = (useAgo: boolean): string => {
return nls.localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
this.productService.version || 'Unknown',
this.productService.commit || 'Unknown',
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
navigator.userAgent
);
};
const detail = detailString(true);
const detailToCopy = detailString(false);
const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 });
if (choice === 0) {
this.clipboardService.writeText(detailToCopy);
}
}
}
registerSingleton(IDialogService, DialogService, true);

View File

@@ -15,7 +15,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFilePath(schema);
options.defaultUri = await this.defaultFilePath(schema);
}
return this.pickFileFolderAndOpenSimplified(schema, options, false);
@@ -25,7 +25,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFilePath(schema);
options.defaultUri = await this.defaultFilePath(schema);
}
return this.pickFileAndOpenSimplified(schema, options, false);
@@ -35,7 +35,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFolderPath(schema);
options.defaultUri = await this.defaultFolderPath(schema);
}
return this.pickFolderAndOpenSimplified(schema, options);
@@ -45,7 +45,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultWorkspacePath(schema);
options.defaultUri = await this.defaultWorkspacePath(schema);
}
return this.pickWorkspaceAndOpenSimplified(schema, options);

View File

@@ -139,7 +139,7 @@ export class SimpleFileDialog {
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IContextKeyService contextKeyService: IContextKeyService,
) {
this.remoteAuthority = this.environmentService.configuration.remoteAuthority;
this.remoteAuthority = this.environmentService.remoteAuthority;
this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService);
this.scheme = this.pathService.defaultUriScheme;
}
@@ -212,7 +212,12 @@ export class SimpleFileDialog {
path = path.replace(/\\/g, '/');
}
const uri: URI = this.scheme === Schemas.file ? URI.file(path) : URI.from({ scheme: this.scheme, path });
return resources.toLocalResource(uri, uri.scheme === Schemas.file ? undefined : this.remoteAuthority, this.pathService.defaultUriScheme);
return resources.toLocalResource(uri,
// If the default scheme is file, then we don't care about the remote authority
uri.scheme === Schemas.file ? undefined : this.remoteAuthority,
// If there is a remote authority, then we should use the system's default URI as the local scheme.
// If there is *no* remote authority, then we should use the default scheme for this dialog as that is already local.
this.remoteAuthority ? this.pathService.defaultUriScheme : uri.scheme);
}
private getScheme(available: readonly string[] | undefined, defaultUri: URI | undefined): string {
@@ -221,6 +226,8 @@ export class SimpleFileDialog {
return defaultUri.scheme;
}
return available[0];
} else if (defaultUri) {
return defaultUri.scheme;
}
return Schemas.file;
}
@@ -582,21 +589,22 @@ export class SimpleFileDialog {
private setActiveItems(value: string) {
const inputBasename = resources.basename(this.remoteUriFrom(value));
// Make sure that the folder whose children we are currently viewing matches the path in the input
const userPath = this.constructFullUserPath();
if (equalsIgnoreCase(userPath, value.substring(0, userPath.length))) {
// Make sure that the folder whose children we are currently viewing matches the path in the input
const pathsEqual = equalsIgnoreCase(userPath, value.substring(0, userPath.length)) ||
equalsIgnoreCase(value, userPath.substring(0, value.length));
if (pathsEqual) {
let hasMatch = false;
if (inputBasename.length > this.userEnteredPathSegment.length) {
for (let i = 0; i < this.filePickBox.items.length; i++) {
const item = <FileQuickPickItem>this.filePickBox.items[i];
if (this.setAutoComplete(value, inputBasename, item)) {
hasMatch = true;
break;
}
for (let i = 0; i < this.filePickBox.items.length; i++) {
const item = <FileQuickPickItem>this.filePickBox.items[i];
if (this.setAutoComplete(value, inputBasename, item)) {
hasMatch = true;
break;
}
}
if (!hasMatch) {
this.userEnteredPathSegment = inputBasename;
const userBasename = inputBasename.length >= 2 ? userPath.substring(userPath.length - inputBasename.length + 2) : '';
this.userEnteredPathSegment = (userBasename === inputBasename) ? inputBasename : '';
this.autoCompletePathSegment = '';
this.filePickBox.activeItems = [];
}
@@ -797,6 +805,7 @@ export class SimpleFileDialog {
}
this.filePickBox.items = items;
this.filePickBox.activeItems = [<FileQuickPickItem>this.filePickBox.items[0]];
if (this.allowFolderSelection) {
this.filePickBox.activeItems = [];
}
@@ -915,7 +924,8 @@ export class SimpleFileDialog {
const ext = resources.extname(file);
for (let i = 0; i < this.options.filters.length; i++) {
for (let j = 0; j < this.options.filters[i].extensions.length; j++) {
if (ext === ('.' + this.options.filters[i].extensions[j])) {
const testExt = this.options.filters[i].extensions[j];
if ((testExt === '*') || (ext === ('.' + testExt))) {
return true;
}
}

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Severity from 'vs/base/common/severity';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInput, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs';
import { DialogsModel, IDialogsModel } from 'vs/workbench/common/dialogs';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class DialogService extends Disposable implements IDialogService {
_serviceBrand: undefined;
readonly model: IDialogsModel = this._register(new DialogsModel());
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
const handle = this.model.show({ confirmArgs: { confirmation } });
return await handle.result as IConfirmationResult;
}
async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult> {
const handle = this.model.show({ showArgs: { severity, message, buttons, options } });
return await handle.result as IShowResult;
}
async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise<IInputResult> {
const handle = this.model.show({ inputArgs: { severity, message, buttons, inputs, options } });
return await handle.result as IInputResult;
}
async about(): Promise<void> {
const handle = this.model.show({});
await handle.result;
}
}
registerSingleton(IDialogService, DialogService, true);

View File

@@ -1,261 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { isLinux, isWindows } from 'vs/base/common/platform';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs';
import { DialogService as HTMLDialogService } from 'vs/workbench/services/dialogs/browser/dialogService';
import { ILogService } from 'vs/platform/log/common/log';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IProductService } from 'vs/platform/product/common/productService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { MessageBoxOptions } from 'vs/base/parts/sandbox/common/electronTypes';
import { fromNow } from 'vs/base/common/date';
import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals';
interface IMassagedMessageBoxOptions {
/**
* OS massaged message box options.
*/
options: MessageBoxOptions;
/**
* Since the massaged result of the message box options potentially
* changes the order of buttons, we have to keep a map of these
* changes so that we can still return the correct index to the caller.
*/
buttonIndexMap: number[];
}
export class DialogService implements IDialogService {
declare readonly _serviceBrand: undefined;
private nativeImpl: IDialogService;
private customImpl: IDialogService;
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@ILogService logService: ILogService,
@ILayoutService layoutService: ILayoutService,
@IThemeService themeService: IThemeService,
@IKeybindingService keybindingService: IKeybindingService,
@IProductService productService: IProductService,
@IClipboardService clipboardService: IClipboardService,
@IElectronService electronService: IElectronService
) {
this.customImpl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService);
this.nativeImpl = new NativeDialogService(logService, electronService, productService, clipboardService);
}
private get useCustomDialog(): boolean {
return this.configurationService.getValue('workbench.dialogs.customEnabled') === true;
}
confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
if (this.useCustomDialog) {
return this.customImpl.confirm(confirmation);
}
return this.nativeImpl.confirm(confirmation);
}
show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions | undefined): Promise<IShowResult> {
if (this.useCustomDialog) {
return this.customImpl.show(severity, message, buttons, options);
}
return this.nativeImpl.show(severity, message, buttons, options);
}
about(): Promise<void> {
return this.nativeImpl.about();
}
}
class NativeDialogService implements IDialogService {
declare readonly _serviceBrand: undefined;
constructor(
@ILogService private readonly logService: ILogService,
@IElectronService private readonly electronService: IElectronService,
@IProductService private readonly productService: IProductService,
@IClipboardService private readonly clipboardService: IClipboardService
) {
}
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
this.logService.trace('DialogService#confirm', confirmation.message);
const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation));
const result = await this.electronService.showMessageBox(options);
return {
confirmed: buttonIndexMap[result.response] === 0 ? true : false,
checkboxChecked: result.checkboxChecked
};
}
private getConfirmOptions(confirmation: IConfirmation): MessageBoxOptions {
const buttons: string[] = [];
if (confirmation.primaryButton) {
buttons.push(confirmation.primaryButton);
} else {
buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes"));
}
if (confirmation.secondaryButton) {
buttons.push(confirmation.secondaryButton);
} else if (typeof confirmation.secondaryButton === 'undefined') {
buttons.push(nls.localize('cancelButton', "Cancel"));
}
const opts: MessageBoxOptions = {
title: confirmation.title,
message: confirmation.message,
buttons,
cancelId: 1
};
if (confirmation.detail) {
opts.detail = confirmation.detail;
}
if (confirmation.type) {
opts.type = confirmation.type;
}
if (confirmation.checkbox) {
opts.checkboxLabel = confirmation.checkbox.label;
opts.checkboxChecked = confirmation.checkbox.checked;
}
return opts;
}
async show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise<IShowResult> {
this.logService.trace('DialogService#show', message);
const { options, buttonIndexMap } = this.massageMessageBoxOptions({
message,
buttons,
type: (severity === Severity.Info) ? 'question' : (severity === Severity.Error) ? 'error' : (severity === Severity.Warning) ? 'warning' : 'none',
cancelId: dialogOptions ? dialogOptions.cancelId : undefined,
detail: dialogOptions ? dialogOptions.detail : undefined,
checkboxLabel: dialogOptions && dialogOptions.checkbox ? dialogOptions.checkbox.label : undefined,
checkboxChecked: dialogOptions && dialogOptions.checkbox ? dialogOptions.checkbox.checked : undefined
});
const result = await this.electronService.showMessageBox(options);
return { choice: buttonIndexMap[result.response], checkboxChecked: result.checkboxChecked };
}
private massageMessageBoxOptions(options: MessageBoxOptions): IMassagedMessageBoxOptions {
let buttonIndexMap = (options.buttons || []).map((button, index) => index);
let buttons = (options.buttons || []).map(button => mnemonicButtonLabel(button));
let cancelId = options.cancelId;
// Linux: order of buttons is reverse
// macOS: also reverse, but the OS handles this for us!
if (isLinux) {
buttons = buttons.reverse();
buttonIndexMap = buttonIndexMap.reverse();
}
// Default Button (always first one)
options.defaultId = buttonIndexMap[0];
// Cancel Button
if (typeof cancelId === 'number') {
// Ensure the cancelId is the correct one from our mapping
cancelId = buttonIndexMap[cancelId];
// macOS/Linux: the cancel button should always be to the left of the primary action
// if we see more than 2 buttons, move the cancel one to the left of the primary
if (!isWindows && buttons.length > 2 && cancelId !== 1) {
const cancelButton = buttons[cancelId];
buttons.splice(cancelId, 1);
buttons.splice(1, 0, cancelButton);
const cancelButtonIndex = buttonIndexMap[cancelId];
buttonIndexMap.splice(cancelId, 1);
buttonIndexMap.splice(1, 0, cancelButtonIndex);
cancelId = 1;
}
}
options.buttons = buttons;
options.cancelId = cancelId;
options.noLink = true;
options.title = options.title || this.productService.nameLong;
return { options, buttonIndexMap };
}
async about(): Promise<void> {
let version = this.productService.version;
if (this.productService.target) {
version = `${version} (${this.productService.target} setup)`;
}
const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION;
const os = await this.electronService.getOS();
const detailString = (useAgo: boolean): string => {
return nls.localize('aboutDetail',
"Version: {0}\nCommit: {1}\nDate: {2}\nVS Code: {8}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}",
version,
this.productService.commit || 'Unknown',
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
process.versions['electron'],
process.versions['chrome'],
process.versions['node'],
process.versions['v8'],
`${os.type} ${os.arch} ${os.release}${isSnap ? ' snap' : ''}`,
this.productService.vscodeVersion
);
};
const detail = detailString(true);
const detailToCopy = detailString(false);
const ok = nls.localize('okButton', "OK");
const copy = mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy"));
let buttons: string[];
if (isLinux) {
buttons = [copy, ok];
} else {
buttons = [ok, copy];
}
const result = await this.electronService.showMessageBox({
title: this.productService.nameLong,
type: 'info',
message: this.productService.nameLong,
detail: `\n${detail}`,
buttons,
noLink: true,
defaultId: buttons.indexOf(ok),
cancelId: buttons.indexOf(ok)
});
if (buttons[result.response] === copy) {
this.clipboardService.writeText(detailToCopy);
}
}
}
registerSingleton(IDialogService, DialogService, true);

View File

@@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService';
import { Schemas } from 'vs/base/common/network';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -36,7 +36,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
@IConfigurationService configurationService: IConfigurationService,
@IFileService fileService: IFileService,
@IOpenerService openerService: IOpenerService,
@IElectronService private readonly electronService: IElectronService,
@INativeHostService private readonly nativeHostService: INativeHostService,
@IDialogService dialogService: IDialogService,
@IModeService modeService: IModeService,
@IWorkspacesService workspacesService: IWorkspacesService,
@@ -58,61 +58,64 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
private shouldUseSimplified(schema: string): { useSimplified: boolean, isSetting: boolean } {
const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true);
const newWindowSetting = (this.configurationService.getValue('window.openFilesInNewWindow') === 'on');
return { useSimplified: (schema !== Schemas.file) || setting, isSetting: newWindowSetting };
return {
useSimplified: ((schema !== Schemas.file) && (schema !== Schemas.userData)) || setting,
isSetting: newWindowSetting
};
}
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFilePath(schema);
options.defaultUri = await this.defaultFilePath(schema);
}
const shouldUseSimplified = this.shouldUseSimplified(schema);
if (shouldUseSimplified.useSimplified) {
return this.pickFileFolderAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);
}
return this.electronService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));
return this.nativeHostService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));
}
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFilePath(schema);
options.defaultUri = await this.defaultFilePath(schema);
}
const shouldUseSimplified = this.shouldUseSimplified(schema);
if (shouldUseSimplified.useSimplified) {
return this.pickFileAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);
}
return this.electronService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));
return this.nativeHostService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));
}
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultFolderPath(schema);
options.defaultUri = await this.defaultFolderPath(schema);
}
if (this.shouldUseSimplified(schema).useSimplified) {
return this.pickFolderAndOpenSimplified(schema, options);
}
return this.electronService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));
return this.nativeHostService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));
}
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
const schema = this.getFileSystemSchema(options);
if (!options.defaultUri) {
options.defaultUri = this.defaultWorkspacePath(schema);
options.defaultUri = await this.defaultWorkspacePath(schema);
}
if (this.shouldUseSimplified(schema).useSimplified) {
return this.pickWorkspaceAndOpenSimplified(schema, options);
}
return this.electronService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
return this.nativeHostService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
}
async pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> {
@@ -121,7 +124,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
if (this.shouldUseSimplified(schema).useSimplified) {
return this.pickFileToSaveSimplified(schema, options);
} else {
const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options));
const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));
if (result && !result.canceled && result.filePath) {
return URI.file(result.filePath);
}
@@ -145,7 +148,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
return this.showSaveDialogSimplified(schema, options);
}
const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options));
const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));
if (result && !result.canceled && result.filePath) {
return URI.file(result.filePath);
}
@@ -183,7 +186,7 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
newOptions.properties.push('multiSelections');
}
const result = await this.electronService.showOpenDialog(newOptions);
const result = await this.nativeHostService.showOpenDialog(newOptions);
return result && Array.isArray(result.filePaths) && result.filePaths.length > 0 ? result.filePaths.map(URI.file) : undefined;
}