mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-13 19:48:37 -05:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
@@ -28,10 +27,17 @@ import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
|
||||
import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { isEqualOrParent, isEqual, joinPath, dirname } from 'vs/base/common/resources';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename } from 'vs/base/common/resources';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { getConfirmMessage, IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { trim } from 'vs/base/common/strings';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export interface IBackupResult {
|
||||
didBackup: boolean;
|
||||
@@ -42,7 +48,7 @@ export interface IBackupResult {
|
||||
*
|
||||
* It also adds diagnostics and logging around file system operations.
|
||||
*/
|
||||
export abstract class TextFileService extends Disposable implements ITextFileService {
|
||||
export class TextFileService extends Disposable implements ITextFileService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
@@ -57,34 +63,38 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
|
||||
private _models: TextFileEditorModelManager;
|
||||
private currentFilesAssociationConfig: { [key: string]: string; };
|
||||
private configuredAutoSaveDelay: number;
|
||||
private configuredAutoSaveDelay?: number;
|
||||
private configuredAutoSaveOnFocusChange: boolean;
|
||||
private configuredAutoSaveOnWindowChange: boolean;
|
||||
private configuredHotExit: string;
|
||||
private autoSaveContext: IContextKey<string>;
|
||||
|
||||
constructor(
|
||||
private lifecycleService: ILifecycleService,
|
||||
private contextService: IWorkspaceContextService,
|
||||
private configurationService: IConfigurationService,
|
||||
protected fileService: IFileService,
|
||||
private untitledEditorService: IUntitledEditorService,
|
||||
private instantiationService: IInstantiationService,
|
||||
private notificationService: INotificationService,
|
||||
protected environmentService: IEnvironmentService,
|
||||
private backupFileService: IBackupFileService,
|
||||
private windowsService: IWindowsService,
|
||||
protected windowService: IWindowService,
|
||||
private historyService: IHistoryService,
|
||||
contextKeyService: IContextKeyService,
|
||||
private modelService: IModelService
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IFileService protected readonly fileService: IFileService,
|
||||
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IBackupFileService private readonly backupFileService: IBackupFileService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IHistoryService private readonly historyService: IHistoryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._models = this.instantiationService.createInstance(TextFileEditorModelManager);
|
||||
this._models = instantiationService.createInstance(TextFileEditorModelManager);
|
||||
this.autoSaveContext = AutoSaveContext.bindTo(contextKeyService);
|
||||
|
||||
const configuration = this.configurationService.getValue<IFilesConfiguration>();
|
||||
const configuration = configurationService.getValue<IFilesConfiguration>();
|
||||
this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations;
|
||||
|
||||
this.onFilesConfigurationChange(configuration);
|
||||
@@ -96,11 +106,124 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return this._models;
|
||||
}
|
||||
|
||||
abstract resolveTextContent(resource: URI, options?: IResolveContentOptions): Promise<IRawTextContent>;
|
||||
resolveTextContent(resource: URI, options?: IResolveContentOptions): Promise<IRawTextContent> {
|
||||
return this.fileService.resolveStreamContent(resource, options).then(streamContent => {
|
||||
return createTextBufferFactoryFromStream(streamContent.value).then(res => {
|
||||
const r: IRawTextContent = {
|
||||
resource: streamContent.resource,
|
||||
name: streamContent.name,
|
||||
mtime: streamContent.mtime,
|
||||
etag: streamContent.etag,
|
||||
encoding: streamContent.encoding,
|
||||
isReadonly: streamContent.isReadonly,
|
||||
value: res
|
||||
};
|
||||
return r;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract promptForPath(resource: URI, defaultPath: URI): Promise<URI>;
|
||||
promptForPath(resource: URI, defaultUri: URI): Promise<URI | undefined> {
|
||||
|
||||
abstract confirmSave(resources?: URI[]): Promise<ConfirmResult>;
|
||||
// Help user to find a name for the file by opening it first
|
||||
return this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true, } }).then(() => {
|
||||
return this.fileDialogService.showSaveDialog(this.getSaveDialogOptions(defaultUri));
|
||||
});
|
||||
}
|
||||
|
||||
private getSaveDialogOptions(defaultUri: URI): ISaveDialogOptions {
|
||||
const options: ISaveDialogOptions = {
|
||||
defaultUri,
|
||||
title: nls.localize('saveAsTitle', "Save As")
|
||||
};
|
||||
|
||||
// Filters are only enabled on Windows where they work properly
|
||||
if (!platform.isWindows) {
|
||||
return options;
|
||||
}
|
||||
|
||||
interface IFilter { name: string; extensions: string[]; }
|
||||
|
||||
// Build the file filter by using our known languages
|
||||
const ext: string | undefined = defaultUri ? extname(defaultUri) : undefined;
|
||||
let matchingFilter: IFilter | undefined;
|
||||
const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
|
||||
const extensions = this.modeService.getExtensions(languageName);
|
||||
if (!extensions || !extensions.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
|
||||
|
||||
if (ext && extensions.indexOf(ext) >= 0) {
|
||||
matchingFilter = filter;
|
||||
|
||||
return null; // matching filter will be added last to the top
|
||||
}
|
||||
|
||||
return filter;
|
||||
}));
|
||||
|
||||
// Filters are a bit weird on Windows, based on having a match or not:
|
||||
// Match: we put the matching filter first so that it shows up selected and the all files last
|
||||
// No match: we put the all files filter first
|
||||
const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] };
|
||||
if (matchingFilter) {
|
||||
filters.unshift(matchingFilter);
|
||||
filters.unshift(allFilesFilter);
|
||||
} else {
|
||||
filters.unshift(allFilesFilter);
|
||||
}
|
||||
|
||||
// Allow to save file without extension
|
||||
filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] });
|
||||
|
||||
options.filters = filters;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
confirmSave(resources?: URI[]): Promise<ConfirmResult> {
|
||||
if (this.environmentService.isExtensionDevelopment) {
|
||||
return Promise.resolve(ConfirmResult.DONT_SAVE); // no veto when we are in extension dev mode because we cannot assum we run interactive (e.g. tests)
|
||||
}
|
||||
|
||||
const resourcesToConfirm = this.getDirty(resources);
|
||||
if (resourcesToConfirm.length === 0) {
|
||||
return Promise.resolve(ConfirmResult.DONT_SAVE);
|
||||
}
|
||||
|
||||
const message = resourcesToConfirm.length === 1 ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", basename(resourcesToConfirm[0]))
|
||||
: getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm);
|
||||
|
||||
const buttons: string[] = [
|
||||
resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
||||
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
|
||||
return this.dialogService.show(Severity.Warning, message, buttons, {
|
||||
cancelId: 2,
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
|
||||
}).then(index => {
|
||||
switch (index) {
|
||||
case 0: return ConfirmResult.SAVE;
|
||||
case 1: return ConfirmResult.DONT_SAVE;
|
||||
default: return ConfirmResult.CANCEL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
confirmOverwrite(resource: URI): Promise<boolean> {
|
||||
const confirm: IConfirmation = {
|
||||
message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)),
|
||||
detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))),
|
||||
primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
|
||||
type: 'warning'
|
||||
};
|
||||
|
||||
return this.dialogService.confirm(confirm).then(result => result.confirmed);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
|
||||
@@ -133,7 +256,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return this.handleDirtyBeforeShutdown(remainingDirty, reason);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -176,7 +299,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
// closed is the only VS Code window open, except for on Mac where hot exit is only
|
||||
// ever activated when quit is requested.
|
||||
|
||||
let doBackup: boolean;
|
||||
let doBackup: boolean | undefined;
|
||||
switch (reason) {
|
||||
case ShutdownReason.CLOSE:
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
||||
@@ -221,7 +344,10 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
const untitledToBackup: URI[] = [];
|
||||
dirtyToBackup.forEach(s => {
|
||||
if (this.fileService.canHandleResource(s)) {
|
||||
filesToBackup.push(textFileEditorModelManager.get(s));
|
||||
const model = textFileEditorModelManager.get(s);
|
||||
if (model) {
|
||||
filesToBackup.push(model);
|
||||
}
|
||||
} else if (s.scheme === Schemas.untitled) {
|
||||
untitledToBackup.push(s);
|
||||
}
|
||||
@@ -231,9 +357,16 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
}
|
||||
|
||||
private doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise<void> {
|
||||
const promises = dirtyFileModels.map(model => {
|
||||
const snapshot = model.createSnapshot();
|
||||
if (snapshot) {
|
||||
return this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId());
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
// Handle file resources first
|
||||
return Promise.all(dirtyFileModels.map(model => this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId()))).then(results => {
|
||||
return Promise.all(promises).then(results => {
|
||||
|
||||
// Handle untitled resources
|
||||
const untitledModelPromises = untitledResources
|
||||
@@ -242,7 +375,11 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
|
||||
return Promise.all(untitledModelPromises).then(untitledModels => {
|
||||
const untitledBackupPromises = untitledModels.map(model => {
|
||||
return this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId());
|
||||
const snapshot = model.createSnapshot();
|
||||
if (snapshot) {
|
||||
return this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId());
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
return Promise.all(untitledBackupPromises).then(() => undefined);
|
||||
@@ -279,7 +416,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return true; // veto
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -392,7 +529,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
}
|
||||
}
|
||||
|
||||
return this.saveAll([resource], options).then(result => result.results.length === 1 && result.results[0].success);
|
||||
return this.saveAll([resource], options).then(result => result.results.length === 1 && !!result.results[0].success);
|
||||
}
|
||||
|
||||
saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise<ITextFileOperationResult>;
|
||||
@@ -434,7 +571,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
|
||||
// Untitled with associated file path don't need to prompt
|
||||
if (this.untitledEditorService.hasAssociatedFilePath(untitled)) {
|
||||
targetUri = untitled.with({ scheme: Schemas.file });
|
||||
targetUri = this.untitledToAssociatedFileResource(untitled);
|
||||
}
|
||||
|
||||
// Otherwise ask user
|
||||
@@ -471,6 +608,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
});
|
||||
}
|
||||
|
||||
private untitledToAssociatedFileResource(untitled: URI): URI {
|
||||
const authority = this.windowService.getConfiguration().remoteAuthority;
|
||||
|
||||
return authority ? untitled.with({ scheme: REMOTE_HOST_SCHEME, authority }) : untitled.with({ scheme: Schemas.file });
|
||||
}
|
||||
|
||||
private doSaveAllFiles(resources?: URI[], options: ISaveOptions = Object.create(null)): Promise<ITextFileOperationResult> {
|
||||
const dirtyFileModels = this.getDirtyFileModels(Array.isArray(resources) ? resources : undefined /* Save All */)
|
||||
.filter(model => {
|
||||
@@ -491,7 +634,10 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return Promise.all(dirtyFileModels.map(model => {
|
||||
return model.save(options).then(() => {
|
||||
if (!model.isDirty()) {
|
||||
mapResourceToResult.get(model.getResource()).success = true;
|
||||
const result = mapResourceToResult.get(model.getResource());
|
||||
if (result) {
|
||||
result.success = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
})).then(r => ({ results: mapResourceToResult.values() }));
|
||||
@@ -518,10 +664,10 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return this.getFileModels(arg1).filter(model => model.isDirty());
|
||||
}
|
||||
|
||||
saveAs(resource: URI, target?: URI, options?: ISaveOptions): Promise<URI> {
|
||||
saveAs(resource: URI, target?: URI, options?: ISaveOptions): Promise<URI | undefined> {
|
||||
|
||||
// Get to target resource
|
||||
let targetPromise: Promise<URI>;
|
||||
let targetPromise: Promise<URI | undefined>;
|
||||
if (target) {
|
||||
targetPromise = Promise.resolve(target);
|
||||
} else {
|
||||
@@ -533,9 +679,9 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
targetPromise = this.promptForPath(resource, dialogPath);
|
||||
}
|
||||
|
||||
return targetPromise.then(target => {
|
||||
return targetPromise.then<URI | undefined>(target => {
|
||||
if (!target) {
|
||||
return null; // user canceled
|
||||
return undefined; // user canceled
|
||||
}
|
||||
|
||||
// Just save if target is same as models own resource
|
||||
@@ -548,17 +694,17 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
});
|
||||
}
|
||||
|
||||
private doSaveAs(resource: URI, target?: URI, options?: ISaveOptions): Promise<URI> {
|
||||
private doSaveAs(resource: URI, target: URI, options?: ISaveOptions): Promise<URI> {
|
||||
|
||||
// Retrieve text model from provided resource if any
|
||||
let modelPromise: Promise<ITextFileEditorModel | UntitledEditorModel> = Promise.resolve(null);
|
||||
let modelPromise: Promise<ITextFileEditorModel | UntitledEditorModel | undefined> = Promise.resolve(undefined);
|
||||
if (this.fileService.canHandleResource(resource)) {
|
||||
modelPromise = Promise.resolve(this._models.get(resource));
|
||||
} else if (resource.scheme === Schemas.untitled && this.untitledEditorService.exists(resource)) {
|
||||
modelPromise = this.untitledEditorService.loadOrCreate({ resource });
|
||||
}
|
||||
|
||||
return modelPromise.then<any>(model => {
|
||||
return modelPromise.then(model => {
|
||||
|
||||
// We have a model: Use it (can be null e.g. if this file is binary and not a text file or was never opened before)
|
||||
if (model) {
|
||||
@@ -566,8 +712,13 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
}
|
||||
|
||||
// Otherwise we can only copy
|
||||
return this.fileService.copyFile(resource, target);
|
||||
}).then(() => {
|
||||
return this.fileService.copyFile(resource, target).then(() => true);
|
||||
}).then(result => {
|
||||
|
||||
// Return early if the operation was not running
|
||||
if (!result) {
|
||||
return target;
|
||||
}
|
||||
|
||||
// Revert the source
|
||||
return this.revert(resource).then(() => {
|
||||
@@ -578,30 +729,61 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
});
|
||||
}
|
||||
|
||||
private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI, options?: ISaveOptions): Promise<void> {
|
||||
private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI, options?: ISaveOptions): Promise<boolean> {
|
||||
let targetModelResolver: Promise<ITextFileEditorModel>;
|
||||
let targetExists: boolean = false;
|
||||
|
||||
// Prefer an existing model if it is already loaded for the given target resource
|
||||
const targetModel = this.models.get(target);
|
||||
if (targetModel && targetModel.isResolved()) {
|
||||
targetModelResolver = Promise.resolve(targetModel);
|
||||
targetExists = true;
|
||||
}
|
||||
|
||||
// Otherwise create the target file empty if it does not exist already and resolve it from there
|
||||
else {
|
||||
targetModelResolver = this.fileService.resolveFile(target).then(stat => stat, () => null).then(stat => stat || this.fileService.updateContent(target, '')).then(stat => {
|
||||
return this.models.loadOrCreate(target);
|
||||
});
|
||||
targetModelResolver = this.fileService.existsFile(target).then<any>(exists => {
|
||||
targetExists = exists;
|
||||
|
||||
// create target model adhoc if file does not exist yet
|
||||
if (!targetExists) {
|
||||
return this.fileService.updateContent(target, '');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}).then(() => this.models.loadOrCreate(target));
|
||||
}
|
||||
|
||||
return targetModelResolver.then(targetModel => {
|
||||
|
||||
// take over encoding and model value from source model
|
||||
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
|
||||
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
|
||||
// Confirm to overwrite if we have an untitled file with associated file where
|
||||
// the file actually exists on disk and we are instructed to save to that file
|
||||
// path. This can happen if the file was created after the untitled file was opened.
|
||||
// See https://github.com/Microsoft/vscode/issues/67946
|
||||
let confirmWrite: Promise<boolean>;
|
||||
if (sourceModel instanceof UntitledEditorModel && sourceModel.hasAssociatedFilePath && targetExists && isEqual(target, this.untitledToAssociatedFileResource(sourceModel.getResource()))) {
|
||||
confirmWrite = this.confirmOverwrite(target);
|
||||
} else {
|
||||
confirmWrite = Promise.resolve(true);
|
||||
}
|
||||
|
||||
// save model
|
||||
return targetModel.save(options);
|
||||
return confirmWrite.then(write => {
|
||||
if (!write) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// take over encoding and model value from source model
|
||||
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
|
||||
if (targetModel.textEditorModel) {
|
||||
const snapshot = sourceModel.createSnapshot();
|
||||
if (snapshot) {
|
||||
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot));
|
||||
}
|
||||
}
|
||||
|
||||
// save model
|
||||
return targetModel.save(options).then(() => true);
|
||||
});
|
||||
}, error => {
|
||||
|
||||
// binary model: delete the file and run the operation again
|
||||
@@ -615,8 +797,8 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
|
||||
private suggestFileName(untitledResource: URI): URI {
|
||||
const untitledFileName = this.untitledEditorService.suggestFileName(untitledResource);
|
||||
|
||||
const schemeFilter = Schemas.file;
|
||||
const remoteAuthority = this.windowService.getConfiguration().remoteAuthority;
|
||||
const schemeFilter = remoteAuthority ? REMOTE_HOST_SCHEME : Schemas.file;
|
||||
|
||||
const lastActiveFile = this.historyService.getLastActiveFile(schemeFilter);
|
||||
if (lastActiveFile) {
|
||||
@@ -629,11 +811,11 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return joinPath(lastActiveFolder, untitledFileName);
|
||||
}
|
||||
|
||||
return URI.file(untitledFileName);
|
||||
return schemeFilter === Schemas.file ? URI.file(untitledFileName) : URI.from({ scheme: schemeFilter, authority: remoteAuthority, path: '/' + untitledFileName });
|
||||
}
|
||||
|
||||
revert(resource: URI, options?: IRevertOptions): Promise<boolean> {
|
||||
return this.revertAll([resource], options).then(result => result.results.length === 1 && result.results[0].success);
|
||||
return this.revertAll([resource], options).then(result => result.results.length === 1 && !!result.results[0].success);
|
||||
}
|
||||
|
||||
revertAll(resources?: URI[], options?: IRevertOptions): Promise<ITextFileOperationResult> {
|
||||
@@ -662,13 +844,19 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
return Promise.all(fileModels.map(model => {
|
||||
return model.revert(options && options.soft).then(() => {
|
||||
if (!model.isDirty()) {
|
||||
mapResourceToResult.get(model.getResource()).success = true;
|
||||
const result = mapResourceToResult.get(model.getResource());
|
||||
if (result) {
|
||||
result.success = true;
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
|
||||
// FileNotFound means the file got deleted meanwhile, so still record as successful revert
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
|
||||
mapResourceToResult.get(model.getResource()).success = true;
|
||||
const result = mapResourceToResult.get(model.getResource());
|
||||
if (result) {
|
||||
result.success = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise bubble up the error
|
||||
@@ -747,14 +935,18 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
// Otherwise a parent folder of the source is being moved, so we need
|
||||
// to compute the target resource based on that
|
||||
else {
|
||||
targetModelResource = sourceModelResource.with({ path: paths.join(target.path, sourceModelResource.path.substr(source.path.length + 1)) });
|
||||
targetModelResource = sourceModelResource.with({ path: joinPath(target, sourceModelResource.path.substr(source.path.length + 1)).path });
|
||||
}
|
||||
|
||||
// Remember as dirty target model to load after the operation
|
||||
dirtyTargetModels.push(targetModelResource);
|
||||
|
||||
// Backup dirty source model to the target resource it will become later
|
||||
return this.backupFileService.backupResource(targetModelResource, sourceModel.createSnapshot(), sourceModel.getVersionId());
|
||||
const snapshot = sourceModel.createSnapshot();
|
||||
if (snapshot) {
|
||||
return this.backupFileService.backupResource(targetModelResource, snapshot, sourceModel.getVersionId());
|
||||
}
|
||||
return Promise.resolve();
|
||||
}));
|
||||
} else {
|
||||
handleDirtySourceModels = Promise.resolve();
|
||||
@@ -818,3 +1010,5 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ITextFileService, TextFileService);
|
||||
Reference in New Issue
Block a user