mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-24 18:49:00 +01:00
Merge branch 'master' into dbaeumer/TS2.0
This commit is contained in:
@@ -13,6 +13,7 @@ import product from 'vs/platform/product';
|
||||
import pkg from 'vs/platform/package';
|
||||
import {ExtHostFileSystemEventService} from 'vs/workbench/api/node/extHostFileSystemEventService';
|
||||
import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments';
|
||||
import {ExtHostDocumentSaveParticipant} from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
|
||||
import {ExtHostConfiguration} from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import {ExtHostWorkspace} from 'vs/workbench/api/node/extHostWorkspace';
|
||||
@@ -102,6 +103,7 @@ export class ExtHostAPIImplementation {
|
||||
|
||||
const extHostHeapMonitor = col.define(ExtHostContext.ExtHostHeapService).set<ExtHostHeapService>(new ExtHostHeapService());
|
||||
const extHostDocuments = col.define(ExtHostContext.ExtHostDocuments).set<ExtHostDocuments>(new ExtHostDocuments(threadService));
|
||||
const estHostDocumentSaveParticipant = col.define(ExtHostContext.ExtHostDocumentSaveParticipant).set<ExtHostDocumentSaveParticipant>(new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
|
||||
const extHostEditors = col.define(ExtHostContext.ExtHostEditors).set<ExtHostEditors>(new ExtHostEditors(threadService, extHostDocuments));
|
||||
const extHostCommands = col.define(ExtHostContext.ExtHostCommands).set<ExtHostCommands>(new ExtHostCommands(threadService, extHostEditors));
|
||||
const extHostConfiguration = col.define(ExtHostContext.ExtHostConfiguration).set<ExtHostConfiguration>(new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration)));
|
||||
@@ -323,6 +325,9 @@ export class ExtHostAPIImplementation {
|
||||
onDidSaveTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
onWillSaveTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return estHostDocumentSaveParticipant.onWillSaveTextDocumentEvent(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeConfiguration: (listener: () => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => {
|
||||
return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables);
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ import {MainProcessTextMateSyntax} from 'vs/editor/node/textMate/TMSyntax';
|
||||
import {MainProcessTextMateSnippet} from 'vs/editor/node/textMate/TMSnippets';
|
||||
import {JSONValidationExtensionPoint} from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint';
|
||||
import {LanguageConfigurationFileHandler} from 'vs/editor/node/languageConfiguration';
|
||||
import {SaveParticipant} from './mainThreadSaveParticipant';
|
||||
|
||||
export class ExtHostContribution implements IWorkbenchContribution {
|
||||
|
||||
@@ -89,6 +90,7 @@ export class ExtHostContribution implements IWorkbenchContribution {
|
||||
create(LanguageConfigurationFileHandler);
|
||||
create(MainThreadFileSystemEventService);
|
||||
create(MainThreadHeapService);
|
||||
create(SaveParticipant);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +232,10 @@ export abstract class ExtHostDocumentsShape {
|
||||
$acceptModelChanged(strURL: string, events: editorCommon.IModelContentChangedEvent2[]): void { throw ni(); }
|
||||
}
|
||||
|
||||
export abstract class ExtHostDocumentSaveParticipantShape {
|
||||
$participateInSave(resource: URI): TPromise<any[]> { throw ni(); }
|
||||
}
|
||||
|
||||
export interface ITextEditorAddData {
|
||||
id: string;
|
||||
document: URI;
|
||||
@@ -342,6 +346,7 @@ export const ExtHostContext = {
|
||||
ExtHostConfiguration: createExtId<ExtHostConfigurationShape>('ExtHostConfiguration', ExtHostConfigurationShape),
|
||||
ExtHostDiagnostics: createExtId<ExtHostDiagnosticsShape>('ExtHostDiagnostics', ExtHostDiagnosticsShape),
|
||||
ExtHostDocuments: createExtId<ExtHostDocumentsShape>('ExtHostDocuments', ExtHostDocumentsShape),
|
||||
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant', ExtHostDocumentSaveParticipantShape),
|
||||
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors', ExtHostEditorsShape),
|
||||
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService', ExtHostFileSystemEventServiceShape),
|
||||
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor', ExtHostHeapServiceShape),
|
||||
|
||||
114
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
Normal file
114
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event from 'vs/base/common/event';
|
||||
import CallbackList from 'vs/base/common/callbackList';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import {sequence} from 'vs/base/common/async';
|
||||
import {illegalState} from 'vs/base/common/errors';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {MainThreadWorkspaceShape, ExtHostDocumentSaveParticipantShape} from 'vs/workbench/api/node/extHost.protocol';
|
||||
import {TextEdit} from 'vs/workbench/api/node/extHostTypes';
|
||||
import {fromRange} from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import {IResourceEdit} from 'vs/editor/common/services/bulkEdit';
|
||||
import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments';
|
||||
|
||||
|
||||
export class ExtHostDocumentSaveParticipant extends ExtHostDocumentSaveParticipantShape {
|
||||
|
||||
private _documents: ExtHostDocuments;
|
||||
private _workspace: MainThreadWorkspaceShape;
|
||||
private _callbacks = new CallbackList();
|
||||
|
||||
constructor(documents: ExtHostDocuments, workspace: MainThreadWorkspaceShape) {
|
||||
super();
|
||||
this._documents = documents;
|
||||
this._workspace = workspace;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._callbacks.dispose();
|
||||
}
|
||||
|
||||
get onWillSaveTextDocumentEvent(): Event<vscode.TextDocumentWillSaveEvent> {
|
||||
return (listener, thisArg, disposables) => {
|
||||
this._callbacks.add(listener, thisArg);
|
||||
const result = { dispose: () => this._callbacks.remove(listener, thisArg) };
|
||||
if (Array.isArray(disposables)) {
|
||||
disposables.push(result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
$participateInSave(resource: URI): TPromise<any[]> {
|
||||
const entries = this._callbacks.entries();
|
||||
|
||||
return sequence(entries.map(([fn, thisArg]) => {
|
||||
return () => {
|
||||
const document = this._documents.getDocumentData(resource).document;
|
||||
return this._deliverEventAsync(fn, thisArg, document);
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
private _deliverEventAsync(listener: Function, thisArg: any, document: vscode.TextDocument): TPromise<any> {
|
||||
|
||||
const promises: TPromise<any | vscode.TextEdit[]>[] = [];
|
||||
|
||||
const {version} = document;
|
||||
|
||||
const event = Object.freeze(<vscode.TextDocumentWillSaveEvent> {
|
||||
document,
|
||||
waitUntil(p: Thenable<any | vscode.TextEdit[]>) {
|
||||
if (Object.isFrozen(promises)) {
|
||||
throw illegalState('waitUntil can not be called async');
|
||||
}
|
||||
promises.push(TPromise.wrap(p));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// fire event
|
||||
listener.apply(thisArg, [event]);
|
||||
} finally {
|
||||
// freeze promises after event call
|
||||
Object.freeze(promises);
|
||||
|
||||
return TPromise.join(promises).then(values => {
|
||||
|
||||
const edits: IResourceEdit[] = [];
|
||||
for (const value of values) {
|
||||
if (Array.isArray(value) && (<vscode.TextEdit[]> value).every(e => e instanceof TextEdit)) {
|
||||
for (const {newText, range} of value) {
|
||||
edits.push({
|
||||
resource: <URI>document.uri,
|
||||
range: fromRange(range),
|
||||
newText
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply edits iff any and iff document
|
||||
// didn't change somehow in the meantime
|
||||
if (edits.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (version === document.version) {
|
||||
return this._workspace.$applyWorkspaceEdit(edits);
|
||||
}
|
||||
|
||||
// TODO@joh bubble this to listener?
|
||||
return new Error('ignoring change because of concurrent edits');
|
||||
|
||||
}, err => {
|
||||
// ignore error
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import {notImplemented} from 'vs/base/common/errors';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ITelemetryService, ITelemetryInfo} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {ITelemetryService, ITelemetryInfo, ITelemetryExperiments} from 'vs/platform/telemetry/common/telemetry';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {MainContext, MainThreadTelemetryShape} from './extHost.protocol';
|
||||
|
||||
@@ -26,6 +26,10 @@ export class RemoteTelemetryService implements ITelemetryService {
|
||||
throw notImplemented();
|
||||
}
|
||||
|
||||
getExperiments(): ITelemetryExperiments {
|
||||
throw notImplemented();
|
||||
}
|
||||
|
||||
getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return this._proxy.$getTelemetryInfo();
|
||||
}
|
||||
|
||||
122
src/vs/workbench/api/node/mainThreadSaveParticipant.ts
Normal file
122
src/vs/workbench/api/node/mainThreadSaveParticipant.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
|
||||
import {TPromise} from 'vs/base/common/winjs.base';
|
||||
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
|
||||
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
|
||||
import {ISaveParticipant, ITextFileEditorModel} from 'vs/workbench/parts/files/common/files';
|
||||
import {IFilesConfiguration} from 'vs/platform/files/common/files';
|
||||
import {IPosition, IModel} from 'vs/editor/common/editorCommon';
|
||||
import {Selection} from 'vs/editor/common/core/selection';
|
||||
import {trimTrailingWhitespace} from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
|
||||
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
|
||||
import {TextFileEditorModel} from 'vs/workbench/parts/files/common/editors/textFileEditorModel';
|
||||
import {ExtHostContext, ExtHostDocumentSaveParticipantShape} from './extHost.protocol';
|
||||
|
||||
// TODO@joh move this to the extension host
|
||||
class TrimWhitespaceParticipant {
|
||||
|
||||
private trimTrailingWhitespace: boolean = false;
|
||||
private toUnbind: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private configurationService: IConfigurationService,
|
||||
@ICodeEditorService private codeEditorService: ICodeEditorService
|
||||
) {
|
||||
this.registerListeners();
|
||||
this.onConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toUnbind = dispose(this.toUnbind);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(e.config)));
|
||||
}
|
||||
|
||||
private onConfigurationChange(configuration: IFilesConfiguration): void {
|
||||
this.trimTrailingWhitespace = configuration && configuration.files && configuration.files.trimTrailingWhitespace;
|
||||
}
|
||||
|
||||
public participate(model: ITextFileEditorModel, env: { isAutoSaved: boolean }): any {
|
||||
if (this.trimTrailingWhitespace) {
|
||||
this.doTrimTrailingWhitespace(model.textEditorModel, env.isAutoSaved);
|
||||
}
|
||||
}
|
||||
|
||||
private doTrimTrailingWhitespace(model: IModel, isAutoSaved: boolean): void {
|
||||
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
|
||||
const cursors: IPosition[] = [];
|
||||
|
||||
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
|
||||
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
|
||||
if (model.isAttachedToEditor()) {
|
||||
const allEditors = this.codeEditorService.listCodeEditors();
|
||||
for (let i = 0, len = allEditors.length; i < len; i++) {
|
||||
const editor = allEditors[i];
|
||||
const editorModel = editor.getModel();
|
||||
|
||||
if (!editorModel) {
|
||||
continue; // empty editor
|
||||
}
|
||||
|
||||
if (model === editorModel) {
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
cursors.push(...prevSelection.map(s => {
|
||||
return {
|
||||
lineNumber: s.positionLineNumber,
|
||||
column: s.positionColumn
|
||||
};
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ops = trimTrailingWhitespace(model, cursors);
|
||||
if (!ops.length) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, ops, (edits) => prevSelection);
|
||||
}
|
||||
}
|
||||
|
||||
// The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace
|
||||
export class SaveParticipant implements ISaveParticipant {
|
||||
|
||||
private _mainThreadSaveParticipant: TrimWhitespaceParticipant;
|
||||
private _extHostSaveParticipant: ExtHostDocumentSaveParticipantShape;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@IThreadService threadService: IThreadService
|
||||
) {
|
||||
this._mainThreadSaveParticipant = new TrimWhitespaceParticipant(configurationService, codeEditorService);
|
||||
this._extHostSaveParticipant = threadService.get(ExtHostContext.ExtHostDocumentSaveParticipant);
|
||||
|
||||
// Hook into model
|
||||
TextFileEditorModel.setSaveParticipant(this);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._mainThreadSaveParticipant.dispose();
|
||||
}
|
||||
|
||||
participate(model: ITextFileEditorModel, env: { isAutoSaved: boolean }): TPromise<any> {
|
||||
try {
|
||||
this._mainThreadSaveParticipant.participate(model, env);
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
return this._extHostSaveParticipant.$participateInSave(model.getResource());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user