Merge VS Code 1.21 source code (#1067)

* Initial VS Code 1.21 file copy with patches

* A few more merges

* Post npm install

* Fix batch of build breaks

* Fix more build breaks

* Fix more build errors

* Fix more build breaks

* Runtime fixes 1

* Get connection dialog working with some todos

* Fix a few packaging issues

* Copy several node_modules to package build to fix loader issues

* Fix breaks from master

* A few more fixes

* Make tests pass

* First pass of license header updates

* Second pass of license header updates

* Fix restore dialog issues

* Remove add additional themes menu items

* fix select box issues where the list doesn't show up

* formatting

* Fix editor dispose issue

* Copy over node modules to correct location on all platforms
This commit is contained in:
Karl Burtram
2018-04-04 15:27:51 -07:00
committed by GitHub
parent 5fba3e31b4
commit dafb780987
9412 changed files with 141255 additions and 98813 deletions

View File

@@ -22,16 +22,17 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, IRawTextContent } from 'vs/workbench/services/textfile/common/textfiles';
import { EncodingMode } from 'vs/workbench/common/editor';
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
import { IFileService, IFileStat, FileOperationError, FileOperationResult, IContent, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IFileService, IFileStat, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IRawTextSource } from 'vs/editor/common/model/textSource';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
/**
* The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk.
@@ -72,7 +73,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
constructor(
resource: URI,
preferredEncoding: string,
@IMessageService private messageService: IMessageService,
@INotificationService private notificationService: INotificationService,
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@IFileService private fileService: IFileService,
@@ -85,10 +86,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
@IHashService private hashService: IHashService
) {
super(modelService, modeService);
// TODO@remote
// assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.');
this.resource = resource;
this.toDispose = [];
this._onDidContentChange = new Emitter<StateChange>();
@@ -145,8 +142,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// We have received reports of users seeing delete events even though the file still
// exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665).
// Since we do not want to mark the model as orphaned, we have to check if the
// file is really gone and not just a faulty file event (TODO@Ben revisit when we
// have a more stable file watcher in place for this scenario).
// file is really gone and not just a faulty file event.
checkOrphanedPromise = TPromise.timeout(100).then(() => {
if (this.disposed) {
return true;
@@ -193,7 +189,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return;
}
const firstLineText = this.getFirstLineText(this.textEditorModel.getValue());
const firstLineText = this.getFirstLineText(this.textEditorModel);
const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText);
this.modelService.setMode(this.textEditorModel, mode);
@@ -295,12 +291,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// If we have a backup, continue loading with it
if (!!backup) {
const content: IContent = {
const content: IRawTextContent = {
resource: this.resource,
name: paths.basename(this.resource.fsPath),
mtime: Date.now(),
etag: void 0,
value: '', /* will be filled later from backup */
value: createTextBufferFactory(''), /* will be filled later from backup */
encoding: this.fileService.getEncoding(this.resource, this.preferredEncoding)
};
@@ -360,7 +356,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return TPromise.wrapError<TextFileEditorModel>(error);
}
private loadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise<TextFileEditorModel> {
private loadWithContent(content: IRawTextContent, backup?: URI): TPromise<TextFileEditorModel> {
return this.doLoadWithContent(content, backup).then(model => {
// Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype
@@ -384,7 +380,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
});
}
private doLoadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise<TextFileEditorModel> {
private doLoadWithContent(content: IRawTextContent, backup?: URI): TPromise<TextFileEditorModel> {
diag('load() - resolved content', this.resource, new Date());
// Update our resolved disk stat model
@@ -394,8 +390,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
mtime: content.mtime,
etag: content.etag,
isDirectory: false,
hasChildren: false,
children: void 0,
isSymbolicLink: false,
children: void 0
};
this.updateLastResolvedDiskStat(resolvedStat);
@@ -426,7 +422,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.doCreateTextModel(content.resource, content.value, backup);
}
private doUpdateTextModel(value: string | IRawTextSource): TPromise<TextFileEditorModel> {
private doUpdateTextModel(value: ITextBufferFactory): TPromise<TextFileEditorModel> {
diag('load() - updated text editor model', this.resource, new Date());
// Ensure we are not tracking a stale state
@@ -446,11 +442,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return TPromise.as<TextFileEditorModel>(this);
}
private doCreateTextModel(resource: URI, value: string | IRawTextSource, backup: URI): TPromise<TextFileEditorModel> {
private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI): TPromise<TextFileEditorModel> {
diag('load() - created text editor model', this.resource, new Date());
this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => {
const hasBackupContent = (typeof backupContent === 'string');
const hasBackupContent = !!backupContent;
return this.createTextEditorModel(hasBackupContent ? backupContent : value, resource).then(() => {
this.createTextEditorModelPromise = null;
@@ -494,14 +490,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.toDispose.push(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged()));
}
private doLoadBackup(backup: URI): TPromise<string> {
private doLoadBackup(backup: URI): TPromise<ITextBufferFactory> {
if (!backup) {
return TPromise.as(null);
}
return this.textFileService.resolveTextContent(backup, BACKUP_FILE_RESOLVE_OPTIONS).then(backup => {
return this.backupFileService.parseBackupContent(backup.value);
}, error => null /* ignore errors */);
return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent, error => null /* ignore errors */);
}
protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): TPromise<IMode> {
@@ -708,12 +702,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Save to Disk
// mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering)
diag(`doSave(${versionId}) - before updateContent()`, this.resource, new Date());
return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.getValue(), {
return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.createSnapshot(), {
overwriteReadonly: options.overwriteReadonly,
overwriteEncoding: options.overwriteEncoding,
mtime: this.lastResolvedDiskStat.mtime,
encoding: this.getEncoding(),
etag: this.lastResolvedDiskStat.etag
etag: this.lastResolvedDiskStat.etag,
writeElevated: options.writeElevated
}).then(stat => {
diag(`doSave(${versionId}) - after updateContent()`, this.resource, new Date());
@@ -920,7 +915,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Decode: Load with encoding
else {
if (this.isDirty()) {
this.messageService.show(Severity.Info, nls.localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding."));
this.notificationService.info(nls.localize('saveFileFirst', "The file is dirty. Please save it first before reopening it with another encoding."));
return;
}
@@ -1088,10 +1083,10 @@ export class SaveSequentializer {
class DefaultSaveErrorHandler implements ISaveErrorHandler {
constructor( @IMessageService private messageService: IMessageService) { }
constructor( @INotificationService private notificationService: INotificationService) { }
public onSaveError(error: any, model: TextFileEditorModel): void {
this.messageService.show(Severity.Error, nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(model.getResource().fsPath), toErrorMessage(error, false)));
this.notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(model.getResource().fsPath), toErrorMessage(error, false)));
}
}

View File

@@ -14,7 +14,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import platform = require('vs/base/common/platform');
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IRevertOptions, IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles';
import { IResult, ITextFileOperationResult, ITextFileService, IRawTextContent, IAutoSaveConfiguration, AutoSaveMode, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, AutoSaveContext } from 'vs/workbench/services/textfile/common/textfiles';
import { ConfirmResult } from 'vs/workbench/common/editor';
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
@@ -22,14 +22,18 @@ import { IFileService, IResolveContentOptions, IFilesConfiguration, FileOperatio
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { ResourceMap } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IRevertOptions } from 'vs/platform/editor/common/editor';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { IModelService } from 'vs/editor/common/services/modelService';
import { INotificationService } from 'vs/platform/notification/common/notification';
export interface IBackupResult {
didBackup: boolean;
@@ -55,6 +59,8 @@ export abstract class TextFileService implements ITextFileService {
private configuredAutoSaveOnFocusChange: boolean;
private configuredAutoSaveOnWindowChange: boolean;
private autoSaveContext: IContextKey<string>;
private configuredHotExit: string;
constructor(
@@ -64,11 +70,13 @@ export abstract class TextFileService implements ITextFileService {
protected fileService: IFileService,
private untitledEditorService: IUntitledEditorService,
private instantiationService: IInstantiationService,
private messageService: IMessageService,
private notificationService: INotificationService,
protected environmentService: IEnvironmentService,
private backupFileService: IBackupFileService,
private windowsService: IWindowsService,
private historyService: IHistoryService
private historyService: IHistoryService,
contextKeyService: IContextKeyService,
private modelService: IModelService
) {
this.toUnbind = [];
@@ -79,6 +87,7 @@ export abstract class TextFileService implements ITextFileService {
this.toUnbind.push(this._onFilesAssociationChange);
this._models = this.instantiationService.createInstance(TextFileEditorModelManager);
this.autoSaveContext = AutoSaveContext.bindTo(contextKeyService);
const configuration = this.configurationService.getValue<IFilesConfiguration>();
this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations;
@@ -94,9 +103,9 @@ export abstract class TextFileService implements ITextFileService {
abstract resolveTextContent(resource: URI, options?: IResolveContentOptions): TPromise<IRawTextContent>;
abstract promptForPath(defaultPath: string): string;
abstract promptForPath(defaultPath: string): TPromise<string>;
abstract confirmSave(resources?: URI[]): ConfirmResult;
abstract confirmSave(resources?: URI[]): TPromise<ConfirmResult>;
public get onAutoSaveConfigurationChange(): Event<IAutoSaveConfiguration> {
return this._onAutoSaveConfigurationChange.event;
@@ -152,7 +161,7 @@ export abstract class TextFileService implements ITextFileService {
return this.confirmBeforeShutdown();
}, errors => {
const firstError = errors[0];
this.messageService.show(Severity.Error, nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", firstError.message));
this.notificationService.error(nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", firstError.message));
return true; // veto, the backups failed
});
@@ -222,9 +231,9 @@ export abstract class TextFileService implements ITextFileService {
const filesToBackup: ITextFileEditorModel[] = [];
const untitledToBackup: URI[] = [];
dirtyToBackup.forEach(s => {
if (s.scheme === Schemas.file) {
if (this.fileService.canHandleResource(s)) {
filesToBackup.push(textFileEditorModelManager.get(s));
} else if (s.scheme === UNTITLED_SCHEMA) {
} else if (s.scheme === Schemas.untitled) {
untitledToBackup.push(s);
}
});
@@ -235,7 +244,7 @@ export abstract class TextFileService implements ITextFileService {
private doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): TPromise<void> {
// Handle file resources first
return TPromise.join(dirtyFileModels.map(model => this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId()))).then(results => {
return TPromise.join(dirtyFileModels.map(model => this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId()))).then(results => {
// Handle untitled resources
const untitledModelPromises = untitledResources
@@ -244,7 +253,7 @@ export abstract class TextFileService implements ITextFileService {
return TPromise.join(untitledModelPromises).then(untitledModels => {
const untitledBackupPromises = untitledModels.map(model => {
return this.backupFileService.backupResource(model.getResource(), model.getValue(), model.getVersionId());
return this.backupFileService.backupResource(model.getResource(), model.createSnapshot(), model.getVersionId());
});
return TPromise.join(untitledBackupPromises).then(() => void 0);
@@ -253,35 +262,36 @@ export abstract class TextFileService implements ITextFileService {
}
private confirmBeforeShutdown(): boolean | TPromise<boolean> {
const confirm = this.confirmSave();
return this.confirmSave().then(confirm => {
// Save
if (confirm === ConfirmResult.SAVE) {
return this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }).then(result => {
if (result.results.some(r => !r.success)) {
return true; // veto if some saves failed
}
// Save
if (confirm === ConfirmResult.SAVE) {
return this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }).then(result => {
if (result.results.some(r => !r.success)) {
return true; // veto if some saves failed
}
return this.noVeto({ cleanUpBackups: true });
});
}
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
return this.noVeto({ cleanUpBackups: true });
});
}
}
// Don't Save
else if (confirm === ConfirmResult.DONT_SAVE) {
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
// Make sure to revert untitled so that they do not restore
// see https://github.com/Microsoft/vscode/issues/29572
this.untitledEditorService.revertAll();
return this.noVeto({ cleanUpBackups: true });
}
// Cancel
else if (confirm === ConfirmResult.CANCEL) {
return true; // veto
}
return void 0;
return void 0;
});
}
private noVeto(options: { cleanUpBackups: boolean }): boolean | TPromise<boolean> {
@@ -304,6 +314,7 @@ export abstract class TextFileService implements ITextFileService {
const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF);
const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF;
this.autoSaveContext.set(autoSaveMode);
switch (autoSaveMode) {
case AutoSaveConfiguration.AFTER_DELAY:
this.configuredAutoSaveDelay = configuration && configuration.files && configuration.files.autoSaveDelay;
@@ -379,7 +390,7 @@ export abstract class TextFileService implements ITextFileService {
public save(resource: URI, options?: ISaveOptions): TPromise<boolean> {
// Run a forced save if we detect the file is not dirty so that save participants can still run
if (options && options.force && resource.scheme === Schemas.file && !this.isDirty(resource)) {
if (options && options.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) {
const model = this._models.get(resource);
if (model) {
model.save({ force: true, reason: SaveReason.EXPLICIT }).then(() => !model.isDirty());
@@ -405,11 +416,7 @@ export abstract class TextFileService implements ITextFileService {
const filesToSave: URI[] = [];
const untitledToSave: URI[] = [];
toSave.forEach(s => {
// TODO@remote
// if (s.scheme === Schemas.file) {
// filesToSave.push(s);
// } else
if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) {
if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === Schemas.untitled) {
untitledToSave.push(s);
} else {
filesToSave.push(s);
@@ -422,7 +429,7 @@ export abstract class TextFileService implements ITextFileService {
private doSaveAll(fileResources: URI[], untitledResources: URI[], options?: ISaveOptions): TPromise<ITextFileOperationResult> {
// Handle files first that can just be saved
return this.doSaveAllFiles(fileResources, options).then(result => {
return this.doSaveAllFiles(fileResources, options).then(async result => {
// Preflight for untitled to handle cancellation from the dialog
const targetsForUntitled: URI[] = [];
@@ -438,7 +445,7 @@ export abstract class TextFileService implements ITextFileService {
// Otherwise ask user
else {
targetPath = this.promptForPath(this.suggestFileName(untitled));
targetPath = await this.promptForPath(this.suggestFileName(untitled));
if (!targetPath) {
return TPromise.as({
results: [...fileResources, ...untitledResources].map(r => {
@@ -525,41 +532,49 @@ export abstract class TextFileService implements ITextFileService {
return this.getFileModels(arg1).filter(model => model.isDirty());
}
public saveAs(resource: URI, target?: URI): TPromise<URI> {
public saveAs(resource: URI, target?: URI, options?: ISaveOptions): TPromise<URI> {
// Get to target resource
if (!target) {
let targetPromise: TPromise<URI>;
if (target) {
targetPromise = TPromise.wrap(target);
} else {
let dialogPath = resource.fsPath;
if (resource.scheme === UNTITLED_SCHEMA) {
if (resource.scheme === Schemas.untitled) {
dialogPath = this.suggestFileName(resource);
}
const pathRaw = this.promptForPath(dialogPath);
if (pathRaw) {
target = URI.file(pathRaw);
targetPromise = this.promptForPath(dialogPath).then(pathRaw => {
if (pathRaw) {
return URI.file(pathRaw);
}
return void 0;
});
}
return targetPromise.then(target => {
if (!target) {
return TPromise.as(null); // user canceled
}
}
if (!target) {
return TPromise.as(null); // user canceled
}
// Just save if target is same as models own resource
if (resource.toString() === target.toString()) {
return this.save(resource, options).then(() => resource);
}
// Just save if target is same as models own resource
if (resource.toString() === target.toString()) {
return this.save(resource).then(() => resource);
}
// Do it
return this.doSaveAs(resource, target);
// Do it
return this.doSaveAs(resource, target, options);
});
}
private doSaveAs(resource: URI, target?: URI): TPromise<URI> {
private doSaveAs(resource: URI, target?: URI, options?: ISaveOptions): TPromise<URI> {
// Retrieve text model from provided resource if any
let modelPromise: TPromise<ITextFileEditorModel | UntitledEditorModel> = TPromise.as(null);
if (resource.scheme === Schemas.file) {
if (this.fileService.canHandleResource(resource)) {
modelPromise = TPromise.as(this._models.get(resource));
} else if (resource.scheme === UNTITLED_SCHEMA && this.untitledEditorService.exists(resource)) {
} else if (resource.scheme === Schemas.untitled && this.untitledEditorService.exists(resource)) {
modelPromise = this.untitledEditorService.loadOrCreate({ resource });
}
@@ -567,7 +582,7 @@ export abstract class TextFileService implements ITextFileService {
// 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) {
return this.doSaveTextFileAs(model, resource, target);
return this.doSaveTextFileAs(model, resource, target, options);
}
// Otherwise we can only copy
@@ -583,7 +598,7 @@ export abstract class TextFileService implements ITextFileService {
});
}
private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI): TPromise<void> {
private doSaveTextFileAs(sourceModel: ITextFileEditorModel | UntitledEditorModel, resource: URI, target: URI, options?: ISaveOptions): TPromise<void> {
let targetModelResolver: TPromise<ITextFileEditorModel>;
// Prefer an existing model if it is already loaded for the given target resource
@@ -603,15 +618,15 @@ export abstract class TextFileService implements ITextFileService {
// take over encoding and model value from source model
targetModel.updatePreferredEncoding(sourceModel.getEncoding());
targetModel.textEditorModel.setValue(sourceModel.getValue());
this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()));
// save model
return targetModel.save();
return targetModel.save(options);
}, error => {
// binary model: delete the file and run the operation again
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) {
return this.fileService.del(target).then(() => this.doSaveTextFileAs(sourceModel, resource, target));
return this.fileService.del(target).then(() => this.doSaveTextFileAs(sourceModel, resource, target, options));
}
return TPromise.wrapError(error);

View File

@@ -9,10 +9,12 @@ import URI from 'vs/base/common/uri';
import Event from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEncodingSupport, ConfirmResult } from 'vs/workbench/common/editor';
import { IBaseStat, IResolveContentOptions } from 'vs/platform/files/common/files';
import { IBaseStat, IResolveContentOptions, ITextSnapshot } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
import { IRawTextSource } from 'vs/editor/common/model/textSource';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { IRevertOptions } from 'vs/platform/editor/common/editor';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
/**
* The save error handler can be installed on the text text file editor model to install code that executes when save errors occur.
@@ -89,6 +91,7 @@ export class TextFileModelChangeEvent {
}
export const TEXT_FILE_SERVICE_ID = 'textFileService';
export const AutoSaveContext = new RawContextKey<string>('config.files.autoSave', undefined);
export interface ITextFileOperationResult {
results: IResult[];
@@ -128,12 +131,7 @@ export interface IRawTextContent extends IBaseStat {
/**
* The line grouped content of a text file.
*/
value: IRawTextSource;
/**
* The line grouped logical hash of a text file.
*/
valueLogicalHash: string;
value: ITextBufferFactory;
/**
* The encoding of the content if known.
@@ -178,6 +176,7 @@ export interface ISaveOptions {
overwriteReadonly?: boolean;
overwriteEncoding?: boolean;
skipSaveParticipants?: boolean;
writeElevated?: boolean;
}
export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport {
@@ -201,7 +200,7 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport
revert(soft?: boolean): TPromise<void>;
getValue(): string;
createSnapshot(): ITextSnapshot;
isDirty(): boolean;
@@ -210,19 +209,6 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport
isDisposed(): boolean;
}
export interface IRevertOptions {
/**
* Forces to load the contents from disk again even if the file is not dirty.
*/
force?: boolean;
/**
* A soft revert will clear dirty state of a file but not attempt to load the contents from disk.
*/
soft?: boolean;
}
export interface ITextFileService extends IDisposable {
_serviceBrand: any;
onAutoSaveConfigurationChange: Event<IAutoSaveConfiguration>;
@@ -258,17 +244,20 @@ export interface ITextFileService extends IDisposable {
* Saves the resource.
*
* @param resource the resource to save
* @param options optional save options
* @return true if the resource was saved.
*/
save(resource: URI, options?: ISaveOptions): TPromise<boolean>;
/**
* Saves the provided resource asking the user for a file name.
* Saves the provided resource asking the user for a file name or using the provided one.
*
* @param resource the resource to save as.
* @param targetResource the optional target to save to.
* @param options optional save options
* @return true if the file was saved.
*/
saveAs(resource: URI, targetResource?: URI): TPromise<URI>;
saveAs(resource: URI, targetResource?: URI, options?: ISaveOptions): TPromise<URI>;
/**
* Saves the set of resources and returns a promise with the operation result.
@@ -298,7 +287,7 @@ export interface ITextFileService extends IDisposable {
* @param resources the resources of the files to ask for confirmation or null if
* confirming for all dirty resources.
*/
confirmSave(resources?: URI[]): ConfirmResult;
confirmSave(resources?: URI[]): TPromise<ConfirmResult>;
/**
* Convinient fast access to the current auto save mode.
@@ -314,4 +303,4 @@ export interface ITextFileService extends IDisposable {
* Convinient fast access to the hot exit file setting.
*/
isHotExitEnabled: boolean;
}
}