Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898 (#15681)

* Merge from vscode a348d103d1256a06a2c9b3f9b406298a9fef6898

* Fixes and cleanup

* Distro

* Fix hygiene yarn

* delete no yarn lock changes file

* Fix hygiene

* Fix layer check

* Fix CI

* Skip lib checks

* Remove tests deleted in vs code

* Fix tests

* Distro

* Fix tests and add removed extension point

* Skip failing notebook tests for now

* Disable broken tests and cleanup build folder

* Update yarn.lock and fix smoke tests

* Bump sqlite

* fix contributed actions and file spacing

* Fix user data path

* Update yarn.locks

Co-authored-by: ADS Merger <karlb@microsoft.com>
This commit is contained in:
Charles Gagnon
2021-06-17 08:17:11 -07:00
committed by GitHub
parent fdcb97c7f7
commit 3cb2f552a6
2582 changed files with 124827 additions and 87099 deletions

View File

@@ -25,6 +25,7 @@ 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';
import { PLAINTEXT_EXTENSION } from 'vs/editor/common/modes/modesRegistry';
export abstract class AbstractFileDialogService implements IFileDialogService {
@@ -162,7 +163,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
}
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow, remoteAuthority: options.remoteAuthority });
} else {
return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
}
@@ -179,7 +180,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
this.workspacesService.addRecentlyOpened([{ fileUri: uri, label: this.labelService.getUriLabel(uri) }]);
if (options.forceNewWindow || preferNewWindow) {
return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow, remoteAuthority: options.remoteAuthority });
} else {
return this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
}
@@ -192,7 +193,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
const uri = await this.pickResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
if (uri) {
return this.hostService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow, remoteAuthority: options.remoteAuthority });
}
}
@@ -203,7 +204,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
if (uri) {
return this.hostService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.hostService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow, remoteAuthority: options.remoteAuthority });
}
}
@@ -285,7 +286,7 @@ export abstract class AbstractFileDialogService implements IFileDialogService {
const filter: IFilter = { name: languageName, extensions: distinct(extensions).slice(0, 10).map(e => trim(e, '.')) };
if (ext && extensions.indexOf(ext) >= 0 && !matchingFilter) {
if (!matchingFilter && extensions.indexOf(ext || PLAINTEXT_EXTENSION /* https://github.com/microsoft/vscode/issues/115860 */) >= 0) {
matchingFilter = filter;
return null; // first matching filter will be added to the top

View File

@@ -8,9 +8,18 @@ import { URI } from 'vs/base/common/uri';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService';
import { Schemas } from 'vs/base/common/network';
import { memoize } from 'vs/base/common/decorators';
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
import { generateUuid } from 'vs/base/common/uuid';
import { localize } from 'vs/nls';
export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {
@memoize
private get fileSystemProvider(): HTMLFileSystemProvider {
return this.fileService.getProvider(Schemas.file) as HTMLFileSystemProvider;
}
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
const schema = this.getFileSystemSchema(options);
@@ -18,7 +27,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
options.defaultUri = await this.defaultFilePath(schema);
}
return this.pickFileFolderAndOpenSimplified(schema, options, false);
if (this.shouldUseSimplified(schema)) {
return this.pickFileFolderAndOpenSimplified(schema, options, false);
}
throw new Error(localize('pickFolderAndOpen', "Can't open folders, try adding a folder to the workspace instead."));
}
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
@@ -28,7 +41,17 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
options.defaultUri = await this.defaultFilePath(schema);
}
return this.pickFileAndOpenSimplified(schema, options, false);
if (this.shouldUseSimplified(schema)) {
return this.pickFileAndOpenSimplified(schema, options, false);
}
const [handle] = await window.showOpenFilePicker({ multiple: false });
const uuid = generateUuid();
const uri = URI.from({ scheme: Schemas.file, path: `/${uuid}/${handle.name}` });
this.fileSystemProvider.registerFileHandle(uuid, handle);
await this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
}
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
@@ -38,7 +61,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
options.defaultUri = await this.defaultFolderPath(schema);
}
return this.pickFolderAndOpenSimplified(schema, options);
if (this.shouldUseSimplified(schema)) {
return this.pickFolderAndOpenSimplified(schema, options);
}
throw new Error(localize('pickFolderAndOpen', "Can't open folders, try adding a folder to the workspace instead."));
}
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
@@ -48,27 +75,68 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
options.defaultUri = await this.defaultWorkspacePath(schema);
}
return this.pickWorkspaceAndOpenSimplified(schema, options);
if (this.shouldUseSimplified(schema)) {
return this.pickWorkspaceAndOpenSimplified(schema, options);
}
throw new Error(localize('pickWorkspaceAndOpen', "Can't open workspaces, try adding a folder to the workspace instead."));
}
async pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> {
const schema = this.getFileSystemSchema({ defaultUri, availableFileSystems });
return this.pickFileToSaveSimplified(schema, this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems));
if (this.shouldUseSimplified(schema)) {
return this.pickFileToSaveSimplified(schema, this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems));
}
const handle = await window.showSaveFilePicker();
const uuid = generateUuid();
const uri = URI.from({ scheme: Schemas.file, path: `/${uuid}/${handle.name}` });
this.fileSystemProvider.registerFileHandle(uuid, handle);
return uri;
}
async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
const schema = this.getFileSystemSchema(options);
return this.showSaveDialogSimplified(schema, options);
if (this.shouldUseSimplified(schema)) {
return this.showSaveDialogSimplified(schema, options);
}
const handle = await window.showSaveFilePicker();
const uuid = generateUuid();
const uri = URI.from({ scheme: Schemas.file, path: `/${uuid}/${handle.name}` });
this.fileSystemProvider.registerFileHandle(uuid, handle);
return uri;
}
async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
const schema = this.getFileSystemSchema(options);
return this.showOpenDialogSimplified(schema, options);
if (this.shouldUseSimplified(schema)) {
return this.showOpenDialogSimplified(schema, options);
}
const handle = await window.showDirectoryPicker();
const uuid = generateUuid();
const uri = URI.from({ scheme: Schemas.file, path: `/${uuid}/${handle.name}` });
this.fileSystemProvider.registerDirectoryHandle(uuid, handle);
return [uri];
}
protected addFileSchemaIfNeeded(schema: string): string[] {
return schema === Schemas.untitled ? [Schemas.file] : [schema];
}
private shouldUseSimplified(scheme: string): boolean {
return ![Schemas.file, Schemas.userData, Schemas.tmp].includes(scheme);
}
}
registerSingleton(IFileDialogService, FileDialogService, true);

