mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 10:38:31 -05:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user