Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -12,7 +12,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { guessMimeTypes } from 'vs/base/common/mime';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import URI from 'vs/base/common/uri';
import * as assert from 'vs/base/common/assert';
// import * as assert from 'vs/base/common/assert';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import paths = require('vs/base/common/paths');
import diagnostics = require('vs/base/common/diagnostics');
@@ -31,9 +31,9 @@ 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 { anonymize } from 'vs/platform/telemetry/common/telemetryUtils';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IRawTextSource } from 'vs/editor/common/model/textSource';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
/**
* 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.
@@ -87,10 +87,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
@IBackupFileService private backupFileService: IBackupFileService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IHashService private hashService: IHashService
) {
super(modelService, modeService);
assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.');
// TODO@remote
// assert.ok(resource.scheme === 'file', 'TextFileEditorModel can only handle file:// resources.');
this.resource = resource;
this.toDispose = [];
@@ -99,6 +101,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.toDispose.push(this._onDidContentChange);
this.toDispose.push(this._onDidStateChange);
this.preferredEncoding = preferredEncoding;
this.inOrphanMode = false;
this.dirty = false;
this.versionId = 0;
this.lastSaveAttemptTime = 0;
@@ -119,16 +122,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.toDispose.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
this.toDispose.push(this.textFileService.onAutoSaveConfigurationChange(config => this.updateAutoSaveConfiguration(config)));
this.toDispose.push(this.textFileService.onFilesAssociationChange(e => this.onFilesAssociationChange()));
this.toDispose.push(this.onDidStateChange(e => {
if (e === StateChange.REVERTED) {
this.toDispose.push(this.onDidStateChange(e => this.onStateChange(e)));
}
// Cancel any content change event promises as they are no longer valid.
this.contentChangeEventScheduler.cancel();
private onStateChange(e: StateChange): void {
if (e === StateChange.REVERTED) {
// Refire state change reverted events as content change events
this._onDidContentChange.fire(StateChange.REVERTED);
}
}));
// Cancel any content change event promises as they are no longer valid.
this.contentChangeEventScheduler.cancel();
// Refire state change reverted events as content change events
this._onDidContentChange.fire(StateChange.REVERTED);
}
}
private onFileChanges(e: FileChangesEvent): void {
@@ -361,10 +366,31 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
private loadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise<TextFileEditorModel> {
diag('load() - resolved content', this.resource, new Date());
return this.doLoadWithContent(content, backup).then(model => {
// Telemetry
this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath), path: anonymize(this.resource.fsPath) });
// Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype
if (this.isSettingsFile()) {
/* __GDPR__
"settingsRead" : {}
*/
this.telemetryService.publicLog('settingsRead'); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data
} else {
/* __GDPR__
"fileGet" : {
"mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"path": { "classification": "CustomerContent", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('fileGet', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.resource.fsPath), path: this.hashService.createSHA1(this.resource.fsPath) });
}
return model;
});
}
private doLoadWithContent(content: IRawTextContent | IContent, backup?: URI): TPromise<TextFileEditorModel> {
diag('load() - resolved content', this.resource, new Date());
// Update our resolved disk stat model
const resolvedStat: IFileStat = {
@@ -449,10 +475,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
this.setDirty(false);
}
// See https://github.com/Microsoft/vscode/issues/30189
// This code has been extracted to a different method because it caused a memory leak
// where `value` was captured in the content change listener closure scope.
this._installChangeContentListener();
// Model Listeners
this.installModelListeners();
return this;
}, error => {
@@ -465,13 +489,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
return this.createTextEditorModelPromise;
}
private _installChangeContentListener(): void {
private installModelListeners(): void {
// See https://github.com/Microsoft/vscode/issues/30189
// This code has been extracted to a different method because it caused a memory leak
// where `value` was captured in the content change listener closure scope.
this.toDispose.push(this.textEditorModel.onDidChangeContent(() => {
this.onModelContentChanged();
}));
// Content Change
this.toDispose.push(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged()));
}
private doLoadBackup(backup: URI): TPromise<string> {
@@ -666,10 +691,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// mark the save participant as current pending save operation
return this.saveSequentializer.setPending(versionId, saveParticipantPromise.then(newVersionId => {
// the model was not dirty and no save participant changed the contents, so we do not have
// to write the contents to disk, as they are already on disk. we still want to trigger
// a change on the file though so that external file watchers can be notified
if (options.force && !this.dirty && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) {
// Under certain conditions a save to the model will not cause the contents to the flushed on
// disk because we can assume that the contents are already on disk. Instead, we just touch the
// file to still trigger external file watchers for example.
// The conditions are all of:
// - a forced, explicit save (Ctrl+S)
// - the model is not dirty (otherwise we know there are changed which needs to go to the file)
// - the model is not in orphan mode (because in that case we know the file does not exist on disk)
// - the model version did not change due to save participants running
if (options.force && !this.dirty && !this.inOrphanMode && options.reason === SaveReason.EXPLICIT && versionId === newVersionId) {
return this.doTouch();
}
@@ -696,8 +726,17 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
// Telemetry
if (this.isSettingsFile()) {
/* __GDPR__
"settingsWritten" : {}
*/
this.telemetryService.publicLog('settingsWritten'); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data
} else {
/* __GDPR__
"filePUT" : {
"mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('filePUT', { mimeType: guessMimeTypes(this.resource.fsPath).join(', '), ext: paths.extname(this.lastResolvedDiskStat.resource.fsPath) });
}
@@ -745,14 +784,10 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
}
// Check for workspace settings file
if (this.contextService.hasWorkspace()) {
return this.contextService.getWorkspace().roots.some(root => {
// {{SQL CARBON EDIT}}
return paths.isEqualOrParent(this.resource.fsPath, path.join(root.fsPath, '.sqlops'));
});
}
return false;
return this.contextService.getWorkspace().folders.some(folder => {
// {{SQL CARBON EDIT}}
return paths.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.sqlops'));
});
}
private doTouch(): TPromise<void> {

View File

@@ -17,7 +17,7 @@ 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 { ConfirmResult } from 'vs/workbench/common/editor';
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IFileService, IResolveContentOptions, IFilesConfiguration, FileOperationError, FileOperationResult, AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -85,8 +85,15 @@ export abstract class TextFileService implements ITextFileService {
const configuration = this.configurationService.getConfiguration<IFilesConfiguration>();
this.currentFilesAssociationConfig = configuration && configuration.files && configuration.files.associations;
this.onConfigurationChange(configuration);
this.onFilesConfigurationChange(configuration);
/* __GDPR__
"autoSave" : {
"${include}": [
"${IAutoSaveConfiguration}"
]
}
*/
this.telemetryService.publicLog('autoSave', this.getAutoSaveConfiguration());
this.registerListeners();
@@ -116,8 +123,12 @@ export abstract class TextFileService implements ITextFileService {
this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown(event.reason)));
this.lifecycleService.onShutdown(this.dispose, this);
// Configuration changes
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>())));
// Files configuration changes
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('files')) {
this.onFilesConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>());
}
}));
}
private beforeShutdown(reason: ShutdownReason): boolean | TPromise<boolean> {
@@ -180,7 +191,7 @@ export abstract class TextFileService implements ITextFileService {
let doBackup: boolean;
switch (reason) {
case ShutdownReason.CLOSE:
if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
} else if (windowCount > 1 || platform.isMacintosh) {
doBackup = false; // do not backup if a window is closed that does not cause quitting of the application
@@ -198,7 +209,7 @@ export abstract class TextFileService implements ITextFileService {
break;
case ShutdownReason.LOAD:
if (this.contextService.hasWorkspace() && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
} else {
doBackup = false; // do not backup because we are switching contexts
@@ -211,6 +222,13 @@ export abstract class TextFileService implements ITextFileService {
}
// Telemetry
/* __GDPR__
"hotExit:triggered" : {
"reason" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"windowCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"fileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('hotExit:triggered', { reason, windowCount, fileCount: dirtyToBackup.length });
// Backup
@@ -302,7 +320,7 @@ export abstract class TextFileService implements ITextFileService {
return this.backupFileService.discardAllWorkspaceBackups();
}
protected onConfigurationChange(configuration: IFilesConfiguration): void {
protected onFilesConfigurationChange(configuration: IFilesConfiguration): void {
const wasAutoSaveEnabled = (this.getAutoSaveMode() !== AutoSaveMode.OFF);
const autoSaveMode = (configuration && configuration.files && configuration.files.autoSave) || AutoSaveConfiguration.OFF;
@@ -347,10 +365,8 @@ export abstract class TextFileService implements ITextFileService {
this._onFilesAssociationChange.fire();
}
// {{SQL CARBON EDIT}}
// Hot exit
//const hotExitMode = configuration && configuration.files ? configuration.files.hotExit : HotExitConfiguration.ON_EXIT;
const hotExitMode = HotExitConfiguration.OFF;
const hotExitMode = configuration && configuration.files ? configuration.files.hotExit : HotExitConfiguration.ON_EXIT;
if (hotExitMode === HotExitConfiguration.OFF || hotExitMode === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
this.configuredHotExit = hotExitMode;
} else {
@@ -409,10 +425,14 @@ export abstract class TextFileService implements ITextFileService {
const filesToSave: URI[] = [];
const untitledToSave: URI[] = [];
toSave.forEach(s => {
if (s.scheme === Schemas.file) {
filesToSave.push(s);
} else if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) {
// TODO@remote
// if (s.scheme === Schemas.file) {
// filesToSave.push(s);
// } else
if ((Array.isArray(arg1) || arg1 === true /* includeUntitled */) && s.scheme === UNTITLED_SCHEMA) {
untitledToSave.push(s);
} else {
filesToSave.push(s);
}
});
@@ -619,12 +639,14 @@ export abstract class TextFileService implements ITextFileService {
}
private suggestFileName(untitledResource: URI): string {
const root = this.historyService.getLastActiveWorkspaceRoot();
if (root) {
return URI.file(paths.join(root.fsPath, this.untitledEditorService.suggestFileName(untitledResource))).fsPath;
const untitledFileName = this.untitledEditorService.suggestFileName(untitledResource);
const lastActiveFile = this.historyService.getLastActiveFile();
if (lastActiveFile) {
return URI.file(paths.join(paths.dirname(lastActiveFile.fsPath), untitledFileName)).fsPath;
}
return this.untitledEditorService.suggestFileName(untitledResource);
return untitledFileName;
}
public revert(resource: URI, options?: IRevertOptions): TPromise<boolean> {
@@ -714,4 +736,4 @@ export abstract class TextFileService implements ITextFileService {
// Clear all caches
this._models.clear();
}
}
}

View File

@@ -100,6 +100,13 @@ export interface IResult {
success?: boolean;
}
/* __GDPR__FRAGMENT__
"IAutoSaveConfiguration" : {
"autoSaveDelay" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"autoSaveFocusChange": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"autoSaveApplicationChange": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
export interface IAutoSaveConfiguration {
autoSaveDelay: number;
autoSaveFocusChange: boolean;