View File

@@ -92,6 +92,7 @@ interface FileQuickPickItem extends IQuickPickItem {
enum UpdateResult {
Updated,
UpdatedWithTrailing,
Updating,
NotUpdated,
InvalidPath
@@ -119,7 +120,7 @@ export class SimpleFileDialog {
private remoteAgentEnvironment: IRemoteAgentEnvironment | null | undefined;
private separator: string = '/';
private readonly onBusyChangeEmitter = new Emitter<boolean>();
private updatingPromise: CancelablePromise<void> | undefined;
private updatingPromise: CancelablePromise<boolean> | undefined;
protected disposables: IDisposable[] = [
this.onBusyChangeEmitter
@@ -214,12 +215,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,
// If the default scheme is file, then we don't care about the remote authority
uri.scheme === Schemas.file ? undefined : this.remoteAuthority,
// If the default scheme is file, then we don't care about the remote authority
const authority = uri.scheme === Schemas.file ? undefined : this.remoteAuthority;
return resources.toLocalResource(uri, authority,
// 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);
authority ? this.pathService.defaultUriScheme : uri.scheme);
}
private getScheme(available: readonly string[] | undefined, defaultUri: URI | undefined): string {
@@ -416,7 +417,7 @@ export class SimpleFileDialog {
if (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, filePickBoxUri)) {
updated = await this.tryUpdateItems(value, filePickBoxUri);
}
if (updated === UpdateResult.NotUpdated) {
if ((updated === UpdateResult.NotUpdated) || (updated === UpdateResult.UpdatedWithTrailing)) {
this.setActiveItems(value);
}
} else {
@@ -538,19 +539,24 @@ export class SimpleFileDialog {
return dir;
}
private tildaReplace(value: string): URI {
const home = this.userHome;
if ((value[0] === '~') && (value.length > 1)) {
return resources.joinPath(home, value.substring(1));
} else if (value[value.length - 1] === '~') {
return home;
}
return this.remoteUriFrom(value);
}
private async tryUpdateItems(value: string, valueUri: URI): Promise<UpdateResult> {
if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) {
let newDir = this.userHome;
if ((value[0] === '~') && (value.length > 1)) {
newDir = resources.joinPath(newDir, value.substring(1));
}
await this.updateItems(newDir, true);
return UpdateResult.Updated;
let newDir = this.tildaReplace(value);
return await this.updateItems(newDir, true) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (value === '\\') {
valueUri = this.root(this.currentFolder);
value = this.pathFromUri(valueUri);
await this.updateItems(valueUri, true);
return UpdateResult.Updated;
return await this.updateItems(valueUri, true) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, valueUri) && (this.endsWithSlash(value) || (!resources.extUriIgnorePathCase.isEqual(this.currentFolder, resources.dirname(valueUri)) && resources.extUriIgnorePathCase.isEqualOrParent(this.currentFolder, resources.dirname(valueUri))))) {
let stat: IFileStat | undefined;
try {
@@ -559,8 +565,7 @@ export class SimpleFileDialog {
// do nothing
}
if (stat && stat.isDirectory && (resources.basename(valueUri) !== '.') && this.endsWithSlash(value)) {
await this.updateItems(valueUri);
return UpdateResult.Updated;
return await this.updateItems(valueUri) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
} else if (this.endsWithSlash(value)) {
// The input box contains a path that doesn't exist on the system.
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.badPath', 'The path does not exist.');
@@ -579,9 +584,8 @@ export class SimpleFileDialog {
// do nothing
}
if (statWithoutTrailing && statWithoutTrailing.isDirectory) {
await this.updateItems(inputUriDirname, false, resources.basename(valueUri));
this.badPath = undefined;
return UpdateResult.Updated;
return await this.updateItems(inputUriDirname, false, resources.basename(valueUri)) ? UpdateResult.UpdatedWithTrailing : UpdateResult.Updated;
}
}
}
@@ -591,6 +595,7 @@ export class SimpleFileDialog {
}
private setActiveItems(value: string) {
value = this.pathFromUri(this.tildaReplace(value));
const inputBasename = resources.basename(this.remoteUriFrom(value));
const userPath = this.constructFullUserPath();
// Make sure that the folder whose children we are currently viewing matches the path in the input
@@ -614,6 +619,7 @@ export class SimpleFileDialog {
} else {
this.userEnteredPathSegment = inputBasename;
this.autoCompletePathSegment = '';
this.filePickBox.activeItems = [];
}
}
@@ -797,31 +803,46 @@ export class SimpleFileDialog {
return Promise.resolve(true);
}
private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) {
// Returns true if there is a file at the end of the URI.
private async updateItems(newFolder: URI, force: boolean = false, trailing?: string): Promise<boolean> {
this.busy = true;
this.userEnteredPathSegment = trailing ? trailing : '';
this.autoCompletePathSegment = '';
const newValue = trailing ? this.pathAppend(newFolder, trailing) : this.pathFromUri(newFolder, true);
this.currentFolder = resources.addTrailingPathSeparator(newFolder, this.separator);
const isSave = !!trailing;
let result = false;
const updatingPromise = createCancelablePromise(async token => {
return this.createItems(this.currentFolder, token).then(items => {
let folderStat: IFileStat | undefined;
try {
folderStat = await this.fileService.resolve(newFolder);
if (!folderStat.isDirectory) {
trailing = resources.basename(newFolder);
newFolder = resources.dirname(newFolder);
folderStat = undefined;
result = true;
}
} catch (e) {
// The file/directory doesn't exist
}
const newValue = trailing ? this.pathAppend(newFolder, trailing) : this.pathFromUri(newFolder, true);
this.currentFolder = resources.addTrailingPathSeparator(newFolder, this.separator);
this.userEnteredPathSegment = trailing ? trailing : '';
return this.createItems(folderStat, this.currentFolder, token).then(items => {
if (token.isCancellationRequested) {
this.busy = false;
return;
return false;
}
this.filePickBox.items = items;
this.filePickBox.activeItems = [<FileQuickPickItem>this.filePickBox.items[0]];
if (this.allowFolderSelection) {
this.filePickBox.activeItems = [];
}
// the user might have continued typing while we were updating. Only update the input box if it doesn't matche directory.
this.filePickBox.activeItems = [];
// the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory.
if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) {
this.filePickBox.valueSelection = [0, this.filePickBox.value.length];
this.insertText(newValue, newValue);
}
if (force && trailing) {
if (force && trailing && isSave) {
// Keep the cursor position in front of the save as name.
this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - trailing.length];
} else if (!trailing) {
@@ -830,6 +851,7 @@ export class SimpleFileDialog {
}
this.busy = false;
this.updatingPromise = undefined;
return result;
});
});
@@ -883,7 +905,7 @@ export class SimpleFileDialog {
}
private createBackItem(currFolder: URI): FileQuickPickItem | null {
const fileRepresentationCurr = this.currentFolder.with({ scheme: Schemas.file });
const fileRepresentationCurr = this.currentFolder.with({ scheme: Schemas.file, authority: '' });
const fileRepresentationParent = resources.dirname(fileRepresentationCurr);
if (!resources.isEqual(fileRepresentationCurr, fileRepresentationParent)) {
const parentFolder = resources.dirname(currFolder);
@@ -892,12 +914,14 @@ export class SimpleFileDialog {
return null;
}
private async createItems(currentFolder: URI, token: CancellationToken): Promise<FileQuickPickItem[]> {
private async createItems(folder: IFileStat | undefined, currentFolder: URI, token: CancellationToken): Promise<FileQuickPickItem[]> {
const result: FileQuickPickItem[] = [];
const backDir = this.createBackItem(currentFolder);
try {
const folder = await this.fileService.resolve(currentFolder);
if (!folder) {
folder = await this.fileService.resolve(currentFolder);
}
const items = folder.children ? await Promise.all(folder.children.map(child => this.createItem(child, currentFolder, token))) : [];
for (let item of items) {
if (item) {

View File

@@ -25,8 +25,6 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {
declare readonly _serviceBrand: undefined;
constructor(
@IHostService hostService: IHostService,
@IWorkspaceContextService contextService: IWorkspaceContextService,