提交 93f3fc71 编写于 作者: B Benjamin Pasero

avoid loading entire backup content for hot exit

fixes #41986
上级 2d01d1c4
......@@ -57,12 +57,16 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory
return builder.finish();
}
export function createTextBufferFactoryFromStream(stream: IStringStream): TPromise<model.ITextBufferFactory> {
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): TPromise<model.ITextBufferFactory> {
return new TPromise<model.ITextBufferFactory>((c, e, p) => {
let done = false;
let builder = createTextBufferBuilder();
stream.on('data', (chunk) => {
if (filter) {
chunk = filter(chunk);
}
builder.acceptChunk(chunk);
});
......
......@@ -12,7 +12,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { BACKUP_FILE_RESOLVE_OPTIONS, IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { TPromise } from 'vs/base/common/winjs.base';
import { Schemas } from 'vs/base/common/network';
......@@ -20,6 +20,8 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Position } from 'vs/platform/editor/common/editor';
import { onUnexpectedError } from 'vs/base/common/errors';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
/**
* Shared function across some editor components to handle drag & drop of external resources. E.g. of folders and workspace files
......@@ -37,6 +39,7 @@ export class EditorAreaDropHandler {
@IEditorGroupService private groupService: IEditorGroupService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IConfigurationService private configurationService: IConfigurationService
) {
}
......@@ -107,13 +110,22 @@ export class EditorAreaDropHandler {
}
// Resolve the contents of the dropped dirty resource from source
return this.textFileService.resolveTextContent(droppedDirtyEditor.backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(content => {
return this.backupFileService.resolveBackupContent(droppedDirtyEditor.backupResource).then(content => {
// Set the contents of to the resource to the target
return this.backupFileService.backupResource(droppedDirtyEditor.resource, this.backupFileService.parseBackupContent(content.value));
return this.backupFileService.backupResource(droppedDirtyEditor.resource, content.create(this.getDefaultEOL()).createSnapshot(true));
}).then(() => false, () => false /* ignore any error */);
}
private getDefaultEOL(): DefaultEndOfLine {
const eol = this.configurationService.getValue('files.eol');
if (eol === '\r\n') {
return DefaultEndOfLine.CRLF;
}
return DefaultEndOfLine.LF;
}
private handleWorkspaceFileDrop(resources: (IDraggedResource | IDraggedEditor)[]): TPromise<boolean> {
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
......
......@@ -16,9 +16,10 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IMode } from 'vs/editor/common/modes';
import Event, { Emitter } from 'vs/base/common/event';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
......@@ -46,7 +47,6 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@IBackupFileService private backupFileService: IBackupFileService,
@ITextFileService private textFileService: ITextFileService,
@ITextResourceConfigurationService private configurationService: ITextResourceConfigurationService
) {
super(modelService, modeService);
......@@ -163,18 +163,24 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
// Check for backups first
return this.backupFileService.loadBackupResource(this.resource).then(backupResource => {
if (backupResource) {
return this.textFileService.resolveTextContent(backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(rawTextContent => {
return this.backupFileService.parseBackupContent(rawTextContent.value);
});
return this.backupFileService.resolveBackupContent(backupResource);
}
return null;
}).then(backupContent => {
}).then(backupTextBufferFactory => {
const hasBackup = !!backupTextBufferFactory;
// untitled associated to file path are dirty right away as well as untitled with content
this.setDirty(this.hasAssociatedFilePath || !!backupContent);
this.setDirty(this.hasAssociatedFilePath || hasBackup);
return this.doLoad(backupContent || this.initialValue || '').then(model => {
let untitledContents: ITextBufferFactory;
if (backupTextBufferFactory) {
untitledContents = backupTextBufferFactory;
} else {
untitledContents = createTextBufferFactory(this.initialValue || '');
}
return this.doLoad(untitledContents).then(model => {
// Encoding
this.configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
......@@ -189,7 +195,7 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin
});
}
private doLoad(content: string): TPromise<UntitledEditorModel> {
private doLoad(content: ITextBufferFactory): TPromise<UntitledEditorModel> {
// Create text editor model if not yet done
if (!this.textEditorModel) {
......
......@@ -65,13 +65,12 @@ export interface IBackupFileService {
getWorkspaceFileBackups(): TPromise<Uri[]>;
/**
* Parses backup raw text content into the content, removing the metadata that is also stored
* in the file.
* Resolves the backup for the given resource.
*
* @param textBufferFactory The ITextBufferFactory from a backup resource.
* @return The backup file's backed up content.
* @param value The contents from a backup resource as stream.
* @return The backup file's backed up content as text buffer factory.
*/
parseBackupContent(textBufferFactory: ITextBufferFactory): string;
resolveBackupContent(backup: Uri): TPromise<ITextBufferFactory>;
/**
* Discards the backup associated with a resource if it exists..
......
......@@ -10,12 +10,12 @@ import * as crypto from 'crypto';
import * as pfs from 'vs/base/node/pfs';
import Uri from 'vs/base/common/uri';
import { ResourceQueue } from 'vs/base/common/async';
import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
import { IFileService, ITextSnapshot, IFileStat } from 'vs/platform/files/common/files';
import { TPromise } from 'vs/base/common/winjs.base';
import { readToMatchingString } from 'vs/base/node/stream';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, ITextBufferFactory, EndOfLinePreference } from 'vs/editor/common/model';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
export interface IBackupFilesModel {
resolve(backupRoot: string): TPromise<IBackupFilesModel>;
......@@ -245,12 +245,27 @@ export class BackupFileService implements IBackupFileService {
});
}
public parseBackupContent(textBufferFactory: ITextBufferFactory): string {
// The first line of a backup text file is the file name
const textBuffer = textBufferFactory.create(DefaultEndOfLine.LF);
const lineCount = textBuffer.getLineCount();
const range = new Range(2, 1, lineCount, textBuffer.getLineLength(lineCount) + 1);
return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined);
public resolveBackupContent(backup: Uri): TPromise<ITextBufferFactory> {
return this.fileService.resolveStreamContent(backup, BACKUP_FILE_RESOLVE_OPTIONS).then(content => {
// Add a filter method to filter out everything until the meta marker
let metaFound = false;
const metaPreambleFilter = (chunk: string) => {
if (!metaFound && chunk) {
const metaIndex = chunk.indexOf(BackupFileService.META_MARKER);
if (metaIndex === -1) {
return ''; // meta not yet found, return empty string
}
metaFound = true;
return chunk.substr(metaIndex + 1); // meta found, return everything after
}
return chunk;
};
return createTextBufferFactoryFromStream(content.value, metaPreambleFilter);
});
}
public toBackupResource(resource: Uri): Uri {
......
......@@ -16,10 +16,12 @@ import pfs = require('vs/base/node/pfs');
import Uri from 'vs/base/common/uri';
import { BackupFileService, BackupFilesModel } from 'vs/workbench/services/backup/node/backupFileService';
import { FileService } from 'vs/workbench/services/files/node/fileService';
import { createTextBufferFactory, TextModel } from 'vs/editor/common/model/textModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { TestContextService, TestTextResourceConfigurationService, getRandomTestPath, TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { snapshotToString } from 'vs/platform/files/common/files';
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
const backupHome = path.join(parentDir, 'Backups');
......@@ -268,10 +270,29 @@ suite('BackupFileService', () => {
});
});
test('parseBackupContent', () => {
test('should separate metadata from content', () => {
const textBufferFactory = createTextBufferFactory('metadata\ncontent');
assert.equal(service.parseBackupContent(textBufferFactory), 'content');
test('resolveBackupContent', () => {
test('should restore the original contents (untitled file)', () => {
const contents = 'test\nand more stuff';
service.backupResource(untitledFile, contents).then(() => {
service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
});
});
});
test('should restore the original contents (text file)', () => {
const contents = [
'Lorem ipsum ',
'dolor öäü sit amet ',
'consectetur ',
'adipiscing ßß elit',
].join('');
service.backupResource(fooFile, contents).then(() => {
service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => {
assert.equal(contents, snapshotToString(factory.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true)));
});
});
});
});
});
......
......@@ -22,7 +22,7 @@ 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 { IBackupFileService } 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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
......@@ -444,7 +444,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
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;
......@@ -488,14 +488,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> {
......
......@@ -872,6 +872,10 @@ export class TestBackupFileService implements IBackupFileService {
return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined);
}
public resolveBackupContent(backup: URI): TPromise<ITextBufferFactory> {
return TPromise.as(null);
}
public discardResourceBackup(resource: URI): TPromise<void> {
return TPromise.as(void 0);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册